Skip to content

Commit 462976c

Browse files
authored
Add "Enter Interpreter Path" option for selecting Python interpreter (#1173)
Fixes #1165
1 parent c9f4774 commit 462976c

File tree

3 files changed

+123
-6
lines changed

3 files changed

+123
-6
lines changed

src/common/localize.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ export namespace Interpreter {
2525
export const statusBarSelect = l10n.t('Select Interpreter');
2626
export const browsePath = l10n.t('Browse...');
2727
export const createVirtualEnvironment = l10n.t('Create Virtual Environment...');
28+
export const enterInterpreterPath = l10n.t('Enter Interpreter Path...');
29+
export const enterInterpreterPathDescription = l10n.t('Browse and select a Python interpreter from anywhere');
2830
}
2931

3032
export namespace PackageManagement {

src/common/pickers/managers.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1-
import { commands, QuickInputButtons, QuickPickItem, QuickPickItemKind, workspace, WorkspaceFolder } from 'vscode';
1+
import {
2+
commands,
3+
QuickInputButtons,
4+
QuickPickItem,
5+
QuickPickItemKind,
6+
ThemeIcon,
7+
workspace,
8+
WorkspaceFolder,
9+
} from 'vscode';
210
import { PythonProjectCreator } from '../../api';
311
import { InternalEnvironmentManager, InternalPackageManager } from '../../internal.api';
4-
import { Common, Pickers } from '../localize';
12+
import { Common, Interpreter, Pickers } from '../localize';
513
import { showQuickPickWithButtons } from '../window.apis';
614

15+
export const ENTER_INTERPRETER_PATH_ID = 'EnterInterpreterPath';
16+
717
function getDescription(mgr: InternalEnvironmentManager | InternalPackageManager): string | undefined {
818
if (mgr.description) {
919
return mgr.description;
@@ -22,17 +32,19 @@ export async function pickEnvironmentManager(
2232
managers: InternalEnvironmentManager[],
2333
defaultManagers?: InternalEnvironmentManager[],
2434
showBackButton?: boolean,
35+
showEnterInterpreterPath?: boolean,
2536
): Promise<string | undefined> {
2637
if (managers.length === 0) {
2738
return;
2839
}
2940

30-
if (managers.length === 1 && !managers[0].supportsQuickCreate) {
41+
if (managers.length === 1 && !managers[0].supportsQuickCreate && !showEnterInterpreterPath) {
3142
// If there's only one manager and it doesn't support quick create, return its ID directly.
3243
return managers[0].id;
3344
}
3445

3546
const items: (QuickPickItem | (QuickPickItem & { id: string }))[] = [];
47+
3648
if (defaultManagers && defaultManagers.length > 0) {
3749
items.push({
3850
label: Common.recommended,
@@ -71,6 +83,23 @@ export async function pickEnvironmentManager(
7183
id: m.id,
7284
})),
7385
);
86+
87+
// Add "Enter Interpreter Path" option at the bottom if enabled
88+
if (showEnterInterpreterPath) {
89+
items.push(
90+
{
91+
label: '',
92+
kind: QuickPickItemKind.Separator,
93+
},
94+
{
95+
label: Interpreter.enterInterpreterPath,
96+
description: Interpreter.enterInterpreterPathDescription,
97+
id: ENTER_INTERPRETER_PATH_ID,
98+
iconPath: new ThemeIcon('folder-opened'),
99+
},
100+
);
101+
}
102+
74103
const item = await showQuickPickWithButtons(items, {
75104
placeHolder: Pickers.Managers.selectEnvironmentManager,
76105
ignoreFocusOut: true,

src/features/envCommands.ts

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
import * as fs from 'fs-extra';
22
import * as path from 'path';
3-
import { commands, QuickInputButtons, TaskExecution, TaskRevealKind, Terminal, Uri, workspace } from 'vscode';
3+
import {
4+
commands,
5+
ProgressLocation,
6+
QuickInputButtons,
7+
TaskExecution,
8+
TaskRevealKind,
9+
Terminal,
10+
Uri,
11+
workspace,
12+
} from 'vscode';
413
import {
514
CreateEnvironmentOptions,
615
PythonEnvironment,
@@ -21,15 +30,25 @@ import { removePythonProjectSetting, setEnvironmentManager, setPackageManager }
2130

2231
import { clipboardWriteText } from '../common/env.apis';
2332
import {} from '../common/errors/utils';
33+
import { Pickers } from '../common/localize';
2434
import { pickEnvironment } from '../common/pickers/environments';
2535
import {
36+
ENTER_INTERPRETER_PATH_ID,
2637
pickCreator,
2738
pickEnvironmentManager,
2839
pickPackageManager,
2940
pickWorkspaceFolder,
3041
} from '../common/pickers/managers';
3142
import { pickProject, pickProjectMany } from '../common/pickers/projects';
32-
import { activeTextEditor, showErrorMessage, showInformationMessage } from '../common/window.apis';
43+
import { isWindows } from '../common/utils/platformUtils';
44+
import { handlePythonPath } from '../common/utils/pythonPath';
45+
import {
46+
activeTextEditor,
47+
showErrorMessage,
48+
showInformationMessage,
49+
showOpenDialog,
50+
withProgress,
51+
} from '../common/window.apis';
3352
import { runAsTask } from './execution/runAsTask';
3453
import { runInTerminal } from './terminal/runInTerminal';
3554
import { TerminalManager } from './terminal/terminalManager';
@@ -44,6 +63,46 @@ import {
4463
PythonEnvTreeItem,
4564
} from './views/treeViewItems';
4665

66+
/**
67+
* Opens a file dialog to browse for a Python interpreter and resolves it using available managers.
68+
*/
69+
async function browseAndResolveInterpreter(
70+
em: EnvironmentManagers,
71+
projectUris?: Uri[],
72+
): Promise<PythonEnvironment | undefined> {
73+
const filters = isWindows() ? { python: ['exe'] } : undefined;
74+
const uris = await showOpenDialog({
75+
canSelectFiles: true,
76+
canSelectFolders: false,
77+
canSelectMany: false,
78+
filters,
79+
title: Pickers.Environments.selectExecutable,
80+
});
81+
if (!uris || uris.length === 0) {
82+
return;
83+
}
84+
const interpreterUri = uris[0];
85+
86+
// Get project-specific managers if projectUris are provided
87+
const projectEnvManagers = projectUris
88+
? projectUris
89+
.map((uri) => em.getEnvironmentManager(uri))
90+
.filter((m): m is InternalEnvironmentManager => m !== undefined)
91+
: [];
92+
93+
const environment = await withProgress(
94+
{
95+
location: ProgressLocation.Notification,
96+
cancellable: false,
97+
},
98+
async (reporter, token) => {
99+
return await handlePythonPath(interpreterUri, em.managers, projectEnvManagers, reporter, token);
100+
},
101+
);
102+
103+
return environment;
104+
}
105+
47106
export async function refreshManagerCommand(context: unknown): Promise<void> {
48107
if (context instanceof EnvManagerTreeItem) {
49108
const manager = (context as EnvManagerTreeItem).manager;
@@ -133,7 +192,22 @@ export async function createAnyEnvironmentCommand(
133192
const select = options?.selectEnvironment;
134193
const projects = pm.getProjects(options?.uri ? [options?.uri] : undefined);
135194
if (projects.length === 0) {
136-
const managerId = await pickEnvironmentManager(em.managers.filter((m) => m.supportsCreate));
195+
const managerId = await pickEnvironmentManager(
196+
em.managers.filter((m) => m.supportsCreate),
197+
undefined,
198+
undefined,
199+
true, // showEnterInterpreterPath
200+
);
201+
202+
// Handle "Enter Interpreter Path" selection
203+
if (managerId === ENTER_INTERPRETER_PATH_ID) {
204+
const env = await browseAndResolveInterpreter(em);
205+
if (select && env) {
206+
await em.setEnvironments('global', env);
207+
}
208+
return env;
209+
}
210+
137211
const manager = em.managers.find((m) => m.id === managerId);
138212
if (manager) {
139213
const env = await manager.create('global', { ...options });
@@ -165,7 +239,19 @@ export async function createAnyEnvironmentCommand(
165239
em.managers.filter((m) => m.supportsCreate),
166240
defaultManagers,
167241
options?.showBackButton,
242+
true, // showEnterInterpreterPath
168243
);
244+
245+
// Handle "Enter Interpreter Path" selection
246+
if (managerId === ENTER_INTERPRETER_PATH_ID) {
247+
const projectUris = selected.map((p) => p.uri);
248+
const env = await browseAndResolveInterpreter(em, projectUris);
249+
if (select && env) {
250+
await em.setEnvironments(projectUris, env);
251+
}
252+
return env;
253+
}
254+
169255
if (managerId?.startsWith('QuickCreate#')) {
170256
quickCreate = true;
171257
managerId = managerId.replace('QuickCreate#', '');

0 commit comments

Comments
 (0)