diff --git a/src/LanguageServer.spec.ts b/src/LanguageServer.spec.ts index dab32a67b..860d1de58 100644 --- a/src/LanguageServer.spec.ts +++ b/src/LanguageServer.spec.ts @@ -1,7 +1,7 @@ import { expect } from './chai-config.spec'; import * as fsExtra from 'fs-extra'; import * as path from 'path'; -import type { DidChangeWatchedFilesParams, Location, PublishDiagnosticsParams } from 'vscode-languageserver'; +import type { DidChangeWatchedFilesParams, Location, PublishDiagnosticsParams, WorkspaceFolder } from 'vscode-languageserver'; import { FileChangeType } from 'vscode-languageserver'; import { Deferred } from './deferred'; import { CustomCommands, LanguageServer } from './LanguageServer'; @@ -525,6 +525,28 @@ describe('LanguageServer', () => { await test(s`${rootDir}/source/main.brs`, false); }); + + it.only('excludes explicit workspaceFolder paths', async () => { + (server as any).connection = connection; + sinon.stub(server['connection'].workspace, 'getWorkspaceFolders').returns(Promise.resolve([{ + name: 'workspace1', + uri: util.pathToUri(s`${tempDir}/project1`) + } as WorkspaceFolder])); + + const stub = sinon.stub(server['projectManager'], 'handleFileChanges').callsFake(() => Promise.resolve()); + + await server['onDidChangeWatchedFiles']({ + changes: [{ + type: FileChangeType.Created, + uri: util.pathToUri(s`${tempDir}/project1`) + }] + } as DidChangeWatchedFilesParams); + + //it did not send along the workspace folder itself + expect( + stub.getCalls()[0].args[0] + ).to.eql([]); + }); }); describe('onDocumentClose', () => { diff --git a/src/LanguageServer.ts b/src/LanguageServer.ts index c05bbd8b1..48f7a6e79 100644 --- a/src/LanguageServer.ts +++ b/src/LanguageServer.ts @@ -353,12 +353,17 @@ export class LanguageServer { */ @AddStackToErrorMessage public async onDidChangeWatchedFiles(params: DidChangeWatchedFilesParams) { - const changes = params.changes.map(x => ({ - srcPath: util.uriToPath(x.uri), - type: x.type, - //if this is an open document, allow this file to be loaded in a standalone project (if applicable) - allowStandaloneProject: this.documents.get(x.uri) !== undefined - })); + const workspacePaths = (await this.connection.workspace.getWorkspaceFolders()).map(x => util.uriToPath(x.uri)); + + let changes = params.changes + .map(x => ({ + srcPath: util.uriToPath(x.uri), + type: x.type, + //if this is an open document, allow this file to be loaded in a standalone project (if applicable) + allowStandaloneProject: this.documents.get(x.uri) !== undefined + })) + //exclude all explicit top-level workspace folder paths (to fix a weird macos fs watcher bug that emits events for the workspace folder itself) + .filter(x => !workspacePaths.includes(x.srcPath)); this.logger.debug('onDidChangeWatchedFiles', changes); diff --git a/src/lsp/ProjectManager.ts b/src/lsp/ProjectManager.ts index 2008d9ac9..8da81629d 100644 --- a/src/lsp/ProjectManager.ts +++ b/src/lsp/ProjectManager.ts @@ -312,11 +312,10 @@ export class ProjectManager { }); public handleFileChanges(changes: FileChange[]) { - this.logger.log('handleFileChanges', changes.map(x => x.srcPath)); + this.logger.debug('handleFileChanges', changes.map(x => `${FileChangeType[x.type]}: ${x.srcPath}`)); //this function should NOT be marked as async, because typescript wraps the body in an async call sometimes. These need to be registered synchronously return this.fileChangesQueue.run(async (changes) => { - this.logger.log('handleFileChanges -> run', changes.map(x => x.srcPath)); //wait for any pending syncs to finish await this.onInitialized(); @@ -332,6 +331,8 @@ export class ProjectManager { //filter any changes that are not allowed by the path filterer changes = this.pathFilterer.filter(changes, x => x.srcPath); + this.logger.debug('handleFileChanges -> filtered', changes.map(x => `${FileChangeType[x.type]}: ${x.srcPath}`)); + //process all file changes in parallel await Promise.all(changes.map(async (change) => { await this.handleFileChange(change);