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: 5 additions & 0 deletions src/common/utils/unsafeEntries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** Converts an object from a trusted source (i.e. without unknown entries) to a typed array */
export default function unsafeEntries<T extends { [key: string]: object }, K extends keyof T>(o: T): [keyof T, T[K]][] {
return Object.entries(o) as [keyof T, T[K]][];
}

32 changes: 18 additions & 14 deletions src/features/common/shellDetector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { isWindows } from '../../managers/common/utils';
import * as os from 'os';
import { vscodeShell } from '../../common/vscodeEnv.apis';
import { getConfiguration } from '../../common/workspace.apis';
import unsafeEntries from '../../common/utils/unsafeEntries';
import { TerminalShellType } from '../../api';

/*
Expand Down Expand Up @@ -30,20 +31,23 @@ const IS_TCSHELL = /(tcsh$)/i;
const IS_NUSHELL = /(nu$)/i;
const IS_XONSH = /(xonsh$)/i;

const detectableShells = new Map<TerminalShellType, RegExp>();
detectableShells.set(TerminalShellType.powershell, IS_POWERSHELL);
detectableShells.set(TerminalShellType.gitbash, IS_GITBASH);
detectableShells.set(TerminalShellType.bash, IS_BASH);
detectableShells.set(TerminalShellType.wsl, IS_WSL);
detectableShells.set(TerminalShellType.zsh, IS_ZSH);
detectableShells.set(TerminalShellType.ksh, IS_KSH);
detectableShells.set(TerminalShellType.commandPrompt, IS_COMMAND);
detectableShells.set(TerminalShellType.fish, IS_FISH);
detectableShells.set(TerminalShellType.tcshell, IS_TCSHELL);
detectableShells.set(TerminalShellType.cshell, IS_CSHELL);
detectableShells.set(TerminalShellType.nushell, IS_NUSHELL);
detectableShells.set(TerminalShellType.powershellCore, IS_POWERSHELL_CORE);
detectableShells.set(TerminalShellType.xonsh, IS_XONSH);
type KnownShellType = Exclude<TerminalShellType, TerminalShellType.unknown>;
const detectableShells = new Map<KnownShellType, RegExp>(unsafeEntries({
[TerminalShellType.powershell]: IS_POWERSHELL,
[TerminalShellType.gitbash]: IS_GITBASH,
[TerminalShellType.bash]: IS_BASH,
[TerminalShellType.wsl]: IS_WSL,
[TerminalShellType.zsh]: IS_ZSH,
[TerminalShellType.ksh]: IS_KSH,
[TerminalShellType.commandPrompt]: IS_COMMAND,
[TerminalShellType.fish]: IS_FISH,
[TerminalShellType.tcshell]: IS_TCSHELL,
[TerminalShellType.cshell]: IS_CSHELL,
[TerminalShellType.nushell]: IS_NUSHELL,
[TerminalShellType.powershellCore]: IS_POWERSHELL_CORE,
[TerminalShellType.xonsh]: IS_XONSH,
// This `satisfies` makes sure all shells are covered
} satisfies Record<KnownShellType, RegExp>));

function identifyShellFromShellPath(shellPath: string): TerminalShellType {
// Remove .exe extension so shells can be more consistently detected
Expand Down
102 changes: 56 additions & 46 deletions src/managers/builtin/venvUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
} from '../../common/window.apis';
import { showErrorMessage } from '../../common/errors/utils';
import { Common, VenvManagerStrings } from '../../common/localize';
import unsafeEntries from '../../common/utils/unsafeEntries';
import { isUvInstalled, runUV, runPython } from './helpers';
import { getWorkspacePackagesToInstall } from './pipUtils';

Expand Down Expand Up @@ -117,56 +118,65 @@ async function getPythonInfo(env: NativeEnvInfo): Promise<PythonEnvironmentInfo>
const name = `${venvName} (${sv})`;

const binDir = path.dirname(env.executable);

interface VenvManager {
activate: PythonCommandRunConfiguration,
deactivate: PythonCommandRunConfiguration,
/// true if created by the builtin `venv` module and not just the `virtualenv` package.
supportsStdlib: boolean,
}

/** Venv activation/deactivation using a command */
const cmdMgr = (suffix = ''): VenvManager => ({
activate: { executable: path.join(binDir, `activate${suffix}`) },
deactivate: { executable: path.join(binDir, `deactivate${suffix}`) },
supportsStdlib: ['', '.bat'].includes(suffix),
});
/** Venv activation/deactivation for a POSIXy shell */
const sourceMgr = (suffix = '', executable = 'source'): VenvManager => ({
activate: { executable, args: [path.join(binDir, `activate${suffix}`)] },
deactivate: { executable: 'deactivate' },
supportsStdlib: ['', '.ps1'].includes(suffix),
});
// satisfies `Record` to make sure all shells are covered
const venvManagers: Record<TerminalShellType, VenvManager> = {
// Shells supported by the builtin `venv` module
[TerminalShellType.bash]: sourceMgr(),
[TerminalShellType.gitbash]: sourceMgr(),
[TerminalShellType.zsh]: sourceMgr(),
[TerminalShellType.wsl]: sourceMgr(),
[TerminalShellType.ksh]: sourceMgr('', '.'),
[TerminalShellType.powershell]: sourceMgr('.ps1', '&'),
[TerminalShellType.powershellCore]: sourceMgr('.ps1', '&'),
[TerminalShellType.commandPrompt]: cmdMgr('.bat'),
// Shells supported by the `virtualenv` package
[TerminalShellType.cshell]: sourceMgr('.csh'),
[TerminalShellType.tcshell]: sourceMgr('.csh'),
[TerminalShellType.fish]: sourceMgr('.fish'),
[TerminalShellType.xonsh]: sourceMgr('.xsh'),
[TerminalShellType.nushell]: {
activate: { executable: 'overlay', args: ['use', path.join(binDir, 'activate.nu')] },
deactivate: { executable: 'overlay', args: ['hide', 'activate'] },
supportsStdlib: false,
},
// Fallback
[TerminalShellType.unknown]: isWindows() ? cmdMgr() : sourceMgr(),
};

