Skip to content

Commit c72de1d

Browse files
authored
Merge branch 'main' into support-quick-create-from-command
2 parents b032651 + 78b3142 commit c72de1d

14 files changed

Lines changed: 1255 additions & 128 deletions

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ The Python Environments extension supports shell startup activation for environm
151151
- (Windows): `~\Documents\PowerShell\Microsoft.PowerShell_profile.ps1`
152152
- **CMD**: `~/.cmdrc/cmd_startup.bat`
153153

154+
If at any time you would like to revert the changes made to the shell's script, you can do so by running `Python Envs: Revert Shell Startup Script Changes` via the Command Palette.
155+
154156
### CMD
155157

156158
1. Add or update `HKCU\\Software\\Microsoft\\Command Processor` `AutoRun` string value to use a command script.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@
361361
{
362362
"command": "python-envs.refreshPackages",
363363
"group": "inline",
364-
"when": "view == env-managers && viewItem == python-package-root"
364+
"when": "view == env-managers && viewItem =~ /.*pythonEnvironment.*/"
365365
},
366366
{
367367
"command": "python-envs.packages",
@@ -395,7 +395,7 @@
395395
{
396396
"command": "python-envs.refreshPackages",
397397
"group": "inline",
398-
"when": "view == python-projects && viewItem == python-package-root"
398+
"when": "view == python-projects && viewItem == python-env"
399399
},
400400
{
401401
"command": "python-envs.removePythonProject",

package.nls.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
"python-envs.copyProjectPath.title": "Copy Project Path",
2020
"python-envs.create.title": "Create Environment",
2121
"python-envs.createAny.title": "Create Environment",
22-
"python-envs.set.title": "Set Workspace Environment",
23-
"python-envs.setEnv.title": "Set As Workspace Environment",
22+
"python-envs.set.title": "Set Project Environment",
23+
"python-envs.setEnv.title": "Set As Project Environment",
2424
"python-envs.reset.title": "Reset to Default",
2525
"python-envs.remove.title": "Delete Environment",
2626
"python-envs.refreshAllManagers.title": "Refresh All Environment Managers",

src/common/localize.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ export namespace PyenvStrings {
158158
export const pyenvRefreshing = l10n.t('Refreshing Pyenv Python versions');
159159
}
160160

161+
export namespace PoetryStrings {
162+
export const poetryManager = l10n.t('Manages Poetry environments');
163+
export const poetryDiscovering = l10n.t('Discovering Poetry environments');
164+
export const poetryRefreshing = l10n.t('Refreshing Poetry environments');
165+
}
166+
161167
export namespace ProjectCreatorString {
162168
export const addExistingProjects = l10n.t('Add Existing Projects');
163169
export const autoFindProjects = l10n.t('Auto Find Projects');
@@ -172,7 +178,7 @@ export namespace ProjectCreatorString {
172178

173179
export namespace EnvViewStrings {
174180
export const selectedGlobalTooltip = l10n.t('This environment is selected for non-workspace files');
175-
export const selectedWorkspaceTooltip = l10n.t('This environment is selected for workspace files');
181+
export const selectedWorkspaceTooltip = l10n.t('This environment is selected for project files');
176182
}
177183

178184
export namespace ActivationStrings {

src/extension.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import { EnvironmentManagers, ProjectCreators, PythonProjectManager } from './in
6767
import { registerSystemPythonFeatures } from './managers/builtin/main';
6868
import { createNativePythonFinder, NativePythonFinder } from './managers/common/nativePythonFinder';
6969
import { registerCondaFeatures } from './managers/conda/main';
70+
import { registerPoetryFeatures } from './managers/poetry/main';
7071
import { registerPyenvFeatures } from './managers/pyenv/main';
7172

7273
export async function activate(context: ExtensionContext): Promise<PythonEnvironmentApi> {
@@ -155,7 +156,7 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
155156
await Promise.all(envManagers.managers.map((m) => m.refresh(undefined)));
156157
}),
157158
commands.registerCommand('python-envs.refreshPackages', async (item) => {
158-
await refreshPackagesCommand(item);
159+
await refreshPackagesCommand(item, envManagers);
159160
}),
160161
commands.registerCommand('python-envs.create', async (item) => {
161162
return await createEnvironmentCommand(item, envManagers, projectManager);
@@ -342,6 +343,7 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
342343
registerSystemPythonFeatures(nativeFinder, context.subscriptions, outputChannel),
343344
registerCondaFeatures(nativeFinder, context.subscriptions, outputChannel),
344345
registerPyenvFeatures(nativeFinder, context.subscriptions),
346+
registerPoetryFeatures(nativeFinder, context.subscriptions, outputChannel),
345347
shellStartupVarsMgr.initialize(),
346348
]);
347349

src/features/envCommands.ts

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
CreateEnvironmentOptions,
44
PythonEnvironment,
55
PythonEnvironmentApi,
6+
PythonProject,
67
PythonProjectCreator,
78
PythonProjectCreatorOptions,
89
} from '../api';
@@ -21,7 +22,7 @@ import {} from '../common/errors/utils';
2122
import { pickEnvironment } from '../common/pickers/environments';
2223
import { pickCreator, pickEnvironmentManager, pickPackageManager } from '../common/pickers/managers';
2324
import { pickProject, pickProjectMany } from '../common/pickers/projects';
24-
import { activeTextEditor, showErrorMessage } from '../common/window.apis';
25+
import { activeTextEditor, showErrorMessage, showInformationMessage } from '../common/window.apis';
2526
import { quoteArgs } from './execution/execUtils';
2627
import { runAsTask } from './execution/runAsTask';
2728
import { runInTerminal } from './terminal/runInTerminal';
@@ -30,12 +31,10 @@ import {
3031
EnvManagerTreeItem,
3132
EnvTreeItemKind,
3233
GlobalProjectItem,
33-
PackageRootTreeItem,
3434
PackageTreeItem,
3535
ProjectEnvironment,
3636
ProjectItem,
3737
ProjectPackage,
38-
ProjectPackageRootTreeItem,
3938
PythonEnvTreeItem,
4039
} from './views/treeViewItems';
4140

@@ -48,15 +47,25 @@ export async function refreshManagerCommand(context: unknown): Promise<void> {
4847
}
4948
}
5049

51-
export async function refreshPackagesCommand(context: unknown) {
52-
if (context instanceof ProjectPackageRootTreeItem) {
53-
const view = context as ProjectPackageRootTreeItem;
54-
const manager = view.manager;
55-
await manager.refresh(view.environment);
56-
} else if (context instanceof PackageRootTreeItem) {
57-
const view = context as PackageRootTreeItem;
58-
const manager = view.manager;
59-
await manager.refresh(view.environment);
50+
export async function refreshPackagesCommand(context: unknown, managers?: EnvironmentManagers): Promise<void> {
51+
if (context instanceof ProjectEnvironment) {
52+
const view = context as ProjectEnvironment;
53+
if (managers) {
54+
const pkgManager = managers.getPackageManager(view.parent.project.uri);
55+
if (pkgManager) {
56+
await pkgManager.refresh(view.environment);
57+
}
58+
}
59+
} else if (context instanceof PythonEnvTreeItem) {
60+
const view = context as PythonEnvTreeItem;
61+
const envManager =
62+
view.parent.kind === EnvTreeItemKind.environmentGroup
63+
? view.parent.parent.manager
64+
: view.parent.manager;
65+
const pkgManager = managers?.getPackageManager(envManager.preferredPackageManagerId);
66+
if (pkgManager) {
67+
await pkgManager.refresh(view.environment);
68+
}
6069
} else {
6170
traceVerbose(`Invalid context for refresh command: ${context}`);
6271
}
@@ -192,7 +201,8 @@ export async function removeEnvironmentCommand(context: unknown, managers: Envir
192201
export async function handlePackageUninstall(context: unknown, em: EnvironmentManagers) {
193202
if (context instanceof PackageTreeItem || context instanceof ProjectPackage) {
194203
const moduleName = context.pkg.name;
195-
const environment = context.parent.environment;
204+
const environment =
205+
context instanceof ProjectPackage ? context.parent.environment : context.parent.environment;
196206
const packageManager = em.getPackageManager(environment);
197207
await packageManager?.manage(environment, { uninstall: [moduleName], install: [] });
198208
return;
@@ -212,8 +222,8 @@ export async function setEnvironmentCommand(
212222
if (projects.length > 0) {
213223
const selected = await pickProjectMany(projects);
214224
if (selected && selected.length > 0) {
215-
const uris = selected.map((p) => p.uri);
216-
await em.setEnvironments(uris, view.environment);
225+
// Check if the selected environment is already the current one for each project
226+
await setEnvironmentForProjects(selected, context.environment, em);
217227
}
218228
} else {
219229
await em.setEnvironments('global', view.environment);
@@ -271,13 +281,47 @@ export async function setEnvironmentCommand(
271281
});
272282

273283
if (selected) {
274-
await em.setEnvironments(uris, selected);
284+
// Use the same logic for checking already set environments
285+
await setEnvironmentForProjects(projects, selected, em);
275286
}
276287
} else {
277288
traceError(`Invalid context for setting environment command: ${context}`);
278289
showErrorMessage('Invalid context for setting environment');
279290
}
280291
}
292+
/**
293+
* Sets the environment for the given projects, showing a warning for those already set.
294+
* @param selectedProjects Array of PythonProject selected by user
295+
* @param environment The environment to set for the projects
296+
* @param em The EnvironmentManagers instance
297+
*/
298+
async function setEnvironmentForProjects(
299+
selectedProjects: PythonProject[],
300+
environment: PythonEnvironment,
301+
em: EnvironmentManagers,
302+
) {
303+
let alreadySet: PythonProject[] = [];
304+
for (const p of selectedProjects) {
305+
const currentEnv = await em.getEnvironment(p.uri);
306+
if (currentEnv?.envId.id === environment.envId.id) {
307+
alreadySet.push(p);
308+
}
309+
}
310+
if (alreadySet.length > 0) {
311+
const env = alreadySet.length > 1 ? 'environments' : 'environment';
312+
showInformationMessage(
313+
`"${environment.name}" is already selected as the ${env} for: ${alreadySet
314+
.map((p) => `"${p.name}"`)
315+
.join(', ')}`,
316+
);
317+
}
318+
const toSet: PythonProject[] = selectedProjects.filter((p) => !alreadySet.includes(p));
319+
const uris = toSet.map((p) => p.uri);
320+
if (uris.length === 0) {
321+
return;
322+
}
323+
await em.setEnvironments(uris, environment);
324+
}
281325

282326
export async function resetEnvironmentCommand(
283327
context: unknown,

src/features/views/envManagersView.ts

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,10 @@ import {
1313
EnvTreeItem,
1414
EnvManagerTreeItem,
1515
PythonEnvTreeItem,
16-
PackageRootTreeItem,
1716
PackageTreeItem,
1817
EnvTreeItemKind,
1918
NoPythonEnvTreeItem,
2019
EnvInfoTreeItem,
21-
PackageRootInfoTreeItem,
2220
PythonGroupEnvTreeItem,
2321
} from './treeViewItems';
2422
import { createSimpleDebounce } from '../../common/utils/debounce';
@@ -31,7 +29,6 @@ export class EnvManagerView implements TreeDataProvider<EnvTreeItem>, Disposable
3129
>();
3230
private revealMap = new Map<string, PythonEnvTreeItem>();
3331
private managerViews = new Map<string, EnvManagerTreeItem>();
34-
private packageRoots = new Map<string, PackageRootTreeItem>();
3532
private selected: Map<string, string> = new Map();
3633
private disposables: Disposable[] = [];
3734

@@ -42,7 +39,6 @@ export class EnvManagerView implements TreeDataProvider<EnvTreeItem>, Disposable
4239

4340
this.disposables.push(
4441
new Disposable(() => {
45-
this.packageRoots.clear();
4642
this.revealMap.clear();
4743
this.managerViews.clear();
4844
this.selected.clear();
@@ -165,32 +161,18 @@ export class EnvManagerView implements TreeDataProvider<EnvTreeItem>, Disposable
165161
const views: EnvTreeItem[] = [];
166162

167163
if (pkgManager) {
168-
const item = new PackageRootTreeItem(parent, pkgManager, environment);
169-
this.packageRoots.set(environment.envId.id, item);
170-
views.push(item);
164+
const packages = await pkgManager.getPackages(environment);
165+
if (packages && packages.length > 0) {
166+
views.push(...packages.map((p) => new PackageTreeItem(p, parent, pkgManager)));
167+
} else {
168+
views.push(new EnvInfoTreeItem(parent, ProjectViews.noPackages));
169+
}
171170
} else {
172171
views.push(new EnvInfoTreeItem(parent, ProjectViews.noPackageManager));
173172
}
174173

175174
return views;
176175
}
177-
178-
if (element.kind === EnvTreeItemKind.packageRoot) {
179-
const root = element as PackageRootTreeItem;
180-
const manager = root.manager;
181-
const environment = root.environment;
182-
183-
let packages = await manager.getPackages(environment);
184-
const views: EnvTreeItem[] = [];
185-
186-
if (packages) {
187-
views.push(...packages.map((p) => new PackageTreeItem(p, root, manager)));
188-
} else {
189-
views.push(new PackageRootInfoTreeItem(root, ProjectViews.noPackages));
190-
}
191-
192-
return views;
193-
}
194176
}
195177

196178
getParent(element: EnvTreeItem): ProviderResult<EnvTreeItem> {
@@ -219,35 +201,32 @@ export class EnvManagerView implements TreeDataProvider<EnvTreeItem>, Disposable
219201
}
220202

221203
private onDidChangePackages(args: InternalDidChangePackagesEventArgs) {
222-
const pkgRoot = this.packageRoots.get(args.environment.envId.id);
223-
if (pkgRoot) {
224-
this.fireDataChanged(pkgRoot);
204+
const view = Array.from(this.revealMap.values()).find(
205+
(v) => v.environment.envId.id === args.environment.envId.id
206+
);
207+
if (view) {
208+
this.fireDataChanged(view);
225209
}
226210
}
227211

228-
private onDidChangePackageManager(args: DidChangePackageManagerEventArgs) {
229-
const roots = Array.from(this.packageRoots.values()).filter((r) => r.manager.id === args.manager.id);
230-
this.fireDataChanged(roots);
212+
private onDidChangePackageManager(_args: DidChangePackageManagerEventArgs) {
213+
// Since we removed the packageRoots level, just refresh all environments
214+
// This is a simplified approach that isn't as targeted but ensures packages get refreshed
215+
this.fireDataChanged(undefined);
231216
}
232217

233218
public environmentChanged(e: DidChangeEnvironmentEventArgs) {
234219
const views = [];
235220
if (e.old) {
236221
this.selected.delete(e.old.envId.id);
237-
let view: EnvTreeItem | undefined = this.packageRoots.get(e.old.envId.id);
238-
if (!view) {
239-
view = this.managerViews.get(e.old.envId.managerId);
240-
}
222+
const view: EnvTreeItem | undefined = this.revealMap.get(e.old.envId.id);
241223
if (view) {
242224
views.push(view);
243225
}
244226
}
245227
if (e.new) {
246228
this.selected.set(e.new.envId.id, e.uri === undefined ? 'global' : e.uri.fsPath);
247-
let view: EnvTreeItem | undefined = this.packageRoots.get(e.new.envId.id);
248-
if (!view) {
249-
view = this.managerViews.get(e.new.envId.managerId);
250-
}
229+
const view: EnvTreeItem | undefined = this.revealMap.get(e.new.envId.id);
251230
if (view && !views.includes(view)) {
252231
views.push(view);
253232
}

0 commit comments

Comments
 (0)