Skip to content

Commit 26060e5

Browse files
committed
fix: use 'command' activation strategy when 'shellStartup' activation is not available
1 parent dcddae9 commit 26060e5

File tree

4 files changed

+66
-67
lines changed

4 files changed

+66
-67
lines changed

src/features/common/activation.ts

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { Terminal } from 'vscode';
2-
import { PythonCommandRunConfiguration, PythonEnvironment } from '../../api';
2+
import { PythonEnvironment } from '../../api';
3+
import {
4+
getShellActivationCommand,
5+
getShellCommandAsString,
6+
getShellDeactivationCommand,
7+
} from '../terminal/shells/common/shellUtils';
38
import { identifyTerminalShell } from './shellDetector';
4-
import { getShellActivationCommand } from '../terminal/shells/common/shellUtils';
59

610
export function isActivatableEnvironment(environment: PythonEnvironment): boolean {
711
return !!environment.execInfo?.activation || !!environment.execInfo?.shellActivation;
@@ -11,31 +15,20 @@ export function isActivatedRunAvailable(environment: PythonEnvironment): boolean
1115
return !!environment.execInfo?.activatedRun;
1216
}
1317

14-
export function getActivationCommand(
15-
terminal: Terminal,
16-
environment: PythonEnvironment,
17-
): PythonCommandRunConfiguration[] | undefined {
18+
export function getActivationCommand(terminal: Terminal, environment: PythonEnvironment): string | undefined {
1819
const shell = identifyTerminalShell(terminal);
19-
return getShellActivationCommand(shell, environment);
20+
const command = getShellActivationCommand(shell, environment);
21+
if (command) {
22+
return getShellCommandAsString(shell, command);
23+
}
24+
return undefined;
2025
}
2126

22-
export function getDeactivationCommand(
23-
terminal: Terminal,
24-
environment: PythonEnvironment,
25-
): PythonCommandRunConfiguration[] | undefined {
27+
export function getDeactivationCommand(terminal: Terminal, environment: PythonEnvironment): string | undefined {
2628
const shell = identifyTerminalShell(terminal);
27-
28-
let deactivation: PythonCommandRunConfiguration[] | undefined;
29-
if (environment.execInfo?.shellDeactivation) {
30-
deactivation = environment.execInfo.shellDeactivation.get(shell);
31-
if (!deactivation) {
32-
deactivation = environment.execInfo.shellDeactivation.get('unknown');
33-
}
29+
const command = getShellDeactivationCommand(shell, environment);
30+
if (command) {
31+
return getShellCommandAsString(shell, command);
3432
}
35-
36-
if (!deactivation) {
37-
deactivation = environment.execInfo?.deactivation;
38-
}
39-
40-
return deactivation;
33+
return undefined;
4134
}

src/features/terminal/shells/common/shellUtils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,21 @@ export function getShellActivationCommand(
5555

5656
return activation;
5757
}
58+
export function getShellDeactivationCommand(
59+
shell: string,
60+
environment: PythonEnvironment,
61+
): PythonCommandRunConfiguration[] | undefined {
62+
let deactivation: PythonCommandRunConfiguration[] | undefined;
63+
if (environment.execInfo?.shellDeactivation) {
64+
deactivation = environment.execInfo.shellDeactivation.get(shell);
65+
if (!deactivation) {
66+
deactivation = environment.execInfo.shellDeactivation.get('unknown');
67+
}
68+
}
69+
70+
if (!deactivation) {
71+
deactivation = environment.execInfo?.deactivation;
72+
}
73+
74+
return deactivation;
75+
}

src/features/terminal/terminalActivationState.ts

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ import {
77
TerminalShellExecutionStartEvent,
88
TerminalShellIntegration,
99
} from 'vscode';
10-
import { PythonCommandRunConfiguration, PythonEnvironment } from '../../api';
11-
import { onDidEndTerminalShellExecution, onDidStartTerminalShellExecution } from '../../common/window.apis';
10+
import { PythonEnvironment } from '../../api';
1211
import { traceError, traceInfo, traceVerbose } from '../../common/logging';
13-
import { isTaskTerminal } from './utils';
12+
import { onDidEndTerminalShellExecution, onDidStartTerminalShellExecution } from '../../common/window.apis';
1413
import { getActivationCommand, getDeactivationCommand } from '../common/activation';
15-
import { quoteArgs } from '../execution/execUtils';
14+
import { isTaskTerminal } from './utils';
1615

1716
export interface DidChangeTerminalActivationStateEvent {
1817
terminal: Terminal;
@@ -92,11 +91,6 @@ export class TerminalActivationImpl implements TerminalActivationInternal {
9291
return;
9392
}
9493

95-
if (terminal.shellIntegration?.env?.value?.['TERM_PROGRAM'] !== 'vscode') {
96-
traceVerbose('Terminal is not a VS Code terminal, skipping activation');
97-
return;
98-
}
99-
10094
if (this.deactivatingTerminals.has(terminal)) {
10195
traceVerbose('Terminal is being deactivated, cannot activate.');
10296
return this.deactivatingTerminals.get(terminal);
@@ -203,23 +197,15 @@ export class TerminalActivationImpl implements TerminalActivationInternal {
203197
private activateLegacy(terminal: Terminal, environment: PythonEnvironment) {
204198
const activationCommands = getActivationCommand(terminal, environment);
205199
if (activationCommands) {
206-
for (const command of activationCommands) {
207-
const args = command.args ?? [];
208-
const text = quoteArgs([command.executable, ...args]).join(' ');
209-
terminal.sendText(text);
210-
}
200+
terminal.sendText(activationCommands);
211201
this.activatedTerminals.set(terminal, environment);
212202
}
213203
}
214204

215205
private deactivateLegacy(terminal: Terminal, environment: PythonEnvironment) {
216206
const deactivationCommands = getDeactivationCommand(terminal, environment);
217207
if (deactivationCommands) {
218-
for (const command of deactivationCommands) {
219-
const args = command.args ?? [];
220-
const text = quoteArgs([command.executable, ...args]).join(' ');
221-
terminal.sendText(text);
222-
}
208+
terminal.sendText(deactivationCommands);
223209
this.activatedTerminals.delete(terminal);
224210
}
225211
}
@@ -229,12 +215,10 @@ export class TerminalActivationImpl implements TerminalActivationInternal {
229215
terminal: Terminal,
230216
environment: PythonEnvironment,
231217
): Promise<void> {
232-
const activationCommands = getActivationCommand(terminal, environment);
233-
if (activationCommands) {
218+
const activationCommand = getActivationCommand(terminal, environment);
219+
if (activationCommand) {
234220
try {
235-
for (const command of activationCommands) {
236-
await this.executeTerminalShellCommandInternal(shellIntegration, command);
237-
}
221+
await this.executeTerminalShellCommandInternal(shellIntegration, activationCommand);
238222
this.activatedTerminals.set(terminal, environment);
239223
} catch {
240224
traceError('Failed to activate environment using shell integration');
@@ -249,12 +233,10 @@ export class TerminalActivationImpl implements TerminalActivationInternal {
249233
terminal: Terminal,
250234
environment: PythonEnvironment,
251235
): Promise<void> {
252-
const deactivationCommands = getDeactivationCommand(terminal, environment);
253-
if (deactivationCommands) {
236+
const deactivationCommand = getDeactivationCommand(terminal, environment);
237+
if (deactivationCommand) {
254238
try {
255-
for (const command of deactivationCommands) {
256-
await this.executeTerminalShellCommandInternal(shellIntegration, command);
257-
}
239+
await this.executeTerminalShellCommandInternal(shellIntegration, deactivationCommand);
258240
this.activatedTerminals.delete(terminal);
259241
} catch {
260242
traceError('Failed to deactivate environment using shell integration');
@@ -266,14 +248,14 @@ export class TerminalActivationImpl implements TerminalActivationInternal {
266248

267249
private async executeTerminalShellCommandInternal(
268250
shellIntegration: TerminalShellIntegration,
269-
command: PythonCommandRunConfiguration,
251+
command: string,
270252
): Promise<boolean> {
271-
const execution = shellIntegration.executeCommand(command.executable, command.args ?? []);
253+
const execution = shellIntegration.executeCommand(command);
272254
const disposables: Disposable[] = [];
273255

274256
const promise = new Promise<void>((resolve) => {
275257
const timer = setTimeout(() => {
276-
traceError(`Shell execution timed out: ${command.executable} ${command.args?.join(' ')}`);
258+
traceError(`Shell execution timed out: ${command}`);
277259
resolve();
278260
}, 2000);
279261

@@ -286,7 +268,7 @@ export class TerminalActivationImpl implements TerminalActivationInternal {
286268
}),
287269
this.onTerminalShellExecutionStart((e: TerminalShellExecutionStartEvent) => {
288270
if (e.execution === execution) {
289-
traceVerbose(`Shell execution started: ${command.executable} ${command.args?.join(' ')}`);
271+
traceVerbose(`Shell execution started: ${command}`);
290272
}
291273
}),
292274
);
@@ -296,7 +278,7 @@ export class TerminalActivationImpl implements TerminalActivationInternal {
296278
await promise;
297279
return true;
298280
} catch {
299-
traceError(`Failed to execute shell command: ${command.executable} ${command.args?.join(' ')}`);
281+
traceError(`Failed to execute shell command: ${command}`);
300282
return false;
301283
} finally {
302284
disposables.forEach((d) => d.dispose());

src/features/terminal/terminalManager.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
1-
import * as path from 'path';
21
import * as fsapi from 'fs-extra';
3-
import { Disposable, EventEmitter, ProgressLocation, Terminal, Uri, TerminalOptions } from 'vscode';
2+
import * as path from 'path';
3+
import { Disposable, EventEmitter, ProgressLocation, Terminal, TerminalOptions, Uri } from 'vscode';
4+
import { PythonEnvironment, PythonEnvironmentApi, PythonProject, PythonTerminalCreateOptions } from '../../api';
5+
import { traceInfo, traceVerbose } from '../../common/logging';
46
import {
57
createTerminal,
68
onDidCloseTerminal,
79
onDidOpenTerminal,
810
terminals,
911
withProgress,
1012
} from '../../common/window.apis';
11-
import { PythonEnvironment, PythonEnvironmentApi, PythonProject, PythonTerminalCreateOptions } from '../../api';
12-
import { isActivatableEnvironment } from '../common/activation';
1313
import { getConfiguration } from '../../common/workspace.apis';
14-
import { getAutoActivationType, getEnvironmentForTerminal, waitForShellIntegration } from './utils';
14+
import { isActivatableEnvironment } from '../common/activation';
15+
import { identifyTerminalShell } from '../common/shellDetector';
16+
import { getPythonApi } from '../pythonApi';
17+
import { ShellEnvsProvider } from './shells/startupProvider';
1518
import {
1619
DidChangeTerminalActivationStateEvent,
1720
TerminalActivation,
1821
TerminalActivationInternal,
1922
TerminalEnvironment,
2023
} from './terminalActivationState';
21-
import { getPythonApi } from '../pythonApi';
22-
import { traceInfo, traceVerbose } from '../../common/logging';
23-
import { ShellEnvsProvider } from './shells/startupProvider';
24+
import { getAutoActivationType, getEnvironmentForTerminal, waitForShellIntegration } from './utils';
2425

2526
export interface TerminalCreation {
2627
create(environment: PythonEnvironment, options: PythonTerminalCreateOptions): Promise<Terminal>;
@@ -102,7 +103,12 @@ export class TerminalManagerImpl implements TerminalManager {
102103
}
103104

104105
private async autoActivateOnTerminalOpen(terminal: Terminal, environment: PythonEnvironment): Promise<void> {
105-
const actType = getAutoActivationType();
106+
let actType = getAutoActivationType();
107+
const shellType = identifyTerminalShell(terminal);
108+
if (shellType === 'shellStartup' && !this.startupProviders.some((p) => p.shellType === shellType)) {
109+
actType = 'command';
110+
traceInfo(`Shell startup not supported for ${shellType}, using command activation`);
111+
}
106112
if (actType === 'command') {
107113
if (isActivatableEnvironment(environment)) {
108114
await withProgress(

0 commit comments

Comments
 (0)