Skip to content

Commit dbb95b2

Browse files
authored
Fix bugs with creation and selection of venv (#23)
Fixes #22 Fixes #20 Fixes #18
1 parent f8a935c commit dbb95b2

21 files changed

+492
-301
lines changed

package.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@
1010
"categories": [
1111
"Other"
1212
],
13+
"capabilities": {
14+
"untrustedWorkspaces": {
15+
"supported": false,
16+
"description": "This extension doesn't support untrusted workspaces."
17+
},
18+
"virtualWorkspaces": {
19+
"supported": false,
20+
"description": "This extension doesn't support virtual workspaces."
21+
}
22+
},
1323
"activationEvents": [
1424
"onLanguage:python"
1525
],
@@ -21,9 +31,6 @@
2131
"bugs": {
2232
"url": "https://github.com/microsoft/vscode-python-environments/issues"
2333
},
24-
"extensionDependencies": [
25-
"ms-python.python"
26-
],
2734
"main": "./dist/extension.js",
2835
"icon": "icon.png",
2936
"contributes": {

src/api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ export type DidChangeEnvironmentEventArgs = {
240240
/**
241241
* The URI of the environment that changed.
242242
*/
243-
readonly uri: Uri;
243+
readonly uri: Uri | undefined;
244244

245245
/**
246246
* The old Python environment before the change.
@@ -968,7 +968,7 @@ export interface PythonPackageManagementApi {
968968
export interface PythonPackageManagerApi
969969
extends PythonPackageManagerRegistrationApi,
970970
PythonPackageGetterApi,
971-
PythonEnvironmentManagerApi,
971+
PythonPackageManagementApi,
972972
PythonPackageItemApi {}
973973

974974
export interface PythonProjectCreationApi {

src/common/pickers/environments.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ export async function pickEnvironment(
165165
return {
166166
label: e.displayName ?? e.name,
167167
description: e.description,
168-
e: { selected: e, manager: manager },
168+
result: e,
169+
manager: manager,
169170
iconPath: getIconPath(e.iconPath),
170171
};
171172
}),

src/common/pickers/packages.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -170,25 +170,30 @@ function getGroupedItems(items: Installable[]): PackageQuickPickItem[] {
170170
return result;
171171
}
172172

173+
async function getInstallables(packageManager: InternalPackageManager, environment: PythonEnvironment) {
174+
const installable = await packageManager?.getInstallable(environment);
175+
if (installable && installable.length === 0) {
176+
traceWarn(`No installable packages found for ${packageManager.id}: ${environment.environmentPath.fsPath}`);
177+
}
178+
return installable;
179+
}
180+
173181
async function getWorkspacePackages(
174-
packageManager: InternalPackageManager,
175-
environment: PythonEnvironment,
182+
installable: Installable[] | undefined,
176183
preSelected?: PackageQuickPickItem[] | undefined,
177184
): Promise<string[] | undefined> {
178185
const items: PackageQuickPickItem[] = [];
179186

180-
let installable = await packageManager?.getInstallable(environment);
181187
if (installable && installable.length > 0) {
182188
items.push(...getGroupedItems(installable));
183189
} else {
184-
traceWarn(`No installable packages found for ${packageManager.id}: ${environment.environmentPath.fsPath}`);
185-
installable = await getCommonPackages();
190+
const common = await getCommonPackages();
186191
items.push(
187192
{
188193
label: PackageManagement.commonPackages,
189194
kind: QuickPickItemKind.Separator,
190195
},
191-
...installable.map(installableToQuickPickItem),
196+
...common.map(installableToQuickPickItem),
192197
);
193198
}
194199

@@ -240,7 +245,7 @@ async function getWorkspacePackages(
240245
return result;
241246
} catch (ex) {
242247
if (ex === QuickInputButtons.Back) {
243-
return getWorkspacePackages(packageManager, environment, selected);
248+
return getWorkspacePackages(installable, selected);
244249
}
245250
return undefined;
246251
}
@@ -250,7 +255,7 @@ async function getWorkspacePackages(
250255
}
251256
}
252257

253-
export async function getCommonPackagesToInstall(
258+
async function getCommonPackagesToInstall(
254259
preSelected?: PackageQuickPickItem[] | undefined,
255260
): Promise<string[] | undefined> {
256261
const common = await getCommonPackages();
@@ -315,7 +320,7 @@ export async function getCommonPackagesToInstall(
315320
}
316321
}
317322

318-
export async function getPackagesToInstall(
323+
export async function getPackagesToInstallFromPackageManager(
319324
packageManager: InternalPackageManager,
320325
environment: PythonEnvironment,
321326
): Promise<string[] | undefined> {
@@ -325,11 +330,12 @@ export async function getPackagesToInstall(
325330

326331
if (packageType === PackageManagement.workspaceDependencies) {
327332
try {
328-
const result = await getWorkspacePackages(packageManager, environment);
333+
const installable = await getInstallables(packageManager, environment);
334+
const result = await getWorkspacePackages(installable);
329335
return result;
330336
} catch (ex) {
331337
if (packageManager.supportsGetInstallable && ex === QuickInputButtons.Back) {
332-
return getPackagesToInstall(packageManager, environment);
338+
return getPackagesToInstallFromPackageManager(packageManager, environment);
333339
}
334340
if (ex === QuickInputButtons.Back) {
335341
throw ex;
@@ -344,7 +350,7 @@ export async function getPackagesToInstall(
344350
return result;
345351
} catch (ex) {
346352
if (packageManager.supportsGetInstallable && ex === QuickInputButtons.Back) {
347-
return getPackagesToInstall(packageManager, environment);
353+
return getPackagesToInstallFromPackageManager(packageManager, environment);
348354
}
349355
if (ex === QuickInputButtons.Back) {
350356
throw ex;
@@ -356,6 +362,13 @@ export async function getPackagesToInstall(
356362
return undefined;
357363
}
358364

365+
export async function getPackagesToInstallFromInstallable(installable: Installable[]): Promise<string[] | undefined> {
366+
if (installable.length === 0) {
367+
return undefined;
368+
}
369+
return getWorkspacePackages(installable);
370+
}
371+
359372
export async function getPackagesToUninstall(packages: Package[]): Promise<Package[] | undefined> {
360373
const items = packages.map((p) => ({
361374
label: p.name,

src/common/utils/pythonPath.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ export async function handlePythonPath(
4040
reporter?: Progress<{ message?: string; increment?: number }>,
4141
token?: CancellationToken,
4242
): Promise<PythonEnvironment | undefined> {
43+
// Use the managers user has set for the project first. Likely, these
44+
// managers are the ones that should be used.
4345
for (const manager of sortManagersByPriority(projectEnvManagers)) {
4446
if (token?.isCancellationRequested) {
4547
return;
@@ -54,6 +56,8 @@ export async function handlePythonPath(
5456
traceVerbose(`Manager ${manager.displayName} (${manager.id}) cannot handle ${interpreterUri.fsPath}`);
5557
}
5658

59+
// If the project managers cannot handle the interpreter, then try all the managers
60+
// that user has installed. Excluding anything that is already checked.
5761
const checkedIds = projectEnvManagers.map((m) => m.id);
5862
const filtered = managers.filter((m) => !checkedIds.includes(m.id));
5963

@@ -70,10 +74,6 @@ export async function handlePythonPath(
7074
}
7175
}
7276

73-
if (token?.isCancellationRequested) {
74-
return;
75-
}
76-
7777
traceError(`Unable to handle ${interpreterUri.fsPath}`);
7878
showErrorMessage(`Unable to handle ${interpreterUri.fsPath}`);
7979
return undefined;

src/common/window.apis.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import {
44
ExtensionTerminalOptions,
55
InputBox,
66
InputBoxOptions,
7+
LogOutputChannel,
78
OpenDialogOptions,
9+
OutputChannel,
810
Progress,
911
ProgressOptions,
1012
QuickInputButton,
@@ -279,3 +281,11 @@ export function showWarningMessage(message: string, ...items: string[]): Thenabl
279281
export function showInputBox(options?: InputBoxOptions, token?: CancellationToken): Thenable<string | undefined> {
280282
return window.showInputBox(options, token);
281283
}
284+
285+
export function createOutputChannel(name: string, languageId?: string): OutputChannel {
286+
return window.createOutputChannel(name, languageId);
287+
}
288+
289+
export function createLogOutputChannel(name: string): LogOutputChannel {
290+
return window.createOutputChannel(name, { log: true });
291+
}

src/common/workspace.apis.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {
33
ConfigurationChangeEvent,
44
ConfigurationScope,
55
Disposable,
6+
FileDeleteEvent,
7+
FileSystemWatcher,
68
GlobPattern,
79
Uri,
810
workspace,
@@ -39,3 +41,20 @@ export function findFiles(
3941
): Thenable<Uri[]> {
4042
return workspace.findFiles(include, exclude, maxResults, token);
4143
}
44+
45+
export function createFileSystemWatcher(
46+
globPattern: GlobPattern,
47+
ignoreCreateEvents?: boolean,
48+
ignoreChangeEvents?: boolean,
49+
ignoreDeleteEvents?: boolean,
50+
): FileSystemWatcher {
51+
return workspace.createFileSystemWatcher(globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents);
52+
}
53+
54+
export function onDidDeleteFiles(
55+
listener: (e: FileDeleteEvent) => any,
56+
thisArgs?: any,
57+
disposables?: Disposable[],
58+
): Disposable {
59+
return workspace.onDidDeleteFiles(listener, thisArgs, disposables);
60+
}

src/extension.ts

Lines changed: 23 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { window, commands, ExtensionContext, LogOutputChannel, TextEditor } from 'vscode';
1+
import { commands, ExtensionContext, LogOutputChannel } from 'vscode';
22

33
import { PythonEnvironmentManagers } from './features/envManagers';
44
import { registerLogger } from './common/logging';
@@ -28,9 +28,8 @@ import { PythonProjectManagerImpl } from './features/projectManager';
2828
import { EnvironmentManagers, ProjectCreators, PythonProjectManager } from './internal.api';
2929
import { getPythonApi, setPythonApi } from './features/pythonApi';
3030
import { setPersistentState } from './common/persistentState';
31-
import { isPythonProjectFile } from './common/utils/fileNameUtils';
3231
import { createNativePythonFinder, NativePythonFinder } from './managers/common/nativePythonFinder';
33-
import { PythonEnvironmentApi, PythonProject } from './api';
32+
import { PythonEnvironmentApi } from './api';
3433
import {
3534
ProjectCreatorsImpl,
3635
registerAutoProjectProvider,
@@ -39,21 +38,31 @@ import {
3938
import { WorkspaceView } from './features/views/projectView';
4039
import { registerCompletionProvider } from './features/settings/settingCompletions';
4140
import { TerminalManager, TerminalManagerImpl } from './features/terminal/terminalManager';
42-
import { activeTerminal, onDidChangeActiveTerminal, onDidChangeActiveTextEditor } from './common/window.apis';
41+
import {
42+
activeTerminal,
43+
createLogOutputChannel,
44+
onDidChangeActiveTerminal,
45+
onDidChangeActiveTextEditor,
46+
} from './common/window.apis';
4347
import {
4448
getEnvironmentForTerminal,
4549
setActivateMenuButtonContext,
4650
updateActivateMenuButtonContext,
4751
} from './features/terminal/activateMenuButton';
52+
import { PythonStatusBarImpl } from './features/views/pythonStatusBar';
53+
import { updateViewsAndStatus } from './features/views/revealHandler';
4854

4955
export async function activate(context: ExtensionContext): Promise<PythonEnvironmentApi> {
5056
// Logging should be set up before anything else.
51-
const outputChannel: LogOutputChannel = window.createOutputChannel('Python Environments', { log: true });
57+
const outputChannel: LogOutputChannel = createLogOutputChannel('Python Environments');
5258
context.subscriptions.push(outputChannel, registerLogger(outputChannel));
5359

5460
// Setup the persistent state for the extension.
5561
setPersistentState(context);
5662

63+
const statusBar = new PythonStatusBarImpl();
64+
context.subscriptions.push(statusBar);
65+
5766
const terminalManager: TerminalManager = new TerminalManagerImpl();
5867
context.subscriptions.push(terminalManager);
5968

@@ -113,26 +122,14 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
113122
commands.registerCommand('python-envs.set', async (item) => {
114123
const result = await setEnvironmentCommand(item, envManagers, projectManager);
115124
if (result) {
116-
const projects: PythonProject[] = [];
117-
result.forEach((r) => {
118-
if (r.project) {
119-
projects.push(r.project);
120-
}
121-
});
122-
workspaceView.updateProject(projects);
125+
workspaceView.updateProject();
123126
await updateActivateMenuButtonContext(terminalManager, projectManager, envManagers);
124127
}
125128
}),
126129
commands.registerCommand('python-envs.setEnv', async (item) => {
127130
const result = await setEnvironmentCommand(item, envManagers, projectManager);
128131
if (result) {
129-
const projects: PythonProject[] = [];
130-
result.forEach((r) => {
131-
if (r.project) {
132-
projects.push(r.project);
133-
}
134-
});
135-
workspaceView.updateProject(projects);
132+
workspaceView.updateProject();
136133
await updateActivateMenuButtonContext(terminalManager, projectManager, envManagers);
137134
}
138135
}),
@@ -193,38 +190,14 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
193190
onDidChangeActiveTerminal(async (t) => {
194191
await updateActivateMenuButtonContext(terminalManager, projectManager, envManagers, t);
195192
}),
196-
onDidChangeActiveTextEditor(async (e: TextEditor | undefined) => {
197-
if (e && !e.document.isUntitled && e.document.uri.scheme === 'file') {
198-
if (
199-
e.document.languageId === 'python' ||
200-
e.document.languageId === 'pip-requirements' ||
201-
isPythonProjectFile(e.document.uri.fsPath)
202-
) {
203-
const env = await workspaceView.reveal(e.document.uri);
204-
await managerView.reveal(env);
205-
}
206-
}
193+
onDidChangeActiveTextEditor(async () => {
194+
updateViewsAndStatus(statusBar, workspaceView, managerView, api);
207195
}),
208-
envManagers.onDidChangeEnvironment(async (e) => {
209-
const activeDocument = window.activeTextEditor?.document;
210-
if (!activeDocument || activeDocument.isUntitled || activeDocument.uri.scheme !== 'file') {
211-
return;
212-
}
213-
214-
if (
215-
activeDocument.languageId !== 'python' &&
216-
activeDocument.languageId !== 'pip-requirements' &&
217-
!isPythonProjectFile(activeDocument.uri.fsPath)
218-
) {
219-
return;
220-
}
221-
222-
const mgr1 = envManagers.getEnvironmentManager(e.uri);
223-
const mgr2 = envManagers.getEnvironmentManager(activeDocument.uri);
224-
if (mgr1 === mgr2 && e.new) {
225-
const env = await workspaceView.reveal(activeDocument.uri);
226-
await managerView.reveal(env);
227-
}
196+
envManagers.onDidChangeEnvironment(async () => {
197+
updateViewsAndStatus(statusBar, workspaceView, managerView, api);
198+
}),
199+
envManagers.onDidChangeEnvironments(async () => {
200+
updateViewsAndStatus(statusBar, workspaceView, managerView, api);
228201
}),
229202
);
230203

0 commit comments

Comments
 (0)