From b073eb99f0f4abfc613f4e1b7adde6ba4b897b97 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Fri, 7 Jan 2022 14:28:00 +0530 Subject: [PATCH 1/7] Add interpreter path text in select folder quickpick --- .../interpreterSelector/commands/base.ts | 38 +++++++++++-------- .../commands/resetInterpreter.ts | 12 +++++- .../commands/setInterpreter.ts | 13 +++++-- .../commands/setShebangInterpreter.ts | 26 ++++++------- 4 files changed, 55 insertions(+), 34 deletions(-) diff --git a/src/client/interpreter/configuration/interpreterSelector/commands/base.ts b/src/client/interpreter/configuration/interpreterSelector/commands/base.ts index 7ecafae3a04c..f81d7ca651a2 100644 --- a/src/client/interpreter/configuration/interpreterSelector/commands/base.ts +++ b/src/client/interpreter/configuration/interpreterSelector/commands/base.ts @@ -8,10 +8,12 @@ import * as path from 'path'; import { ConfigurationTarget, Disposable, QuickPickItem, Uri } from 'vscode'; import { IExtensionSingleActivationService } from '../../../../activation/types'; import { IApplicationShell, ICommandManager, IWorkspaceService } from '../../../../common/application/types'; -import { IDisposable, Resource } from '../../../../common/types'; +import { IConfigurationService, IDisposable, IPathUtils, Resource } from '../../../../common/types'; import { Interpreters } from '../../../../common/utils/localize'; import { IPythonPathUpdaterServiceManager } from '../../types'; - +export interface WorkspaceSelectionQuickPickItem extends QuickPickItem { + uri: Uri; +} @injectable() export abstract class BaseInterpreterSelectorCommand implements IExtensionSingleActivationService, IDisposable { public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: true }; @@ -21,6 +23,8 @@ export abstract class BaseInterpreterSelectorCommand implements IExtensionSingle @unmanaged() protected readonly commandManager: ICommandManager, @unmanaged() protected readonly applicationShell: IApplicationShell, @unmanaged() protected readonly workspaceService: IWorkspaceService, + @unmanaged() protected readonly pathUtils: IPathUtils, + @unmanaged() protected readonly configurationService: IConfigurationService, ) { this.disposables.push(this); } @@ -38,34 +42,38 @@ export abstract class BaseInterpreterSelectorCommand implements IExtensionSingle } | undefined > { - if ( - !Array.isArray(this.workspaceService.workspaceFolders) || - this.workspaceService.workspaceFolders.length === 0 - ) { + const workspaceFolders = this.workspaceService.workspaceFolders; + if (workspaceFolders === undefined || workspaceFolders.length === 0) { return { folderUri: undefined, configTarget: ConfigurationTarget.Global, }; } - if (!this.workspaceService.workspaceFile && this.workspaceService.workspaceFolders.length === 1) { + if (!this.workspaceService.workspaceFile && workspaceFolders.length === 1) { return { - folderUri: this.workspaceService.workspaceFolders[0].uri, + folderUri: workspaceFolders[0].uri, configTarget: ConfigurationTarget.WorkspaceFolder, }; } // Ok we have multiple workspaces, get the user to pick a folder. - type WorkspaceSelectionQuickPickItem = QuickPickItem & { uri: Uri }; const quickPickItems: WorkspaceSelectionQuickPickItem[] = [ - ...this.workspaceService.workspaceFolders.map((w) => ({ - label: w.name, - description: path.dirname(w.uri.fsPath), - uri: w.uri, - })), + ...workspaceFolders.map((w) => { + const selectedInterpreter = this.pathUtils.getDisplayName( + this.configurationService.getSettings(w.uri).pythonPath, + w.uri.fsPath, + ); + return { + label: w.name, + description: this.pathUtils.getDisplayName(path.dirname(w.uri.fsPath)), + uri: w.uri, + detail: selectedInterpreter, + }; + }), { label: Interpreters.entireWorkspace(), - uri: this.workspaceService.workspaceFolders[0].uri, + uri: workspaceFolders[0].uri, }, ]; diff --git a/src/client/interpreter/configuration/interpreterSelector/commands/resetInterpreter.ts b/src/client/interpreter/configuration/interpreterSelector/commands/resetInterpreter.ts index 7864a3133bf8..8c5733380585 100644 --- a/src/client/interpreter/configuration/interpreterSelector/commands/resetInterpreter.ts +++ b/src/client/interpreter/configuration/interpreterSelector/commands/resetInterpreter.ts @@ -6,6 +6,7 @@ import { inject, injectable } from 'inversify'; import { IApplicationShell, ICommandManager, IWorkspaceService } from '../../../../common/application/types'; import { Commands } from '../../../../common/constants'; +import { IConfigurationService, IPathUtils } from '../../../../common/types'; import { IPythonPathUpdaterServiceManager } from '../../types'; import { BaseInterpreterSelectorCommand } from './base'; @@ -16,8 +17,17 @@ export class ResetInterpreterCommand extends BaseInterpreterSelectorCommand { @inject(ICommandManager) commandManager: ICommandManager, @inject(IApplicationShell) applicationShell: IApplicationShell, @inject(IWorkspaceService) workspaceService: IWorkspaceService, + @inject(IPathUtils) pathUtils: IPathUtils, + @inject(IConfigurationService) configurationService: IConfigurationService, ) { - super(pythonPathUpdaterService, commandManager, applicationShell, workspaceService); + super( + pythonPathUpdaterService, + commandManager, + applicationShell, + workspaceService, + pathUtils, + configurationService, + ); } public async activate() { diff --git a/src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts b/src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts index 2189aa154b67..4a3e936fa6c9 100644 --- a/src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts +++ b/src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts @@ -55,10 +55,10 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand { constructor( @inject(IApplicationShell) applicationShell: IApplicationShell, - @inject(IPathUtils) private readonly pathUtils: IPathUtils, + @inject(IPathUtils) pathUtils: IPathUtils, @inject(IPythonPathUpdaterServiceManager) pythonPathUpdaterService: IPythonPathUpdaterServiceManager, - @inject(IConfigurationService) private readonly configurationService: IConfigurationService, + @inject(IConfigurationService) configurationService: IConfigurationService, @inject(ICommandManager) commandManager: ICommandManager, @inject(IMultiStepInputFactory) private readonly multiStepFactory: IMultiStepInputFactory, @inject(IPlatformService) private readonly platformService: IPlatformService, @@ -66,7 +66,14 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand { @inject(IWorkspaceService) workspaceService: IWorkspaceService, @inject(IInterpreterService) private readonly interpreterService: IInterpreterService, ) { - super(pythonPathUpdaterService, commandManager, applicationShell, workspaceService); + super( + pythonPathUpdaterService, + commandManager, + applicationShell, + workspaceService, + pathUtils, + configurationService, + ); } public async activate(): Promise { diff --git a/src/client/interpreter/configuration/interpreterSelector/commands/setShebangInterpreter.ts b/src/client/interpreter/configuration/interpreterSelector/commands/setShebangInterpreter.ts index 72d5b65ced8a..94ec84b82c42 100644 --- a/src/client/interpreter/configuration/interpreterSelector/commands/setShebangInterpreter.ts +++ b/src/client/interpreter/configuration/interpreterSelector/commands/setShebangInterpreter.ts @@ -5,29 +5,25 @@ import { inject, injectable } from 'inversify'; import { ConfigurationTarget } from 'vscode'; -import { - IApplicationShell, - ICommandManager, - IDocumentManager, - IWorkspaceService, -} from '../../../../common/application/types'; +import { IExtensionSingleActivationService } from '../../../../activation/types'; +import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../../../common/application/types'; import { Commands } from '../../../../common/constants'; +import { IDisposableRegistry } from '../../../../common/types'; import { IShebangCodeLensProvider } from '../../../contracts'; import { IPythonPathUpdaterServiceManager } from '../../types'; -import { BaseInterpreterSelectorCommand } from './base'; @injectable() -export class SetShebangInterpreterCommand extends BaseInterpreterSelectorCommand { +export class SetShebangInterpreterCommand implements IExtensionSingleActivationService { + public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: true }; constructor( - @inject(IWorkspaceService) workspaceService: IWorkspaceService, + @inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService, @inject(IDocumentManager) private readonly documentManager: IDocumentManager, - @inject(IPythonPathUpdaterServiceManager) pythonPathUpdaterService: IPythonPathUpdaterServiceManager, + @inject(IPythonPathUpdaterServiceManager) + private readonly pythonPathUpdaterService: IPythonPathUpdaterServiceManager, @inject(IShebangCodeLensProvider) private readonly shebangCodeLensProvider: IShebangCodeLensProvider, - @inject(ICommandManager) commandManager: ICommandManager, - @inject(ICommandManager) applicationShell: IApplicationShell, - ) { - super(pythonPathUpdaterService, commandManager, applicationShell, workspaceService); - } + @inject(ICommandManager) private readonly commandManager: ICommandManager, + @inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry, + ) {} public async activate() { this.disposables.push( From 85c39b45657984b1e6a7f2f8f8f85d4111335523 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Fri, 7 Jan 2022 18:39:15 +0530 Subject: [PATCH 2/7] Add an option to clear interpreters for all folders --- .../interpreterSelector/commands/base.ts | 60 +++++++++++++------ .../commands/resetInterpreter.ts | 15 +++-- .../commands/setInterpreter.ts | 6 +- 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/src/client/interpreter/configuration/interpreterSelector/commands/base.ts b/src/client/interpreter/configuration/interpreterSelector/commands/base.ts index f81d7ca651a2..987b353631cd 100644 --- a/src/client/interpreter/configuration/interpreterSelector/commands/base.ts +++ b/src/client/interpreter/configuration/interpreterSelector/commands/base.ts @@ -12,7 +12,7 @@ import { IConfigurationService, IDisposable, IPathUtils, Resource } from '../../ import { Interpreters } from '../../../../common/utils/localize'; import { IPythonPathUpdaterServiceManager } from '../../types'; export interface WorkspaceSelectionQuickPickItem extends QuickPickItem { - uri: Uri; + uri?: Uri; } @injectable() export abstract class BaseInterpreterSelectorCommand implements IExtensionSingleActivationService, IDisposable { @@ -35,30 +35,43 @@ export abstract class BaseInterpreterSelectorCommand implements IExtensionSingle public abstract activate(): Promise; - protected async getConfigTarget(): Promise< + protected async getConfigTargets(options?: { + resetTarget?: boolean; + }): Promise< | { folderUri: Resource; configTarget: ConfigurationTarget; - } + }[] | undefined > { const workspaceFolders = this.workspaceService.workspaceFolders; if (workspaceFolders === undefined || workspaceFolders.length === 0) { - return { - folderUri: undefined, - configTarget: ConfigurationTarget.Global, - }; + return [ + { + folderUri: undefined, + configTarget: ConfigurationTarget.Global, + }, + ]; } if (!this.workspaceService.workspaceFile && workspaceFolders.length === 1) { - return { - folderUri: workspaceFolders[0].uri, - configTarget: ConfigurationTarget.WorkspaceFolder, - }; + return [ + { + folderUri: workspaceFolders[0].uri, + configTarget: ConfigurationTarget.WorkspaceFolder, + }, + ]; } // Ok we have multiple workspaces, get the user to pick a folder. - const quickPickItems: WorkspaceSelectionQuickPickItem[] = [ + let quickPickItems: WorkspaceSelectionQuickPickItem[] = options?.resetTarget + ? [ + { + label: 'Clear all', + }, + ] + : []; + quickPickItems.push( ...workspaceFolders.map((w) => { const selectedInterpreter = this.pathUtils.getDisplayName( this.configurationService.getSettings(w.uri).pythonPath, @@ -72,19 +85,32 @@ export abstract class BaseInterpreterSelectorCommand implements IExtensionSingle }; }), { - label: Interpreters.entireWorkspace(), + label: options?.resetTarget ? 'Clear at workspace level' : Interpreters.entireWorkspace(), uri: workspaceFolders[0].uri, }, - ]; + ); const selection = await this.applicationShell.showQuickPick(quickPickItems, { - placeHolder: 'Select the workspace to set the interpreter', + placeHolder: options?.resetTarget + ? 'Select the workspace folder to clear the interpreter for' + : 'Select the workspace folder to set the interpreter', }); + if (selection?.label === 'Clear all') { + const folderTargets: { + folderUri: Resource; + configTarget: ConfigurationTarget; + }[] = workspaceFolders.map((w) => ({ + folderUri: w.uri, + configTarget: ConfigurationTarget.WorkspaceFolder, + })); + return [...folderTargets, { folderUri: undefined, configTarget: ConfigurationTarget.Workspace }]; + } + return selection ? selection.label === Interpreters.entireWorkspace() - ? { folderUri: selection.uri, configTarget: ConfigurationTarget.Workspace } - : { folderUri: selection.uri, configTarget: ConfigurationTarget.WorkspaceFolder } + ? [{ folderUri: selection.uri, configTarget: ConfigurationTarget.Workspace }] + : [{ folderUri: selection.uri, configTarget: ConfigurationTarget.WorkspaceFolder }] : undefined; } } diff --git a/src/client/interpreter/configuration/interpreterSelector/commands/resetInterpreter.ts b/src/client/interpreter/configuration/interpreterSelector/commands/resetInterpreter.ts index 8c5733380585..82b40a3ff5e8 100644 --- a/src/client/interpreter/configuration/interpreterSelector/commands/resetInterpreter.ts +++ b/src/client/interpreter/configuration/interpreterSelector/commands/resetInterpreter.ts @@ -37,13 +37,16 @@ export class ResetInterpreterCommand extends BaseInterpreterSelectorCommand { } public async resetInterpreter() { - const targetConfig = await this.getConfigTarget(); - if (!targetConfig) { + const targetConfigs = await this.getConfigTargets({ resetTarget: true }); + if (!targetConfigs) { return; } - const configTarget = targetConfig.configTarget; - const wkspace = targetConfig.folderUri; - - await this.pythonPathUpdaterService.updatePythonPath(undefined, configTarget, 'ui', wkspace); + await Promise.all( + targetConfigs.map(async (targetConfig) => { + const configTarget = targetConfig.configTarget; + const wkspace = targetConfig.folderUri; + await this.pythonPathUpdaterService.updatePythonPath(undefined, configTarget, 'ui', wkspace); + }), + ); } } diff --git a/src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts b/src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts index 4a3e936fa6c9..047e26c80fe4 100644 --- a/src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts +++ b/src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts @@ -309,13 +309,13 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand { @captureTelemetry(EventName.SELECT_INTERPRETER) public async setInterpreter(): Promise { - const targetConfig = await this.getConfigTarget(); + const targetConfig = await this.getConfigTargets(); if (!targetConfig) { return; } - const { configTarget } = targetConfig; - const wkspace = targetConfig.folderUri; + const { configTarget } = targetConfig[0]; + const wkspace = targetConfig[0].folderUri; const interpreterState: InterpreterStateArgs = { path: undefined, workspace: wkspace }; const multiStep = this.multiStepFactory.create(); await multiStep.run((input, s) => this._pickInterpreter(input, s), interpreterState); From dc3150690863d147a25bf5743304cb91b5aaa055 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Fri, 7 Jan 2022 20:46:45 +0530 Subject: [PATCH 3/7] Fix some tests --- src/client/common/utils/localize.ts | 2 ++ .../interpreterSelector/commands/base.ts | 10 +++---- .../commands/resetInterpreter.unit.test.ts | 30 +++++++++++++++---- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/client/common/utils/localize.ts b/src/client/common/utils/localize.ts index bdbdb6c9b588..9030d256cc74 100644 --- a/src/client/common/utils/localize.ts +++ b/src/client/common/utils/localize.ts @@ -91,6 +91,7 @@ export namespace Common { export const and = localize('Common.and', 'and'); export const reportThisIssue = localize('Common.reportThisIssue', 'Report this issue'); export const recommended = localize('Common.recommended', 'Recommended'); + export const clearAll = localize('Common.clearAll', 'Clear all'); } export namespace CommonSurvey { @@ -282,6 +283,7 @@ export namespace Interpreters { 'We noticed a new virtual environment has been created. Do you want to select it for the workspace folder?', ); export const entireWorkspace = localize('Interpreters.entireWorkspace', 'Select at workspace level'); + export const clearAtWorkspace = localize('Interpreters.clearAtWorkspace', 'Clear at workspace level'); export const selectInterpreterTip = localize( 'Interpreters.selectInterpreterTip', 'Tip: you can change the Python interpreter used by the Python extension by clicking on the Python version in the status bar', diff --git a/src/client/interpreter/configuration/interpreterSelector/commands/base.ts b/src/client/interpreter/configuration/interpreterSelector/commands/base.ts index 987b353631cd..0f365b8ce773 100644 --- a/src/client/interpreter/configuration/interpreterSelector/commands/base.ts +++ b/src/client/interpreter/configuration/interpreterSelector/commands/base.ts @@ -9,7 +9,7 @@ import { ConfigurationTarget, Disposable, QuickPickItem, Uri } from 'vscode'; import { IExtensionSingleActivationService } from '../../../../activation/types'; import { IApplicationShell, ICommandManager, IWorkspaceService } from '../../../../common/application/types'; import { IConfigurationService, IDisposable, IPathUtils, Resource } from '../../../../common/types'; -import { Interpreters } from '../../../../common/utils/localize'; +import { Common, Interpreters } from '../../../../common/utils/localize'; import { IPythonPathUpdaterServiceManager } from '../../types'; export interface WorkspaceSelectionQuickPickItem extends QuickPickItem { uri?: Uri; @@ -67,7 +67,7 @@ export abstract class BaseInterpreterSelectorCommand implements IExtensionSingle let quickPickItems: WorkspaceSelectionQuickPickItem[] = options?.resetTarget ? [ { - label: 'Clear all', + label: Common.clearAll(), }, ] : []; @@ -85,7 +85,7 @@ export abstract class BaseInterpreterSelectorCommand implements IExtensionSingle }; }), { - label: options?.resetTarget ? 'Clear at workspace level' : Interpreters.entireWorkspace(), + label: options?.resetTarget ? Interpreters.clearAtWorkspace() : Interpreters.entireWorkspace(), uri: workspaceFolders[0].uri, }, ); @@ -96,7 +96,7 @@ export abstract class BaseInterpreterSelectorCommand implements IExtensionSingle : 'Select the workspace folder to set the interpreter', }); - if (selection?.label === 'Clear all') { + if (selection?.label === Common.clearAll()) { const folderTargets: { folderUri: Resource; configTarget: ConfigurationTarget; @@ -108,7 +108,7 @@ export abstract class BaseInterpreterSelectorCommand implements IExtensionSingle } return selection - ? selection.label === Interpreters.entireWorkspace() + ? selection.label === Interpreters.entireWorkspace() || selection.label === Interpreters.clearAtWorkspace() ? [{ folderUri: selection.uri, configTarget: ConfigurationTarget.Workspace }] : [{ folderUri: selection.uri, configTarget: ConfigurationTarget.WorkspaceFolder }] : undefined; diff --git a/src/test/configuration/interpreterSelector/commands/resetInterpreter.unit.test.ts b/src/test/configuration/interpreterSelector/commands/resetInterpreter.unit.test.ts index db9ed887a8e6..766647d02eaf 100644 --- a/src/test/configuration/interpreterSelector/commands/resetInterpreter.unit.test.ts +++ b/src/test/configuration/interpreterSelector/commands/resetInterpreter.unit.test.ts @@ -5,7 +5,9 @@ import * as path from 'path'; import * as TypeMoq from 'typemoq'; import { ConfigurationTarget, Uri } from 'vscode'; import { IApplicationShell, ICommandManager, IWorkspaceService } from '../../../../client/common/application/types'; -import { Interpreters } from '../../../../client/common/utils/localize'; +import { PathUtils } from '../../../../client/common/platform/pathUtils'; +import { IConfigurationService } from '../../../../client/common/types'; +import { Common, Interpreters } from '../../../../client/common/utils/localize'; import { ResetInterpreterCommand } from '../../../../client/interpreter/configuration/interpreterSelector/commands/resetInterpreter'; import { IPythonPathUpdaterServiceManager } from '../../../../client/interpreter/configuration/types'; @@ -14,12 +16,18 @@ suite('Reset Interpreter Command', () => { let appShell: TypeMoq.IMock; let commandManager: TypeMoq.IMock; let pythonPathUpdater: TypeMoq.IMock; + let configurationService: TypeMoq.IMock; const folder1 = { name: 'one', uri: Uri.parse('one'), index: 1 }; const folder2 = { name: 'two', uri: Uri.parse('two'), index: 2 }; let resetInterpreterCommand: ResetInterpreterCommand; setup(() => { + configurationService = TypeMoq.Mock.ofType(); + configurationService + .setup((c) => c.getSettings(TypeMoq.It.isAny())) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .returns(() => ({ pythonPath: 'pythonPath' } as any)); commandManager = TypeMoq.Mock.ofType(); appShell = TypeMoq.Mock.ofType(); pythonPathUpdater = TypeMoq.Mock.ofType(); @@ -30,6 +38,8 @@ suite('Reset Interpreter Command', () => { commandManager.object, appShell.object, workspace.object, + new PathUtils(false), + configurationService.object, ); }); @@ -81,18 +91,21 @@ suite('Reset Interpreter Command', () => { test('Update selected workspace folder settings when there is more than one workspace folder', async () => { workspace.setup((w) => w.workspaceFolders).returns(() => [folder1, folder2]); const expectedItems = [ + { label: Common.clearAll() }, { label: 'one', description: path.dirname(folder1.uri.fsPath), uri: folder1.uri, + detail: 'pythonPath', }, { label: 'two', description: path.dirname(folder2.uri.fsPath), uri: folder2.uri, + detail: 'pythonPath', }, { - label: Interpreters.entireWorkspace(), + label: Interpreters.clearAtWorkspace(), uri: folder1.uri, }, ]; @@ -103,6 +116,7 @@ suite('Reset Interpreter Command', () => { label: 'two', description: path.dirname(folder2.uri.fsPath), uri: folder2.uri, + detail: 'pythonPath', }), ) .verifiable(TypeMoq.Times.once()); @@ -127,18 +141,21 @@ suite('Reset Interpreter Command', () => { test('Update entire workspace settings when there is more than one workspace folder and `Select at workspace level` is selected', async () => { workspace.setup((w) => w.workspaceFolders).returns(() => [folder1, folder2]); const expectedItems = [ + { label: Common.clearAll() }, { label: 'one', description: path.dirname(folder1.uri.fsPath), uri: folder1.uri, + detail: 'pythonPath', }, { label: 'two', description: path.dirname(folder2.uri.fsPath), uri: folder2.uri, + detail: 'pythonPath', }, { - label: Interpreters.entireWorkspace(), + label: Interpreters.clearAtWorkspace(), uri: folder1.uri, }, ]; @@ -146,7 +163,7 @@ suite('Reset Interpreter Command', () => { .setup((s) => s.showQuickPick(TypeMoq.It.isValue(expectedItems), TypeMoq.It.isAny())) .returns(() => Promise.resolve({ - label: Interpreters.entireWorkspace(), + label: Interpreters.clearAtWorkspace(), uri: folder1.uri, }), ) @@ -173,18 +190,21 @@ suite('Reset Interpreter Command', () => { workspace.setup((w) => w.workspaceFolders).returns(() => [folder1, folder2]); const expectedItems = [ + { label: Common.clearAll() }, { label: 'one', description: path.dirname(folder1.uri.fsPath), uri: folder1.uri, + detail: 'pythonPath', }, { label: 'two', description: path.dirname(folder2.uri.fsPath), uri: folder2.uri, + detail: 'pythonPath', }, { - label: Interpreters.entireWorkspace(), + label: Interpreters.clearAtWorkspace(), uri: folder1.uri, }, ]; From 6a581cdfb08b5d35c9aed980fd836f69bb6fcd97 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Fri, 7 Jan 2022 20:48:00 +0530 Subject: [PATCH 4/7] News --- news/1 Enhancements/17693.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/1 Enhancements/17693.md diff --git a/news/1 Enhancements/17693.md b/news/1 Enhancements/17693.md new file mode 100644 index 000000000000..9da79af64b3c --- /dev/null +++ b/news/1 Enhancements/17693.md @@ -0,0 +1 @@ +Add an option to clear interpreter setting for all workspace folders in multiroot scenario. From db36bd53af031bd27fcc3aaace60c7f03a9a644a Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Fri, 7 Jan 2022 22:24:01 +0530 Subject: [PATCH 5/7] Localize --- package.nls.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.nls.json b/package.nls.json index e1e0f49407ff..ab5baa1a454e 100644 --- a/package.nls.json +++ b/package.nls.json @@ -46,6 +46,7 @@ "Pylance.pylanceRevertToJedi": "Revert to Jedi", "Experiments.inGroup": "Experiment '{0}' is active", "Experiments.optedOutOf": "Experiment '{0}' is inactive", + "Interpreters.clearAtWorkspace" : "Clear at workspace level", "Interpreters.RefreshingInterpreters": "Refreshing Python Interpreters", "Interpreters.entireWorkspace": "Select at workspace level", "Interpreters.pythonInterpreterPath": "Python interpreter path: {0}", @@ -61,6 +62,7 @@ "InterpreterQuickPickList.browsePath.title": "Select Python interpreter", "InterpreterQuickPickList.defaultInterpreterPath.label": "Use Python from `python.defaultInterpreterPath` setting", "diagnostics.upgradeCodeRunner": "Please update the Code Runner extension for it to be compatible with the Python extension.", + "Common.clearAll" : "Clear all", "Common.bannerLabelYes": "Yes", "Common.bannerLabelNo": "No", "Common.doNotShowAgain": "Do not show again", From e090c0eb33b05a6adab306f3a98c85573dbd7de7 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Fri, 7 Jan 2022 22:37:15 +0530 Subject: [PATCH 6/7] Fix tests --- .../configuration/interpreterSelector/commands/base.ts | 6 ++---- .../commands/setInterpreter.unit.test.ts | 8 ++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/client/interpreter/configuration/interpreterSelector/commands/base.ts b/src/client/interpreter/configuration/interpreterSelector/commands/base.ts index 0f365b8ce773..8ed7bc3bd455 100644 --- a/src/client/interpreter/configuration/interpreterSelector/commands/base.ts +++ b/src/client/interpreter/configuration/interpreterSelector/commands/base.ts @@ -73,10 +73,8 @@ export abstract class BaseInterpreterSelectorCommand implements IExtensionSingle : []; quickPickItems.push( ...workspaceFolders.map((w) => { - const selectedInterpreter = this.pathUtils.getDisplayName( - this.configurationService.getSettings(w.uri).pythonPath, - w.uri.fsPath, - ); + const p = this.configurationService.getSettings(w.uri).pythonPath; + const selectedInterpreter = this.pathUtils.getDisplayName(p, w.uri.fsPath); return { label: w.name, description: this.pathUtils.getDisplayName(path.dirname(w.uri.fsPath)), diff --git a/src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts b/src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts index db80d6f2cbe6..dd3f1454baa8 100644 --- a/src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts +++ b/src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts @@ -773,11 +773,13 @@ suite('Set Interpreter Command', () => { label: 'one', description: path.dirname(folder1.uri.fsPath), uri: folder1.uri, + detail: 'python', }, { label: 'two', description: path.dirname(folder2.uri.fsPath), uri: folder2.uri, + detail: 'python', }, { label: Interpreters.entireWorkspace(), @@ -801,6 +803,7 @@ suite('Set Interpreter Command', () => { label: 'two', description: path.dirname(folder2.uri.fsPath), uri: folder2.uri, + detail: 'python', }), ) .verifiable(TypeMoq.Times.once()); @@ -839,11 +842,13 @@ suite('Set Interpreter Command', () => { label: 'one', description: path.dirname(folder1.uri.fsPath), uri: folder1.uri, + detail: 'python', }, { label: 'two', description: path.dirname(folder2.uri.fsPath), uri: folder2.uri, + detail: 'python', }, { label: Interpreters.entireWorkspace(), @@ -887,6 +892,7 @@ suite('Set Interpreter Command', () => { pythonPathUpdater.verifyAll(); }); test('Do not update anything when user does not select a workspace folder and there is more than one workspace folder', async () => { + pythonSettings.setup((p) => p.pythonPath).returns(() => 'python'); workspace.setup((w) => w.workspaceFolders).returns(() => [folder1, folder2]); interpreterSelector.setup((i) => i.getSuggestions(TypeMoq.It.isAny())).returns(() => []); @@ -897,11 +903,13 @@ suite('Set Interpreter Command', () => { label: 'one', description: path.dirname(folder1.uri.fsPath), uri: folder1.uri, + detail: 'python', }, { label: 'two', description: path.dirname(folder2.uri.fsPath), uri: folder2.uri, + detail: 'python', }, { label: Interpreters.entireWorkspace(), From cff2208c18cb25e0ef5e55ffca04658e06c87b0f Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Fri, 7 Jan 2022 22:53:42 +0530 Subject: [PATCH 7/7] Add test --- .../interpreterSelector/commands/base.ts | 11 ++- .../commands/resetInterpreter.unit.test.ts | 70 +++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/client/interpreter/configuration/interpreterSelector/commands/base.ts b/src/client/interpreter/configuration/interpreterSelector/commands/base.ts index 8ed7bc3bd455..e549745ff58f 100644 --- a/src/client/interpreter/configuration/interpreterSelector/commands/base.ts +++ b/src/client/interpreter/configuration/interpreterSelector/commands/base.ts @@ -73,8 +73,10 @@ export abstract class BaseInterpreterSelectorCommand implements IExtensionSingle : []; quickPickItems.push( ...workspaceFolders.map((w) => { - const p = this.configurationService.getSettings(w.uri).pythonPath; - const selectedInterpreter = this.pathUtils.getDisplayName(p, w.uri.fsPath); + const selectedInterpreter = this.pathUtils.getDisplayName( + this.configurationService.getSettings(w.uri).pythonPath, + w.uri.fsPath, + ); return { label: w.name, description: this.pathUtils.getDisplayName(path.dirname(w.uri.fsPath)), @@ -102,7 +104,10 @@ export abstract class BaseInterpreterSelectorCommand implements IExtensionSingle folderUri: w.uri, configTarget: ConfigurationTarget.WorkspaceFolder, })); - return [...folderTargets, { folderUri: undefined, configTarget: ConfigurationTarget.Workspace }]; + return [ + ...folderTargets, + { folderUri: workspaceFolders[0].uri, configTarget: ConfigurationTarget.Workspace }, + ]; } return selection diff --git a/src/test/configuration/interpreterSelector/commands/resetInterpreter.unit.test.ts b/src/test/configuration/interpreterSelector/commands/resetInterpreter.unit.test.ts index 766647d02eaf..c555734e5165 100644 --- a/src/test/configuration/interpreterSelector/commands/resetInterpreter.unit.test.ts +++ b/src/test/configuration/interpreterSelector/commands/resetInterpreter.unit.test.ts @@ -186,6 +186,76 @@ suite('Reset Interpreter Command', () => { workspace.verifyAll(); pythonPathUpdater.verifyAll(); }); + test('Update all folders and workspace scope if `Clear all` is selected', async () => { + workspace.setup((w) => w.workspaceFolders).returns(() => [folder1, folder2]); + const expectedItems = [ + { label: Common.clearAll() }, + { + label: 'one', + description: path.dirname(folder1.uri.fsPath), + uri: folder1.uri, + detail: 'pythonPath', + }, + { + label: 'two', + description: path.dirname(folder2.uri.fsPath), + uri: folder2.uri, + detail: 'pythonPath', + }, + { + label: Interpreters.clearAtWorkspace(), + uri: folder1.uri, + }, + ]; + appShell + .setup((s) => s.showQuickPick(TypeMoq.It.isValue(expectedItems), TypeMoq.It.isAny())) + .returns(() => + Promise.resolve({ + label: Common.clearAll(), + uri: folder1.uri, + }), + ) + .verifiable(TypeMoq.Times.once()); + pythonPathUpdater + .setup((p) => + p.updatePythonPath( + TypeMoq.It.isValue(undefined), + TypeMoq.It.isValue(ConfigurationTarget.Workspace), + TypeMoq.It.isValue('ui'), + TypeMoq.It.isValue(folder1.uri), + ), + ) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.once()); + pythonPathUpdater + .setup((p) => + p.updatePythonPath( + TypeMoq.It.isValue(undefined), + TypeMoq.It.isValue(ConfigurationTarget.WorkspaceFolder), + TypeMoq.It.isValue('ui'), + TypeMoq.It.isValue(folder2.uri), + ), + ) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.once()); + + pythonPathUpdater + .setup((p) => + p.updatePythonPath( + TypeMoq.It.isValue(undefined), + TypeMoq.It.isValue(ConfigurationTarget.WorkspaceFolder), + TypeMoq.It.isValue('ui'), + TypeMoq.It.isValue(folder1.uri), + ), + ) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.once()); + await resetInterpreterCommand.resetInterpreter(); + + appShell.verifyAll(); + workspace.verifyAll(); + pythonPathUpdater.verifyAll(); + }); test('Do not update anything when user does not select a workspace folder and there is more than one workspace folder', async () => { workspace.setup((w) => w.workspaceFolders).returns(() => [folder1, folder2]);