Skip to content

Commit

Permalink
attempt to fall back to chokidar file watcher if the lsp client doesn…
Browse files Browse the repository at this point in the history
…'t support `capabilities.workspace.didChangeWatchedFiles.dynamicRegistration`
  • Loading branch information
DetachHead committed Jan 23, 2025
1 parent 3bb4dd2 commit d08a3cd
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
import * as chokidar from 'chokidar';

import { ConsoleInterface } from './console';
import { FileWatcher, FileWatcherEventHandler, FileWatcherProvider } from './fileWatcher';
import { FileWatcherEventHandler, FileWatcherProvider } from './fileWatcher';

const _isMacintosh = process.platform === 'darwin';
const _isLinux = process.platform === 'linux';

export class ChokidarFileWatcherProvider implements FileWatcherProvider {
constructor(private _console?: ConsoleInterface) {}

createFileWatcher(paths: string[], listener: FileWatcherEventHandler): FileWatcher {
createFileWatcher(paths: string[], listener: FileWatcherEventHandler): chokidar.FSWatcher {
return this._createFileSystemWatcher(paths).on('all', listener);
}

Expand Down Expand Up @@ -56,8 +56,8 @@ export class ChokidarFileWatcherProvider implements FileWatcherProvider {
watcherOptions.ignored = excludes;

const watcher = chokidar.watch(paths, watcherOptions);
watcher.on('error', (_) => {
this._console?.error('Error returned from file system watcher.');
watcher.on('error', (e) => {
this._console?.error(`Error returned from file system watcher: ${e}`);
});

// Detect if for some reason the native watcher library fails to load
Expand Down
34 changes: 34 additions & 0 deletions packages/pyright-internal/src/common/realFileSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { FileUri, FileUriSchema } from './uri/fileUri';
import { Uri } from './uri/uri';
import { getRootUri } from './uri/uriUtils';
import { isMainThread } from './workersHost';
import { ChokidarFileWatcherProvider } from './chokidarFileWatcherProvider';
import { FSWatcher } from 'chokidar';

// Automatically remove files created by tmp at process exit.
tmp.setGracefulCleanup();
Expand Down Expand Up @@ -466,10 +468,38 @@ interface WorkspaceFileWatcher extends FileWatcher {
eventHandler: FileWatcherEventHandler;
}

/**
* file watcher provider for lsp clients that support `capabilities.workspace.didChangeWatchedFiles.dynamicRegistration`.
*
* this class also contains functionality to convert its file watchers to chokidar ones. this is a bit gross but necessary
* because this class is created before we know which kid of file watcher the lsp client requires.
*/
export class WorkspaceFileWatcherProvider implements FileWatcherProvider, FileWatcherHandler {
private _fileWatchers: WorkspaceFileWatcher[] = [];
private _chokidarFileWatchers?: FSWatcher[];
private _chokidarFileWatcherProvider?: ChokidarFileWatcherProvider;

/**
* converts all file watchers created with {@link createFileWatcher} to chokidar file watchers.
*/
convertToChokidar = (console: ConsoleInterface) => {
console.info(`existing file watchers: ${this._fileWatchers}`);
if (this._chokidarFileWatchers) {
return;
}
this._chokidarFileWatcherProvider = new ChokidarFileWatcherProvider(console);
this._chokidarFileWatchers = this._fileWatchers.map((fileWatcher) => {
return this._chokidarFileWatcherProvider!.createFileWatcher(
fileWatcher.workspacePaths,
fileWatcher.eventHandler
);
});
};

createFileWatcher(workspacePaths: string[], listener: FileWatcherEventHandler): FileWatcher {
if (this._chokidarFileWatcherProvider) {
return this._chokidarFileWatcherProvider.createFileWatcher(workspacePaths, listener);
}
const self = this;
const fileWatcher: WorkspaceFileWatcher = {
close() {
Expand All @@ -487,6 +517,10 @@ export class WorkspaceFileWatcherProvider implements FileWatcherProvider, FileWa
}

onFileChange(eventType: FileWatcherEventType, fileUri: Uri): void {
if (this._chokidarFileWatchers) {
this._chokidarFileWatchers.forEach((fileWatcher) => fileWatcher.emit(eventType, fileUri.getFilePath()));
return;
}
// Since file watcher is a server wide service, we don't know which watcher is
// for which workspace (for multi workspace case), also, we don't know which watcher
// is for source or library. so we need to solely rely on paths that can cause us
Expand Down
3 changes: 3 additions & 0 deletions packages/pyright-internal/src/languageServerBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ import { SemanticTokensProvider, SemanticTokensProviderLegend } from './language
import { RenameUsageFinder } from './analyzer/renameUsageFinder';
import { BaselineHandler } from './baseline';
import { assert } from './common/debug';
import { WorkspaceFileWatcherProvider } from './common/realFileSystem';

export abstract class LanguageServerBase implements LanguageServerInterface, Disposable {
// We support running only one "find all reference" at a time.
Expand Down Expand Up @@ -594,6 +595,8 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
this.workspaceFactory
)
);
} else if (this.serverOptions.fileWatcherHandler instanceof WorkspaceFileWatcherProvider) {
this.serverOptions.fileWatcherHandler.convertToChokidar(this.console);
}
const result: InitializeResult = {
capabilities: {
Expand Down

0 comments on commit d08a3cd

Please sign in to comment.