diff --git a/.vscode/launch.json b/.vscode/launch.json index 2cf53d4f..de45ae43 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,7 @@ "--extensionDevelopmentPath=${workspaceRoot}/packages/labs" ], "outFiles": [ - "${workspaceRoot}/*/*/out/**/*.js" + "${workspaceRoot}/extensions/labs/dist/**/*.js" ], }, ], diff --git a/extensions/labs/scripts/build.js b/extensions/labs/scripts/build.js index a3902c3c..375fb723 100644 --- a/extensions/labs/scripts/build.js +++ b/extensions/labs/scripts/build.js @@ -2,8 +2,9 @@ require('esbuild').context({ entryPoints: { - extension: './src/extension.js', + extension: './src/extension.ts', }, + sourcemap: true, bundle: true, metafile: process.argv.includes('--metafile'), outdir: './dist', diff --git a/extensions/labs/src/views/virtualFilesView.ts b/extensions/labs/src/views/virtualFilesView.ts index 4f86576d..ce901905 100644 --- a/extensions/labs/src/views/virtualFilesView.ts +++ b/extensions/labs/src/views/virtualFilesView.ts @@ -53,25 +53,25 @@ export function activate(context: vscode.ExtensionContext) { getTreeItem(element) { if ('virtualFile' in element) { - const uri = vscode.Uri.file(element.virtualFile.fileName).with({ scheme: element.client.name.replace(/ /g, '_').toLowerCase() }); + const uri = vscode.Uri.parse(element.virtualFile.id).with({ scheme: element.client.name.replace(/ /g, '_').toLowerCase() }); virtualUriToSourceUri.set(uri.toString(), element.sourceDocumentUri); const virtualFileUris = sourceUriToVirtualUris.get(element.sourceDocumentUri) ?? new Set(); virtualFileUris.add(uri.toString()); sourceUriToVirtualUris.set(element.sourceDocumentUri, virtualFileUris); - let label = path.basename(element.virtualFile.fileName); + let label = path.basename(element.virtualFile.id); const version = (element.virtualFile as any).version; label += ` (kind: ${element.virtualFile.kind}, version: ${version})`; return { iconPath: element.client.clientOptions.initializationOptions.codegenStack ? new vscode.ThemeIcon('debug-breakpoint') : new vscode.ThemeIcon('file'), label, collapsibleState: element.virtualFile.embeddedFiles.length ? vscode.TreeItemCollapsibleState.Expanded : vscode.TreeItemCollapsibleState.None, - resourceUri: vscode.Uri.file(element.virtualFile.fileName), + resourceUri: vscode.Uri.parse(element.virtualFile.id), command: { command: '_volar.action.openVirtualFile', title: '', - arguments: [vscode.Uri.file(element.virtualFile.fileName).with({ scheme: element.client.name.replace(/ /g, '_').toLowerCase() })], + arguments: [vscode.Uri.parse(element.virtualFile.id).with({ scheme: element.client.name.replace(/ /g, '_').toLowerCase() })], }, }; } diff --git a/packages/kit/lib/createChecker.ts b/packages/kit/lib/createChecker.ts index 8ca336d6..b5370aee 100644 --- a/packages/kit/lib/createChecker.ts +++ b/packages/kit/lib/createChecker.ts @@ -72,7 +72,7 @@ function createTypeScriptCheckerWorker( }; const projectHost = getTypeScriptProjectHost(env); - const project = createTypeScriptProject(projectHost, languages, resolveCommonLanguageId); + const project = createTypeScriptProject(languages, projectHost, env.fileNameToUri, resolveCommonLanguageId); const service = createLanguageService( { typescript: ts as any }, services, diff --git a/packages/kit/lib/createFormatter.ts b/packages/kit/lib/createFormatter.ts index fc3a7047..e3dd3a69 100644 --- a/packages/kit/lib/createFormatter.ts +++ b/packages/kit/lib/createFormatter.ts @@ -9,7 +9,6 @@ export function createFormatter( ) { let fakeUri = 'file:///dummy.txt'; - let fakeFileName = '/dummy.txt'; let settings = {}; const env = createServiceEnvironment(() => settings); @@ -36,7 +35,7 @@ export function createFormatter( async function format(content: string, languageId: string, options: FormattingOptions): Promise { - fileProvider.updateSource(fakeFileName, ts.ScriptSnapshot.fromString(content), languageId); + fileProvider.updateSourceFile(fakeUri, ts.ScriptSnapshot.fromString(content), languageId); const document = service.context.getTextDocument(fakeUri)!; const edits = await service.format(fakeUri, options, undefined, undefined); diff --git a/packages/language-core/lib/createFileProvider.ts b/packages/language-core/lib/createFileProvider.ts index b6e80deb..2d14f19a 100644 --- a/packages/language-core/lib/createFileProvider.ts +++ b/packages/language-core/lib/createFileProvider.ts @@ -1,36 +1,32 @@ import { SourceMap } from '@volar/source-map'; import type * as ts from 'typescript/lib/tsserverlibrary'; import { MirrorMap } from './mirrorMap'; -import type { FileRangeCapabilities, Language, VirtualFile } from './types'; - -export interface Source { - fileName: string; - languageId: string; - snapshot: ts.IScriptSnapshot; - root?: VirtualFile; - language?: Language; -} +import type { FileRangeCapabilities, Language, SourceFile, VirtualFile } from './types'; + +const caseSensitive = false; // TODO: use ts.sys.useCaseSensitiveFileNames export function createFileProvider(languages: Language[], sync: () => void) { - const sourceFiles = new Map(); - const virtualFiles = new Map(); + const sourceFileRegistry = new Map(); + const virtualFileRegistry = new Map(); const virtualFileMaps = new WeakMap]>>(); const virtualFileToMirrorMap = new WeakMap(); + const normalizeId = caseSensitive + ? (id: string) => id + : (id: string) => id.toLowerCase(); return { - getAllSources() { + getAllSourceFiles() { sync(); - return sourceFiles; + return sourceFileRegistry.values(); }, - updateSource(fileName: string, snapshot: ts.IScriptSnapshot, languageId: string): VirtualFile | undefined { - const key = normalizePath(fileName); - const value = sourceFiles.get(key); + updateSourceFile(id: string, snapshot: ts.IScriptSnapshot, languageId: string): VirtualFile | undefined { + const value = sourceFileRegistry.get(normalizeId(id)); if (value) { if (value.languageId !== languageId) { // languageId changed - this.deleteSource(fileName); - return this.updateSource(fileName, snapshot, languageId); + this.deleteSourceFile(id); + return this.updateSourceFile(id, snapshot, languageId); } else if (value.snapshot !== snapshot) { value.snapshot = snapshot; @@ -46,24 +42,23 @@ export function createFileProvider(languages: Language[], sync: () => void) { } } for (const language of languages) { - const virtualFile = language.createVirtualFile(fileName, snapshot, languageId); + const virtualFile = language.createVirtualFile(id, languageId, snapshot); if (virtualFile) { - const source: Source = { fileName, languageId, snapshot, root: virtualFile, language }; - sourceFiles.set(key, source); + const source: SourceFile = { id: id, languageId, snapshot, root: virtualFile, language }; + sourceFileRegistry.set(normalizeId(id), source); updateVirtualFiles(source); return virtualFile; // created } } - sourceFiles.set(key, { fileName, languageId, snapshot }); + sourceFileRegistry.set(normalizeId(id), { id: id, languageId, snapshot }); }, - deleteSource(fileName: string) { - const key = normalizePath(fileName); - const value = sourceFiles.get(key); + deleteSourceFile(id: string) { + const value = sourceFileRegistry.get(normalizeId(id)); if (value) { if (value.language && value.root) { value.language.deleteVirtualFile?.(value.root); } - sourceFiles.delete(key); // deleted + sourceFileRegistry.delete(normalizeId(id)); // deleted deleteVirtualFiles(value); } }, @@ -79,35 +74,26 @@ export function createFileProvider(languages: Language[], sync: () => void) { virtualFileMaps.set(virtualFile.snapshot, new Map()); } - updateVirtualFileMaps(virtualFile, sourceFileName => { - if (sourceFileName) { - const source = sourceFiles.get(normalizePath(sourceFileName))!; - return [sourceFileName, source.snapshot]; + updateVirtualFileMaps(virtualFile, sourceId => { + if (sourceId) { + const sourceFile = sourceFileRegistry.get(normalizeId(sourceId))!; + return [sourceId, sourceFile.snapshot]; } else { - const source = virtualFiles.get(normalizePath(virtualFile.fileName))!.source; - return [source.fileName, source.snapshot]; + const source = virtualFileRegistry.get(normalizeId(virtualFile.id))!.source; + return [source.id, source.snapshot]; } }, virtualFileMaps.get(virtualFile.snapshot)); return virtualFileMaps.get(virtualFile.snapshot)!; }, - getSource(fileName: string) { - sync(); - const key = normalizePath(fileName); - return sourceFiles.get(key); - }, - hasSource(fileName: string) { + getSourceFile(id: string) { sync(); - return sourceFiles.has(normalizePath(fileName)); + return sourceFileRegistry.get(normalizeId(id)); }, - hasVirtualFile(fileName: string) { + getVirtualFile(id: string) { sync(); - return !!virtualFiles.get(normalizePath(fileName)); - }, - getVirtualFile(fileName: string) { - sync(); - const sourceAndVirtual = virtualFiles.get(normalizePath(fileName)); + const sourceAndVirtual = virtualFileRegistry.get(normalizeId(id)); if (sourceAndVirtual) { return [sourceAndVirtual.virtualFile, sourceAndVirtual.source] as const; } @@ -115,18 +101,18 @@ export function createFileProvider(languages: Language[], sync: () => void) { }, }; - function deleteVirtualFiles(source: Source) { + function deleteVirtualFiles(source: SourceFile) { if (source.root) { forEachEmbeddedFile(source.root, file => { - virtualFiles.delete(normalizePath(file.fileName)); + virtualFileRegistry.delete(normalizeId(file.id)); }); } } - function updateVirtualFiles(source: Source) { + function updateVirtualFiles(source: SourceFile) { if (source.root) { forEachEmbeddedFile(source.root, file => { - virtualFiles.set(normalizePath(file.fileName), { virtualFile: file, source }); + virtualFileRegistry.set(normalizeId(file.id), { virtualFile: file, source }); }); } } @@ -134,7 +120,7 @@ export function createFileProvider(languages: Language[], sync: () => void) { export function updateVirtualFileMaps( virtualFile: VirtualFile, - getSourceSnapshot: (source: string | undefined) => [string, ts.IScriptSnapshot] | undefined, + getSourceSnapshot: (sourceUri: string | undefined) => [string, ts.IScriptSnapshot] | undefined, map: Map]> = new Map(), ) { @@ -165,7 +151,3 @@ export function forEachEmbeddedFile(file: VirtualFile, cb: (embedded: VirtualFil forEachEmbeddedFile(embeddedFile, cb); } } - -function normalizePath(fileName: string) { - return fileName.replace(/\\/g, '/').toLowerCase(); -} diff --git a/packages/language-core/lib/createTypeScriptProject.ts b/packages/language-core/lib/createTypeScriptProject.ts index 482231ba..6dac0285 100644 --- a/packages/language-core/lib/createTypeScriptProject.ts +++ b/packages/language-core/lib/createTypeScriptProject.ts @@ -3,19 +3,20 @@ import { Language, TypeScriptProjectHost, Project } from './types'; import type * as ts from 'typescript/lib/tsserverlibrary'; export function createTypeScriptProject( - projectHost: TypeScriptProjectHost, languages: Language[], - getLanguageId: (fileName: string) => string, + projectHost: TypeScriptProjectHost, + fileNameToId: (fileName: string) => string, + getLanguageId: (fileName: string) => string ): Project { for (const language of languages) { - if (language.resolveTypeScriptProjectHost) { - projectHost = language.resolveTypeScriptProjectHost(projectHost); + if (language.typescript?.resolveProjectHost) { + projectHost = language.typescript.resolveProjectHost(projectHost); } } let lastRootFiles = new Map(); - let lastProjectVersion: number | string | undefined; + let lastProjectVersion: string | undefined; const fileProvider = createFileProvider(languages, () => { @@ -34,17 +35,19 @@ export function createTypeScriptProject( for (const [fileName, snapshot] of newRootFiles) { remainRootFiles.delete(fileName); if (lastRootFiles.get(fileName) !== newRootFiles.get(fileName)) { + const id = fileNameToId(fileName); if (snapshot) { - fileProvider.updateSource(fileName, snapshot, getLanguageId(fileName)); + fileProvider.updateSourceFile(id, snapshot, getLanguageId(fileName)); } else { - fileProvider.deleteSource(fileName); + fileProvider.deleteSourceFile(id); } } } for (const fileName of remainRootFiles) { - fileProvider.deleteSource(fileName); + const id = fileNameToId(fileName); + fileProvider.deleteSourceFile(id); } lastRootFiles = newRootFiles; diff --git a/packages/language-core/lib/types.ts b/packages/language-core/lib/types.ts index ac21c8c0..0c86bffa 100644 --- a/packages/language-core/lib/types.ts +++ b/packages/language-core/lib/types.ts @@ -75,10 +75,12 @@ export enum FileKind { TypeScriptHostFile = 1, } -export interface VirtualFile { - fileName: string, - snapshot: ts.IScriptSnapshot, - languageId: string, +export interface SourceFile extends BaesFile { + root?: VirtualFile; + language?: Language; +} + +export interface VirtualFile extends BaesFile { kind: FileKind, capabilities: FileCapabilities, mappings: Mapping[], @@ -87,11 +89,24 @@ export interface VirtualFile { embeddedFiles: VirtualFile[], } +export interface BaesFile { + /** + * for language-server, kit, monaco, this is uri + * + * for typescript server plugin, tsc, this is fileName + */ + id: string, + languageId: string, + snapshot: ts.IScriptSnapshot, +} + export interface Language { - createVirtualFile(fileName: string, snapshot: ts.IScriptSnapshot, languageId: string): T | undefined; + createVirtualFile(id: string, languageId: string, snapshot: ts.IScriptSnapshot): T | undefined; updateVirtualFile(virtualFile: T, snapshot: ts.IScriptSnapshot): void; deleteVirtualFile?(virtualFile: T): void; - resolveTypeScriptProjectHost?(host: T): T; + typescript?: { + resolveProjectHost?(host: T): T; + }; } export interface TypeScriptProjectHost extends Pick< diff --git a/packages/language-server/lib/project/simpleProject.ts b/packages/language-server/lib/project/simpleProject.ts index c5c13c2a..6a09283c 100644 --- a/packages/language-server/lib/project/simpleProject.ts +++ b/packages/language-server/lib/project/simpleProject.ts @@ -15,7 +15,6 @@ export async function createSimpleServerProject( let shouldUpdate = true; let lastSnapshots = new Map(); - const { uriToFileName } = context.server.runtimeEnv; const config = await getConfig(context, plugins, serviceEnv, undefined); context.workspaces.documents.onDidChangeContent(() => { @@ -55,16 +54,16 @@ export async function createSimpleServerProject( const newSnapshot = snapshot?.getSnapshot(); if (lastSnapshots.get(uri) !== snapshot) { if (snapshot && newSnapshot) { - fileProvider.updateSource(uriToFileName(uri), newSnapshot, snapshot.languageId); + fileProvider.updateSourceFile(uri, newSnapshot, snapshot.languageId); } else { - fileProvider.deleteSource(uriToFileName(uri)); + fileProvider.deleteSourceFile(uri); } } } for (const uri of remain) { - fileProvider.deleteSource(uriToFileName(uri)); + fileProvider.deleteSourceFile(uri); } const _newSnapshots = new Map(); diff --git a/packages/language-server/lib/project/typescriptProject.ts b/packages/language-server/lib/project/typescriptProject.ts index f465dbb9..630d114c 100644 --- a/packages/language-server/lib/project/typescriptProject.ts +++ b/packages/language-server/lib/project/typescriptProject.ts @@ -107,8 +107,9 @@ export async function createTypeScriptServerProject( Object.values(config.services ?? {}), serviceEnv, createTypeScriptProject( - typescriptProjectHost, Object.values(config.languages ?? {}), + typescriptProjectHost, + serviceEnv.fileNameToUri, fileName => context.workspaces.documents.data.pathGet(fileName)?.languageId ?? resolveCommonLanguageId(fileName), ), ); diff --git a/packages/language-server/lib/register/registerEditorFeatures.ts b/packages/language-server/lib/register/registerEditorFeatures.ts index d53e052b..bc792e46 100644 --- a/packages/language-server/lib/register/registerEditorFeatures.ts +++ b/packages/language-server/lib/register/registerEditorFeatures.ts @@ -23,18 +23,19 @@ export function registerEditorFeatures( }); connection.onRequest(GetVirtualFilesRequest.type, async document => { const languageService = (await projectProvider.getProject(document.uri)).getLanguageService(); - const file = languageService.context.project.fileProvider.getSource(env.uriToFileName(document.uri))?.root; + const file = languageService.context.project.fileProvider.getSourceFile(document.uri)?.root; return file ? prune(file) : undefined; function prune(file: VirtualFile): VirtualFile { - let version = scriptVersions.get(file.fileName) ?? 0; + let version = scriptVersions.get(file.id) ?? 0; if (!scriptVersionSnapshots.has(file.snapshot)) { version++; - scriptVersions.set(file.fileName, version); + scriptVersions.set(file.id, version); scriptVersionSnapshots.add(file.snapshot); } return { - fileName: file.fileName, + uri: file.id, + languageId: file.languageId, kind: file.kind, capabilities: file.capabilities, embeddedFiles: file.embeddedFiles.map(prune), @@ -47,10 +48,13 @@ export function registerEditorFeatures( let content: string = ''; let codegenStacks: Stack[] = []; const mappings: Record[]> = {}; - for (const [file, map] of languageService.context.documents.getMapsByVirtualFileName(params.virtualFileName)) { - content = map.virtualFileDocument.getText(); - codegenStacks = file.codegenStacks; - mappings[map.sourceFileDocument.uri] = map.map.mappings; + const [virtualFile] = languageService.context.project.fileProvider.getVirtualFile(params.virtualFileName); + if (virtualFile) { + for (const map of languageService.context.documents.getMapsByVirtualFile(virtualFile)) { + content = map.virtualFileDocument.getText(); + codegenStacks = virtualFile.codegenStacks; + mappings[map.sourceFileDocument.uri] = map.map.mappings; + } } return { content, @@ -70,16 +74,15 @@ export function registerEditorFeatures( // global virtual files if (languageService.context.project.typescript?.projectHost) { - const rootPath = languageService.context.project.typescript?.projectHost.getCurrentDirectory(); + const rootUri = languageService.context.env.workspaceFolder.uri.toString(); - for (const [fileName] of languageService.context.project.fileProvider.getAllSources()) { - const source = languageService.context.project.fileProvider.getSource(fileName); - if (source?.root) { - forEachEmbeddedFile(source.root, virtualFile => { + for (const sourceFile of languageService.context.project.fileProvider.getAllSourceFiles()) { + if (sourceFile.root) { + forEachEmbeddedFile(sourceFile.root, virtualFile => { if (virtualFile.kind === FileKind.TypeScriptHostFile) { - if (virtualFile.fileName.startsWith(rootPath)) { + if (virtualFile.id.startsWith(rootUri)) { const snapshot = virtualFile.snapshot; - fs.writeFile(virtualFile.fileName, snapshot.getText(0, snapshot.getLength()), () => { }); + fs.writeFile(languageService.context.env.uriToFileName(virtualFile.id), snapshot.getText(0, snapshot.getLength()), () => { }); } } }); diff --git a/packages/language-service/lib/baseLanguageService.ts b/packages/language-service/lib/baseLanguageService.ts index bc08f9ed..7a5eb3f5 100644 --- a/packages/language-service/lib/baseLanguageService.ts +++ b/packages/language-service/lib/baseLanguageService.ts @@ -44,7 +44,7 @@ function createServiceContext( services: Service[], ) { - const textDocumentMapper = createDocumentProvider(env, project); + const documents = createDocumentProvider(project.fileProvider); const context: ServiceContext = { env, project, @@ -58,7 +58,7 @@ function createServiceContext( throw `No service provide ${key as any}`; }, services: [], - documents: textDocumentMapper, + documents: documents, commands: { rename: { create(uri, position) { @@ -87,8 +87,9 @@ function createServiceContext( } const sourceReferences: vscode.Location[] = []; for (const reference of locations) { - if (context.documents.isVirtualFileUri(reference.uri)) { - for (const [_, map] of context.documents.getMapsByVirtualFileUri(reference.uri)) { + const [virtualFile] = context.project.fileProvider.getVirtualFile(reference.uri); + if (virtualFile) { + for (const map of context.documents.getMapsByVirtualFile(virtualFile)) { const range = map.toSourceRange(reference.range); if (range) { sourceReferences.push({ uri: map.sourceFileDocument.uri, range }); @@ -143,34 +144,36 @@ function createServiceContext( return context; function toSourceLocation(uri: string, position: vscode.Position, filter?: (data: FileRangeCapabilities) => boolean) { - if (!textDocumentMapper.isVirtualFileUri(uri)) { + + const [virtualFile] = project.fileProvider.getVirtualFile(uri); + + if (!virtualFile) { return { uri, position }; } - const map = textDocumentMapper.getVirtualFileByUri(uri); - if (map) { - for (const [_, map] of context.documents.getMapsByVirtualFileUri(uri)) { - const sourcePosition = map.toSourcePosition(position, filter); - if (sourcePosition) { - return { - uri: map.sourceFileDocument.uri, - position: sourcePosition, - }; - } + + for (const map of context.documents.getMapsByVirtualFile(virtualFile)) { + const sourcePosition = map.toSourcePosition(position, filter); + if (sourcePosition) { + return { + uri: map.sourceFileDocument.uri, + position: sourcePosition, + }; } } } function getTextDocument(uri: string) { - for (const [_, map] of context.documents.getMapsByVirtualFileUri(uri)) { - return map.virtualFileDocument; + const [virtualFile] = project.fileProvider.getVirtualFile(uri); + if (virtualFile) { + for (const map of context.documents.getMapsByVirtualFile(virtualFile)) { + return map.virtualFileDocument; + } } - const fileName = env.uriToFileName(uri); - const source = project.fileProvider.getSource(fileName); - - if (source) { - return context.documents.getDocumentByUri(source.snapshot, uri, source.languageId); + const sourceFile = project.fileProvider.getSourceFile(uri); + if (sourceFile) { + return context.documents.getDocumentByUri(uri, sourceFile.languageId, sourceFile.snapshot); } } } diff --git a/packages/language-service/lib/documentFeatures/format.ts b/packages/language-service/lib/documentFeatures/format.ts index 0569d5ad..bca1e1d3 100644 --- a/packages/language-service/lib/documentFeatures/format.ts +++ b/packages/language-service/lib/documentFeatures/format.ts @@ -31,8 +31,8 @@ export function register(context: ServiceContext) { end: document.positionAt(document.getText().length), }; - const source = context.documents.getSourceByUri(document.uri); - if (!source?.language || !source.root) { + const sourceFile = context.project.fileProvider.getSourceFile(document.uri); + if (!sourceFile?.language || !sourceFile.root) { return onTypeParams ? (await tryFormat(document, onTypeParams.position, onTypeParams.ch))?.edits : (await tryFormat(document, range, undefined))?.edits; @@ -40,8 +40,8 @@ export function register(context: ServiceContext) { const initialIndentLanguageId = await context.env.getConfiguration?.>('volar.format.initialIndent') ?? { html: true }; - let tempSourceSnapshot = source.snapshot; - const tempVirtualFile = source.language.createVirtualFile(source.fileName, source.snapshot, source.languageId)!; + let tempSourceSnapshot = sourceFile.snapshot; + const tempVirtualFile = sourceFile.language.createVirtualFile(sourceFile.id, sourceFile.languageId, sourceFile.snapshot)!; const originalDocument = document; let level = 0; @@ -54,7 +54,7 @@ export function register(context: ServiceContext) { let edits: vscode.TextEdit[] = []; const toPatchIndent: { - virtualFileName: string; + virtualFileUri: string; isCodeBlock: boolean; service: ReturnType; }[] = []; @@ -68,7 +68,7 @@ export function register(context: ServiceContext) { if (onTypeParams && !isCodeBlock) continue; - const docMap = createDocMap(file, source.fileName, tempSourceSnapshot, source.languageId); + const docMap = createDocMap(file, sourceFile.id, sourceFile.languageId, tempSourceSnapshot); if (!docMap) continue; let embeddedCodeResult: Awaited> | undefined; @@ -96,7 +96,7 @@ export function register(context: ServiceContext) { continue; toPatchIndent.push({ - virtualFileName: file.fileName, + virtualFileUri: file.id, isCodeBlock, service: embeddedCodeResult.service, }); @@ -118,7 +118,7 @@ export function register(context: ServiceContext) { const newText = TextDocument.applyEdits(document, edits); document = TextDocument.create(document.uri, document.languageId, document.version + 1, newText); tempSourceSnapshot = stringToSnapshot(newText); - source.language.updateVirtualFile(tempVirtualFile, tempSourceSnapshot); + sourceFile.language.updateVirtualFile(tempVirtualFile, tempSourceSnapshot); } if (level > 1) { @@ -138,11 +138,11 @@ export function register(context: ServiceContext) { let virtualFile!: VirtualFile; forEachEmbeddedFile(tempVirtualFile, file => { - if (file.fileName === item.virtualFileName) { + if (file.id === item.virtualFileUri) { virtualFile = file; } }); - const docMap = createDocMap(virtualFile, source.fileName, tempSourceSnapshot, source.languageId); + const docMap = createDocMap(virtualFile, sourceFile.id, sourceFile.languageId, tempSourceSnapshot); if (!docMap) continue; const indentSensitiveLines = new Set(); @@ -191,7 +191,7 @@ export function register(context: ServiceContext) { const newText = TextDocument.applyEdits(document, indentEdits); document = TextDocument.create(document.uri, document.languageId, document.version + 1, newText); tempSourceSnapshot = stringToSnapshot(newText); - source.language.updateVirtualFile(tempVirtualFile, tempSourceSnapshot); + sourceFile.language.updateVirtualFile(tempVirtualFile, tempSourceSnapshot); } } } @@ -270,29 +270,29 @@ export function register(context: ServiceContext) { } }; - function createDocMap(file: VirtualFile, _sourceFileName: string, _sourceSnapshot: ts.IScriptSnapshot, sourceLanguageId: string | undefined) { + function createDocMap(file: VirtualFile, sourceFileUri: string, sourceLanguageId: string, _sourceSnapshot: ts.IScriptSnapshot) { const maps = updateVirtualFileMaps(file, (sourceFileName) => { if (!sourceFileName) { - return [_sourceFileName, _sourceSnapshot]; + return [sourceFileUri, _sourceSnapshot]; } }); - if (maps.has(_sourceFileName) && maps.get(_sourceFileName)![0] === _sourceSnapshot) { - const [_, map] = maps.get(_sourceFileName)!; + if (maps.has(sourceFileUri) && maps.get(sourceFileUri)![0] === _sourceSnapshot) { + const map = maps.get(sourceFileUri)!; const version = fakeVersion++; return new SourceMapWithDocuments( TextDocument.create( - context.env.fileNameToUri(_sourceFileName), - sourceLanguageId ?? resolveCommonLanguageId(_sourceFileName), + sourceFileUri, + sourceLanguageId ?? resolveCommonLanguageId(sourceFileUri), version, _sourceSnapshot.getText(0, _sourceSnapshot.getLength()) ), TextDocument.create( - context.env.fileNameToUri(file.fileName), + file.id, file.languageId, version, file.snapshot.getText(0, file.snapshot.getLength()) ), - map, + map[1], ); } } diff --git a/packages/language-service/lib/documents.ts b/packages/language-service/lib/documents.ts index 330a5e4c..2f11f233 100644 --- a/packages/language-service/lib/documents.ts +++ b/packages/language-service/lib/documents.ts @@ -1,9 +1,8 @@ -import { FileRangeCapabilities, MirrorBehaviorCapabilities, MirrorMap, Project, VirtualFile, forEachEmbeddedFile, Source } from '@volar/language-core'; +import { FileProvider, FileRangeCapabilities, MirrorBehaviorCapabilities, MirrorMap, SourceFile, VirtualFile, forEachEmbeddedFile } from '@volar/language-core'; import { Mapping, SourceMap } from '@volar/source-map'; import type * as ts from 'typescript/lib/tsserverlibrary'; import type * as vscode from 'vscode-languageserver-protocol'; import { TextDocument } from 'vscode-languageserver-textdocument'; -import { ServiceEnvironment } from './types'; export type DocumentProvider = ReturnType; @@ -165,7 +164,7 @@ export class MirrorMapWithDocument extends SourceMapWithDocuments<[MirrorBehavio } } -export function createDocumentProvider(env: ServiceEnvironment, project: Project) { +export function createDocumentProvider(fileProvider: FileProvider) { let version = 0; @@ -174,50 +173,16 @@ export function createDocumentProvider(env: ServiceEnvironment, project: Project const snapshot2Doc = new WeakMap>(); return { - getSourceByUri(sourceFileUri: string) { - return project.fileProvider.getSource(env.uriToFileName(sourceFileUri)); - }, - isVirtualFileUri(virtualFileUri: string) { - return project.fileProvider.hasVirtualFile(env.uriToFileName(virtualFileUri)); - }, - getVirtualFileByUri(virtualFileUri: string) { - return project.fileProvider.getVirtualFile(env.uriToFileName(virtualFileUri)); - }, - getMirrorMapByUri(virtualFileUri: string) { - const fileName = env.uriToFileName(virtualFileUri); - const [virtualFile] = project.fileProvider.getVirtualFile(fileName); - if (virtualFile) { - const map = project.fileProvider.getMirrorMap(virtualFile); - if (map) { - if (!mirrorMap2DocMirrorMap.has(map)) { - mirrorMap2DocMirrorMap.set(map, new MirrorMapWithDocument( - getDocumentByFileName(virtualFile.snapshot, fileName, virtualFile.languageId), - map, - )); - } - return [virtualFile, mirrorMap2DocMirrorMap.get(map)!] as const; - } - } - }, - getMapsBySourceFileUri(uri: string) { - return this.getMapsBySourceFileName(env.uriToFileName(uri)); - }, - getMapsBySourceFileName(fileName: string) { - const source = project.fileProvider.getSource(fileName); - if (source) { - return this.getMapsBySource(source); - } - }, - getMapsBySource(source: Source) { + getMapsBySourceFile(source: SourceFile) { if (source?.root) { const result: [VirtualFile, SourceMapWithDocuments][] = []; forEachEmbeddedFile(source.root, (virtualFile) => { - for (const [sourceFileName, [sourceSnapshot, map]] of project.fileProvider.getMaps(virtualFile)) { + for (const [sourceUri, [sourceSnapshot, map]] of fileProvider.getMaps(virtualFile)) { if (sourceSnapshot === source.snapshot) { if (!map2DocMap.has(map)) { map2DocMap.set(map, new SourceMapWithDocuments( - getDocumentByFileName(sourceSnapshot, sourceFileName, source.languageId), - getDocumentByFileName(virtualFile.snapshot, virtualFile.fileName, virtualFile.languageId), + getDocumentByUri(sourceUri, source.languageId, sourceSnapshot), + getDocumentByUri(virtualFile.id, virtualFile.languageId, virtualFile.snapshot), map, )); } @@ -225,49 +190,49 @@ export function createDocumentProvider(env: ServiceEnvironment, project: Project } } }); - return { - snapshot: source.snapshot, - maps: result, - }; + return result; } }, - getMapsByVirtualFileUri(virtualFileUri: string) { - return this.getMapsByVirtualFileName(env.uriToFileName(virtualFileUri)); - }, - *getMapsByVirtualFileName(virtualFileName: string): IterableIterator<[VirtualFile, SourceMapWithDocuments]> { - const [virtualFile] = project.fileProvider.getVirtualFile(virtualFileName); - if (virtualFile) { - for (const [sourceFileName, [sourceSnapshot, map]] of project.fileProvider.getMaps(virtualFile)) { - if (!map2DocMap.has(map)) { - map2DocMap.set(map, new SourceMapWithDocuments( - getDocumentByFileName(sourceSnapshot, sourceFileName, project.fileProvider.getSource(sourceFileName)!.languageId), - getDocumentByFileName(virtualFile.snapshot, virtualFileName, virtualFile.languageId), - map, - )); - } - yield [virtualFile, map2DocMap.get(map)!]; + *getMapsByVirtualFile(virtualFile: VirtualFile) { + for (const [sourceUri, [sourceSnapshot, map]] of fileProvider.getMaps(virtualFile)) { + if (!map2DocMap.has(map)) { + map2DocMap.set(map, new SourceMapWithDocuments( + getDocumentByUri(sourceUri, fileProvider.getSourceFile(sourceUri)!.languageId, sourceSnapshot), + getDocumentByUri(virtualFile.id, virtualFile.languageId, virtualFile.snapshot), + map, + )); } + yield map2DocMap.get(map)!; } }, - getDocumentByUri(snapshot: ts.IScriptSnapshot, uri: string, languageId: string) { - return this.getDocumentByFileName(snapshot, env.uriToFileName(uri), languageId); + getMirrorMap(virtualFile: VirtualFile) { + const map = fileProvider.getMirrorMap(virtualFile); + if (map) { + if (!mirrorMap2DocMirrorMap.has(map)) { + mirrorMap2DocMirrorMap.set(map, new MirrorMapWithDocument( + getDocumentByUri(virtualFile.id, virtualFile.languageId, virtualFile.snapshot), + map, + )); + } + return mirrorMap2DocMirrorMap.get(map)!; + } }, - getDocumentByFileName, + getDocumentByUri, }; - function getDocumentByFileName(snapshot: ts.IScriptSnapshot, fileName: string, languageId: string) { + function getDocumentByUri(uri: string, languageId: string, snapshot: ts.IScriptSnapshot) { if (!snapshot2Doc.has(snapshot)) { snapshot2Doc.set(snapshot, new Map()); } const map = snapshot2Doc.get(snapshot)!; - if (!map.has(fileName)) { - map.set(fileName, TextDocument.create( - env.fileNameToUri(fileName), + if (!map.has(uri)) { + map.set(uri, TextDocument.create( + uri, languageId, version++, snapshot.getText(0, snapshot.getLength()), )); } - return map.get(fileName)!; + return map.get(uri)!; } } diff --git a/packages/language-service/lib/languageFeatures/callHierarchy.ts b/packages/language-service/lib/languageFeatures/callHierarchy.ts index 773d28fb..1eb9f74f 100644 --- a/packages/language-service/lib/languageFeatures/callHierarchy.ts +++ b/packages/language-service/lib/languageFeatures/callHierarchy.ts @@ -66,7 +66,9 @@ export function register(context: ServiceContext) { if (data.virtualDocumentUri) { - if (context.documents.isVirtualFileUri(data.virtualDocumentUri)) { + const [virtualFile] = context.project.fileProvider.getVirtualFile(data.virtualDocumentUri); + + if (virtualFile) { const _calls = await service.provideCallHierarchyIncomingCalls(item, token); @@ -122,7 +124,9 @@ export function register(context: ServiceContext) { if (data.virtualDocumentUri) { - if (context.documents.isVirtualFileUri(data.virtualDocumentUri)) { + const [virtualFile] = context.project.fileProvider.getVirtualFile(data.virtualDocumentUri); + + if (virtualFile) { const _calls = await service.provideCallHierarchyOutgoingCalls(item, token); @@ -165,10 +169,12 @@ export function register(context: ServiceContext) { function transformCallHierarchyItem(tsItem: vscode.CallHierarchyItem, tsRanges: vscode.Range[]): [vscode.CallHierarchyItem, vscode.Range[]] | undefined { - if (!context.documents.isVirtualFileUri(tsItem.uri)) + const [virtualFile] = context.project.fileProvider.getVirtualFile(tsItem.uri); + + if (!virtualFile) return [tsItem, tsRanges]; - for (const [_, map] of context.documents.getMapsByVirtualFileUri(tsItem.uri)) { + for (const map of context.documents.getMapsByVirtualFile(virtualFile)) { let range = map.toSourceRange(tsItem.range); if (!range) { diff --git a/packages/language-service/lib/languageFeatures/codeActionResolve.ts b/packages/language-service/lib/languageFeatures/codeActionResolve.ts index ca8ef229..38ef3551 100644 --- a/packages/language-service/lib/languageFeatures/codeActionResolve.ts +++ b/packages/language-service/lib/languageFeatures/codeActionResolve.ts @@ -26,7 +26,7 @@ export function register(context: ServiceContext) { ...item, edit: embeddedEditToSourceEdit( item.edit, - context.documents, + context, 'codeAction', { [data.uri]: data.version }, ), diff --git a/packages/language-service/lib/languageFeatures/codeActions.ts b/packages/language-service/lib/languageFeatures/codeActions.ts index 8851f958..e45ff08c 100644 --- a/packages/language-service/lib/languageFeatures/codeActions.ts +++ b/packages/language-service/lib/languageFeatures/codeActions.ts @@ -132,7 +132,7 @@ export function register(context: ServiceContext) { if (action.edit) { const edit = embeddedEditToSourceEdit( action.edit, - context.documents, + context, 'codeAction', ); if (!edit) { diff --git a/packages/language-service/lib/languageFeatures/complete.ts b/packages/language-service/lib/languageFeatures/complete.ts index 45e85854..028c6a66 100644 --- a/packages/language-service/lib/languageFeatures/complete.ts +++ b/packages/language-service/lib/languageFeatures/complete.ts @@ -48,7 +48,11 @@ export function register(context: ServiceContext) { if (cacheData.virtualDocumentUri) { - for (const [_, map] of context.documents.getMapsByVirtualFileUri(cacheData.virtualDocumentUri)) { + const [virtualFile] = context.project.fileProvider.getVirtualFile(cacheData.virtualDocumentUri); + if (!virtualFile) + continue; + + for (const map of context.documents.getMapsByVirtualFile(virtualFile)) { for (const mapped of map.toGeneratedPositions(position, data => !!data.completion)) { @@ -109,7 +113,7 @@ export function register(context: ServiceContext) { } else { - const rootFile = context.documents.getSourceByUri(uri)?.root; + const rootVirtualFile = context.project.fileProvider.getSourceFile(uri)?.root; cache = { uri, @@ -120,9 +124,9 @@ export function register(context: ServiceContext) { // monky fix https://github.com/johnsoncodehk/volar/issues/1358 let isFirstMapping = true; - if (rootFile) { + if (rootVirtualFile) { - await visitEmbedded(context.documents, rootFile, async (_, map) => { + await visitEmbedded(context, rootVirtualFile, async (_, map) => { const services = [...context.services].sort(sortServices); diff --git a/packages/language-service/lib/languageFeatures/completeResolve.ts b/packages/language-service/lib/languageFeatures/completeResolve.ts index f279bfee..ca956d06 100644 --- a/packages/language-service/lib/languageFeatures/completeResolve.ts +++ b/packages/language-service/lib/languageFeatures/completeResolve.ts @@ -21,14 +21,19 @@ export function register(context: ServiceContext) { if (data.virtualDocumentUri) { - for (const [_, map] of context.documents.getMapsByVirtualFileUri(data.virtualDocumentUri)) { - - item = await service.resolveCompletionItem(item, token); - item = service.transformCompletionItem?.(item) ?? transformer.asCompletionItem( - item, - embeddedRange => map.toSourceRange(embeddedRange), - map.virtualFileDocument, - ); + const [virtualFile] = context.project.fileProvider.getVirtualFile(data.virtualDocumentUri); + + if (virtualFile) { + + for (const map of context.documents.getMapsByVirtualFile(virtualFile)) { + + item = await service.resolveCompletionItem(item, token); + item = service.transformCompletionItem?.(item) ?? transformer.asCompletionItem( + item, + embeddedRange => map.toSourceRange(embeddedRange), + map.virtualFileDocument, + ); + } } } else { diff --git a/packages/language-service/lib/languageFeatures/definition.ts b/packages/language-service/lib/languageFeatures/definition.ts index c5c397bc..5a35784e 100644 --- a/packages/language-service/lib/languageFeatures/definition.ts +++ b/packages/language-service/lib/languageFeatures/definition.ts @@ -53,7 +53,8 @@ export function register( recursiveChecker.add({ uri: definition.targetUri, range: { start: definition.targetRange.start, end: definition.targetRange.start } }); - const mirrorMap = context.documents.getMirrorMapByUri(definition.targetUri)?.[1]; + const [virtualFile] = context.project.fileProvider.getVirtualFile(definition.targetUri); + const mirrorMap = virtualFile ? context.documents.getMirrorMap(virtualFile) : undefined; if (mirrorMap) { @@ -99,40 +100,45 @@ export function register( let foundTargetSelectionRange = false; - for (const [_, targetSourceMap] of context.documents.getMapsByVirtualFileUri(link.targetUri)) { + const [targetVirtualFile] = context.project.fileProvider.getVirtualFile(link.targetUri); - const targetSelectionRange = targetSourceMap.toSourceRange(link.targetSelectionRange); - if (!targetSelectionRange) - continue; + if (targetVirtualFile) { - foundTargetSelectionRange = true; + for (const targetSourceMap of context.documents.getMapsByVirtualFile(targetVirtualFile)) { - let targetRange = targetSourceMap.toSourceRange(link.targetRange); + const targetSelectionRange = targetSourceMap.toSourceRange(link.targetSelectionRange); + if (!targetSelectionRange) + continue; - link.targetUri = targetSourceMap.sourceFileDocument.uri; - // loose range mapping to for template slots, slot properties - link.targetRange = targetRange ?? targetSelectionRange; - link.targetSelectionRange = targetSelectionRange; - } + foundTargetSelectionRange = true; + + let targetRange = targetSourceMap.toSourceRange(link.targetRange); + + link.targetUri = targetSourceMap.sourceFileDocument.uri; + // loose range mapping to for template slots, slot properties + link.targetRange = targetRange ?? targetSelectionRange; + link.targetSelectionRange = targetSelectionRange; + } - if (apiName === 'provideDefinition' && context.documents.isVirtualFileUri(link.targetUri) && !foundTargetSelectionRange) { - for (const [_, targetMap] of context.documents.getMapsByVirtualFileUri(link.targetUri)) { - if (targetMap && targetMap.sourceFileDocument.uri !== uri) { - return { - ...link, - targetUri: targetMap.sourceFileDocument.uri, - targetRange: { - start: { line: 0, character: 0 }, - end: { line: 0, character: 0 }, - }, - targetSelectionRange: { - start: { line: 0, character: 0 }, - end: { line: 0, character: 0 }, - }, - }; + if (apiName === 'provideDefinition' && !foundTargetSelectionRange) { + for (const targetMap of context.documents.getMapsByVirtualFile(targetVirtualFile)) { + if (targetMap && targetMap.sourceFileDocument.uri !== uri) { + return { + ...link, + targetUri: targetMap.sourceFileDocument.uri, + targetRange: { + start: { line: 0, character: 0 }, + end: { line: 0, character: 0 }, + }, + targetSelectionRange: { + start: { line: 0, character: 0 }, + end: { line: 0, character: 0 }, + }, + }; + } } + return; } - return; } return link; diff --git a/packages/language-service/lib/languageFeatures/documentHighlights.ts b/packages/language-service/lib/languageFeatures/documentHighlights.ts index c0e7906c..c14c9f12 100644 --- a/packages/language-service/lib/languageFeatures/documentHighlights.ts +++ b/packages/language-service/lib/languageFeatures/documentHighlights.ts @@ -48,7 +48,8 @@ export function register(context: ServiceContext) { recursiveChecker.add({ uri: document.uri, range: { start: reference.range.start, end: reference.range.start } }); - const mirrorMap = context.documents.getMirrorMapByUri(document.uri)?.[1]; + const [virtualFile] = context.project.fileProvider.getVirtualFile(document.uri); + const mirrorMap = virtualFile ? context.documents.getMirrorMap(virtualFile) : undefined; if (mirrorMap) { diff --git a/packages/language-service/lib/languageFeatures/documentLinkResolve.ts b/packages/language-service/lib/languageFeatures/documentLinkResolve.ts index 9e91c27c..2d45fc7f 100644 --- a/packages/language-service/lib/languageFeatures/documentLinkResolve.ts +++ b/packages/language-service/lib/languageFeatures/documentLinkResolve.ts @@ -30,9 +30,10 @@ export function transformDocumentLinkTarget(target: string, context: ServiceCont const targetUri = URI.parse(target); const clearUri = targetUri.with({ fragment: '' }).toString(); + const [virtualFile] = context.project.fileProvider.getVirtualFile(clearUri); - if (context.documents.isVirtualFileUri(clearUri)) { - for (const [virtualFile, map] of context.documents.getMapsByVirtualFileUri(clearUri)) { + if (virtualFile) { + for (const map of context.documents.getMapsByVirtualFile(virtualFile)) { if (!virtualFile.capabilities.documentSymbol) { continue; diff --git a/packages/language-service/lib/languageFeatures/documentLinks.ts b/packages/language-service/lib/languageFeatures/documentLinks.ts index 42b0217c..0929231b 100644 --- a/packages/language-service/lib/languageFeatures/documentLinks.ts +++ b/packages/language-service/lib/languageFeatures/documentLinks.ts @@ -62,9 +62,9 @@ export function register(context: ServiceContext) { }).filter(notEmpty), arr => arr.flat(), ) ?? []; - const source = context.documents.getSourceByUri(uri); - const maps = context.documents.getMapsBySourceFileUri(uri); - const fictitiousLinks = source && maps ? getFictitiousLinks(context.documents.getDocumentByUri(maps.snapshot, uri, source.languageId), maps.maps) : []; + const sourceFile = context.project.fileProvider.getSourceFile(uri); + const maps = sourceFile ? context.documents.getMapsBySourceFile(sourceFile) : undefined; + const fictitiousLinks = sourceFile && maps ? getFictitiousLinks(context.documents.getDocumentByUri(uri, sourceFile.languageId, sourceFile.snapshot), maps) : []; return [ ...pluginLinks, diff --git a/packages/language-service/lib/languageFeatures/fileReferences.ts b/packages/language-service/lib/languageFeatures/fileReferences.ts index 6d92abe4..598a0c47 100644 --- a/packages/language-service/lib/languageFeatures/fileReferences.ts +++ b/packages/language-service/lib/languageFeatures/fileReferences.ts @@ -25,11 +25,13 @@ export function register(context: ServiceContext) { }, (data) => data.map(reference => { - if (!context.documents.isVirtualFileUri(reference.uri)) { + const [virtualFile] = context.project.fileProvider.getVirtualFile(reference.uri); + + if (!virtualFile) { return reference; } - for (const [_, map] of context.documents.getMapsByVirtualFileUri(reference.uri)) { + for (const map of context.documents.getMapsByVirtualFile(virtualFile)) { const range = map.toSourceRange(reference.range); if (range) { reference.uri = map.sourceFileDocument.uri; diff --git a/packages/language-service/lib/languageFeatures/fileRename.ts b/packages/language-service/lib/languageFeatures/fileRename.ts index 34629c6f..ab302338 100644 --- a/packages/language-service/lib/languageFeatures/fileRename.ts +++ b/packages/language-service/lib/languageFeatures/fileRename.ts @@ -9,15 +9,16 @@ export function register(context: ServiceContext) { return async (oldUri: string, newUri: string, token = NoneCancellationToken) => { - const rootFile = context.documents.getSourceByUri(oldUri)?.root; + const sourceFile = context.project.fileProvider.getSourceFile(oldUri); + const rootFile = sourceFile?.root; - if (rootFile) { + if (sourceFile && rootFile) { let tsExt: string | undefined; - forEachEmbeddedFile(rootFile, embedded => { - if (embedded.kind === FileKind.TypeScriptHostFile && embedded.fileName.replace(rootFile.fileName, '').match(/^\.(js|ts)x?$/)) { - tsExt = embedded.fileName.substring(embedded.fileName.lastIndexOf('.')); + forEachEmbeddedFile(rootFile, virtualFile => { + if (virtualFile.kind === FileKind.TypeScriptHostFile && virtualFile.id.replace(sourceFile.id, '').match(/^\.(js|ts)x?$/)) { + tsExt = virtualFile.id.substring(virtualFile.id.lastIndexOf('.')); } }); @@ -43,7 +44,7 @@ export function register(context: ServiceContext) { const result = embeddedEditToSourceEdit( workspaceEdit, - context.documents, + context, 'fileName', ); diff --git a/packages/language-service/lib/languageFeatures/references.ts b/packages/language-service/lib/languageFeatures/references.ts index 466fbdd1..dea5aeb1 100644 --- a/packages/language-service/lib/languageFeatures/references.ts +++ b/packages/language-service/lib/languageFeatures/references.ts @@ -44,7 +44,8 @@ export function register(context: ServiceContext) { recursiveChecker.add({ uri: reference.uri, range: { start: reference.range.start, end: reference.range.start } }); - const mirrorMap = context.documents.getMirrorMapByUri(reference.uri)?.[1]; + const [virtualFile] = context.project.fileProvider.getVirtualFile(reference.uri); + const mirrorMap = virtualFile ? context.documents.getMirrorMap(virtualFile) : undefined; if (mirrorMap) { @@ -73,8 +74,11 @@ export function register(context: ServiceContext) { const results: vscode.Location[] = []; for (const reference of data) { - if (context.documents.isVirtualFileUri(reference.uri)) { - for (const [_, map] of context.documents.getMapsByVirtualFileUri(reference.uri)) { + + const [virtualFile] = context.project.fileProvider.getVirtualFile(reference.uri); + + if (virtualFile) { + for (const map of context.documents.getMapsByVirtualFile(virtualFile)) { const range = map.toSourceRange(reference.range, data => !!data.references); if (range) { results.push({ diff --git a/packages/language-service/lib/languageFeatures/rename.ts b/packages/language-service/lib/languageFeatures/rename.ts index 416af914..277f8acd 100644 --- a/packages/language-service/lib/languageFeatures/rename.ts +++ b/packages/language-service/lib/languageFeatures/rename.ts @@ -1,11 +1,10 @@ +import { FileRangeCapabilities } from '@volar/language-core'; import type * as vscode from 'vscode-languageserver-protocol'; -import type { ServiceContext } from '../types'; -import { languageFeatureWorker } from '../utils/featureWorkers'; -import * as dedupe from '../utils/dedupe'; import { TextDocument } from 'vscode-languageserver-textdocument'; -import { DocumentProvider } from '../documents'; -import { FileRangeCapabilities } from '@volar/language-core'; +import type { ServiceContext } from '../types'; import { NoneCancellationToken } from '../utils/cancellation'; +import * as dedupe from '../utils/dedupe'; +import { languageFeatureWorker } from '../utils/featureWorkers'; export function register(context: ServiceContext) { @@ -74,7 +73,8 @@ export function register(context: ServiceContext) { recursiveChecker.add({ uri: editUri, range: { start: textEdit.range.start, end: textEdit.range.start } }); - const mirrorMap = context.documents.getMirrorMapByUri(editUri)?.[1]; + const [virtualFile] = context.project.fileProvider.getVirtualFile(editUri); + const mirrorMap = virtualFile ? context.documents.getMirrorMap(virtualFile) : undefined; if (mirrorMap) { @@ -129,7 +129,7 @@ export function register(context: ServiceContext) { (data) => { return embeddedEditToSourceEdit( data, - context.documents, + context, 'rename', ); }, @@ -183,7 +183,7 @@ export function mergeWorkspaceEdits(original: vscode.WorkspaceEdit, ...others: v export function embeddedEditToSourceEdit( tsResult: vscode.WorkspaceEdit, - documents: DocumentProvider, + { documents, project }: ServiceContext, mode: 'fileName' | 'rename' | 'codeAction' | 'format', versions: Record = {}, ) { @@ -196,56 +196,60 @@ export function embeddedEditToSourceEdit( sourceResult.changeAnnotations ??= {}; const tsAnno = tsResult.changeAnnotations[tsUri]; + const [virtualFile] = project.fileProvider.getVirtualFile(tsUri); - if (!documents.isVirtualFileUri(tsUri)) { - sourceResult.changeAnnotations[tsUri] = tsAnno; - } - else { - for (const [_, map] of documents.getMapsByVirtualFileUri(tsUri)) { + if (virtualFile) { + for (const map of documents.getMapsByVirtualFile(virtualFile)) { // TODO: check capability? const uri = map.sourceFileDocument.uri; sourceResult.changeAnnotations[uri] = tsAnno; } } + else { + sourceResult.changeAnnotations[tsUri] = tsAnno; + } } for (const tsUri in tsResult.changes) { sourceResult.changes ??= {}; - if (!documents.isVirtualFileUri(tsUri)) { - sourceResult.changes[tsUri] = tsResult.changes[tsUri]; - hasResult = true; - continue; - } - for (const [_, map] of documents.getMapsByVirtualFileUri(tsUri)) { - const tsEdits = tsResult.changes[tsUri]; - for (const tsEdit of tsEdits) { - if (mode === 'rename' || mode === 'fileName' || mode === 'codeAction') { - let _data: FileRangeCapabilities | undefined; - const range = map.toSourceRange(tsEdit.range, data => { - _data = data; - return typeof data.rename === 'object' ? !!data.rename.apply : !!data.rename; - }); - if (range) { - let newText = tsEdit.newText; - if (_data && typeof _data.rename === 'object' && _data.rename.apply) { - newText = _data.rename.apply(tsEdit.newText); + const [virtualFile] = project.fileProvider.getVirtualFile(tsUri); + + if (virtualFile) { + for (const map of documents.getMapsByVirtualFile(virtualFile)) { + const tsEdits = tsResult.changes[tsUri]; + for (const tsEdit of tsEdits) { + if (mode === 'rename' || mode === 'fileName' || mode === 'codeAction') { + let _data: FileRangeCapabilities | undefined; + const range = map.toSourceRange(tsEdit.range, data => { + _data = data; + return typeof data.rename === 'object' ? !!data.rename.apply : !!data.rename; + }); + if (range) { + let newText = tsEdit.newText; + if (_data && typeof _data.rename === 'object' && _data.rename.apply) { + newText = _data.rename.apply(tsEdit.newText); + } + sourceResult.changes[map.sourceFileDocument.uri] ??= []; + sourceResult.changes[map.sourceFileDocument.uri].push({ newText, range }); + hasResult = true; } - sourceResult.changes[map.sourceFileDocument.uri] ??= []; - sourceResult.changes[map.sourceFileDocument.uri].push({ newText, range }); - hasResult = true; } - } - else { - const range = map.toSourceRange(tsEdit.range); - if (range) { - sourceResult.changes[map.sourceFileDocument.uri] ??= []; - sourceResult.changes[map.sourceFileDocument.uri].push({ newText: tsEdit.newText, range }); - hasResult = true; + else { + const range = map.toSourceRange(tsEdit.range); + if (range) { + sourceResult.changes[map.sourceFileDocument.uri] ??= []; + sourceResult.changes[map.sourceFileDocument.uri].push({ newText: tsEdit.newText, range }); + hasResult = true; + } } } } } + else { + sourceResult.changes[tsUri] = tsResult.changes[tsUri]; + hasResult = true; + } } if (tsResult.documentChanges) { for (const tsDocEdit of tsResult.documentChanges) { @@ -254,8 +258,11 @@ export function embeddedEditToSourceEdit( let sourceEdit: typeof tsDocEdit | undefined; if ('textDocument' in tsDocEdit) { - if (documents.isVirtualFileUri(tsDocEdit.textDocument.uri)) { - for (const [_, map] of documents.getMapsByVirtualFileUri(tsDocEdit.textDocument.uri)) { + + const [virtualFile] = project.fileProvider.getVirtualFile(tsDocEdit.textDocument.uri); + + if (virtualFile) { + for (const map of documents.getMapsByVirtualFile(virtualFile)) { sourceEdit = { textDocument: { uri: map.sourceFileDocument.uri, @@ -307,11 +314,11 @@ export function embeddedEditToSourceEdit( sourceEdit = tsDocEdit; // TODO: remove .ts? } else if (tsDocEdit.kind === 'rename') { - if (!documents.isVirtualFileUri(tsDocEdit.oldUri)) { - sourceEdit = tsDocEdit; - } - else { - for (const [_, map] of documents.getMapsByVirtualFileUri(tsDocEdit.oldUri)) { + + const [virtualFile] = project.fileProvider.getVirtualFile(tsDocEdit.oldUri); + + if (virtualFile) { + for (const map of documents.getMapsByVirtualFile(virtualFile)) { // TODO: check capability? sourceEdit = { kind: 'rename', @@ -322,13 +329,16 @@ export function embeddedEditToSourceEdit( } satisfies vscode.RenameFile; } } - } - else if (tsDocEdit.kind === 'delete') { - if (!documents.isVirtualFileUri(tsDocEdit.uri)) { + else { sourceEdit = tsDocEdit; } - else { - for (const [_, map] of documents.getMapsByVirtualFileUri(tsDocEdit.uri)) { + } + else if (tsDocEdit.kind === 'delete') { + + const [virtualFile] = project.fileProvider.getVirtualFile(tsDocEdit.uri); + + if (virtualFile) { + for (const map of documents.getMapsByVirtualFile(virtualFile)) { // TODO: check capability? sourceEdit = { kind: 'delete', @@ -338,6 +348,9 @@ export function embeddedEditToSourceEdit( } satisfies vscode.DeleteFile; } } + else { + sourceEdit = tsDocEdit; + } } if (sourceEdit) { pushEditToDocumentChanges(sourceResult.documentChanges, sourceEdit); diff --git a/packages/language-service/lib/languageFeatures/validation.ts b/packages/language-service/lib/languageFeatures/validation.ts index 4632c442..80e9b810 100644 --- a/packages/language-service/lib/languageFeatures/validation.ts +++ b/packages/language-service/lib/languageFeatures/validation.ts @@ -166,7 +166,7 @@ export function register(context: ServiceContext) { syntax_rules: { errors: [] }, format_rules: { errors: [] }, }).get(uri)!; - const newSnapshot = context.project.fileProvider.getSource(context.env.uriToFileName(uri))?.snapshot; + const newSnapshot = context.project.fileProvider.getSourceFile(uri)?.snapshot; let updateCacheRangeFailed = false; let errorsUpdated = false; @@ -325,8 +325,11 @@ export function register(context: ServiceContext) { const relatedInfos: vscode.DiagnosticRelatedInformation[] = []; for (const info of _error.relatedInformation) { - if (context.documents.isVirtualFileUri(info.location.uri)) { - for (const [_, map] of context.documents.getMapsByVirtualFileUri(info.location.uri)) { + + const [virtualFile] = context.project.fileProvider.getVirtualFile(info.location.uri); + + if (virtualFile) { + for (const map of context.documents.getMapsByVirtualFile(virtualFile)) { const range = map.toSourceRange(info.location.range, filter); if (range) { relatedInfos.push({ diff --git a/packages/language-service/lib/languageFeatures/workspaceSymbols.ts b/packages/language-service/lib/languageFeatures/workspaceSymbols.ts index e656acb4..73e095ca 100644 --- a/packages/language-service/lib/languageFeatures/workspaceSymbols.ts +++ b/packages/language-service/lib/languageFeatures/workspaceSymbols.ts @@ -23,8 +23,11 @@ export function register(context: ServiceContext) { continue; const symbols = embeddedSymbols.map(symbol => transformer.asWorkspaceSymbol(symbol, loc => { - if (context.documents.isVirtualFileUri(loc.uri)) { - for (const [_, map] of context.documents.getMapsByVirtualFileUri(loc.uri)) { + + const [virtualFile] = context.project.fileProvider.getVirtualFile(loc.uri); + + if (virtualFile) { + for (const map of context.documents.getMapsByVirtualFile(virtualFile)) { const range = map.toSourceRange(loc.range); if (range) { return { uri: map.sourceFileDocument.uri, range }; diff --git a/packages/language-service/lib/utils/definePlugin.ts b/packages/language-service/lib/utils/definePlugin.ts index 35909437..9df70374 100644 --- a/packages/language-service/lib/utils/definePlugin.ts +++ b/packages/language-service/lib/utils/definePlugin.ts @@ -1,21 +1,23 @@ -import { DocumentProvider, SourceMapWithDocuments } from '../documents'; import { FileRangeCapabilities, VirtualFile } from '@volar/language-core'; +import { SourceMapWithDocuments } from '../documents'; +import { ServiceContext } from '../types'; export async function visitEmbedded( - documents: DocumentProvider, + context: ServiceContext, current: VirtualFile, cb: (file: VirtualFile, sourceMap: SourceMapWithDocuments) => Promise, rootFile = current, ) { for (const embedded of current.embeddedFiles) { - if (!await visitEmbedded(documents, embedded, cb, rootFile)) { + if (!await visitEmbedded(context, embedded, cb, rootFile)) { return false; } } - for (const [_, map] of documents.getMapsByVirtualFileName(current.fileName)) { - if (documents.getSourceByUri(map.sourceFileDocument.uri)?.root === rootFile) { + for (const map of context.documents.getMapsByVirtualFile(current)) { + const sourceFile = context.project.fileProvider.getSourceFile(map.sourceFileDocument.uri); + if (sourceFile?.root === rootFile) { if (!await cb(current, map)) { return false; } diff --git a/packages/language-service/lib/utils/featureWorkers.ts b/packages/language-service/lib/utils/featureWorkers.ts index 229e204b..509e5b63 100644 --- a/packages/language-service/lib/utils/featureWorkers.ts +++ b/packages/language-service/lib/utils/featureWorkers.ts @@ -40,13 +40,13 @@ export async function languageFeatureWorker( ) { const document = context.getTextDocument(uri); - const virtualFile = context.documents.getSourceByUri(uri)?.root; + const virtualFile = context.project.fileProvider.getSourceFile(uri)?.root; let results: NonNullable>[] = []; if (virtualFile) { - await visitEmbedded(context.documents, virtualFile, async (file, map) => { + await visitEmbedded(context, virtualFile, async (file, map) => { for (const mappedArg of transformArg(arg, map, file)) { diff --git a/packages/monaco/worker.ts b/packages/monaco/worker.ts index dfe1cfbb..7ca12063 100644 --- a/packages/monaco/worker.ts +++ b/packages/monaco/worker.ts @@ -25,7 +25,7 @@ export function createSimpleWorkerService( return createWorkerService( modules, services, - env => { + () => { const lastSnapshots = new Map(); const fileProvider = createFileProvider( @@ -48,9 +48,9 @@ export function createSimpleWorkerService( getLength: () => text.length, getChangeRange: () => undefined, }; - const fileName = env.uriToFileName(model.uri.toString(true)); lastSnapshots.set(model, [model.version, snapshot]); - fileProvider.updateSource(fileName, snapshot, resolveCommonLanguageId(fileName)); + const uri = model.uri.toString(true); + fileProvider.updateSourceFile(uri, snapshot, resolveCommonLanguageId(uri)); } } ); @@ -137,8 +137,9 @@ export function createTypeScriptWorkerService( }; return createTypeScriptProject( - projectHost, languages, + projectHost, + env.fileNameToUri, resolveCommonLanguageId ); }, diff --git a/packages/typescript/index.ts b/packages/typescript/index.ts index 251f7460..ec57723b 100644 --- a/packages/typescript/index.ts +++ b/packages/typescript/index.ts @@ -1,6 +1,6 @@ export * from './lib/documentRegistry'; -export * from './lib/languageService'; -export * from './lib/languageServiceHost'; -export * from './lib/sys'; -export * from './lib/getProgram'; -export * from './lib/serverPlugin'; +export * from './lib/node/decorateLanguageService'; +export * from './lib/node/decorateLanguageServiceHost'; +export * from './lib/protocol/createLanguageServiceHost'; +export * from './lib/protocol/createSys'; +export * from './lib/protocol/getProgram'; diff --git a/packages/typescript/lib/languageService.ts b/packages/typescript/lib/node/decorateLanguageService.ts similarity index 96% rename from packages/typescript/lib/languageService.ts rename to packages/typescript/lib/node/decorateLanguageService.ts index 026ee8a7..e2b8bb59 100644 --- a/packages/typescript/lib/languageService.ts +++ b/packages/typescript/lib/node/decorateLanguageService.ts @@ -26,13 +26,13 @@ export function decorateLanguageService(virtualFiles: FileProvider, languageServ // apis function organizeImports(args: ts.OrganizeImportsArgs, formatOptions: ts.FormatCodeSettings, preferences: ts.UserPreferences | undefined): ReturnType { let edits: readonly ts.FileTextChanges[] = []; - const file = virtualFiles.getSource(args.fileName)?.root; + const file = virtualFiles.getSourceFile(args.fileName)?.root; if (file) { forEachEmbeddedFile(file, embeddedFile => { if (embeddedFile.kind === FileKind.TypeScriptHostFile && embeddedFile.capabilities.codeAction) { edits = edits.concat(_organizeImports({ ...args, - fileName: embeddedFile.fileName, + fileName: embeddedFile.id, }, formatOptions, preferences)); } }); @@ -208,7 +208,7 @@ export function decorateLanguageService(virtualFiles: FileProvider, languageServ if (source) { return { ...changes, - fileName: source.fileName, + fileName: source.id, textChanges: changes.textChanges.map(c => { const span = transformSpan(changes.fileName, c.span); if (span) { @@ -250,7 +250,7 @@ export function decorateLanguageService(virtualFiles: FileProvider, languageServ const [virtualFile, source] = getVirtualFile(documentSpan.fileName); if (virtualFile && source) { textSpan = { - fileName: source.fileName, + fileName: source.id, textSpan: { start: 0, length: 0 }, }; } @@ -288,7 +288,7 @@ export function decorateLanguageService(virtualFiles: FileProvider, languageServ const sourceLoc = map.toSourceOffset(textSpan.start); if (sourceLoc) { return { - fileName: source.fileName, + fileName: source.id, textSpan: { start: sourceLoc[0], length: textSpan.length, @@ -308,12 +308,12 @@ export function decorateLanguageService(virtualFiles: FileProvider, languageServ function getVirtualFile(fileName: string) { if (isTsPlugin) { let result: VirtualFile | undefined; - const source = virtualFiles.getSource(fileName); + const source = virtualFiles.getSourceFile(fileName); if (source?.root) { - forEachEmbeddedFile(source.root, file => { - const ext = file.fileName.replace(fileName, ''); - if (file.kind === FileKind.TypeScriptHostFile && (ext === '.d.ts' || ext.match(/^\.(js|ts)x?$/))) { - result = file; + forEachEmbeddedFile(source.root, virtualFile => { + const ext = virtualFile.id.substring(fileName.length); + if (virtualFile.kind === FileKind.TypeScriptHostFile && (ext === '.d.ts' || ext.match(/^\.(js|ts)x?$/))) { + result = virtualFile; } }); } diff --git a/packages/typescript/lib/serverPlugin.ts b/packages/typescript/lib/node/decorateLanguageServiceHost.ts similarity index 95% rename from packages/typescript/lib/serverPlugin.ts rename to packages/typescript/lib/node/decorateLanguageServiceHost.ts index e8d15659..4610e0c4 100644 --- a/packages/typescript/lib/serverPlugin.ts +++ b/packages/typescript/lib/node/decorateLanguageServiceHost.ts @@ -176,11 +176,11 @@ export function decorateLanguageServiceHost( if (text !== undefined) { extraProjectVersion++; - const virtualFile = virtualFiles.updateSource(fileName, ts.ScriptSnapshot.fromString(text), resolveCommonLanguageId(fileName)); + const virtualFile = virtualFiles.updateSourceFile(fileName, ts.ScriptSnapshot.fromString(text), resolveCommonLanguageId(fileName)); if (virtualFile) { let patchedText = text.split('\n').map(line => ' '.repeat(line.length)).join('\n'); forEachEmbeddedFile(virtualFile, file => { - const ext = file.fileName.substring(fileName.length); + const ext = file.id.substring(fileName.length); if (file.kind === FileKind.TypeScriptHostFile && (ext === '.d.ts' || ext.match(/^\.(js|ts)x?$/))) { extension = ext; patchedText += file.snapshot.getText(0, file.snapshot.getLength()); @@ -189,9 +189,9 @@ export function decorateLanguageServiceHost( snapshot = ts.ScriptSnapshot.fromString(patchedText); } } - else if (virtualFiles.hasSource(fileName)) { + else if (virtualFiles.getSourceFile(fileName)) { extraProjectVersion++; - virtualFiles.deleteSource(fileName); + virtualFiles.deleteSourceFile(fileName); } scripts.set(fileName, { diff --git a/packages/typescript/lib/languageServiceHost.ts b/packages/typescript/lib/protocol/createLanguageServiceHost.ts similarity index 88% rename from packages/typescript/lib/languageServiceHost.ts rename to packages/typescript/lib/protocol/createLanguageServiceHost.ts index 5c1cff89..eca02a86 100644 --- a/packages/typescript/lib/languageServiceHost.ts +++ b/packages/typescript/lib/protocol/createLanguageServiceHost.ts @@ -1,13 +1,17 @@ -import { type FileKind, type TypeScriptProjectHost, type VirtualFile, type FileProvider, resolveCommonLanguageId } from '@volar/language-core'; -import type * as ts from 'typescript/lib/tsserverlibrary'; +import { resolveCommonLanguageId, type FileKind, type FileProvider, type TypeScriptProjectHost, type VirtualFile } from '@volar/language-core'; import * as path from 'path-browserify'; -import { matchFiles } from './typescript/utilities'; +import type * as ts from 'typescript/lib/tsserverlibrary'; +import { matchFiles } from '../typescript/utilities'; const fileVersions = new Map; }>(); export function createLanguageServiceHost( projectHost: TypeScriptProjectHost, fileProvider: FileProvider, + { fileNameToId, idToFileName }: { + fileNameToId(fileName: string): string; + idToFileName(id: string): string; + }, ts: typeof import('typescript/lib/tsserverlibrary'), sys: ts.System & { version?: number; @@ -58,7 +62,7 @@ export function createLanguageServiceHost( getScriptKind(fileName) { if (ts) { - if (fileProvider.hasSource(fileName)) + if (fileProvider.getSourceFile(fileNameToId(fileName))) return ts.ScriptKind.Deferred; switch (path.extname(fileName)) { @@ -165,10 +169,9 @@ export function createLanguageServiceHost( const newTsVirtualFileSnapshots = new Set(); const newOtherVirtualFileSnapshots = new Set(); - for (const [fileName] of fileProvider.getAllSources()) { - const source = fileProvider.getSource(fileName); - if (source?.root) { - forEachEmbeddedFile(source.root, embedded => { + for (const sourceFile of fileProvider.getAllSourceFiles()) { + if (sourceFile.root) { + forEachEmbeddedFile(sourceFile.root, embedded => { if (embedded.kind === 1 satisfies FileKind.TypeScriptHostFile) { newTsVirtualFileSnapshots.add(embedded.snapshot); } @@ -191,18 +194,18 @@ export function createLanguageServiceHost( oldOtherVirtualFileSnapshots = newOtherVirtualFileSnapshots; const tsFileNamesSet = new Set(); - for (const [fileName] of fileProvider.getAllSources()) { - const source = fileProvider.getSource(fileName); - if (source?.root) { - forEachEmbeddedFile(source.root, embedded => { + for (const sourceFile of fileProvider.getAllSourceFiles()) { + if (sourceFile.root) { + forEachEmbeddedFile(sourceFile.root, embedded => { if (embedded.kind === 1 satisfies FileKind.TypeScriptHostFile) { - tsFileNamesSet.add(embedded.fileName); // virtual .ts + tsFileNamesSet.add(idToFileName(embedded.id)); // virtual .ts } }); } } for (const fileName of projectHost.getScriptFileNames()) { - if (!fileProvider.getSource(fileName)?.root) { + const uri = fileNameToId(fileName); + if (!fileProvider.getSourceFile(uri)?.root) { tsFileNamesSet.add(fileName); // .ts } } @@ -251,9 +254,9 @@ export function createLanguageServiceHost( sys?.realpath ? (path => sys.realpath!(path)) : (path => path), ); matches = matches.map(match => { - const [_, source] = fileProvider.getVirtualFile(match); + const [_, source] = fileProvider.getVirtualFile(fileNameToId(match)); if (source) { - return source.fileName; + return idToFileName(source.id); } return match; }); @@ -288,7 +291,8 @@ export function createLanguageServiceHost( function getScriptSnapshot(fileName: string) { // virtual files - const [virtualFile] = fileProvider.getVirtualFile(fileName); + const uri = fileNameToId(fileName); + const [virtualFile] = fileProvider.getVirtualFile(uri); if (virtualFile) { return virtualFile.snapshot; } @@ -315,7 +319,8 @@ export function createLanguageServiceHost( function getScriptVersion(fileName: string) { // virtual files / root files / opened files - const [virtualFile] = fileProvider.getVirtualFile(fileName); + const uri = fileNameToId(fileName); + const [virtualFile] = fileProvider.getVirtualFile(uri); const snapshot = virtualFile?.snapshot ?? projectHost.getScriptSnapshot(fileName); if (snapshot) { if (!fileVersions.has(fileName)) { @@ -338,7 +343,6 @@ export function createLanguageServiceHost( function fileExists(fileName: string) { // fill external virtual files - const ext = fileName.substring(fileName.lastIndexOf('.')); if ( ext === '.js' @@ -356,17 +360,19 @@ export function createLanguageServiceHost( */ const sourceFileName = fileName.substring(0, fileName.lastIndexOf('.')); + const sourceFileUri = fileNameToId(sourceFileName); - if (!fileProvider.hasSource(sourceFileName)) { + if (!fileProvider.getSourceFile(sourceFileUri)) { const scriptSnapshot = getScriptSnapshot(sourceFileName); if (scriptSnapshot) { - fileProvider.updateSource(sourceFileName, scriptSnapshot, resolveCommonLanguageId(sourceFileName)); + fileProvider.updateSourceFile(sourceFileUri, scriptSnapshot, resolveCommonLanguageId(sourceFileName)); } } } // virtual files - if (fileProvider.hasVirtualFile(fileName)) { + const uri = fileNameToId(fileName); + if (fileProvider.getVirtualFile(uri)[0]) { return true; } diff --git a/packages/typescript/lib/sys.ts b/packages/typescript/lib/protocol/createSys.ts similarity index 99% rename from packages/typescript/lib/sys.ts rename to packages/typescript/lib/protocol/createSys.ts index f5fd72c1..8fb44bba 100644 --- a/packages/typescript/lib/sys.ts +++ b/packages/typescript/lib/protocol/createSys.ts @@ -1,7 +1,7 @@ import type { FileChangeType, FileType, ServiceEnvironment, Disposable, FileStat } from '@volar/language-service'; import type * as ts from 'typescript/lib/tsserverlibrary'; import * as path from 'path-browserify'; -import { matchFiles } from './typescript/utilities'; +import { matchFiles } from '../typescript/utilities'; interface File { text?: string; diff --git a/packages/typescript/lib/getProgram.ts b/packages/typescript/lib/protocol/getProgram.ts similarity index 89% rename from packages/typescript/lib/getProgram.ts rename to packages/typescript/lib/protocol/getProgram.ts index 231b37ad..2d050370 100644 --- a/packages/typescript/lib/getProgram.ts +++ b/packages/typescript/lib/protocol/getProgram.ts @@ -4,6 +4,10 @@ import type * as ts from 'typescript/lib/tsserverlibrary'; export function getProgram( ts: typeof import('typescript/lib/tsserverlibrary'), fileProvider: FileProvider, + { fileNameToId, idToFileName }: { + fileNameToId(fileName: string): string; + idToFileName(id: string): string; + }, ls: ts.LanguageService, sys: ts.System, ): ts.Program { @@ -67,7 +71,8 @@ export function getProgram( if (sourceFile) { - const [virtualFile, source] = fileProvider.getVirtualFile(sourceFile.fileName); + const uri = fileNameToId(sourceFile.fileName); + const [virtualFile, source] = fileProvider.getVirtualFile(uri); if (virtualFile && source) { @@ -106,11 +111,14 @@ export function getProgram( && diagnostic.length !== undefined ) { - const [virtualFile, source] = fileProvider.getVirtualFile(diagnostic.file.fileName); + const uri = fileNameToId(diagnostic.file.fileName); + const [virtualFile, source] = fileProvider.getVirtualFile(uri); if (virtualFile && source) { - if (sys.fileExists?.(source.fileName) === false) + const sourceFileName = idToFileName(source.id); + + if (sys.fileExists?.(sourceFileName) === false) continue; for (const [_, [sourceSnapshot, map]] of fileProvider.getMaps(virtualFile)) { @@ -130,7 +138,7 @@ export function getProgram( if (!reportEnd) continue; - onMapping(diagnostic, source.fileName, start[0], end[0], source.snapshot.getText(0, source.snapshot.getLength())); + onMapping(diagnostic, sourceFileName, start[0], end[0], source.snapshot.getText(0, source.snapshot.getLength())); break; } break; @@ -160,7 +168,8 @@ export function getProgram( if (!file) { if (docText === undefined) { - const snapshot = fileProvider.getSource(fileName)?.snapshot; + const uri = fileNameToId(fileName); + const snapshot = fileProvider.getSourceFile(uri)?.snapshot; if (snapshot) { docText = snapshot.getText(0, snapshot.getLength()); }