diff --git a/news/2 Fixes/5333.md b/news/2 Fixes/5333.md new file mode 100644 index 000000000000..30f632405b25 --- /dev/null +++ b/news/2 Fixes/5333.md @@ -0,0 +1 @@ +Restrict files from being processed by `Language Server` only when in a mult-root workspace. \ No newline at end of file diff --git a/src/client/activation/languageServer/analysisOptions.ts b/src/client/activation/languageServer/analysisOptions.ts index caa9172c7e45..b6bbb4f8cab5 100644 --- a/src/client/activation/languageServer/analysisOptions.ts +++ b/src/client/activation/languageServer/analysisOptions.ts @@ -5,8 +5,8 @@ import { inject, injectable, named } from 'inversify'; import * as path from 'path'; -import { CancellationToken, CompletionContext, ConfigurationChangeEvent, Disposable, Event, EventEmitter, OutputChannel, Position, TextDocument } from 'vscode'; -import { LanguageClientOptions, ProvideCompletionItemsSignature, RevealOutputChannelOn } from 'vscode-languageclient'; +import { CancellationToken, CompletionContext, ConfigurationChangeEvent, Disposable, Event, EventEmitter, OutputChannel, Position, TextDocument, WorkspaceFolder } from 'vscode'; +import { DocumentFilter, DocumentSelector, LanguageClientOptions, ProvideCompletionItemsSignature, RevealOutputChannelOn } from 'vscode-languageclient'; import { IWorkspaceService } from '../../common/application/types'; import { isTestExecution, PYTHON_LANGUAGE, STANDARD_OUTPUT_CHANNEL } from '../../common/constants'; import { traceDecorators, traceError } from '../../common/logger'; @@ -103,17 +103,10 @@ export class LanguageServerAnalysisOptions implements ILanguageServerAnalysisOpt this.excludedFiles = this.getExcludedFiles(); this.typeshedPaths = this.getTypeshedPaths(); const workspaceFolder = this.workspace.getWorkspaceFolder(this.resource); - const documentSelector = [ - { scheme: 'file', language: PYTHON_LANGUAGE }, - { scheme: 'untitled', language: PYTHON_LANGUAGE } - ]; - if (workspaceFolder) { - // tslint:disable-next-line:no-any - (documentSelector[0] as any).pattern = `${workspaceFolder.uri.fsPath}/**/*`; - } - // Options to control the language client + const documentSelector = this.getDocumentSelector(workspaceFolder); + // Options to control the language client. return { - // Register the server for Python documents + // Register the server for Python documents. documentSelector, workspaceFolder, synchronize: { @@ -148,6 +141,18 @@ export class LanguageServerAnalysisOptions implements ILanguageServerAnalysisOpt } }; } + protected getDocumentSelector(workspaceFolder?: WorkspaceFolder): DocumentSelector { + const documentSelector: DocumentFilter[] = [ + { scheme: 'file', language: PYTHON_LANGUAGE }, + { scheme: 'untitled', language: PYTHON_LANGUAGE } + ]; + // Set the document selector only when in a multi-root workspace scenario. + if (workspaceFolder && Array.isArray(this.workspace.workspaceFolders) && this.workspace.workspaceFolders!.length > 1) { + // tslint:disable-next-line:no-any + documentSelector[0].pattern = `${workspaceFolder.uri.fsPath}/**/*`; + } + return documentSelector; + } protected getExcludedFiles(): string[] { const list: string[] = ['**/Lib/**', '**/site-packages/**']; this.getVsCodeExcludeSection('search.exclude', list); diff --git a/src/test/activation/languageServer/analysisOptions.unit.test.ts b/src/test/activation/languageServer/analysisOptions.unit.test.ts index 9d452a048d7a..5a6fb078f30c 100644 --- a/src/test/activation/languageServer/analysisOptions.unit.test.ts +++ b/src/test/activation/languageServer/analysisOptions.unit.test.ts @@ -6,13 +6,15 @@ import { expect } from 'chai'; import { instance, mock, verify, when } from 'ts-mockito'; import * as typemoq from 'typemoq'; -import { ConfigurationChangeEvent, Uri } from 'vscode'; +import { ConfigurationChangeEvent, Uri, WorkspaceFolder } from 'vscode'; +import { DocumentSelector } from 'vscode-languageclient'; import { LanguageServerAnalysisOptions } from '../../../client/activation/languageServer/analysisOptions'; import { LanguageServerFolderService } from '../../../client/activation/languageServer/languageServerFolderService'; import { ILanguageServerFolderService } from '../../../client/activation/types'; import { IWorkspaceService } from '../../../client/common/application/types'; import { WorkspaceService } from '../../../client/common/application/workspace'; import { ConfigurationService } from '../../../client/common/configuration/service'; +import { PYTHON_LANGUAGE } from '../../../client/common/constants'; import { PathUtils } from '../../../client/common/platform/pathUtils'; import { IConfigurationService, IDisposable, IExtensionContext, IOutputChannel, IPathUtils, IPythonExtensionBanner } from '../../../client/common/types'; import { EnvironmentVariablesProvider } from '../../../client/common/variables/environmentVariablesProvider'; @@ -26,6 +28,9 @@ import { sleep } from '../../core'; suite('Language Server - Analysis Options', () => { class TestClass extends LanguageServerAnalysisOptions { + public getDocumentSelector(workspaceFolder?: WorkspaceFolder): DocumentSelector { + return super.getDocumentSelector(workspaceFolder); + } public getExcludedFiles(): string[] { return super.getExcludedFiles(); } @@ -199,4 +204,43 @@ suite('Language Server - Analysis Options', () => { expect(settingsChangedInvokedCount).to.be.equal(1); }); + test('Ensure search pattern is not provided when there are no workspaces', () => { + when(workspace.workspaceFolders).thenReturn([]); + + const expectedSelector = [ + { scheme: 'file', language: PYTHON_LANGUAGE }, + { scheme: 'untitled', language: PYTHON_LANGUAGE } + ]; + + const selector = analysisOptions.getDocumentSelector(); + + expect(selector).to.deep.equal(expectedSelector); + }); + test('Ensure search pattern is not provided in single-root workspaces', () => { + const workspaceFolder: WorkspaceFolder = { name: '', index: 0, uri: Uri.file(__dirname) }; + when(workspace.workspaceFolders).thenReturn([workspaceFolder]); + + const expectedSelector = [ + { scheme: 'file', language: PYTHON_LANGUAGE }, + { scheme: 'untitled', language: PYTHON_LANGUAGE } + ]; + + const selector = analysisOptions.getDocumentSelector(workspaceFolder); + + expect(selector).to.deep.equal(expectedSelector); + }); + test('Ensure search pattern is provided in a multi-root workspace', () => { + const workspaceFolder1 = { name: '1', index: 0, uri: Uri.file(__dirname) }; + const workspaceFolder2 = { name: '2', index: 1, uri: Uri.file(__dirname) }; + when(workspace.workspaceFolders).thenReturn([workspaceFolder1, workspaceFolder2]); + + const expectedSelector = [ + { scheme: 'file', language: PYTHON_LANGUAGE, pattern: `${workspaceFolder1.uri.fsPath}/**/*` }, + { scheme: 'untitled', language: PYTHON_LANGUAGE } + ]; + + const selector = analysisOptions.getDocumentSelector(workspaceFolder1); + + expect(selector).to.deep.equal(expectedSelector); + }); });