Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions src/features/terminal/shells/bash/bashStartup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import which from 'which';
import { traceError, traceInfo, traceVerbose } from '../../../../common/logging';
import { ShellConstants } from '../../../common/shellConstants';
import { hasStartupCode, insertStartupCode, removeStartupCode } from '../common/editUtils';
import { shellIntegrationForActiveTerminal } from '../common/shellUtils';
import { isWsl, shellIntegrationForActiveTerminal } from '../common/shellUtils';
import { ShellScriptEditState, ShellSetupState, ShellStartupScriptProvider } from '../startupProvider';
import { BASH_ENV_KEY, BASH_OLD_ENV_KEY, BASH_SCRIPT_VERSION, ZSH_ENV_KEY, ZSH_OLD_ENV_KEY } from './bashConstants';

Expand Down Expand Up @@ -61,16 +61,14 @@ function getActivationContent(key: string): string {
async function isStartupSetup(profile: string, key: string): Promise<ShellSetupState> {
if (await fs.pathExists(profile)) {
const content = await fs.readFile(profile, 'utf8');
return hasStartupCode(content, regionStart, regionEnd, [key])
? ShellSetupState.Setup
: ShellSetupState.NotSetup;
} else {
return ShellSetupState.NotSetup;
if (hasStartupCode(content, regionStart, regionEnd, [key])) {
return ShellSetupState.Setup;
}
}
return ShellSetupState.NotSetup;
}

async function setupStartup(profile: string, key: string, name: string): Promise<boolean> {
if (shellIntegrationForActiveTerminal(name, profile)) {
if (shellIntegrationForActiveTerminal(name, profile) && !isWsl()) {
removeStartup(profile, key);
return true;
}
Expand Down
11 changes: 9 additions & 2 deletions src/features/terminal/shells/common/shellUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,17 @@ export function shellIntegrationForActiveTerminal(name: string, profile?: string

if (hasShellIntegration) {
traceInfo(
`SHELL: Shell integration is available on your active terminal. Python activate scripts will be evaluated at shell integration level.
Skipping modification of ${name} profile at: ${profile}`,
`SHELL: Shell integration is available on your active terminal, with name ${name} and profile ${profile}. Python activate scripts will be evaluated at shell integration level, except in WSL.`
);
return true;
}
return false;
}

export function isWsl(): boolean {
// WSL sets these environment variables
return !!(process.env.WSL_DISTRO_NAME ||
process.env.WSL_INTEROP ||
process.env.WSLENV);
}

4 changes: 2 additions & 2 deletions src/features/terminal/shells/fish/fishStartup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import which from 'which';
import { traceError, traceInfo, traceVerbose } from '../../../../common/logging';
import { ShellConstants } from '../../../common/shellConstants';
import { hasStartupCode, insertStartupCode, removeStartupCode } from '../common/editUtils';
import { shellIntegrationForActiveTerminal } from '../common/shellUtils';
import { isWsl, shellIntegrationForActiveTerminal } from '../common/shellUtils';
import { ShellScriptEditState, ShellSetupState, ShellStartupScriptProvider } from '../startupProvider';
import { FISH_ENV_KEY, FISH_OLD_ENV_KEY, FISH_SCRIPT_VERSION } from './fishConstants';

Expand Down Expand Up @@ -58,7 +58,7 @@ async function isStartupSetup(profilePath: string, key: string): Promise<boolean

async function setupStartup(profilePath: string, key: string): Promise<boolean> {
try {
if (shellIntegrationForActiveTerminal('fish', profilePath)) {
if (shellIntegrationForActiveTerminal('fish', profilePath) && !isWsl()) {
removeFishStartup(profilePath, key);
return true;
}
Expand Down
3 changes: 2 additions & 1 deletion src/features/terminal/shells/pwsh/pwshStartup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ShellConstants } from '../../../common/shellConstants';
import { hasStartupCode, insertStartupCode, removeStartupCode } from '../common/editUtils';
import {
extractProfilePath,
isWsl,
PROFILE_TAG_END,
PROFILE_TAG_START,
shellIntegrationForActiveTerminal,
Expand Down Expand Up @@ -145,7 +146,7 @@ async function isPowerShellStartupSetup(shell: string, profile: string): Promise
}

async function setupPowerShellStartup(shell: string, profile: string): Promise<boolean> {
if (shellIntegrationForActiveTerminal(shell, profile)) {
if (shellIntegrationForActiveTerminal(shell, profile) && !isWsl()) {
removePowerShellStartup(shell, profile, POWERSHELL_OLD_ENV_KEY);
removePowerShellStartup(shell, profile, POWERSHELL_ENV_KEY);
return true;
Expand Down
2 changes: 0 additions & 2 deletions src/features/terminal/terminalEnvVarInjector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ export class TerminalEnvVarInjector implements Disposable {
await this.injectEnvironmentVariablesForWorkspace(workspaceFolder);
} else {
// No provided workspace - update all workspaces
this.envVarCollection.clear();

const workspaceFolders = workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) {
Expand Down Expand Up @@ -140,7 +139,6 @@ export class TerminalEnvVarInjector implements Disposable {

// use scoped environment variable collection
const envVarScope = this.getEnvironmentVariableCollectionScoped({ workspaceFolder });
envVarScope.clear(); // Clear existing variables for this workspace

// Check if env file injection is enabled
const config = getConfiguration('python', workspaceUri);
Expand Down
40 changes: 29 additions & 11 deletions src/features/terminal/terminalManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { getConfiguration, onDidChangeConfiguration } from '../../common/workspa
import { isActivatableEnvironment } from '../common/activation';
import { identifyTerminalShell } from '../common/shellDetector';
import { getPythonApi } from '../pythonApi';
import { shellIntegrationForActiveTerminal } from './shells/common/shellUtils';
import { isWsl, shellIntegrationForActiveTerminal } from './shells/common/shellUtils';
import { ShellEnvsProvider, ShellSetupState, ShellStartupScriptProvider } from './shells/startupProvider';
import { handleSettingUpShellProfile } from './shellStartupSetupHandlers';
import {
Expand Down Expand Up @@ -129,7 +129,9 @@ export class TerminalManagerImpl implements TerminalManager {
await this.handleSetupCheck(shells);
}
} else {
traceVerbose(`Auto activation type changed to ${actType}`);
traceVerbose(`Auto activation type changed to ${actType}, we are cleaning up shell startup setup`);
// Teardown scripts when switching away from shell startup activation
await Promise.all(this.startupScriptProviders.map((p) => p.teardownScripts()));
this.shellSetup.clear();
}
}
Expand All @@ -143,32 +145,47 @@ export class TerminalManagerImpl implements TerminalManager {
private async handleSetupCheck(shellType: string | Set<string>): Promise<void> {
const shellTypes = typeof shellType === 'string' ? new Set([shellType]) : shellType;
const providers = this.startupScriptProviders.filter((p) => shellTypes.has(p.shellType));
if (providers.length > 0) {
if (providers.length > 0) {
const shellsToSetup: ShellStartupScriptProvider[] = [];
await Promise.all(
providers.map(async (p) => {
const state = await p.isSetup();
const currentSetup = (state === ShellSetupState.Setup);
// Check if we already processed this shell and the state hasn't changed
if (this.shellSetup.has(p.shellType)) {
traceVerbose(`Shell profile for ${p.shellType} already checked.`);
return;
const cachedSetup = this.shellSetup.get(p.shellType);
if (currentSetup === cachedSetup) {
traceVerbose(`Shell profile for ${p.shellType} already checked, state unchanged.`);
return;
}
traceVerbose(`Shell profile for ${p.shellType} state changed from ${cachedSetup} to ${currentSetup}, re-evaluating.`);
}
traceVerbose(`Checking shell profile for ${p.shellType}.`);
const state = await p.isSetup();
if (state === ShellSetupState.NotSetup) {
// Check if shell integration is available before marking for setup
if (shellIntegrationForActiveTerminal(p.name)) {
traceVerbose(`WSL detected: ${isWsl()}, Shell integration available: ${shellIntegrationForActiveTerminal(p.name)}`);

if (shellIntegrationForActiveTerminal(p.name) && !isWsl()) {
// Shell integration available and NOT in WSL - skip setup
await p.teardownScripts();
this.shellSetup.set(p.shellType, true);
traceVerbose(
`Shell integration available for ${p.shellType}, skipping prompt, and profile modification.`,
`Shell integration available for ${p.shellType} (not WSL), skipping prompt, and profile modification.`,
);
} else {
// No shell integration, mark for setup
// WSL (regardless of integration) OR no shell integration - needs setup
this.shellSetup.set(p.shellType, false);
shellsToSetup.push(p);
traceVerbose(
`Shell integration is NOT avaoiable. Shell profile for ${p.shellType} is not setup.`,
`Shell integration is NOT available. Shell profile for ${p.shellType} is not setup.`,
);
}
} else if (state === ShellSetupState.Setup) {
if (shellIntegrationForActiveTerminal(p.name) && !isWsl()) {
await p.teardownScripts();
traceVerbose(
`Shell integration available for ${p.shellType}, removed profile script in favor of shell integration.`,
);
}
this.shellSetup.set(p.shellType, true);
traceVerbose(`Shell profile for ${p.shellType} is setup.`);
} else if (state === ShellSetupState.NotInstalled) {
Expand Down Expand Up @@ -228,6 +245,7 @@ export class TerminalManagerImpl implements TerminalManager {
let actType = getAutoActivationType();
const shellType = identifyTerminalShell(terminal);
if (actType === ACT_TYPE_SHELL) {
await this.handleSetupCheck(shellType);
actType = await this.getEffectiveActivationType(shellType);
}

Expand Down
38 changes: 25 additions & 13 deletions src/features/terminal/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,32 +103,44 @@ export type AutoActivationType = 'off' | 'command' | 'shellStartup';
* - 'off': Auto-activation is disabled
*
* Priority order:
* 1. python-envs.terminal.autoActivationType setting
* 2. python.terminal.activateEnvironment setting (if false updates python-envs.terminal.autoActivationType)
* 1. python-envs.terminal.autoActivationType
* a. globalRemoteValue
* b. globalLocalValue
* c. globalValue
* 2. python.terminal.activateEnvironment setting (if false, returns 'off' & sets autoActivationType to 'off')
* 3. Default to 'command' if no setting is found
*
* @returns {AutoActivationType} The determined auto-activation type
*/
export function getAutoActivationType(): AutoActivationType {
const pyEnvsConfig = getConfiguration('python-envs');
const pyEnvsActivationType = pyEnvsConfig.inspect<AutoActivationType>('terminal.autoActivationType');

const pyEnvsActivationType = pyEnvsConfig.get<AutoActivationType | undefined>(
'terminal.autoActivationType',
undefined,
);
if (pyEnvsActivationType !== undefined) {
return pyEnvsActivationType;
if (pyEnvsActivationType) {
// Priority order: globalRemoteValue > globalLocalValue > globalValue
const activationType = pyEnvsActivationType as Record<string, unknown>;

if ('globalRemoteValue' in pyEnvsActivationType && activationType.globalRemoteValue !== undefined) {
return activationType.globalRemoteValue as AutoActivationType;
}
if ('globalLocalValue' in pyEnvsActivationType && activationType.globalLocalValue !== undefined) {
return activationType.globalLocalValue as AutoActivationType;
}
if (pyEnvsActivationType.globalValue !== undefined) {
return pyEnvsActivationType.globalValue;
}
}

// If none of the python-envs settings are defined, check the legacy python setting
const pythonConfig = getConfiguration('python');
const pythonActivateSetting = pythonConfig.get<boolean | undefined>('terminal.activateEnvironment', undefined);
if (pythonActivateSetting !== undefined) {
if (pythonActivateSetting === false) {
pyEnvsConfig.set('terminal.autoActivationType', ACT_TYPE_OFF);
}
return pythonActivateSetting ? ACT_TYPE_COMMAND : ACT_TYPE_OFF;
if (pythonActivateSetting === false) {
// Set autoActivationType to 'off' if python.terminal.activateEnvironment is false
pyEnvsConfig.update('terminal.autoActivationType', ACT_TYPE_OFF);
return ACT_TYPE_OFF;
}

// Default to 'command' if no settings are found or if pythonActivateSetting is true/undefined
return ACT_TYPE_COMMAND;
}

Expand Down
Loading
Loading