From 1e256a1532cffc1c07d90f3fca68fa596facacdc Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Tue, 23 Feb 2021 09:48:55 +0800 Subject: [PATCH 1/2] fallback file watcher --- .../src/lib/FallbackWatcher.ts | 47 +++++++++++++++++++ packages/language-server/src/server.ts | 26 ++++++++-- 2 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 packages/language-server/src/lib/FallbackWatcher.ts diff --git a/packages/language-server/src/lib/FallbackWatcher.ts b/packages/language-server/src/lib/FallbackWatcher.ts new file mode 100644 index 000000000..79dbfce5a --- /dev/null +++ b/packages/language-server/src/lib/FallbackWatcher.ts @@ -0,0 +1,47 @@ +import { FSWatcher, watch } from 'chokidar'; +import { join } from 'path'; +import { DidChangeWatchedFilesParams, FileChangeType, FileEvent } from 'vscode-languageserver'; +import { pathToUrl } from '../utils'; + +type DidChangeHandler = (para: DidChangeWatchedFilesParams) => void; + +export class FallbackWatcher { + private readonly watcher: FSWatcher; + private readonly callbacks: DidChangeHandler[] = []; + + constructor(glob: string, private readonly workspacePath: string) { + this.watcher = watch(glob, { + cwd: workspacePath + }); + + this.watcher.on('add', (path) => this.callback(path, FileChangeType.Created)); + this.watcher.on('unlink', (path) => this.callback(path, FileChangeType.Deleted)); + this.watcher.on('change', (path) => this.callback(path, FileChangeType.Changed)); + } + + private convert(relativePath: string, type: FileChangeType): DidChangeWatchedFilesParams { + const path = join(this.workspacePath, relativePath); + + const event: FileEvent = { + type, + uri: pathToUrl(path) + }; + + return { + changes: [event] + }; + } + + private callback(path: string, type: FileChangeType) { + const para = this.convert(path, type); + this.callbacks.forEach((callback) => callback(para)); + } + + onDidChangeWatchedFiles(callback: DidChangeHandler) { + this.callbacks.push(callback); + } + + dispose() { + this.watcher.close(); + } +} diff --git a/packages/language-server/src/server.ts b/packages/language-server/src/server.ts index e42e1bee0..5cbabff53 100644 --- a/packages/language-server/src/server.ts +++ b/packages/language-server/src/server.ts @@ -14,7 +14,8 @@ import { TextDocumentSyncKind, WorkspaceEdit, SemanticTokensRequest, - SemanticTokensRangeRequest + SemanticTokensRangeRequest, + DidChangeWatchedFilesParams } from 'vscode-languageserver'; import { IPCMessageReader, IPCMessageWriter, createConnection } from 'vscode-languageserver/node'; import { DiagnosticsManager } from './lib/DiagnosticsManager'; @@ -31,7 +32,8 @@ import { TypeScriptPlugin, OnWatchFileChangesPara } from './plugins'; -import { urlToPath } from './utils'; +import { isNotNullOrUndefined, urlToPath } from './utils'; +import { FallbackWatcher } from './lib/FallbackWatcher'; namespace TagCloseRequest { export const type: RequestType< @@ -85,6 +87,7 @@ export function startServer(options?: LSOptions) { const configManager = new LSConfigManager(); const pluginHost = new PluginHost(docManager); let sveltePlugin: SveltePlugin = undefined as any; + let watchers: FallbackWatcher[] | undefined; connection.onInitialize((evt) => { const workspaceUris = evt.workspaceFolders?.map((folder) => folder.uri.toString()) ?? [ @@ -95,6 +98,14 @@ export function startServer(options?: LSOptions) { Logger.error('No workspace path set'); } + if (!evt.capabilities.workspace?.didChangeWatchedFiles) { + watchers = workspaceUris + .map(urlToPath) + .filter(isNotNullOrUndefined) + .map((path) => new FallbackWatcher('**/*.{ts,js}', path)); + watchers.forEach((watcher) => watcher.onDidChangeWatchedFiles(onDidChangeWatchedFiles)); + } + configManager.update(evt.initializationOptions?.config || {}); configManager.updateTsJsUserPreferences(evt.initializationOptions?.typescriptConfig || {}); configManager.updateEmmetConfig(evt.initializationOptions?.emmetConfig || {}); @@ -199,6 +210,10 @@ export function startServer(options?: LSOptions) { }; }); + connection.onExit(() => { + watchers?.forEach((watcher) => watcher.dispose()); + }); + connection.onRenameRequest((req) => pluginHost.rename(req.textDocument, req.position, req.newName) ); @@ -285,7 +300,9 @@ export function startServer(options?: LSOptions) { ); const updateAllDiagnostics = _.debounce(() => diagnosticsManager.updateAll(), 1000); - connection.onDidChangeWatchedFiles((para) => { + + connection.onDidChangeWatchedFiles(onDidChangeWatchedFiles); + function onDidChangeWatchedFiles(para: DidChangeWatchedFilesParams) { const onWatchFileChangesParas = para.changes .map((change) => ({ fileName: urlToPath(change.uri), @@ -296,7 +313,8 @@ export function startServer(options?: LSOptions) { pluginHost.onWatchFileChanges(onWatchFileChangesParas); updateAllDiagnostics(); - }); + } + connection.onDidSaveTextDocument(updateAllDiagnostics); connection.onNotification('$/onDidChangeTsOrJsFile', async (e: any) => { const path = urlToPath(e.uri); From 077bad72be926d2da9634d2673c7ebf583d4f3ae Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Tue, 23 Feb 2021 13:28:27 +0800 Subject: [PATCH 2/2] only one watcher --- .../src/lib/FallbackWatcher.ts | 19 ++++++++----------- packages/language-server/src/server.ts | 12 +++++------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/packages/language-server/src/lib/FallbackWatcher.ts b/packages/language-server/src/lib/FallbackWatcher.ts index 79dbfce5a..9e82e4bfa 100644 --- a/packages/language-server/src/lib/FallbackWatcher.ts +++ b/packages/language-server/src/lib/FallbackWatcher.ts @@ -9,19 +9,16 @@ export class FallbackWatcher { private readonly watcher: FSWatcher; private readonly callbacks: DidChangeHandler[] = []; - constructor(glob: string, private readonly workspacePath: string) { - this.watcher = watch(glob, { - cwd: workspacePath - }); - - this.watcher.on('add', (path) => this.callback(path, FileChangeType.Created)); - this.watcher.on('unlink', (path) => this.callback(path, FileChangeType.Deleted)); - this.watcher.on('change', (path) => this.callback(path, FileChangeType.Changed)); - } + constructor(glob: string, workspacePaths: string[]) { + this.watcher = watch(workspacePaths.map((workspacePath) => join(workspacePath, glob))); - private convert(relativePath: string, type: FileChangeType): DidChangeWatchedFilesParams { - const path = join(this.workspacePath, relativePath); + this.watcher + .on('add', (path) => this.callback(path, FileChangeType.Created)) + .on('unlink', (path) => this.callback(path, FileChangeType.Deleted)) + .on('change', (path) => this.callback(path, FileChangeType.Changed)); + } + private convert(path: string, type: FileChangeType): DidChangeWatchedFilesParams { const event: FileEvent = { type, uri: pathToUrl(path) diff --git a/packages/language-server/src/server.ts b/packages/language-server/src/server.ts index 5cbabff53..9b8d677d9 100644 --- a/packages/language-server/src/server.ts +++ b/packages/language-server/src/server.ts @@ -87,7 +87,7 @@ export function startServer(options?: LSOptions) { const configManager = new LSConfigManager(); const pluginHost = new PluginHost(docManager); let sveltePlugin: SveltePlugin = undefined as any; - let watchers: FallbackWatcher[] | undefined; + let watcher: FallbackWatcher | undefined; connection.onInitialize((evt) => { const workspaceUris = evt.workspaceFolders?.map((folder) => folder.uri.toString()) ?? [ @@ -99,11 +99,9 @@ export function startServer(options?: LSOptions) { } if (!evt.capabilities.workspace?.didChangeWatchedFiles) { - watchers = workspaceUris - .map(urlToPath) - .filter(isNotNullOrUndefined) - .map((path) => new FallbackWatcher('**/*.{ts,js}', path)); - watchers.forEach((watcher) => watcher.onDidChangeWatchedFiles(onDidChangeWatchedFiles)); + const workspacePaths = workspaceUris.map(urlToPath).filter(isNotNullOrUndefined); + watcher = new FallbackWatcher('**/*.{ts,js}', workspacePaths); + watcher.onDidChangeWatchedFiles(onDidChangeWatchedFiles); } configManager.update(evt.initializationOptions?.config || {}); @@ -211,7 +209,7 @@ export function startServer(options?: LSOptions) { }); connection.onExit(() => { - watchers?.forEach((watcher) => watcher.dispose()); + watcher?.dispose(); }); connection.onRenameRequest((req) =>