diff --git a/src/features/terminal/shells/bash/bashStartup.ts b/src/features/terminal/shells/bash/bashStartup.ts index 52e18861..db9be723 100644 --- a/src/features/terminal/shells/bash/bashStartup.ts +++ b/src/features/terminal/shells/bash/bashStartup.ts @@ -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 { isWsl, shellIntegrationForActiveTerminal } from '../common/shellUtils'; +import { getShellIntegrationEnabledCache, 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'; @@ -69,7 +69,8 @@ async function isStartupSetup(profile: string, key: string): Promise { - if ((await shellIntegrationForActiveTerminal(name, profile)) && !isWsl()) { + const shellIntegrationEnabled = await getShellIntegrationEnabledCache(); + if ((shellIntegrationEnabled || (await shellIntegrationForActiveTerminal(name, profile))) && !isWsl()) { removeStartup(profile, key); return true; } diff --git a/src/features/terminal/shells/common/shellUtils.ts b/src/features/terminal/shells/common/shellUtils.ts index 5dae76a9..381be880 100644 --- a/src/features/terminal/shells/common/shellUtils.ts +++ b/src/features/terminal/shells/common/shellUtils.ts @@ -1,12 +1,16 @@ import { PythonCommandRunConfiguration, PythonEnvironment } from '../../../../api'; import { traceInfo } from '../../../../common/logging'; +import { getGlobalPersistentState } from '../../../../common/persistentState'; import { sleep } from '../../../../common/utils/asyncUtils'; import { isWindows } from '../../../../common/utils/platformUtils'; import { activeTerminalShellIntegration } from '../../../../common/window.apis'; +import { getConfiguration } from '../../../../common/workspace.apis'; import { ShellConstants } from '../../../common/shellConstants'; import { quoteArgs } from '../../../execution/execUtils'; import { SHELL_INTEGRATION_POLL_INTERVAL, SHELL_INTEGRATION_TIMEOUT } from '../../utils'; +export const SHELL_INTEGRATION_STATE_KEY = 'shellIntegration.enabled'; + function getCommandAsString(command: PythonCommandRunConfiguration[], shell: string, delimiter: string): string { const parts = []; for (const cmd of command) { @@ -114,6 +118,11 @@ export async function shellIntegrationForActiveTerminal(name: string, profile?: traceInfo( `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.`, ); + + // Update persistent storage to reflect that shell integration is available + const persistentState = await getGlobalPersistentState(); + await persistentState.set(SHELL_INTEGRATION_STATE_KEY, true); + return true; } return false; @@ -123,3 +132,32 @@ export function isWsl(): boolean { // WSL sets these environment variables return !!(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP || process.env.WSLENV); } + +export async function getShellIntegrationEnabledCache(): Promise { + const persistentState = await getGlobalPersistentState(); + const shellIntegrationInspect = + getConfiguration('terminal.integrated').inspect('shellIntegration.enabled'); + + let shellIntegrationEnabled = true; + if (shellIntegrationInspect) { + // Priority: workspaceFolder > workspace > globalRemoteValue > globalLocalValue > global > default + const inspectValue = shellIntegrationInspect as Record; + + if (shellIntegrationInspect.workspaceFolderValue !== undefined) { + shellIntegrationEnabled = shellIntegrationInspect.workspaceFolderValue; + } else if (shellIntegrationInspect.workspaceValue !== undefined) { + shellIntegrationEnabled = shellIntegrationInspect.workspaceValue; + } else if ('globalRemoteValue' in shellIntegrationInspect && inspectValue.globalRemoteValue !== undefined) { + shellIntegrationEnabled = inspectValue.globalRemoteValue as boolean; + } else if ('globalLocalValue' in shellIntegrationInspect && inspectValue.globalLocalValue !== undefined) { + shellIntegrationEnabled = inspectValue.globalLocalValue as boolean; + } else if (shellIntegrationInspect.globalValue !== undefined) { + shellIntegrationEnabled = shellIntegrationInspect.globalValue; + } else if (shellIntegrationInspect.defaultValue !== undefined) { + shellIntegrationEnabled = shellIntegrationInspect.defaultValue; + } + } + + await persistentState.set(SHELL_INTEGRATION_STATE_KEY, shellIntegrationEnabled); + return shellIntegrationEnabled; +} diff --git a/src/features/terminal/shells/fish/fishStartup.ts b/src/features/terminal/shells/fish/fishStartup.ts index 6621e141..40de3e98 100644 --- a/src/features/terminal/shells/fish/fishStartup.ts +++ b/src/features/terminal/shells/fish/fishStartup.ts @@ -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 { isWsl, shellIntegrationForActiveTerminal } from '../common/shellUtils'; +import { getShellIntegrationEnabledCache, isWsl, shellIntegrationForActiveTerminal } from '../common/shellUtils'; import { ShellScriptEditState, ShellSetupState, ShellStartupScriptProvider } from '../startupProvider'; import { FISH_ENV_KEY, FISH_OLD_ENV_KEY, FISH_SCRIPT_VERSION } from './fishConstants'; @@ -58,7 +58,8 @@ async function isStartupSetup(profilePath: string, key: string): Promise { try { - if ((await shellIntegrationForActiveTerminal('fish', profilePath)) && !isWsl()) { + const shellIntegrationEnabled = await getShellIntegrationEnabledCache(); + if ((shellIntegrationEnabled || (await shellIntegrationForActiveTerminal('fish', profilePath))) && !isWsl()) { removeFishStartup(profilePath, key); return true; } diff --git a/src/features/terminal/shells/pwsh/pwshStartup.ts b/src/features/terminal/shells/pwsh/pwshStartup.ts index e3962f6e..45bb33d5 100644 --- a/src/features/terminal/shells/pwsh/pwshStartup.ts +++ b/src/features/terminal/shells/pwsh/pwshStartup.ts @@ -13,6 +13,7 @@ import { ShellConstants } from '../../../common/shellConstants'; import { hasStartupCode, insertStartupCode, removeStartupCode } from '../common/editUtils'; import { extractProfilePath, + getShellIntegrationEnabledCache, isWsl, PROFILE_TAG_END, PROFILE_TAG_START, @@ -168,7 +169,9 @@ async function isPowerShellStartupSetup(shell: string, profile: string): Promise } async function setupPowerShellStartup(shell: string, profile: string): Promise { - if ((await shellIntegrationForActiveTerminal(shell, profile)) && !isWsl()) { + const shellIntegrationEnabled = await getShellIntegrationEnabledCache(); + + if ((shellIntegrationEnabled || (await shellIntegrationForActiveTerminal(shell, profile))) && !isWsl()) { removePowerShellStartup(shell, profile, POWERSHELL_OLD_ENV_KEY); removePowerShellStartup(shell, profile, POWERSHELL_ENV_KEY); return true; diff --git a/src/features/terminal/terminalManager.ts b/src/features/terminal/terminalManager.ts index b66f95e6..f7acb2d4 100644 --- a/src/features/terminal/terminalManager.ts +++ b/src/features/terminal/terminalManager.ts @@ -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 { isWsl, shellIntegrationForActiveTerminal } from './shells/common/shellUtils'; +import { getShellIntegrationEnabledCache, isWsl, shellIntegrationForActiveTerminal } from './shells/common/shellUtils'; import { ShellEnvsProvider, ShellSetupState, ShellStartupScriptProvider } from './shells/startupProvider'; import { handleSettingUpShellProfile } from './shellStartupSetupHandlers'; import { @@ -137,6 +137,20 @@ export class TerminalManagerImpl implements TerminalManager { this.shellSetup.clear(); } } + if (e.affectsConfiguration('terminal.integrated.shellIntegration.enabled')) { + traceInfo('Shell integration setting changed, invalidating cache'); + const updatedShellIntegrationSetting = await getShellIntegrationEnabledCache(); + if (!updatedShellIntegrationSetting) { + const shells = new Set( + terminals() + .map((t) => identifyTerminalShell(t)) + .filter((t) => t !== 'unknown'), + ); + if (shells.size > 0) { + await this.handleSetupCheck(shells); + } + } + } }), onDidChangeWindowState((e) => { this.hasFocus = e.focused; @@ -152,27 +166,19 @@ export class TerminalManagerImpl implements TerminalManager { 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)) { - 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 shellIntegrationEnabled = await getShellIntegrationEnabledCache(); + traceVerbose(`Checking shell profile for ${p.shellType}, with state: ${state}`); if (state === ShellSetupState.NotSetup) { traceVerbose( - `WSL detected: ${isWsl()}, Shell integration available: ${await shellIntegrationForActiveTerminal( + `WSL detected: ${isWsl()}, Shell integration available from setting, or active terminal: ${shellIntegrationEnabled}, or ${await shellIntegrationForActiveTerminal( p.name, )}`, ); - if ((await shellIntegrationForActiveTerminal(p.name)) && !isWsl()) { + if ( + (shellIntegrationEnabled || (await shellIntegrationForActiveTerminal(p.name))) && + !isWsl() + ) { // Shell integration available and NOT in WSL - skip setup await p.teardownScripts(); this.shellSetup.set(p.shellType, true); @@ -188,7 +194,10 @@ export class TerminalManagerImpl implements TerminalManager { ); } } else if (state === ShellSetupState.Setup) { - if ((await shellIntegrationForActiveTerminal(p.name)) && !isWsl()) { + if ( + (shellIntegrationEnabled || (await shellIntegrationForActiveTerminal(p.name))) && + !isWsl() + ) { await p.teardownScripts(); traceVerbose( `Shell integration available for ${p.shellType}, removed profile script in favor of shell integration.`,