diff --git a/extensions/html-language-features/client/src/customData.ts b/extensions/html-language-features/client/src/customData.ts index ecf964056e5..02c5b0d0a7d 100644 --- a/extensions/html-language-features/client/src/customData.ts +++ b/extensions/html-language-features/client/src/customData.ts @@ -6,6 +6,7 @@ import { workspace, extensions, Uri, EventEmitter, Disposable } from 'vscode'; import { resolvePath, joinPath } from './requests'; + export function getCustomDataSource(toDispose: Disposable[]) { let pathsInWorkspace = getCustomDataPathsInAllWorkspaces(); let pathsInExtensions = getCustomDataPathsFromAllExtensions(); @@ -14,7 +15,7 @@ export function getCustomDataSource(toDispose: Disposable[]) { toDispose.push(extensions.onDidChange(_ => { const newPathsInExtensions = getCustomDataPathsFromAllExtensions(); - if (newPathsInExtensions.length !== pathsInExtensions.length || !newPathsInExtensions.every((val, idx) => val === pathsInExtensions[idx])) { + if (pathsInExtensions.size !== newPathsInExtensions.size || ![...pathsInExtensions].every(path => newPathsInExtensions.has(path))) { pathsInExtensions = newPathsInExtensions; onChange.fire(); } @@ -26,9 +27,16 @@ export function getCustomDataSource(toDispose: Disposable[]) { } })); + toDispose.push(workspace.onDidChangeTextDocument(e => { + const path = e.document.uri.toString(); + if (pathsInExtensions.has(path) || pathsInWorkspace.has(path)) { + onChange.fire(); + } + })); + return { get uris() { - return pathsInWorkspace.concat(pathsInExtensions); + return [...pathsInWorkspace].concat([...pathsInExtensions]); }, get onDidChange() { return onChange.event; @@ -36,21 +44,31 @@ export function getCustomDataSource(toDispose: Disposable[]) { }; } +function isURI(uriOrPath: string) { + return /^(?\w[\w\d+.-]*):/.test(uriOrPath); +} -function getCustomDataPathsInAllWorkspaces(): string[] { + +function getCustomDataPathsInAllWorkspaces(): Set { const workspaceFolders = workspace.workspaceFolders; - const dataPaths: string[] = []; + const dataPaths = new Set(); if (!workspaceFolders) { return dataPaths; } - const collect = (paths: string[] | undefined, rootFolder: Uri) => { - if (Array.isArray(paths)) { - for (const path of paths) { - if (typeof path === 'string') { - dataPaths.push(resolvePath(rootFolder, path).toString()); + const collect = (uriOrPaths: string[] | undefined, rootFolder: Uri) => { + if (Array.isArray(uriOrPaths)) { + for (const uriOrPath of uriOrPaths) { + if (typeof uriOrPath === 'string') { + if (!isURI(uriOrPath)) { + // path in the workspace + dataPaths.add(resolvePath(rootFolder, uriOrPath).toString()); + } else { + // external uri + dataPaths.add(uriOrPath); + } } } } @@ -74,13 +92,20 @@ function getCustomDataPathsInAllWorkspaces(): string[] { return dataPaths; } -function getCustomDataPathsFromAllExtensions(): string[] { - const dataPaths: string[] = []; +function getCustomDataPathsFromAllExtensions(): Set { + const dataPaths = new Set(); for (const extension of extensions.all) { const customData = extension.packageJSON?.contributes?.html?.customData; if (Array.isArray(customData)) { - for (const rp of customData) { - dataPaths.push(joinPath(extension.extensionUri, rp).toString()); + for (const uriOrPath of customData) { + if (!isURI(uriOrPath)) { + // relative path in an extension + dataPaths.add(joinPath(extension.extensionUri, uriOrPath).toString()); + } else { + // external uri + dataPaths.add(uriOrPath); + } + } } } diff --git a/extensions/html-language-features/client/src/htmlClient.ts b/extensions/html-language-features/client/src/htmlClient.ts index 42e75a297e5..bfb241c2250 100644 --- a/extensions/html-language-features/client/src/htmlClient.ts +++ b/extensions/html-language-features/client/src/htmlClient.ts @@ -16,7 +16,7 @@ import { DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, TextDocumentIdentifier, RequestType0, Range as LspRange, NotificationType, CommonLanguageClient } from 'vscode-languageclient'; import { activateTagClosing } from './tagClosing'; -import { RequestService } from './requests'; +import { RequestService, serveFileSystemRequests } from './requests'; import { getCustomDataSource } from './customData'; namespace CustomDataChangedNotification { @@ -120,6 +120,8 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua toDispose.push(disposable); client.onReady().then(() => { + toDispose.push(serveFileSystemRequests(client, runtime)); + client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); customDataSource.onDidChange(() => { client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); diff --git a/extensions/html-language-features/client/src/requests.ts b/extensions/html-language-features/client/src/requests.ts index f127c88562f..867ad6fc7d4 100644 --- a/extensions/html-language-features/client/src/requests.ts +++ b/extensions/html-language-features/client/src/requests.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Uri, workspace } from 'vscode'; +import { Uri, workspace, Disposable } from 'vscode'; import { RequestType, CommonLanguageClient } from 'vscode-languageclient'; import { Runtime } from './htmlClient'; @@ -18,8 +18,9 @@ export namespace FsReadDirRequest { export const type: RequestType = new RequestType('fs/readDir'); } -export function serveFileSystemRequests(client: CommonLanguageClient, runtime: Runtime) { - client.onRequest(FsContentRequest.type, (param: { uri: string; encoding?: string; }) => { +export function serveFileSystemRequests(client: CommonLanguageClient, runtime: Runtime): Disposable { + const disposables = []; + disposables.push(client.onRequest(FsContentRequest.type, (param: { uri: string; encoding?: string; }) => { const uri = Uri.parse(param.uri); if (uri.scheme === 'file' && runtime.fs) { return runtime.fs.getContent(param.uri); @@ -27,21 +28,22 @@ export function serveFileSystemRequests(client: CommonLanguageClient, runtime: R return workspace.fs.readFile(uri).then(buffer => { return new runtime.TextDecoder(param.encoding).decode(buffer); }); - }); - client.onRequest(FsReadDirRequest.type, (uriString: string) => { + })); + disposables.push(client.onRequest(FsReadDirRequest.type, (uriString: string) => { const uri = Uri.parse(uriString); if (uri.scheme === 'file' && runtime.fs) { return runtime.fs.readDirectory(uriString); } return workspace.fs.readDirectory(uri); - }); - client.onRequest(FsStatRequest.type, (uriString: string) => { + })); + disposables.push(client.onRequest(FsStatRequest.type, (uriString: string) => { const uri = Uri.parse(uriString); if (uri.scheme === 'file' && runtime.fs) { return runtime.fs.stat(uriString); } return workspace.fs.stat(uri); - }); + })); + return Disposable.from(...disposables); } export enum FileType {