diff --git a/packages/typescript/lib/plugins/syntactic.ts b/packages/typescript/lib/plugins/syntactic.ts index 04dceb67..d3616801 100644 --- a/packages/typescript/lib/plugins/syntactic.ts +++ b/packages/typescript/lib/plugins/syntactic.ts @@ -12,7 +12,39 @@ import { convertOutliningSpan, convertTextChange } from '../utils/lspConverters'; -import { getLanguageService } from '../syntacticLanguageService'; +import { createSyntaxOnlyService } from '../syntaxOnlyService'; +import type * as ts from 'typescript'; + +const snapshots = new WeakMap(); + +let created: ReturnType | undefined; + +export function getLanguageServiceByDocument(ts: typeof import('typescript'), document: TextDocument) { + if (!created) { + created = createSyntaxOnlyService(ts, true); + } + let cache = snapshots.get(document); + if (!cache || cache[0] !== document.version) { + const snapshot = ts.ScriptSnapshot.fromString(document.getText()); + cache = [document.version, snapshot]; + snapshots.set(document, cache); + created.updateFile( + document.uri, + cache[1], + document.languageId === 'javascript' + ? ts.ScriptKind.JS + : document.languageId === 'javascriptreact' + ? ts.ScriptKind.JSX + : document.languageId === 'typescriptreact' + ? ts.ScriptKind.TSX + : ts.ScriptKind.TS + ); + } + return { + languageService: created.languageService, + fileName: document.uri, + }; +} export function create( ts: typeof import('typescript'), @@ -141,17 +173,3 @@ export function create( }, }; } - -import type * as ts from 'typescript'; - -const snapshots = new WeakMap(); - -export function getLanguageServiceByDocument(ts: typeof import('typescript'), document: TextDocument) { - let cache = snapshots.get(document); - if (!cache || cache[0] !== document.version) { - const snapshot = ts.ScriptSnapshot.fromString(document.getText()); - cache = [document.version, snapshot]; - snapshots.set(document, cache); - } - return getLanguageService(ts, cache[1], document.languageId, true); -} diff --git a/packages/typescript/lib/syntacticLanguageService.ts b/packages/typescript/lib/syntacticLanguageService.ts deleted file mode 100644 index e92eb97f..00000000 --- a/packages/typescript/lib/syntacticLanguageService.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type * as ts from 'typescript'; - -let currentProjectVersion = -1; -let currentFileName = ''; -let currentSnapshot: ts.IScriptSnapshot | undefined; -let languageService: ts.LanguageService | undefined; -let syntaxOnlyLanguageService: ts.LanguageService | undefined; - -const host: ts.LanguageServiceHost = { - getProjectVersion: () => currentProjectVersion.toString(), - getScriptFileNames: () => [currentFileName], - getScriptVersion: () => currentProjectVersion.toString(), - getScriptSnapshot: fileName => fileName === currentFileName ? currentSnapshot : undefined, - getCompilationSettings: () => ({}), - getCurrentDirectory: () => '', - getDefaultLibFileName: () => '', - readFile: () => undefined, - fileExists: fileName => fileName === currentFileName, -}; - -export function getLanguageService( - ts: typeof import('typescript'), - snapshot: ts.IScriptSnapshot, - languageId: string, - syntaxOnly: boolean, -) { - if (currentSnapshot !== snapshot) { - currentSnapshot = snapshot; - currentFileName = '/tmp.' + ( - languageId === 'javascript' ? 'js' : - languageId === 'typescriptreact' ? 'tsx' : - languageId === 'javascriptreact' ? 'jsx' : - 'ts' - ); - currentProjectVersion++; - } - if (syntaxOnly) { - syntaxOnlyLanguageService ??= ts.createLanguageService(host, undefined, ts.LanguageServiceMode.Syntactic); - return { - languageService: syntaxOnlyLanguageService, - fileName: currentFileName, - }; - } - else { - languageService ??= ts.createLanguageService(host); - return { - languageService, - fileName: currentFileName, - }; - } -} diff --git a/packages/typescript/lib/syntaxOnlyService.ts b/packages/typescript/lib/syntaxOnlyService.ts new file mode 100644 index 00000000..27eb5753 --- /dev/null +++ b/packages/typescript/lib/syntaxOnlyService.ts @@ -0,0 +1,49 @@ +import type * as ts from 'typescript'; + +export function createSyntaxOnlyService(ts: typeof import('typescript'), syntaxOnly: boolean) { + let currentProjectVersion = -1; + let fileNames: string[] = []; + + const scriptInfos = new Map(); + const host: ts.LanguageServiceHost = { + getProjectVersion: () => currentProjectVersion.toString(), + getScriptFileNames: () => fileNames, + getScriptVersion: () => currentProjectVersion.toString(), + getScriptSnapshot: fileName => scriptInfos.get(fileName)!.snapshot, + getScriptKind: fileName => scriptInfos.get(fileName)!.kind, + getCompilationSettings: () => ({}), + getCurrentDirectory: () => '', + getDefaultLibFileName: () => '', + readFile: () => undefined, + fileExists: fileName => scriptInfos.has(fileName), + }; + + return { + languageService: syntaxOnly + ? ts.createLanguageService(host, undefined, ts.LanguageServiceMode.Syntactic) + : ts.createLanguageService(host), + updateFile, + }; + + function updateFile(fileName: string, snapshot: ts.IScriptSnapshot, scriptKind: ts.ScriptKind) { + let scriptInfo = scriptInfos.get(fileName); + if (scriptInfo?.snapshot === snapshot && scriptInfo.kind === scriptKind) { + return; + } + currentProjectVersion++; + scriptInfo = { + snapshot, + kind: scriptKind, + version: (scriptInfo?.version ?? 0) + 1, + }; + const filesChanged = !scriptInfos.has(fileName); + scriptInfos.set(fileName, scriptInfo); + if (filesChanged) { + fileNames = [...scriptInfos.keys()]; + } + } +}