Skip to content

Commit 2b8b8b3

Browse files
committed
Auto activation of environment in new terminals
1 parent b3e9919 commit 2b8b8b3

File tree

10 files changed

+77
-17
lines changed

10 files changed

+77
-17
lines changed

src/client/common/application/terminalManager.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ export class TerminalManager implements ITerminalManager {
1010
public get onDidCloseTerminal(): Event<Terminal> {
1111
return window.onDidCloseTerminal;
1212
}
13+
public get onDidOpenTerminal(): Event<Terminal> {
14+
return window.onDidOpenTerminal;
15+
}
1316
public createTerminal(options: TerminalOptions): Terminal {
1417
return window.createTerminal(options);
1518
}

src/client/common/application/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,11 @@ export interface ITerminalManager {
522522
* An [event](#Event) which fires when a terminal is disposed.
523523
*/
524524
readonly onDidCloseTerminal: Event<Terminal>;
525+
/**
526+
* An [event](#Event) which fires when a terminal has been created, either through the
527+
* [createTerminal](#window.createTerminal) API or commands.
528+
*/
529+
readonly onDidOpenTerminal: Event<Terminal>;
525530
/**
526531
* Creates a [Terminal](#Terminal). The cwd of the terminal will be the workspace directory
527532
* if it exists, regardless of whether an explicit customStartPath setting exists.

src/client/common/terminal/factory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { ITerminalService, ITerminalServiceFactory } from './types';
1212
export class TerminalServiceFactory implements ITerminalServiceFactory {
1313
private terminalServices: Map<string, ITerminalService>;
1414

15-
constructor( @inject(IServiceContainer) private serviceContainer: IServiceContainer) {
15+
constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {
1616

1717
this.terminalServices = new Map<string, ITerminalService>();
1818
}

src/client/common/terminal/helper.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import { inject, injectable } from 'inversify';
55
import { Terminal, Uri } from 'vscode';
6+
import { sleep } from '../../../utils/async';
67
import { ICondaService } from '../../interpreter/contracts';
78
import { IServiceContainer } from '../../ioc/types';
89
import { ITerminalManager, IWorkspaceService } from '../application/types';
@@ -29,9 +30,11 @@ const IS_TCSHELL = /(tcsh$)/i;
2930
@injectable()
3031
export class TerminalHelper implements ITerminalHelper {
3132
private readonly detectableShells: Map<TerminalShellType, RegExp>;
33+
private readonly activatedTerminals: Set<Terminal>;
3234
constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {
3335

3436
this.detectableShells = new Map<TerminalShellType, RegExp>();
37+
this.activatedTerminals = new Set<Terminal>();
3538
this.detectableShells.set(TerminalShellType.powershell, IS_POWERSHELL);
3639
this.detectableShells.set(TerminalShellType.gitbash, IS_GITBASH);
3740
this.detectableShells.set(TerminalShellType.bash, IS_BASH);
@@ -108,4 +111,26 @@ export class TerminalHelper implements ITerminalHelper {
108111
}
109112
}
110113
}
114+
115+
public async activateEnvironmentInTerminal(terminal: Terminal, preserveFocus: boolean = true, resource?: Uri) {
116+
if (this.activatedTerminals.has(terminal)) {
117+
return;
118+
}
119+
this.activatedTerminals.add(terminal);
120+
const shellPath = this.getTerminalShellPath();
121+
const terminalShellType = !shellPath || shellPath.length === 0 ? TerminalShellType.other : this.identifyTerminalShell(shellPath);
122+
123+
const activationCommamnds = await this.getEnvironmentActivationCommands(terminalShellType, resource);
124+
if (activationCommamnds) {
125+
for (const command of activationCommamnds!) {
126+
terminal.show(preserveFocus);
127+
terminal.sendText(command);
128+
129+
// Give the command some time to complete.
130+
// Its been observed that sending commands too early will strip some text off.
131+
const delay = (terminalShellType === TerminalShellType.powershell || TerminalShellType.powershellCore) ? 1000 : 500;
132+
await sleep(delay);
133+
}
134+
}
135+
}
111136
}

src/client/common/terminal/service.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
import { inject, injectable } from 'inversify';
55
import { Disposable, Event, EventEmitter, Terminal, Uri } from 'vscode';
6-
import { sleep } from '../../../utils/async';
76
import '../../common/extensions';
87
import { IInterpreterService } from '../../interpreter/contracts';
98
import { IServiceContainer } from '../../ioc/types';
@@ -64,18 +63,7 @@ export class TerminalService implements ITerminalService, Disposable {
6463
// Sometimes the terminal takes some time to start up before it can start accepting input.
6564
await new Promise(resolve => setTimeout(resolve, 100));
6665

67-
const activationCommamnds = await this.terminalHelper.getEnvironmentActivationCommands(this.terminalShellType, this.resource);
68-
if (activationCommamnds) {
69-
for (const command of activationCommamnds!) {
70-
this.terminal!.show(preserveFocus);
71-
this.terminal!.sendText(command);
72-
73-
// Give the command some time to complete.
74-
// Its been observed that sending commands too early will strip some text off.
75-
const delay = (this.terminalShellType === TerminalShellType.powershell || TerminalShellType.powershellCore) ? 1000 : 500;
76-
await sleep(delay);
77-
}
78-
}
66+
await this.terminalHelper.activateEnvironmentInTerminal(this.terminal!, preserveFocus, this.resource);
7967

8068
this.terminal!.show(preserveFocus);
8169

src/client/common/terminal/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export interface ITerminalHelper {
4949
getTerminalShellPath(): string;
5050
buildCommandForTerminal(terminalShellType: TerminalShellType, command: string, args: string[]): string;
5151
getEnvironmentActivationCommands(terminalShellType: TerminalShellType, resource?: Uri): Promise<string[] | undefined>;
52+
activateEnvironmentInTerminal(terminal: Terminal, preserveFocus?: boolean, resource?: Uri): Promise<void>;
5253
}
5354

5455
export const ITerminalActivationCommandProvider = Symbol('ITerminalActivationCommandProvider');

src/client/extension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ import * as sortImports from './sortImports';
5858
import { sendTelemetryEvent } from './telemetry';
5959
import { EDITOR_LOAD } from './telemetry/constants';
6060
import { registerTypes as commonRegisterTerminalTypes } from './terminals/serviceRegistry';
61-
import { ICodeExecutionManager } from './terminals/types';
61+
import { ICodeExecutionManager, ITerminalAutoActivation } from './terminals/types';
6262
import { BlockFormatProviders } from './typeFormatters/blockFormatProvider';
6363
import { OnEnterFormatter } from './typeFormatters/onEnterFormatter';
6464
import { TEST_OUTPUT_CHANNEL } from './unittests/common/constants';
@@ -82,6 +82,7 @@ export async function activate(context: ExtensionContext) {
8282
interpreterManager.initialize();
8383
await interpreterManager.autoSetInterpreter();
8484

85+
serviceManager.get<ITerminalAutoActivation>(ITerminalAutoActivation).initilialize();
8586
const configuration = serviceManager.get<IConfigurationService>(IConfigurationService);
8687
const pythonSettings = configuration.getSettings();
8788

src/client/terminals/activation.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
import { inject, injectable } from 'inversify';
7+
import { Disposable, Terminal } from 'vscode';
8+
import { ITerminalManager } from '../common/application/types';
9+
import { ITerminalHelper } from '../common/terminal/types';
10+
import { IDisposableRegistry } from '../common/types';
11+
import { IServiceContainer } from '../ioc/types';
12+
import { ITerminalAutoActivation } from './types';
13+
14+
@injectable()
15+
export class TerminalAutoActivation implements ITerminalAutoActivation {
16+
private readonly helper: ITerminalHelper;
17+
constructor(@inject(IServiceContainer) private container: IServiceContainer) {
18+
this.helper = container.get<ITerminalHelper>(ITerminalHelper);
19+
}
20+
public initilialize() {
21+
const manager = this.container.get<ITerminalManager>(ITerminalManager);
22+
const disposables = this.container.get<Disposable[]>(IDisposableRegistry);
23+
const disposable = manager.onDidOpenTerminal(this.activateTerminal, this);
24+
disposables.push(disposable);
25+
}
26+
public activateTerminal(terminal: Terminal): Promise<void> {
27+
return this.helper.activateEnvironmentInTerminal(terminal);
28+
}
29+
}

src/client/terminals/serviceRegistry.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
// Licensed under the MIT License.
33

44
import { IServiceManager } from '../ioc/types';
5+
import { TerminalAutoActivation } from './activation';
56
import { CodeExecutionManager } from './codeExecution/codeExecutionManager';
67
import { DjangoShellCodeExecutionProvider } from './codeExecution/djangoShellCodeExecution';
78
import { CodeExecutionHelper } from './codeExecution/helper';
89
import { ReplProvider } from './codeExecution/repl';
910
import { TerminalCodeExecutionProvider } from './codeExecution/terminalCodeExecution';
10-
import { ICodeExecutionHelper, ICodeExecutionManager, ICodeExecutionService } from './types';
11+
import { ICodeExecutionHelper, ICodeExecutionManager, ICodeExecutionService, ITerminalAutoActivation } from './types';
1112

1213
export function registerTypes(serviceManager: IServiceManager) {
1314
serviceManager.addSingleton<ICodeExecutionHelper>(ICodeExecutionHelper, CodeExecutionHelper);
1415
serviceManager.addSingleton<ICodeExecutionManager>(ICodeExecutionManager, CodeExecutionManager);
1516
serviceManager.addSingleton<ICodeExecutionService>(ICodeExecutionService, DjangoShellCodeExecutionProvider, 'djangoShell');
1617
serviceManager.addSingleton<ICodeExecutionService>(ICodeExecutionService, TerminalCodeExecutionProvider, 'standard');
1718
serviceManager.addSingleton<ICodeExecutionService>(ICodeExecutionService, ReplProvider, 'repl');
19+
serviceManager.addSingleton<ITerminalAutoActivation>(ITerminalAutoActivation, TerminalAutoActivation);
1820
}

src/client/terminals/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
import { TextEditor, Uri } from 'vscode';
4+
import { Terminal, TextEditor, Uri } from 'vscode';
55

66
export const ICodeExecutionService = Symbol('ICodeExecutionService');
77

@@ -25,3 +25,9 @@ export const ICodeExecutionManager = Symbol('ICodeExecutionManager');
2525
export interface ICodeExecutionManager {
2626
registerCommands(): void;
2727
}
28+
29+
export const ITerminalAutoActivation = Symbol('ITerminalAutoActivation');
30+
export interface ITerminalAutoActivation {
31+
initilialize(): void;
32+
activateTerminal(terminal: Terminal): Promise<void>;
33+
}

0 commit comments

Comments
 (0)