mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
484 lines
22 KiB
TypeScript
484 lines
22 KiB
TypeScript
import {
|
|
arrayFrom,
|
|
compareStringsCaseSensitive,
|
|
Debug,
|
|
DocumentPositionMapper,
|
|
emptyArray,
|
|
identity,
|
|
identitySourceMapConsumer,
|
|
isString,
|
|
noop,
|
|
SourceMapper,
|
|
toSorted,
|
|
} from "./_namespaces/ts.js";
|
|
import {
|
|
AutoImportProviderProject,
|
|
AuxiliaryProject,
|
|
ConfiguredProject,
|
|
isBackgroundProject,
|
|
isConfiguredProject,
|
|
LogLevel,
|
|
Project,
|
|
ProjectKind,
|
|
ProjectService,
|
|
ScriptInfo,
|
|
SourceMapFileWatcher,
|
|
TextStorage,
|
|
} from "./_namespaces/ts.server.js";
|
|
import { LoggerWithInMemoryLogs } from "./tsserverLogger.js";
|
|
|
|
interface ProjectData {
|
|
projectStateVersion: Project["projectStateVersion"];
|
|
projectProgramVersion: Project["projectProgramVersion"];
|
|
dirty: Project["dirty"];
|
|
initialLoadPending: Project["initialLoadPending"];
|
|
isClosed: ReturnType<Project["isClosed"]>;
|
|
isOrphan: ReturnType<Project["isOrphan"]>;
|
|
noOpenRef: boolean;
|
|
deferredClose: ConfiguredProject["deferredClose"];
|
|
documentPositionMappers: SourceMapper["documentPositionMappers"];
|
|
autoImportProviderHost: Project["autoImportProviderHost"];
|
|
noDtsResolutionProject: Project["noDtsResolutionProject"];
|
|
originalConfiguredProjects: Project["originalConfiguredProjects"];
|
|
}
|
|
|
|
interface SourceMapFileWatcherData {
|
|
sourceInfos: SourceMapFileWatcher["sourceInfos"];
|
|
}
|
|
|
|
interface ScriptInfoData {
|
|
open: ReturnType<ScriptInfo["isScriptOpen"]>;
|
|
version: ReturnType<TextStorage["getVersion"]>;
|
|
pendingReloadFromDisk: TextStorage["pendingReloadFromDisk"];
|
|
deferredDelete: ScriptInfo["deferredDelete"];
|
|
sourceMapFilePath: Exclude<ScriptInfo["sourceMapFilePath"], SourceMapFileWatcher> | SourceMapFileWatcherData | undefined;
|
|
declarationInfoPath: ScriptInfo["declarationInfoPath"];
|
|
sourceInfos: ScriptInfo["sourceInfos"];
|
|
documentPositionMapper: ScriptInfo["documentPositionMapper"];
|
|
containingProjects: Set<Project>;
|
|
}
|
|
|
|
enum Diff {
|
|
None = "",
|
|
New = " *new*",
|
|
Change = " *changed*",
|
|
Deleted = " *deleted*",
|
|
}
|
|
|
|
type StatePropertyLog = string | string[];
|
|
type StateItemLog = [string, StatePropertyLog[]];
|
|
|
|
export function patchServiceForStateBaseline(service: ProjectService): void {
|
|
if (!service.logger.isTestLogger || !service.logger.hasLevel(LogLevel.verbose)) return;
|
|
if (service.baseline !== noop) return; // Already patched
|
|
|
|
const projects = new Map<Project, ProjectData>();
|
|
const scriptInfos = new Map<ScriptInfo, ScriptInfoData>();
|
|
const documentPositionMappers = new Map<DocumentPositionMapper, DocumentPositionMapper>();
|
|
let nextDocumentPositionMapperId = 1;
|
|
const mapperToId = new Map<DocumentPositionMapper, number>();
|
|
const logger = service.logger as LoggerWithInMemoryLogs;
|
|
service.baseline = title => {
|
|
const currentMappers = new Set<DocumentPositionMapper>();
|
|
const projectLogs = baselineProjects(currentMappers);
|
|
const infoLogs = baselineScriptInfos(currentMappers);
|
|
currentMappers.delete(identitySourceMapConsumer);
|
|
const mapperLogs = baselineDocumentPositionMappers(currentMappers);
|
|
if (projectLogs?.length || infoLogs?.length || mapperLogs?.length) {
|
|
if (title) logger.log(title);
|
|
sendLogsToLogger("Projects::", projectLogs);
|
|
sendLogsToLogger("ScriptInfos::", infoLogs);
|
|
sendLogsToLogger("DocumentPositionMappers::", mapperLogs);
|
|
}
|
|
};
|
|
|
|
function sendLogsToLogger(title: string, logs: StateItemLog[] | undefined) {
|
|
if (!logs) return;
|
|
logger.log(title);
|
|
toSorted(logs, (a, b) => compareStringsCaseSensitive(a[0], b[0]))
|
|
.forEach(([title, propertyLogs]) => {
|
|
logger.log(title);
|
|
propertyLogs.forEach(p => isString(p) ? logger.log(p) : p.forEach(s => logger.log(s)));
|
|
});
|
|
logger.log("");
|
|
}
|
|
|
|
function baselineProjects(currentMappers: Set<DocumentPositionMapper>) {
|
|
const autoImportProviderProjects = [] as AutoImportProviderProject[];
|
|
const auxiliaryProjects = [] as AuxiliaryProject[];
|
|
const orphanConfiguredProjects = service.getOrphanConfiguredProjects(
|
|
/*toRetainConfiguredProjects*/ undefined,
|
|
/*openFilesWithRetainedConfiguredProject*/ undefined,
|
|
/*externalProjectsRetainingConfiguredProjects*/ undefined,
|
|
);
|
|
const noOpenRef = (project: Project) => isConfiguredProject(project) && (project.isClosed() || orphanConfiguredProjects.has(project));
|
|
return baselineState(
|
|
[service.externalProjects, service.configuredProjects, service.inferredProjects, autoImportProviderProjects, auxiliaryProjects],
|
|
projects,
|
|
(logs, project, data) => {
|
|
if (project.autoImportProviderHost) autoImportProviderProjects.push(project.autoImportProviderHost);
|
|
if (project.noDtsResolutionProject) auxiliaryProjects.push(project.noDtsResolutionProject);
|
|
let projectDiff = newOrDeleted(project, projects, data);
|
|
if (projectDiff !== Diff.Deleted) getSourceMapper(project)?.documentPositionMappers.forEach(mapper => currentMappers.add(mapper));
|
|
const projectPropertyLogs = [] as string[];
|
|
projectDiff = printProperty(PrintPropertyWhen.Always, data, "projectStateVersion", project.projectStateVersion, projectDiff, projectPropertyLogs);
|
|
projectDiff = printProperty(PrintPropertyWhen.Always, data, "projectProgramVersion", project.projectProgramVersion, projectDiff, projectPropertyLogs);
|
|
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "dirty", project.dirty, projectDiff, projectPropertyLogs);
|
|
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "initialLoadPending", project.initialLoadPending, projectDiff, projectPropertyLogs);
|
|
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "isClosed", project.isClosed(), projectDiff, projectPropertyLogs);
|
|
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "isOrphan", !isBackgroundProject(project) && project.isOrphan(), projectDiff, projectPropertyLogs);
|
|
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "noOpenRef", noOpenRef(project), projectDiff, projectPropertyLogs);
|
|
projectDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "deferredClose", isConfiguredProject(project) && project.deferredClose, projectDiff, projectPropertyLogs);
|
|
projectDiff = printMapPropertyValue(
|
|
PrintPropertyWhen.Changed,
|
|
data?.documentPositionMappers,
|
|
"documentPositionMappers",
|
|
getSourceMapper(project)?.documentPositionMappers,
|
|
projectDiff,
|
|
projectPropertyLogs,
|
|
toStringDocumentPostionMapper,
|
|
);
|
|
projectDiff = printProperty(PrintPropertyWhen.DefinedOrChangedOrNew, data, "autoImportProviderHost", project.autoImportProviderHost, projectDiff, projectPropertyLogs, project.autoImportProviderHost ? project.autoImportProviderHost.projectName : project.autoImportProviderHost);
|
|
projectDiff = printProperty(PrintPropertyWhen.DefinedOrChangedOrNew, data, "noDtsResolutionProject", project.noDtsResolutionProject, projectDiff, projectPropertyLogs, project.noDtsResolutionProject ? project.noDtsResolutionProject.projectName : project.noDtsResolutionProject);
|
|
return printSetPropertyAndCreateStatementLog(
|
|
logs,
|
|
`${project.projectName} (${ProjectKind[project.projectKind]})`,
|
|
PrintPropertyWhen.DefinedOrChangedOrNew,
|
|
data?.originalConfiguredProjects,
|
|
"originalConfiguredProjects",
|
|
project.originalConfiguredProjects,
|
|
projectDiff,
|
|
projectPropertyLogs,
|
|
identity,
|
|
);
|
|
},
|
|
project => ({
|
|
projectStateVersion: project.projectStateVersion,
|
|
projectProgramVersion: project.projectProgramVersion,
|
|
dirty: project.dirty,
|
|
initialLoadPending: project.initialLoadPending,
|
|
isClosed: project.isClosed(),
|
|
isOrphan: !isBackgroundProject(project) && project.isOrphan(),
|
|
noOpenRef: noOpenRef(project),
|
|
deferredClose: isConfiguredProject(project) && project.deferredClose,
|
|
autoImportProviderHost: project.autoImportProviderHost,
|
|
noDtsResolutionProject: project.noDtsResolutionProject,
|
|
originalConfiguredProjects: project.originalConfiguredProjects && new Set(project.originalConfiguredProjects),
|
|
documentPositionMappers: new Map(getSourceMapper(project)?.documentPositionMappers),
|
|
}),
|
|
);
|
|
}
|
|
|
|
function baselineScriptInfos(currentMappers: Set<DocumentPositionMapper>) {
|
|
return baselineState(
|
|
[service.filenameToScriptInfo],
|
|
scriptInfos,
|
|
(logs, info, data) => {
|
|
let infoDiff = newOrDeleted(info, scriptInfos, data);
|
|
if (infoDiff !== Diff.Deleted && info.documentPositionMapper) currentMappers.add(info.documentPositionMapper);
|
|
const infoPropertyLogs = [] as string[];
|
|
const isOpen = info.isScriptOpen();
|
|
infoDiff = printProperty(PrintPropertyWhen.Changed, data, "open", isOpen, infoDiff, infoPropertyLogs);
|
|
infoDiff = printProperty(PrintPropertyWhen.Always, data, "version", info.textStorage.getVersion(), infoDiff, infoPropertyLogs);
|
|
infoDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "pendingReloadFromDisk", info.textStorage.pendingReloadFromDisk, infoDiff, infoPropertyLogs);
|
|
infoDiff = printProperty(PrintPropertyWhen.TruthyOrChangedOrNew, data, "deferredDelete", info.deferredDelete, infoDiff, infoPropertyLogs);
|
|
infoDiff = printScriptInfoSourceMapFilePath(data, info, infoDiff, infoPropertyLogs);
|
|
infoDiff = printProperty(PrintPropertyWhen.DefinedOrChangedOrNew, data, "declarationInfoPath", info.declarationInfoPath, infoDiff, infoPropertyLogs);
|
|
infoDiff = printSetPropertyValueWorker(PrintPropertyWhen.DefinedOrChangedOrNew, data?.sourceInfos, "sourceInfos", info.sourceInfos, infoDiff, infoPropertyLogs, identity);
|
|
infoDiff = printProperty(PrintPropertyWhen.DefinedOrChangedOrNew, data, "documentPositionMapper", info.documentPositionMapper, infoDiff, infoPropertyLogs, info.documentPositionMapper ? toStringDocumentPostionMapper(info.documentPositionMapper) : undefined);
|
|
let defaultProject: Project | undefined;
|
|
try {
|
|
if (isOpen) defaultProject = info.getDefaultProject();
|
|
}
|
|
catch (e) {
|
|
Debug.assert(e.message === "No Project.");
|
|
}
|
|
return printSetPropertyAndCreateStatementLog(
|
|
logs,
|
|
`${info.fileName}${info.isDynamic ? " (Dynamic)" : ""}${isOpen ? " (Open)" : ""}`,
|
|
PrintPropertyWhen.Always,
|
|
data?.containingProjects,
|
|
"containingProjects",
|
|
new Set(info.containingProjects),
|
|
infoDiff,
|
|
infoPropertyLogs,
|
|
project => `${project.projectName}${defaultProject === project ? " *default*" : ""}`,
|
|
);
|
|
},
|
|
info => ({
|
|
open: info.isScriptOpen(),
|
|
version: info.textStorage.getVersion(),
|
|
pendingReloadFromDisk: info.textStorage.pendingReloadFromDisk,
|
|
sourceMapFilePath: info.sourceMapFilePath && !isString(info.sourceMapFilePath) ?
|
|
{ original: info.sourceMapFilePath, sourceInfos: new Set(info.sourceMapFilePath.sourceInfos) } :
|
|
info.sourceMapFilePath,
|
|
declarationInfoPath: info.declarationInfoPath,
|
|
sourceInfos: info.sourceInfos && new Set(info.sourceInfos),
|
|
documentPositionMapper: info.documentPositionMapper,
|
|
containingProjects: new Set(info.containingProjects),
|
|
deferredDelete: info.deferredDelete,
|
|
}),
|
|
);
|
|
}
|
|
|
|
function baselineDocumentPositionMappers(currentMappers: Set<DocumentPositionMapper>) {
|
|
const result = baselineState(
|
|
[currentMappers],
|
|
documentPositionMappers,
|
|
(logs, mapper, data) => {
|
|
const mapperDiff = newOrDeleted(mapper, documentPositionMappers, data);
|
|
logs.push([
|
|
`${toStringDocumentPostionMapper(mapper)}${mapperDiff}`,
|
|
emptyArray,
|
|
]);
|
|
return mapperDiff;
|
|
},
|
|
identity,
|
|
);
|
|
mapperToId.forEach((_id, mapper) => {
|
|
if (!documentPositionMappers.has(mapper)) mapperToId.delete(mapper);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
function baselineState<T, Data>(
|
|
currentCaches: (T[] | Map<any, T> | Set<T>)[],
|
|
dataMap: Map<T, Data>,
|
|
printWorker: (logs: StateItemLog[], current: T, data: Data | undefined) => Diff,
|
|
toData: (current: T) => Data,
|
|
) {
|
|
const logs = [] as StateItemLog[];
|
|
let hasChange = false;
|
|
const currentSet = new Set<T>();
|
|
currentCaches.forEach(currentCache => currentCache.forEach(printCurrent));
|
|
if (currentSet.size !== dataMap.size) {
|
|
for (const [key, data] of arrayFrom(dataMap.entries())) {
|
|
if (!currentSet.has(key)) {
|
|
dataMap.delete(key);
|
|
printWorker(logs, key, data);
|
|
}
|
|
}
|
|
hasChange = true;
|
|
}
|
|
return hasChange ? logs : undefined;
|
|
|
|
function printCurrent(current: T) {
|
|
if (!current) return;
|
|
currentSet.add(current);
|
|
if (printWorker(logs, current, dataMap.get(current))) {
|
|
hasChange = true;
|
|
dataMap.set(current, toData(current));
|
|
}
|
|
}
|
|
}
|
|
|
|
function newOrDeleted<T, Data>(current: T, dataMap: Map<T, Data>, data: Data | undefined) {
|
|
return !data ? Diff.New : !dataMap.has(current) ? Diff.Deleted : Diff.None;
|
|
}
|
|
|
|
enum PrintPropertyWhen {
|
|
Always,
|
|
TruthyOrChangedOrNew,
|
|
Changed,
|
|
DefinedOrChangedOrNew,
|
|
}
|
|
function printProperty<Data, Key extends keyof Data & string>(
|
|
printWhen: PrintPropertyWhen,
|
|
data: Data | undefined,
|
|
key: Key,
|
|
value: Data[Key],
|
|
dataDiff: Diff,
|
|
propertyLogs: StatePropertyLog[],
|
|
stringValue?: Data[Key] | string,
|
|
) {
|
|
return printPropertyWorker(
|
|
printWhen,
|
|
value,
|
|
!!data && data[key] !== value ? Diff.Change : Diff.None,
|
|
dataDiff,
|
|
propertyLogs,
|
|
`${key}: ${stringValue === undefined ? value : stringValue}`,
|
|
);
|
|
}
|
|
|
|
function printSetPropertyValueWorker<T>(
|
|
printWhen: PrintPropertyWhen.Always | PrintPropertyWhen.DefinedOrChangedOrNew,
|
|
dataValue: Set<T> | undefined,
|
|
propertyName: string,
|
|
propertyValue: Set<T> | undefined,
|
|
dataDiff: Diff,
|
|
propertyLogs: StatePropertyLog[],
|
|
toStringPropertyValue: (v: T) => string,
|
|
) {
|
|
return printMapOrSetPropertyValueWorker(
|
|
printWhen,
|
|
dataValue,
|
|
propertyName,
|
|
propertyValue,
|
|
dataDiff,
|
|
propertyLogs,
|
|
value => dataDiff !== Diff.New && !dataValue?.has(value) ? Diff.New : Diff.None,
|
|
toStringPropertyValue,
|
|
);
|
|
}
|
|
|
|
function printMapPropertyValue<T>(
|
|
printWhen: PrintPropertyWhen.Changed,
|
|
dataValue: Map<string, T> | undefined,
|
|
propertyName: string,
|
|
propertyValue: Map<string, T> | undefined,
|
|
dataDiff: Diff,
|
|
propertyLogs: StatePropertyLog[],
|
|
toStringPropertyValue: (v: T) => string,
|
|
) {
|
|
return printMapOrSetPropertyValueWorker(
|
|
printWhen,
|
|
dataValue,
|
|
propertyName,
|
|
propertyValue,
|
|
dataDiff,
|
|
propertyLogs,
|
|
(value, key) =>
|
|
dataDiff !== Diff.New && dataValue?.get(key) !== value ?
|
|
!dataValue?.has(key) ? Diff.New : Diff.Change : Diff.None,
|
|
(value, key) => `${key}: ${toStringPropertyValue(value)}`,
|
|
);
|
|
}
|
|
|
|
type MapOrSetPropertyKey<T> = T extends Map<infer U, any> ? U : T extends Set<infer U> ? U : never;
|
|
type MapOrSetPropertyValue<T> = T extends Map<any, infer U> ? U : T extends Set<infer U> ? U : never;
|
|
function printMapOrSetPropertyValueWorker<T extends Map<string, any> | Set<any>>(
|
|
printWhen: PrintPropertyWhen.Always | PrintPropertyWhen.DefinedOrChangedOrNew | PrintPropertyWhen.Changed,
|
|
dataValue: T | undefined,
|
|
propertyName: string,
|
|
propertyValue: T | undefined,
|
|
dataDiff: Diff,
|
|
propertyLogs: StatePropertyLog[],
|
|
getPropertyDiff: (value: MapOrSetPropertyValue<T>, key: MapOrSetPropertyKey<T>) => Diff,
|
|
toStringPropertyValue: (value: MapOrSetPropertyValue<T>, key: MapOrSetPropertyKey<T>) => string,
|
|
) {
|
|
// Print changes
|
|
const mapPropertyLogs = [] as string[];
|
|
let mapPropertyDiff = Diff.None;
|
|
propertyValue?.forEach((p, key) =>
|
|
mapPropertyDiff = printPropertyWorker(
|
|
PrintPropertyWhen.Always,
|
|
p,
|
|
getPropertyDiff(p, key),
|
|
mapPropertyDiff,
|
|
mapPropertyLogs,
|
|
` ${toStringPropertyValue(p, key)}`,
|
|
)
|
|
);
|
|
dataValue?.forEach((p, key) => {
|
|
if (propertyValue?.has(key)) return;
|
|
mapPropertyDiff = Diff.Change;
|
|
mapPropertyLogs.push(` ${toStringPropertyValue(p, key)}${Diff.Deleted}`);
|
|
});
|
|
dataDiff = printPropertyWorker(
|
|
printWhen,
|
|
propertyValue,
|
|
!!mapPropertyDiff ? Diff.Change : Diff.None,
|
|
dataDiff,
|
|
propertyLogs,
|
|
`${propertyName}: ${propertyValue?.size || 0}`,
|
|
);
|
|
if (printWhen !== PrintPropertyWhen.Changed || mapPropertyDiff) propertyLogs.push(mapPropertyLogs);
|
|
return dataDiff;
|
|
}
|
|
|
|
function printPropertyWorker(
|
|
printWhen: PrintPropertyWhen,
|
|
value: any,
|
|
propertyDiff: Diff,
|
|
dataDiff: Diff,
|
|
propertyLogs: StatePropertyLog[],
|
|
stringValue: string,
|
|
) {
|
|
const result = !dataDiff && propertyDiff ? Diff.Change : dataDiff;
|
|
switch (printWhen) {
|
|
case PrintPropertyWhen.Changed:
|
|
if (!propertyDiff) return result;
|
|
break;
|
|
case PrintPropertyWhen.TruthyOrChangedOrNew:
|
|
if (!value && !propertyDiff) return result;
|
|
break;
|
|
case PrintPropertyWhen.Always:
|
|
break;
|
|
case PrintPropertyWhen.DefinedOrChangedOrNew:
|
|
if (value === undefined && !propertyDiff) return result;
|
|
break;
|
|
default:
|
|
Debug.assertNever(printWhen);
|
|
}
|
|
propertyLogs.push(` ${stringValue}${propertyDiff}`);
|
|
return result;
|
|
}
|
|
|
|
function printSetPropertyAndCreateStatementLog<T>(
|
|
logs: StateItemLog[],
|
|
header: string,
|
|
printWhen: PrintPropertyWhen.Always | PrintPropertyWhen.DefinedOrChangedOrNew,
|
|
dataValue: Set<T> | undefined,
|
|
propertyName: string,
|
|
propertyValue: Set<T> | undefined,
|
|
dataDiff: Diff,
|
|
propertyLogs: StatePropertyLog[],
|
|
toStringPropertyValue: (v: T) => string,
|
|
) {
|
|
dataDiff = printSetPropertyValueWorker(
|
|
printWhen,
|
|
dataValue,
|
|
propertyName,
|
|
propertyValue,
|
|
dataDiff,
|
|
propertyLogs,
|
|
toStringPropertyValue,
|
|
);
|
|
logs.push([
|
|
`${header}${dataDiff}`,
|
|
propertyLogs,
|
|
]);
|
|
return dataDiff;
|
|
}
|
|
|
|
function printScriptInfoSourceMapFilePath(
|
|
data: ScriptInfoData | undefined,
|
|
info: ScriptInfo,
|
|
infoDiff: Diff,
|
|
infoPropertyLogs: StatePropertyLog[],
|
|
) {
|
|
return !info.sourceMapFilePath || isString(info.sourceMapFilePath) ?
|
|
printProperty(
|
|
PrintPropertyWhen.DefinedOrChangedOrNew,
|
|
data,
|
|
"sourceMapFilePath",
|
|
info.sourceMapFilePath,
|
|
infoDiff,
|
|
infoPropertyLogs,
|
|
) :
|
|
printSetPropertyValueWorker(
|
|
PrintPropertyWhen.DefinedOrChangedOrNew,
|
|
data?.sourceMapFilePath && !isString(data?.sourceMapFilePath) ? data.sourceMapFilePath.sourceInfos : undefined,
|
|
"sourceMapFilePath sourceInfos",
|
|
info.sourceMapFilePath.sourceInfos,
|
|
infoDiff,
|
|
infoPropertyLogs,
|
|
identity,
|
|
);
|
|
}
|
|
|
|
function toStringDocumentPostionMapper(documentPositionMapper: DocumentPositionMapper) {
|
|
if (documentPositionMapper === identitySourceMapConsumer) return "identitySourceMapConsumer";
|
|
let id = mapperToId.get(documentPositionMapper);
|
|
if (!id) mapperToId.set(documentPositionMapper, id = nextDocumentPositionMapperId++);
|
|
return `DocumentPositionMapper${id}`;
|
|
}
|
|
|
|
function getSourceMapper(project: Project) {
|
|
return project.projectKind !== ProjectKind.AutoImportProvider ?
|
|
project.getLanguageService(/*ensureSynchronized*/ false)?.getSourceMapper() :
|
|
undefined;
|
|
}
|
|
}
|