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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@
},
{
"command": "python-envs.removePythonProject",
"when": "view == python-projects && viewItem == python-workspace"
"when": "view == python-projects && viewItem == python-workspace-removable"
},
{
"command": "python-envs.set",
Expand Down
12 changes: 7 additions & 5 deletions src/common/utils/fileNameUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as path from 'path';
import * as fsapi from 'fs-extra';
import { KNOWN_FILES, KNOWN_TEMPLATE_ENDINGS } from '../constants';
import { Uri, workspace } from 'vscode';
import { Uri } from 'vscode';
import { getWorkspaceFolders } from '../workspace.apis';

export function isPythonProjectFile(fileName: string): boolean {
const baseName = path.basename(fileName).toLowerCase();
Expand All @@ -20,14 +21,15 @@ export async function getAbsolutePath(fsPath: string): Promise<Uri | undefined>
return Uri.file(fsPath);
}

if (workspace.workspaceFolders && workspace.workspaceFolders.length > 0) {
if (workspace.workspaceFolders.length === 1) {
const absPath = path.resolve(workspace.workspaceFolders[0].uri.fsPath, fsPath);
const workspaceFolders = getWorkspaceFolders() ?? [];
if (workspaceFolders.length > 0) {
if (workspaceFolders.length === 1) {
const absPath = path.resolve(workspaceFolders[0].uri.fsPath, fsPath);
if (await fsapi.pathExists(absPath)) {
return Uri.file(absPath);
}
} else {
const workspaces = Array.from(workspace.workspaceFolders)
const workspaces = Array.from(workspaceFolders)
.sort((a, b) => a.uri.fsPath.length - b.uri.fsPath.length)
.reverse();
for (const folder of workspaces) {
Expand Down
8 changes: 6 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,12 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
commands.registerCommand('python-envs.create', async (item) => {
return await createEnvironmentCommand(item, envManagers, projectManager);
}),
commands.registerCommand('python-envs.createAny', async () => {
return await createAnyEnvironmentCommand(envManagers, projectManager);
commands.registerCommand('python-envs.createAny', async (options) => {
return await createAnyEnvironmentCommand(
envManagers,
projectManager,
options ?? { selectEnvironment: true },
);
}),
commands.registerCommand('python-envs.remove', async (item) => {
await removeEnvironmentCommand(item, envManagers);
Expand Down
66 changes: 54 additions & 12 deletions src/features/envCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
ProjectCreators,
PythonProjectManager,
} from '../internal.api';
import { traceError, traceVerbose } from '../common/logging';
import { traceError, traceInfo, traceVerbose } from '../common/logging';
import { PythonEnvironment, PythonEnvironmentApi, PythonProject, PythonProjectCreator } from '../api';
import * as path from 'path';
import {
Expand Down Expand Up @@ -71,11 +71,25 @@ export async function createEnvironmentCommand(
): Promise<PythonEnvironment | undefined> {
if (context instanceof EnvManagerTreeItem) {
const manager = (context as EnvManagerTreeItem).manager;
const projects = await pickProjectMany(pm.getProjects());
if (projects) {
return await manager.create(projects.length === 0 ? 'global' : projects.map((p) => p.uri));
} else {
traceError(`No projects found for ${context}`);
const projects = pm.getProjects();
if (projects.length === 0) {
const env = await manager.create('global');
if (env) {
await em.setEnvironments('global', env);
}
return env;
} else if (projects.length > 0) {
const selected = await pickProjectMany(projects);
if (selected) {
const scope = selected.length === 0 ? 'global' : selected.map((p) => p.uri);
const env = await manager.create(scope);
if (env) {
await em.setEnvironments(scope, env);
}
return env;
} else {
traceInfo('No project selected or global condition met for environment creation');
}
}
} else if (context instanceof Uri) {
const manager = em.getEnvironmentManager(context as Uri);
Expand All @@ -93,7 +107,10 @@ export async function createEnvironmentCommand(
export async function createAnyEnvironmentCommand(
em: EnvironmentManagers,
pm: PythonProjectManager,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
options?: any,
): Promise<PythonEnvironment | undefined> {
const select = options?.selectEnvironment;
const projects = await pickProjectMany(pm.getProjects());
if (projects && projects.length > 0) {
const defaultManagers: InternalEnvironmentManager[] = [];
Expand All @@ -112,14 +129,25 @@ export async function createAnyEnvironmentCommand(

const manager = em.managers.find((m) => m.id === managerId);
if (manager) {
return await manager.create(projects.map((p) => p.uri));
const env = await manager.create(projects.map((p) => p.uri));
if (select) {
await em.setEnvironments(
projects.map((p) => p.uri),
env,
);
}
return env;
}
} else if (projects && projects.length === 0) {
const managerId = await pickEnvironmentManager(em.managers.filter((m) => m.supportsCreate));

const manager = em.managers.find((m) => m.id === managerId);
if (manager) {
return await manager.create('global');
const env = await manager.create('global');
if (select) {
await manager.set(undefined, env);
}
return env;
}
}
}
Expand Down Expand Up @@ -203,10 +231,24 @@ export async function setEnvironmentCommand(
await setEnvironmentCommand([context], em, wm);
} else if (context === undefined) {
try {
const projects = await pickProjectMany(wm.getProjects());
const projects = wm.getProjects();
if (projects && projects.length > 0) {
const uris = projects.map((p) => p.uri);
await setEnvironmentCommand(uris, em, wm);
const selected = await pickProjectMany(projects);
if (selected && selected.length > 0) {
const uris = selected.map((p) => p.uri);
await setEnvironmentCommand(uris, em, wm);
}
} else {
const globalEnvManager = em.getEnvironmentManager(undefined);
const recommended = globalEnvManager ? await globalEnvManager.get(undefined) : undefined;
const selected = await pickEnvironment(em.managers, globalEnvManager ? [globalEnvManager] : [], {
projects: [],
recommended,
showBackButton: false,
});
if (selected) {
await em.setEnvironments('global', selected);
}
}
} catch (ex) {
if (ex === QuickInputButtons.Back) {
Expand Down Expand Up @@ -487,7 +529,7 @@ export async function runAsTaskCommand(item: unknown, api: PythonEnvironmentApi)
const uri = item as Uri;
const project = api.getPythonProject(uri);
const environment = await api.getEnvironment(uri);
if (environment && project) {
if (environment) {
return await runAsTask(
environment,
{
Expand Down
77 changes: 57 additions & 20 deletions src/features/envManagers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
}
}

public async setEnvironments(scope: Uri[], environment?: PythonEnvironment): Promise<void> {
public async setEnvironments(scope: Uri[] | string, environment?: PythonEnvironment): Promise<void> {
if (environment) {
const manager = this.managers.find((m) => m.id === environment.envId.managerId);
if (!manager) {
Expand All @@ -287,50 +287,87 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
const promises: Promise<void>[] = [];
const settings: EditAllManagerSettings[] = [];
const events: DidChangeEnvironmentEventArgs[] = [];
scope.forEach((uri) => {
const m = this.getEnvironmentManager(uri);
promises.push(manager.set(uri, environment));
if (Array.isArray(scope) && scope.every((s) => s instanceof Uri)) {
scope.forEach((uri) => {
const m = this.getEnvironmentManager(uri);
promises.push(manager.set(uri, environment));
if (manager.id !== m?.id) {
settings.push({
project: this.pm.get(uri),
envManager: manager.id,
packageManager: manager.preferredPackageManagerId,
});
}

const project = this.pm.get(uri);
const oldEnv = this._previousEnvironments.get(project?.uri.toString() ?? 'global');
if (oldEnv?.envId.id !== environment?.envId.id) {
this._previousEnvironments.set(project?.uri.toString() ?? 'global', environment);
events.push({ uri: project?.uri, new: environment, old: oldEnv });
}
});
} else if (typeof scope === 'string' && scope === 'global') {
const m = this.getEnvironmentManager(undefined);
promises.push(manager.set(undefined, environment));
if (manager.id !== m?.id) {
settings.push({
project: this.pm.get(uri),
project: undefined,
envManager: manager.id,
packageManager: manager.preferredPackageManagerId,
});
}

const project = this.pm.get(uri);
const oldEnv = this._previousEnvironments.get(project?.uri.toString() ?? 'global');
const oldEnv = this._previousEnvironments.get('global');
if (oldEnv?.envId.id !== environment?.envId.id) {
this._previousEnvironments.set(project?.uri.toString() ?? 'global', environment);
events.push({ uri: project?.uri, new: environment, old: oldEnv });
this._previousEnvironments.set('global', environment);
events.push({ uri: undefined, new: environment, old: oldEnv });
}
});
}
await Promise.all(promises);
await setAllManagerSettings(settings);
setImmediate(() => events.forEach((e) => this._onDidChangeEnvironmentFiltered.fire(e)));
} else {
const promises: Promise<void>[] = [];
const events: DidChangeEnvironmentEventArgs[] = [];
scope.forEach((uri) => {
const manager = this.getEnvironmentManager(uri);
if (Array.isArray(scope) && scope.every((s) => s instanceof Uri)) {
scope.forEach((uri) => {
const manager = this.getEnvironmentManager(uri);
if (manager) {
const setAndAddEvent = async () => {
await manager.set(uri);

const project = this.pm.get(uri);

// Always get the new first, then compare with the old. This has minor impact on the ordering of
// events. But it ensures that we always get the latest environment at the time of this call.
const newEnv = await manager.get(uri);
const oldEnv = this._previousEnvironments.get(project?.uri.toString() ?? 'global');
if (oldEnv?.envId.id !== newEnv?.envId.id) {
this._previousEnvironments.set(project?.uri.toString() ?? 'global', newEnv);
events.push({ uri: project?.uri, new: newEnv, old: oldEnv });
}
};
promises.push(setAndAddEvent());
}
});
} else if (typeof scope === 'string' && scope === 'global') {
const manager = this.getEnvironmentManager(undefined);
if (manager) {
const setAndAddEvent = async () => {
await manager.set(uri);

const project = this.pm.get(uri);
await manager.set(undefined);

// Always get the new first, then compare with the old. This has minor impact on the ordering of
// events. But it ensures that we always get the latest environment at the time of this call.
const newEnv = await manager.get(uri);
const oldEnv = this._previousEnvironments.get(project?.uri.toString() ?? 'global');
const newEnv = await manager.get(undefined);
const oldEnv = this._previousEnvironments.get('global');
if (oldEnv?.envId.id !== newEnv?.envId.id) {
this._previousEnvironments.set(project?.uri.toString() ?? 'global', newEnv);
events.push({ uri: project?.uri, new: newEnv, old: oldEnv });
this._previousEnvironments.set('global', newEnv);
events.push({ uri: undefined, new: newEnv, old: oldEnv });
}
};
promises.push(setAndAddEvent());
}
});
}
await Promise.all(promises);
setImmediate(() => events.forEach((e) => this._onDidChangeEnvironmentFiltered.fire(e)));
}
Expand Down
2 changes: 1 addition & 1 deletion src/features/settings/settingHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export async function setAllManagerSettings(edits: EditAllManagerSettings[]): Pr

noWorkspace.forEach((e) => {
if (e.project) {
traceError(`Unable to find workspace for ${e.project.uri.fsPath}`);
traceInfo(`Unable to find workspace for ${e.project.uri.fsPath}, will use global settings for this.`);
}
});

Expand Down
26 changes: 16 additions & 10 deletions src/features/views/projectView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
ProjectEnvironmentInfo,
ProjectPackage,
ProjectPackageRootInfoTreeItem,
GlobalProjectItem,
} from './treeViewItems';
import { onDidChangeConfiguration } from '../../common/workspace.apis';
import { createSimpleDebounce } from '../../common/utils/debounce';
Expand Down Expand Up @@ -100,12 +101,11 @@ export class WorkspaceView implements TreeDataProvider<ProjectTreeItem> {
reveal(context: Uri | PythonEnvironment): PythonEnvironment | undefined {
if (context instanceof Uri) {
const pw = this.projectManager.get(context);
if (pw) {
const view = this.revealMap.get(pw.uri.fsPath);
if (view) {
this.revealInternal(view);
return view.environment;
}
const key = pw ? pw.uri.fsPath : 'global';
const view = this.revealMap.get(key);
if (view) {
this.revealInternal(view);
return view.environment;
}
} else {
const view = Array.from(this.revealMap.values()).find((v) => v.environment.envId.id === context.envId.id);
Expand All @@ -128,12 +128,17 @@ export class WorkspaceView implements TreeDataProvider<ProjectTreeItem> {
if (element === undefined) {
this.projectViews.clear();
const views: ProjectTreeItem[] = [];
this.projectManager.getProjects().forEach((w) => {
const projects = this.projectManager.getProjects();
projects.forEach((w) => {
const view = new ProjectItem(w);
this.projectViews.set(w.uri.fsPath, view);
views.push(view);
});

if (projects.length === 0) {
views.push(new GlobalProjectItem());
}

return views;
}

Expand All @@ -152,7 +157,8 @@ export class WorkspaceView implements TreeDataProvider<ProjectTreeItem> {
];
}

const manager = this.envManagers.getEnvironmentManager(projectItem.project.uri);
const uri = projectItem.id === 'global' ? undefined : projectItem.project.uri;
const manager = this.envManagers.getEnvironmentManager(uri);
if (!manager) {
return [
new NoProjectEnvironment(
Expand All @@ -164,7 +170,7 @@ export class WorkspaceView implements TreeDataProvider<ProjectTreeItem> {
];
}

const environment = await manager?.get(projectItem.project.uri);
const environment = await manager?.get(uri);
if (!environment) {
return [
new NoProjectEnvironment(
Expand All @@ -175,7 +181,7 @@ export class WorkspaceView implements TreeDataProvider<ProjectTreeItem> {
];
}
const view = new ProjectEnvironment(projectItem, environment);
this.revealMap.set(projectItem.project.uri.fsPath, view);
this.revealMap.set(uri ? uri.fsPath : 'global', view);
return [view];
}

Expand Down
Loading