From f6b51aca6bd0e084604caa9175a905628b6efd9e Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 8 Oct 2025 10:55:29 -0700 Subject: [PATCH 1/3] Better shell integration check --- .../terminal/shells/bash/bashStartup.ts | 2 +- .../terminal/shells/common/shellUtils.ts | 20 ++++++++++------ src/features/terminal/terminalManager.ts | 24 ++++++++++++------- src/features/terminal/utils.ts | 4 ++-- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/features/terminal/shells/bash/bashStartup.ts b/src/features/terminal/shells/bash/bashStartup.ts index 24dae7e9..52e18861 100644 --- a/src/features/terminal/shells/bash/bashStartup.ts +++ b/src/features/terminal/shells/bash/bashStartup.ts @@ -69,7 +69,7 @@ async function isStartupSetup(profile: string, key: string): Promise { - if (shellIntegrationForActiveTerminal(name, profile) && !isWsl()) { + if ((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 20d310b5..5dae76a9 100644 --- a/src/features/terminal/shells/common/shellUtils.ts +++ b/src/features/terminal/shells/common/shellUtils.ts @@ -1,9 +1,11 @@ import { PythonCommandRunConfiguration, PythonEnvironment } from '../../../../api'; import { traceInfo } from '../../../../common/logging'; +import { sleep } from '../../../../common/utils/asyncUtils'; import { isWindows } from '../../../../common/utils/platformUtils'; import { activeTerminalShellIntegration } from '../../../../common/window.apis'; import { ShellConstants } from '../../../common/shellConstants'; import { quoteArgs } from '../../../execution/execUtils'; +import { SHELL_INTEGRATION_POLL_INTERVAL, SHELL_INTEGRATION_TIMEOUT } from '../../utils'; function getCommandAsString(command: PythonCommandRunConfiguration[], shell: string, delimiter: string): string { const parts = []; @@ -98,12 +100,19 @@ export function extractProfilePath(content: string): string | undefined { return undefined; } -export function shellIntegrationForActiveTerminal(name: string, profile?: string): boolean { - const hasShellIntegration = activeTerminalShellIntegration(); +export async function shellIntegrationForActiveTerminal(name: string, profile?: string): Promise { + let hasShellIntegration = activeTerminalShellIntegration(); + let timeout = 0; + + while (!hasShellIntegration && timeout < SHELL_INTEGRATION_TIMEOUT) { + await sleep(SHELL_INTEGRATION_POLL_INTERVAL); + timeout += SHELL_INTEGRATION_POLL_INTERVAL; + hasShellIntegration = activeTerminalShellIntegration(); + } if (hasShellIntegration) { 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.` + `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; } @@ -112,8 +121,5 @@ export function shellIntegrationForActiveTerminal(name: string, profile?: string export function isWsl(): boolean { // WSL sets these environment variables - return !!(process.env.WSL_DISTRO_NAME || - process.env.WSL_INTEROP || - process.env.WSLENV); + return !!(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP || process.env.WSLENV); } - diff --git a/src/features/terminal/terminalManager.ts b/src/features/terminal/terminalManager.ts index 81768dbd..aa95645d 100644 --- a/src/features/terminal/terminalManager.ts +++ b/src/features/terminal/terminalManager.ts @@ -129,8 +129,10 @@ export class TerminalManagerImpl implements TerminalManager { await this.handleSetupCheck(shells); } } else { - traceVerbose(`Auto activation type changed to ${actType}, we are cleaning up shell startup setup`); - // Teardown scripts when switching away from shell startup activation + 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(); } @@ -145,12 +147,12 @@ export class TerminalManagerImpl implements TerminalManager { private async handleSetupCheck(shellType: string | Set): Promise { 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); + 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); @@ -158,13 +160,19 @@ export class TerminalManagerImpl implements TerminalManager { 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( + `Shell profile for ${p.shellType} state changed from ${cachedSetup} to ${currentSetup}, re-evaluating.`, + ); } traceVerbose(`Checking shell profile for ${p.shellType}.`); if (state === ShellSetupState.NotSetup) { - traceVerbose(`WSL detected: ${isWsl()}, Shell integration available: ${shellIntegrationForActiveTerminal(p.name)}`); + traceVerbose( + `WSL detected: ${isWsl()}, Shell integration available: ${shellIntegrationForActiveTerminal( + p.name, + )}`, + ); - if (shellIntegrationForActiveTerminal(p.name) && !isWsl()) { + if ((await shellIntegrationForActiveTerminal(p.name)) && !isWsl()) { // Shell integration available and NOT in WSL - skip setup await p.teardownScripts(); this.shellSetup.set(p.shellType, true); @@ -180,7 +188,7 @@ export class TerminalManagerImpl implements TerminalManager { ); } } else if (state === ShellSetupState.Setup) { - if (shellIntegrationForActiveTerminal(p.name) && !isWsl()) { + if ((await shellIntegrationForActiveTerminal(p.name)) && !isWsl()) { await p.teardownScripts(); traceVerbose( `Shell integration available for ${p.shellType}, removed profile script in favor of shell integration.`, diff --git a/src/features/terminal/utils.ts b/src/features/terminal/utils.ts index 9476ac10..c8f6443d 100644 --- a/src/features/terminal/utils.ts +++ b/src/features/terminal/utils.ts @@ -4,8 +4,8 @@ import { PythonEnvironment, PythonProject, PythonProjectEnvironmentApi, PythonPr import { sleep } from '../../common/utils/asyncUtils'; import { getConfiguration, getWorkspaceFolders } from '../../common/workspace.apis'; -const SHELL_INTEGRATION_TIMEOUT = 500; // 0.5 seconds -const SHELL_INTEGRATION_POLL_INTERVAL = 20; // 0.02 seconds +export const SHELL_INTEGRATION_TIMEOUT = 500; // 0.5 seconds +export const SHELL_INTEGRATION_POLL_INTERVAL = 20; // 0.02 seconds export async function waitForShellIntegration(terminal: Terminal): Promise { let timeout = 0; From a090e920784d718f35dbf812ed54a7d85c598d65 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 8 Oct 2025 10:58:52 -0700 Subject: [PATCH 2/3] missing awaits --- src/features/terminal/shells/fish/fishStartup.ts | 2 +- src/features/terminal/shells/pwsh/pwshStartup.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/terminal/shells/fish/fishStartup.ts b/src/features/terminal/shells/fish/fishStartup.ts index 395829e9..6621e141 100644 --- a/src/features/terminal/shells/fish/fishStartup.ts +++ b/src/features/terminal/shells/fish/fishStartup.ts @@ -58,7 +58,7 @@ async function isStartupSetup(profilePath: string, key: string): Promise { try { - if (shellIntegrationForActiveTerminal('fish', profilePath) && !isWsl()) { + if ((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 5f0e1fe2..e3962f6e 100644 --- a/src/features/terminal/shells/pwsh/pwshStartup.ts +++ b/src/features/terminal/shells/pwsh/pwshStartup.ts @@ -168,7 +168,7 @@ async function isPowerShellStartupSetup(shell: string, profile: string): Promise } async function setupPowerShellStartup(shell: string, profile: string): Promise { - if (shellIntegrationForActiveTerminal(shell, profile) && !isWsl()) { + if ((await shellIntegrationForActiveTerminal(shell, profile)) && !isWsl()) { removePowerShellStartup(shell, profile, POWERSHELL_OLD_ENV_KEY); removePowerShellStartup(shell, profile, POWERSHELL_ENV_KEY); return true; From 03e0e58e8a03fbe5254f9361576b5cce2a9259bc Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 8 Oct 2025 11:06:00 -0700 Subject: [PATCH 3/3] fix log --- src/features/terminal/terminalManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/terminal/terminalManager.ts b/src/features/terminal/terminalManager.ts index aa95645d..b66f95e6 100644 --- a/src/features/terminal/terminalManager.ts +++ b/src/features/terminal/terminalManager.ts @@ -167,7 +167,7 @@ export class TerminalManagerImpl implements TerminalManager { traceVerbose(`Checking shell profile for ${p.shellType}.`); if (state === ShellSetupState.NotSetup) { traceVerbose( - `WSL detected: ${isWsl()}, Shell integration available: ${shellIntegrationForActiveTerminal( + `WSL detected: ${isWsl()}, Shell integration available: ${await shellIntegrationForActiveTerminal( p.name, )}`, );