Skip to content

Commit 0438813

Browse files
author
Kartik Raj
authored
Guide users to install workaround when deactivate command is run (#22223)
1 parent a55484d commit 0438813

29 files changed

+979
-115
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"quickPickSortByLabel",
2323
"testObserver",
2424
"quickPickItemTooltip",
25-
"saveEditor"
25+
"saveEditor",
26+
"terminalDataWriteEvent"
2627
],
2728
"author": {
2829
"name": "Microsoft Corporation"

pythonFiles/deactivate

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Same as deactivate in "<venv>/bin/activate"
2+
deactivate () {
3+
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
4+
PATH="${_OLD_VIRTUAL_PATH:-}"
5+
export PATH
6+
unset _OLD_VIRTUAL_PATH
7+
fi
8+
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
9+
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
10+
export PYTHONHOME
11+
unset _OLD_VIRTUAL_PYTHONHOME
12+
fi
13+
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
14+
hash -r 2> /dev/null
15+
fi
16+
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
17+
PS1="${_OLD_VIRTUAL_PS1:-}"
18+
export PS1
19+
unset _OLD_VIRTUAL_PS1
20+
fi
21+
unset VIRTUAL_ENV
22+
unset VIRTUAL_ENV_PROMPT
23+
if [ ! "${1:-}" = "nondestructive" ] ; then
24+
unset -f deactivate
25+
fi
26+
}
27+
28+
# Initialize the variables required by deactivate function
29+
_OLD_VIRTUAL_PS1="${PS1:-}"
30+
_OLD_VIRTUAL_PATH="$PATH"
31+
if [ -n "${PYTHONHOME:-}" ] ; then
32+
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
33+
fi

pythonFiles/deactivate.csh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Same as deactivate in "<venv>/bin/activate.csh"
2+
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
3+
4+
# Initialize the variables required by deactivate function
5+
set _OLD_VIRTUAL_PROMPT="$prompt"
6+
set _OLD_VIRTUAL_PATH="$PATH"

pythonFiles/deactivate.fish

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Same as deactivate in "<venv>/bin/activate.fish"
2+
function deactivate -d "Exit virtual environment and return to normal shell environment"
3+
# reset old environment variables
4+
if test -n "$_OLD_VIRTUAL_PATH"
5+
set -gx PATH $_OLD_VIRTUAL_PATH
6+
set -e _OLD_VIRTUAL_PATH
7+
end
8+
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
9+
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
10+
set -e _OLD_VIRTUAL_PYTHONHOME
11+
end
12+
13+
if test -n "$vscode_python_old_fish_prompt_OVERRIDE"
14+
set -e vscode_python_old_fish_prompt_OVERRIDE
15+
if functions -q vscode_python_old_fish_prompt
16+
functions -e fish_prompt
17+
functions -c vscode_python_old_fish_prompt fish_prompt
18+
functions -e vscode_python_old_fish_prompt
19+
end
20+
end
21+
22+
set -e VIRTUAL_ENV
23+
set -e VIRTUAL_ENV_PROMPT
24+
if test "$argv[1]" != "nondestructive"
25+
functions -e deactivate
26+
end
27+
end
28+
29+
# Initialize the variables required by deactivate function
30+
set -gx _OLD_VIRTUAL_PATH $PATH
31+
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
32+
functions -c fish_prompt vscode_python_old_fish_prompt
33+
end
34+
if set -q PYTHONHOME
35+
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
36+
end

pythonFiles/deactivate.ps1

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Same as deactivate in "Activate.ps1"
2+
function global:deactivate ([switch]$NonDestructive) {
3+
if (Test-Path function:_OLD_VIRTUAL_PROMPT) {
4+
copy-item function:_OLD_VIRTUAL_PROMPT function:prompt
5+
remove-item function:_OLD_VIRTUAL_PROMPT
6+
}
7+
if (Test-Path env:_OLD_VIRTUAL_PYTHONHOME) {
8+
copy-item env:_OLD_VIRTUAL_PYTHONHOME env:PYTHONHOME
9+
remove-item env:_OLD_VIRTUAL_PYTHONHOME
10+
}
11+
if (Test-Path env:_OLD_VIRTUAL_PATH) {
12+
copy-item env:_OLD_VIRTUAL_PATH env:PATH
13+
remove-item env:_OLD_VIRTUAL_PATH
14+
}
15+
if (Test-Path env:VIRTUAL_ENV) {
16+
remove-item env:VIRTUAL_ENV
17+
}
18+
if (!$NonDestructive) {
19+
remove-item function:deactivate
20+
}
21+
}
22+
23+
# Initialize the variables required by deactivate function
24+
if (! $env:VIRTUAL_ENV_DISABLE_PROMPT) {
25+
function global:_OLD_VIRTUAL_PROMPT {""}
26+
copy-item function:prompt function:_OLD_VIRTUAL_PROMPT
27+
}
28+
if (Test-Path env:PYTHONHOME) {
29+
copy-item env:PYTHONHOME env:_OLD_VIRTUAL_PYTHONHOME
30+
}
31+
copy-item env:PATH env:_OLD_VIRTUAL_PATH

src/client/common/application/applicationShell.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
DocumentSelector,
1111
env,
1212
Event,
13+
EventEmitter,
1314
InputBox,
1415
InputBoxOptions,
1516
languages,
@@ -37,7 +38,8 @@ import {
3738
WorkspaceFolder,
3839
WorkspaceFolderPickOptions,
3940
} from 'vscode';
40-
import { IApplicationShell } from './types';
41+
import { traceError } from '../../logging';
42+
import { IApplicationShell, TerminalDataWriteEvent } from './types';
4143

4244
@injectable()
4345
export class ApplicationShell implements IApplicationShell {
@@ -172,4 +174,12 @@ export class ApplicationShell implements IApplicationShell {
172174
public createLanguageStatusItem(id: string, selector: DocumentSelector): LanguageStatusItem {
173175
return languages.createLanguageStatusItem(id, selector);
174176
}
177+
public get onDidWriteTerminalData(): Event<TerminalDataWriteEvent> {
178+
try {
179+
return window.onDidWriteTerminalData;
180+
} catch (ex) {
181+
traceError('Failed to get proposed API onDidWriteTerminalData', ex);
182+
return new EventEmitter<TerminalDataWriteEvent>().event;
183+
}
184+
}
175185
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { ProgressOptions } from 'vscode';
5+
import { Deferred, createDeferred } from '../utils/async';
6+
import { IApplicationShell } from './types';
7+
8+
export class ProgressService {
9+
private deferred: Deferred<void> | undefined;
10+
11+
constructor(private readonly shell: IApplicationShell) {}
12+
13+
public showProgress(options: ProgressOptions): void {
14+
if (!this.deferred) {
15+
this.createProgress(options);
16+
}
17+
}
18+
19+
public hideProgress(): void {
20+
if (this.deferred) {
21+
this.deferred.resolve();
22+
this.deferred = undefined;
23+
}
24+
}
25+
26+
private createProgress(options: ProgressOptions) {
27+
this.shell.withProgress(options, () => {
28+
this.deferred = createDeferred();
29+
return this.deferred.promise;
30+
});
31+
}
32+
}

src/client/common/application/types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,17 @@ import { Resource } from '../types';
6767
import { ICommandNameArgumentTypeMapping } from './commands';
6868
import { ExtensionContextKey } from './contextKeys';
6969

70+
export interface TerminalDataWriteEvent {
71+
/**
72+
* The {@link Terminal} for which the data was written.
73+
*/
74+
readonly terminal: Terminal;
75+
/**
76+
* The data being written.
77+
*/
78+
readonly data: string;
79+
}
80+
7081
export const IApplicationShell = Symbol('IApplicationShell');
7182
export interface IApplicationShell {
7283
/**
@@ -75,6 +86,13 @@ export interface IApplicationShell {
7586
*/
7687
readonly onDidChangeWindowState: Event<WindowState>;
7788

89+
/**
90+
* An event which fires when the terminal's child pseudo-device is written to (the shell).
91+
* In other words, this provides access to the raw data stream from the process running
92+
* within the terminal, including VT sequences.
93+
*/
94+
readonly onDidWriteTerminalData: Event<TerminalDataWriteEvent>;
95+
7896
showInformationMessage(message: string, ...items: string[]): Thenable<string | undefined>;
7997

8098
/**

src/client/common/experiments/helpers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import { env, workspace } from 'vscode';
77
import { IExperimentService } from '../types';
88
import { TerminalEnvVarActivation } from './groups';
99
import { isTestExecution } from '../constants';
10+
import { traceInfo } from '../../logging';
1011

1112
export function inTerminalEnvVarExperiment(experimentService: IExperimentService): boolean {
12-
if (!isTestExecution() && workspace.workspaceFile && env.remoteName) {
13+
if (!isTestExecution() && env.remoteName && workspace.workspaceFolders && workspace.workspaceFolders.length > 1) {
1314
// TODO: Remove this if statement once https://github.com/microsoft/vscode/issues/180486 is fixed.
15+
traceInfo('Not enabling terminal env var experiment in multiroot remote workspaces');
1416
return false;
1517
}
1618
if (!experimentService.inExperimentSync(TerminalEnvVarActivation.experiment)) {

src/client/common/platform/fs-paths.ts

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

44
import * as nodepath from 'path';
55
import { getSearchPathEnvVarNames } from '../utils/exec';
6+
import * as fs from 'fs-extra';
67
import { getOSType, OSType } from '../utils/platform';
78
import { IExecutables, IFileSystemPaths, IFileSystemPathUtils } from './types';
89

@@ -170,3 +171,22 @@ export function isParentPath(filePath: string, parentPath: string): boolean {
170171
export function arePathsSame(path1: string, path2: string): boolean {
171172
return normCasePath(path1) === normCasePath(path2);
172173
}
174+
175+
export async function copyFile(src: string, dest: string): Promise<void> {
176+
const destDir = nodepath.dirname(dest);
177+
if (!(await fs.pathExists(destDir))) {
178+
await fs.mkdirp(destDir);
179+
}
180+
181+
await fs.copy(src, dest, {
182+
overwrite: true,
183+
});
184+
}
185+
186+
export function pathExists(absPath: string): Promise<boolean> {
187+
return fs.pathExists(absPath);
188+
}
189+
190+
export function createFile(filename: string): Promise<void> {
191+
return fs.createFile(filename);
192+
}

src/client/common/utils/localize.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export namespace Common {
6161
export const noIWillDoItLater = l10n.t('No, I will do it later');
6262
export const notNow = l10n.t('Not now');
6363
export const doNotShowAgain = l10n.t("Don't show again");
64+
export const editSomething = l10n.t('Edit {0}');
6465
export const reload = l10n.t('Reload');
6566
export const moreInfo = l10n.t('More Info');
6667
export const learnMore = l10n.t('Learn more');
@@ -198,6 +199,11 @@ export namespace Interpreters {
198199
export const terminalEnvVarCollectionPrompt = l10n.t(
199200
'The Python extension automatically activates all terminals using the selected environment, even when the name of the environment{0} is not present in the terminal prompt. [Learn more](https://aka.ms/vscodePythonTerminalActivation).',
200201
);
202+
export const terminalDeactivateProgress = l10n.t('Editing {0}...');
203+
export const restartingTerminal = l10n.t('Restarting terminal and deactivating...');
204+
export const terminalDeactivatePrompt = l10n.t(
205+
'Deactivating virtual environments may not work by default due to a technical limitation in our activation approach, but it can be resolved by appending a line to "{0}". Be sure to restart the shell afterward. [Learn more](https://aka.ms/AAmx2ft).',
206+
);
201207
export const activatedCondaEnvLaunch = l10n.t(
202208
'We noticed VS Code was launched from an activated conda environment, would you like to select it?',
203209
);

src/client/interpreter/activation/types.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,3 @@ export interface IEnvironmentActivationService {
2121
interpreter?: PythonEnvironment,
2222
): Promise<string[] | undefined>;
2323
}
24-
25-
export const ITerminalEnvVarCollectionService = Symbol('ITerminalEnvVarCollectionService');
26-
export interface ITerminalEnvVarCollectionService {
27-
/**
28-
* Returns true if we know with high certainity the terminal prompt is set correctly for a particular resource.
29-
*/
30-
isTerminalPromptSetCorrectly(resource?: Resource): boolean;
31-
}

src/client/interpreter/serviceRegistry.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66
import { IExtensionActivationService, IExtensionSingleActivationService } from '../activation/types';
77
import { IServiceManager } from '../ioc/types';
88
import { EnvironmentActivationService } from './activation/service';
9-
import { TerminalEnvVarCollectionPrompt } from './activation/terminalEnvVarCollectionPrompt';
10-
import { TerminalEnvVarCollectionService } from './activation/terminalEnvVarCollectionService';
11-
import { IEnvironmentActivationService, ITerminalEnvVarCollectionService } from './activation/types';
9+
import { IEnvironmentActivationService } from './activation/types';
1210
import { InterpreterAutoSelectionService } from './autoSelection/index';
1311
import { InterpreterAutoSelectionProxyService } from './autoSelection/proxy';
1412
import { IInterpreterAutoSelectionService, IInterpreterAutoSelectionProxyService } from './autoSelection/types';
@@ -110,13 +108,4 @@ export function registerTypes(serviceManager: IServiceManager): void {
110108
IEnvironmentActivationService,
111109
EnvironmentActivationService,
112110
);
113-
serviceManager.addSingleton<ITerminalEnvVarCollectionService>(
114-
ITerminalEnvVarCollectionService,
115-
TerminalEnvVarCollectionService,
116-
);
117-
serviceManager.addBinding(ITerminalEnvVarCollectionService, IExtensionActivationService);
118-
serviceManager.addSingleton<IExtensionSingleActivationService>(
119-
IExtensionSingleActivationService,
120-
TerminalEnvVarCollectionPrompt,
121-
);
122111
}

src/client/interpreter/virtualEnvs/activatedEnvLaunch.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ export class ActivatedEnvironmentLaunch implements IActivatedEnvironmentLaunch {
9191
}
9292
if (process.env.VSCODE_CLI !== '1') {
9393
// We only want to select the interpreter if VS Code was launched from the command line.
94-
traceVerbose('VS Code was not launched from the command line, not selecting activated interpreter');
94+
traceVerbose(
95+
'VS Code was not launched from the command line, not selecting activated interpreter',
96+
JSON.stringify(process.env, undefined, 4),
97+
);
9598
return undefined;
9699
}
97100
const prefix = await this.getPrefixOfSelectedActivatedEnv();

0 commit comments

Comments
 (0)