diff --git a/news/1 Enhancements/7665.md b/news/1 Enhancements/7665.md new file mode 100644 index 0000000000000..9fc5f6a91503a --- /dev/null +++ b/news/1 Enhancements/7665.md @@ -0,0 +1 @@ +Added excension option `activateEnvInCurrentTerminal` to detect if environment should be activated in the current open terminal. diff --git a/package.json b/package.json index 4a271daa031d7..e7efbf8447c47 100644 --- a/package.json +++ b/package.json @@ -2289,6 +2289,12 @@ "description": "Python launch arguments to use when executing a file in the terminal.", "scope": "resource" }, + "python.terminal.activateEnvInCurrentTerminal": { + "type": "boolean", + "default": false, + "description": "Activate Python Environment in the current Terminal on load of the Extension.", + "scope": "resource" + }, "python.testing.cwd": { "type": "string", "default": null, diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index e483f3629c271..3205f441e0cdc 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -354,7 +354,8 @@ export class PythonSettings implements IPythonSettings { this.terminal = this.terminal ? this.terminal : { executeInFileDir: true, launchArgs: [], - activateEnvironment: true + activateEnvironment: true, + activateEnvInCurrentTerminal: false }; const experiments = systemVariables.resolveAny(pythonSettings.get('experiments'))!; diff --git a/src/client/common/types.ts b/src/client/common/types.ts index 296b594d804b1..94500ef29791c 100644 --- a/src/client/common/types.ts +++ b/src/client/common/types.ts @@ -276,6 +276,7 @@ export interface ITerminalSettings { readonly executeInFileDir: boolean; readonly launchArgs: string[]; readonly activateEnvironment: boolean; + readonly activateEnvInCurrentTerminal: boolean; } export interface IExperiments { diff --git a/src/client/extension.ts b/src/client/extension.ts index eb27073ed60c9..7cbc9d132a545 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -183,7 +183,10 @@ async function activateUnsafe(context: ExtensionContext): Promise context.subscriptions.push(deprecationMgr); context.subscriptions.push(new ReplProvider(serviceContainer)); - context.subscriptions.push(new TerminalProvider(serviceContainer)); + + const terminalProvider = new TerminalProvider(serviceContainer); + await terminalProvider.initialize(window.activeTerminal); + context.subscriptions.push(terminalProvider); context.subscriptions.push(languages.registerCodeActionsProvider(PYTHON, new PythonCodeActionProvider(), { providedCodeActionKinds: [CodeActionKind.SourceOrganizeImports] })); diff --git a/src/client/providers/terminalProvider.ts b/src/client/providers/terminalProvider.ts index b4bc02e9c3b2b..a5a68c85bc914 100644 --- a/src/client/providers/terminalProvider.ts +++ b/src/client/providers/terminalProvider.ts @@ -1,10 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { Disposable, Uri } from 'vscode'; +import { Disposable, Terminal, Uri } from 'vscode'; import { ICommandManager, IDocumentManager, IWorkspaceService } from '../common/application/types'; import { Commands } from '../common/constants'; -import { ITerminalServiceFactory } from '../common/terminal/types'; +import { ITerminalActivator, ITerminalServiceFactory } from '../common/terminal/types'; +import { IConfigurationService } from '../common/types'; import { IServiceContainer } from '../ioc/types'; import { captureTelemetry } from '../telemetry'; import { EventName } from '../telemetry/constants'; @@ -14,6 +15,15 @@ export class TerminalProvider implements Disposable { constructor(private serviceContainer: IServiceContainer) { this.registerCommands(); } + public async initialize(currentTerminal: Terminal | undefined) { + const configuration = this.serviceContainer.get(IConfigurationService); + const pythonSettings = configuration.getSettings(); + + if (pythonSettings.terminal.activateEnvInCurrentTerminal && currentTerminal) { + const terminalActivator = this.serviceContainer.get(ITerminalActivator); + await terminalActivator.activateEnvironmentInTerminal(currentTerminal, undefined, true); + } + } public dispose() { this.disposables.forEach(disposable => disposable.dispose()); } diff --git a/src/test/datascience/dataScienceIocContainer.ts b/src/test/datascience/dataScienceIocContainer.ts index f7ff5ab410915..b14fb9fa5a01a 100644 --- a/src/test/datascience/dataScienceIocContainer.ts +++ b/src/test/datascience/dataScienceIocContainer.ts @@ -510,7 +510,8 @@ export class DataScienceIocContainer extends UnitTestIocContainer { this.pythonSettings.terminal = { executeInFileDir: false, launchArgs: [], - activateEnvironment: true + activateEnvironment: true, + activateEnvInCurrentTerminal: false }; condaService.setup(c => c.isCondaAvailable()).returns(() => Promise.resolve(false)); diff --git a/src/test/providers/terminal.unit.test.ts b/src/test/providers/terminal.unit.test.ts index 6573ef9d231a7..1d24f4c308798 100644 --- a/src/test/providers/terminal.unit.test.ts +++ b/src/test/providers/terminal.unit.test.ts @@ -3,11 +3,12 @@ import { expect } from 'chai'; import * as TypeMoq from 'typemoq'; -import { Disposable, TextDocument, TextEditor, Uri, WorkspaceFolder } from 'vscode'; +import { Disposable, Terminal, TextDocument, TextEditor, Uri, WorkspaceFolder } from 'vscode'; import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../client/common/application/types'; import { Commands } from '../../client/common/constants'; import { TerminalService } from '../../client/common/terminal/service'; -import { ITerminalServiceFactory } from '../../client/common/terminal/types'; +import { ITerminalActivator, ITerminalServiceFactory } from '../../client/common/terminal/types'; +import { IConfigurationService, IPythonSettings, ITerminalSettings } from '../../client/common/types'; import { IServiceContainer } from '../../client/ioc/types'; import { TerminalProvider } from '../../client/providers/terminalProvider'; @@ -149,4 +150,55 @@ suite('Terminal Provider', () => { commandHandler!.call(terminalProvider); terminalService.verify(t => t.show(false), TypeMoq.Times.once()); }); + + suite('terminal.activateCurrentTerminal setting', () => { + + let pythonSettings: TypeMoq.IMock; + let terminalSettings: TypeMoq.IMock; + let configService: TypeMoq.IMock; + let terminalActivator: TypeMoq.IMock; + let terminal: TypeMoq.IMock; + + setup(() => { + configService = TypeMoq.Mock.ofType(); + serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IConfigurationService))).returns(() => configService.object); + pythonSettings = TypeMoq.Mock.ofType(); + configService.setup(c => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object); + + terminalSettings = TypeMoq.Mock.ofType(); + pythonSettings.setup(s => s.terminal).returns(() => terminalSettings.object); + + terminalActivator = TypeMoq.Mock.ofType(); + serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ITerminalActivator))).returns(() => terminalActivator.object); + + terminal = TypeMoq.Mock.ofType(); + }); + + test('If terminal.activateCurrentTerminal setting is set, provided terminal should be activated', async () => { + terminalSettings.setup(t => t.activateEnvInCurrentTerminal).returns(() => true); + + terminalProvider = new TerminalProvider(serviceContainer.object); + await terminalProvider.initialize(terminal.object); + + terminalActivator.verify(a => a.activateEnvironmentInTerminal(terminal.object, undefined, true), TypeMoq.Times.once()); + }); + + test('If terminal.activateCurrentTerminal setting is not set, provided terminal should not be activated', async () => { + terminalSettings.setup(t => t.activateEnvInCurrentTerminal).returns(() => false); + + terminalProvider = new TerminalProvider(serviceContainer.object); + await terminalProvider.initialize(terminal.object); + + terminalActivator.verify(a => a.activateEnvironmentInTerminal(TypeMoq.It.isAny(), undefined, true), TypeMoq.Times.never()); + }); + + test('terminal.activateCurrentTerminal setting is set but provided terminal is undefined', async () => { + terminalSettings.setup(t => t.activateEnvInCurrentTerminal).returns(() => true); + + terminalProvider = new TerminalProvider(serviceContainer.object); + await terminalProvider.initialize(undefined); + + terminalActivator.verify(a => a.activateEnvironmentInTerminal(TypeMoq.It.isAny(), undefined, true), TypeMoq.Times.never()); + }); + }); });