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
5 changes: 3 additions & 2 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 { 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';

Expand Down Expand Up @@ -69,7 +69,8 @@ async function isStartupSetup(profile: string, key: string): Promise<ShellSetupS
return ShellSetupState.NotSetup;
}
async function setupStartup(profile: string, key: string, name: string): Promise<boolean> {
if ((await shellIntegrationForActiveTerminal(name, profile)) && !isWsl()) {
const shellIntegrationEnabled = await getShellIntegrationEnabledCache();
if ((shellIntegrationEnabled || (await shellIntegrationForActiveTerminal(name, profile))) && !isWsl()) {
removeStartup(profile, key);
return true;
}
Expand Down
38 changes: 38 additions & 0 deletions src/features/terminal/shells/common/shellUtils.ts
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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<boolean> {
const persistentState = await getGlobalPersistentState();
const shellIntegrationInspect =
getConfiguration('terminal.integrated').inspect<boolean>('shellIntegration.enabled');

let shellIntegrationEnabled = true;
if (shellIntegrationInspect) {
// Priority: workspaceFolder > workspace > globalRemoteValue > globalLocalValue > global > default
const inspectValue = shellIntegrationInspect as Record<string, unknown>;

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;
}
5 changes: 3 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 { 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';

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

async function setupStartup(profilePath: string, key: string): Promise<boolean> {
try {
if ((await shellIntegrationForActiveTerminal('fish', profilePath)) && !isWsl()) {
const shellIntegrationEnabled = await getShellIntegrationEnabledCache();
if ((shellIntegrationEnabled || (await shellIntegrationForActiveTerminal('fish', profilePath))) && !isWsl()) {
removeFishStartup(profilePath, key);
return true;
}
Expand Down
5 changes: 4 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,
getShellIntegrationEnabledCache,
isWsl,
PROFILE_TAG_END,
PROFILE_TAG_START,
Expand Down Expand Up @@ -168,7 +169,9 @@ async function isPowerShellStartupSetup(shell: string, profile: string): Promise
}

async function setupPowerShellStartup(shell: string, profile: string): Promise<boolean> {
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;
Expand Down
43 changes: 26 additions & 17 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 { 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 {
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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.`,
Expand Down
Loading