Skip to content

Commit af00ec3

Browse files
authored
Merge branch 'main' into orange-butterfly
2 parents 95f13e9 + 5a2439a commit af00ec3

4 files changed

Lines changed: 60 additions & 43 deletions

File tree

src/features/creators/newScriptProject.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { isCopilotInstalled, manageCopilotInstructionsFile, replaceInFilesAndNam
1111
export class NewScriptProject implements PythonProjectCreator {
1212
public readonly name = l10n.t('newScript');
1313
public readonly displayName = l10n.t('Script');
14-
public readonly description = l10n.t('Creates a new script folder in your current workspace with PEP 723 support');
14+
public readonly description = l10n.t('Creates a new script folder in your current workspace');
1515
public readonly tooltip = new MarkdownString(l10n.t('Create a new Python script'));
1616

1717
constructor(private readonly projectManager: PythonProjectManager) {}

src/features/views/pythonStatusBar.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { Disposable, StatusBarAlignment, StatusBarItem, ThemeColor } from 'vscode';
2+
import { PythonEnvironment } from '../../api';
23
import { createStatusBarItem } from '../../common/window.apis';
34

45
export interface PythonStatusBar extends Disposable {
5-
show(text?: string): void;
6+
show(env?: PythonEnvironment): void;
67
hide(): void;
78
}
89

@@ -19,9 +20,15 @@ export class PythonStatusBarImpl implements Disposable {
1920
this.disposables.push(this.statusBarItem);
2021
}
2122

22-
public show(text?: string) {
23-
this.statusBarItem.text = text ?? 'Select Python Interpreter';
24-
this.statusBarItem.backgroundColor = text ? undefined : new ThemeColor('statusBarItem.warningBackground');
23+
public show(env?: PythonEnvironment) {
24+
if (env) {
25+
this.statusBarItem.text = env.displayName ?? 'Select Python Interpreter';
26+
this.statusBarItem.tooltip = env.environmentPath?.fsPath ?? '';
27+
} else {
28+
this.statusBarItem.text = 'Select Python Interpreter';
29+
this.statusBarItem.tooltip = 'Select Python Interpreter';
30+
}
31+
this.statusBarItem.backgroundColor = env ? undefined : new ThemeColor('statusBarItem.warningBackground');
2532
this.statusBarItem.show();
2633
}
2734

src/features/views/revealHandler.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
import { PythonEnvironmentApi } from '../../api';
2+
import { isPythonProjectFile } from '../../common/utils/fileNameUtils';
13
import { activeTextEditor } from '../../common/window.apis';
2-
import { ProjectView } from './projectView';
34
import { EnvManagerView } from './envManagersView';
5+
import { ProjectView } from './projectView';
46
import { PythonStatusBar } from './pythonStatusBar';
5-
import { isPythonProjectFile } from '../../common/utils/fileNameUtils';
6-
import { PythonEnvironmentApi } from '../../api';
77

88
export function updateViewsAndStatus(
99
statusBar: PythonStatusBar,
@@ -31,7 +31,7 @@ export function updateViewsAndStatus(
3131
workspaceView.reveal(activeDocument.uri);
3232
setImmediate(async () => {
3333
const env = await api.getEnvironment(activeDocument.uri);
34-
statusBar.show(env?.displayName);
34+
statusBar.show(env);
3535
managerView.reveal(env);
3636
});
3737
}

src/managers/builtin/venvManager.ts

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,13 @@ export class VenvManager implements EnvironmentManager {
103103
}
104104
}
105105

106+
/**
107+
* Returns configuration for quick create in the workspace root, undefined if no suitable Python 3 version is found.
108+
*/
106109
quickCreateConfig(): QuickCreateConfig | undefined {
107110
if (!this.globalEnv || !this.globalEnv.version.startsWith('3.')) {
108111
return undefined;
109112
}
110-
111113
return {
112114
description: l10n.t('Create a virtual environment in workspace root'),
113115
detail: l10n.t(
@@ -217,6 +219,9 @@ export class VenvManager implements EnvironmentManager {
217219
}
218220
}
219221

222+
/**
223+
* Removes the specified Python environment, updates internal collections, and fires change events as needed.
224+
*/
220225
async remove(environment: PythonEnvironment): Promise<void> {
221226
try {
222227
this.skipWatcherRefresh = true;
@@ -470,6 +475,9 @@ export class VenvManager implements EnvironmentManager {
470475
await this.loadGlobalEnv(globals);
471476
}
472477

478+
/**
479+
* Loads and sets the global Python environment from the provided list, resolving if necessary. O(g) where g = globals.length
480+
*/
473481
private async loadGlobalEnv(globals: PythonEnvironment[]) {
474482
this.globalEnv = undefined;
475483

@@ -502,30 +510,28 @@ export class VenvManager implements EnvironmentManager {
502510
}
503511
}
504512

513+
/**
514+
* Loads and maps Python environments to their corresponding project paths in the workspace. about O(p × e) where p = projects.len and e = environments.len
515+
*/
505516
private async loadEnvMap() {
506517
const globals = await this.baseManager.getEnvironments('global');
507518
await this.loadGlobalEnv(globals);
508519

509520
this.fsPathToEnv.clear();
510521

511522
const sorted = sortEnvironments(this.collection);
512-
const paths = this.api.getPythonProjects().map((p) => path.normalize(p.uri.fsPath));
523+
const projectPaths = this.api.getPythonProjects().map((p) => path.normalize(p.uri.fsPath));
513524
const events: (() => void)[] = [];
514-
for (const p of paths) {
525+
// Iterates through all workspace projects
526+
for (const p of projectPaths) {
515527
const env = await getVenvForWorkspace(p);
516-
517528
if (env) {
518-
const found = this.findEnvironmentByPath(env, sorted) ?? this.findEnvironmentByPath(env, globals);
519-
const previous = this.fsPathToEnv.get(p);
529+
// from env path find PythonEnvironment object in the collection.
530+
let foundEnv = this.findEnvironmentByPath(env, sorted) ?? this.findEnvironmentByPath(env, globals);
531+
const previousEnv = this.fsPathToEnv.get(p);
520532
const pw = this.api.getPythonProject(Uri.file(p));
521-
if (found) {
522-
this.fsPathToEnv.set(p, found);
523-
if (pw && previous?.envId.id !== found.envId.id) {
524-
events.push(() =>
525-
this._onDidChangeEnvironment.fire({ uri: pw.uri, old: undefined, new: found }),
526-
);
527-
}
528-
} else {
533+
if (!foundEnv) {
534+
// attempt to resolve
529535
const resolved = await resolveVenvPythonEnvironmentPath(
530536
env,
531537
this.nativeFinder,
@@ -534,39 +540,39 @@ export class VenvManager implements EnvironmentManager {
534540
this.baseManager,
535541
);
536542
if (resolved) {
537-
// If resolved add it to the collection
538-
this.fsPathToEnv.set(p, resolved);
543+
// If resolved; add it to the venvManager collection
539544
this.addEnvironment(resolved, false);
540-
if (pw && previous?.envId.id !== resolved.envId.id) {
541-
events.push(() =>
542-
this._onDidChangeEnvironment.fire({ uri: pw.uri, old: undefined, new: resolved }),
543-
);
544-
}
545+
foundEnv = resolved;
545546
} else {
546547
this.log.error(`Failed to resolve python environment: ${env}`);
548+
return;
547549
}
548550
}
551+
// Given found env, add it to the map and fire the event if needed.
552+
this.fsPathToEnv.set(p, foundEnv);
553+
if (pw && previousEnv?.envId.id !== foundEnv.envId.id) {
554+
events.push(() =>
555+
this._onDidChangeEnvironment.fire({ uri: pw.uri, old: undefined, new: foundEnv }),
556+
);
557+
}
549558
} else {
550-
// There is NO selected venv, then try and choose the venv that is in the workspace.
551-
if (sorted.length === 1) {
552-
this.fsPathToEnv.set(p, sorted[0]);
553-
} else {
554-
// These are sorted by version and by path length. The assumption is that the user would want to pick
555-
// latest version and the one that is closest to the workspace.
556-
const found = sorted.find((e) => {
557-
const t = this.api.getPythonProject(e.environmentPath)?.uri.fsPath;
558-
return t && path.normalize(t) === p;
559-
});
560-
if (found) {
561-
this.fsPathToEnv.set(p, found);
562-
}
559+
// Search through all known environments (e) and check if any are associated with the current project path. If so, add that environment and path in the map.
560+
const found = sorted.find((e) => {
561+
const t = this.api.getPythonProject(e.environmentPath)?.uri.fsPath;
562+
return t && path.normalize(t) === p;
563+
});
564+
if (found) {
565+
this.fsPathToEnv.set(p, found);
563566
}
564567
}
565568
}
566569

567570
events.forEach((e) => e());
568571
}
569572

573+
/**
574+
* Finds a PythonEnvironment in the given collection (or all environments) that matches the provided file system path. O(e) where e = environments.len
575+
*/
570576
private findEnvironmentByPath(fsPath: string, collection?: PythonEnvironment[]): PythonEnvironment | undefined {
571577
const normalized = path.normalize(fsPath);
572578
const envs = collection ?? this.collection;
@@ -576,6 +582,10 @@ export class VenvManager implements EnvironmentManager {
576582
});
577583
}
578584

585+
/**
586+
* Returns all Python projects associated with the given environment.
587+
* O(p), where p is project.len
588+
*/
579589
public getProjectsByEnvironment(environment: PythonEnvironment): PythonProject[] {
580590
const projects: PythonProject[] = [];
581591
this.fsPathToEnv.forEach((env, fsPath) => {

0 commit comments

Comments
 (0)