const shellActivation: Map<TerminalShellType, PythonCommandRunConfiguration[]> = new Map();
const shellDeactivation: Map<TerminalShellType, PythonCommandRunConfiguration[]> = new Map();

// Commands for bash
shellActivation.set(TerminalShellType.bash, [{ executable: 'source', args: [path.join(binDir, 'activate')] }]);
shellDeactivation.set(TerminalShellType.bash, [{ executable: 'deactivate' }]);

// Commands for csh
shellActivation.set(TerminalShellType.cshell, [
{ executable: 'source', args: [path.join(binDir, 'activate')] },
]);
shellDeactivation.set(TerminalShellType.cshell, [{ executable: 'deactivate' }]);

// Commands for zsh
shellActivation.set(TerminalShellType.zsh, [{ executable: 'source', args: [path.join(binDir, 'activate')] }]);
shellDeactivation.set(TerminalShellType.zsh, [{ executable: 'deactivate' }]);

// Commands for powershell
shellActivation.set(TerminalShellType.powershell, [
{ executable: '&', args: [path.join(binDir, 'Activate.ps1')] },
]);
shellActivation.set(TerminalShellType.powershellCore, [
{ executable: '&', args: [path.join(binDir, 'Activate.ps1')] },
]);
shellDeactivation.set(TerminalShellType.powershell, [{ executable: 'deactivate' }]);
shellDeactivation.set(TerminalShellType.powershellCore, [{ executable: 'deactivate' }]);

// Commands for command prompt
shellActivation.set(TerminalShellType.commandPrompt, [{ executable: path.join(binDir, 'activate.bat') }]);
shellDeactivation.set(TerminalShellType.commandPrompt, [{ executable: path.join(binDir, 'deactivate.bat') }]);

// Commands for fish
if (await fsapi.pathExists(path.join(binDir, 'activate.fish'))) {
shellActivation.set(TerminalShellType.fish, [
{ executable: 'source', args: [path.join(binDir, 'activate.fish')] },
]);
shellDeactivation.set(TerminalShellType.fish, [{ executable: 'deactivate' }]);
}

// Commands for unknown cases
if (isWindows()) {
shellActivation.set(TerminalShellType.unknown, [{ executable: path.join(binDir, 'activate') }]);
shellDeactivation.set(TerminalShellType.unknown, [{ executable: 'deactivate' }]);
} else {
shellActivation.set(TerminalShellType.unknown, [
{ executable: 'source', args: [path.join(binDir, 'activate')] },
]);
shellDeactivation.set(TerminalShellType.unknown, [{ executable: 'deactivate' }]);
}
await Promise.all(unsafeEntries(venvManagers).map(async ([shell, mgr]) => {
if (
!mgr.supportsStdlib &&
mgr.activate.args &&
!await fsapi.pathExists(mgr.activate.args[mgr.activate.args.length - 1])
) {
return;
}
shellActivation.set(shell, [mgr.activate]);
shellDeactivation.set(shell, [mgr.deactivate]);
}));

return {
name: name,
Expand Down
Loading