From 3c707678f5fec3f1205500ea45012f73c2d4b347 Mon Sep 17 00:00:00 2001 From: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Thu, 24 Apr 2025 11:35:42 -0700 Subject: [PATCH 1/7] switch new project add to create method --- src/api.ts | 3 +- src/extension.ts | 4 +- src/features/envCommands.ts | 115 ++++++++++++--------------------- src/features/projectManager.ts | 20 +++++- src/internal.api.ts | 2 +- 5 files changed, 63 insertions(+), 81 deletions(-) diff --git a/src/api.ts b/src/api.ts index 924d955a..e58d6762 100644 --- a/src/api.ts +++ b/src/api.ts @@ -702,7 +702,8 @@ export interface PythonProjectCreator { readonly iconPath?: IconPath; /** - * Creates a new Python project or projects. + * Creates a new Python project or projects. The create method is required to add the created project if successful to the + * list of projects. * @param options - Optional parameters for creating the Python project. * @returns A promise that resolves to a Python project, an array of Python projects, or undefined. */ diff --git a/src/extension.ts b/src/extension.ts index 226e4821..4289a478 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -4,7 +4,7 @@ import { PythonEnvironmentManagers } from './features/envManagers'; import { registerLogger, traceError, traceInfo } from './common/logging'; import { EnvManagerView } from './features/views/envManagersView'; import { - addPythonProject, + addPythonProjectCommand, createEnvironmentCommand, createTerminalCommand, getPackageCommandOptions, @@ -164,7 +164,7 @@ export async function activate(context: ExtensionContext): Promise { - await addPythonProject(resource, projectManager, envManagers, projectCreators); + await addPythonProjectCommand(resource, projectManager, envManagers, projectCreators); }), commands.registerCommand('python-envs.removePythonProject', async (item) => { await resetEnvironmentCommand(item, envManagers, projectManager); diff --git a/src/features/envCommands.ts b/src/features/envCommands.ts index 3417a31c..f9df3d32 100644 --- a/src/features/envCommands.ts +++ b/src/features/envCommands.ts @@ -13,19 +13,10 @@ import { PythonEnvironmentApi, PythonProject, PythonProjectCreator, + PythonProjectCreatorOptions, } from '../api'; -import * as path from 'path'; -import { - setEnvironmentManager, - setPackageManager, - addPythonProjectSetting, - removePythonProjectSetting, - getDefaultEnvManagerSetting, - getDefaultPkgManagerSetting, - EditProjectSettings, -} from './settings/settingHelpers'; - -import { getAbsolutePath } from '../common/utils/fileNameUtils'; +import { setEnvironmentManager, setPackageManager, removePythonProjectSetting } from './settings/settingHelpers'; + import { runAsTask } from './execution/runAsTask'; import { EnvManagerTreeItem, @@ -338,84 +329,58 @@ export async function setPackageManagerCommand(em: EnvironmentManagers, wm: Pyth } } -export async function addPythonProject( +/** + * Creates a new Python project using a selected PythonProjectCreator. + * + * This function calls create on the selected creator and handles the creation process. Will return + * without doing anything if the resource is a ProjectItem, as the project is already created. + * + * @param resource - The resource to use for project creation (can be a Uri, ProjectItem, or undefined). + * @param wm - The PythonProjectManager instance for managing projects. + * @param em - The EnvironmentManagers instance for managing environments. + * @param pc - The ProjectCreators instance for accessing available project creators. + * @returns A promise that resolves when the project has been created, or void if cancelled or invalid. + */ +export async function addPythonProjectCommand( resource: unknown, wm: PythonProjectManager, em: EnvironmentManagers, pc: ProjectCreators, -): Promise { +): Promise { if (wm.getProjects().length === 0) { showErrorMessage('Please open a folder/project before adding a workspace'); return; } + if (resource instanceof ProjectItem) { + // If the context is a ProjectItem, project is already created. + return; + } + let options: PythonProjectCreatorOptions | undefined; if (resource instanceof Uri) { - const uri = resource as Uri; - const envManagerId = getDefaultEnvManagerSetting(wm, uri); - const pkgManagerId = getDefaultPkgManagerSetting( - wm, - uri, - em.getEnvironmentManager(envManagerId)?.preferredPackageManagerId, - ); - const pw = wm.create(path.basename(uri.fsPath), uri); - await addPythonProjectSetting([{ project: pw, envManager: envManagerId, packageManager: pkgManagerId }]); - return pw; + // Use resource as the URI for the project if it is a URI. + options = { + name: resource.fsPath, + uri: resource, + }; } - if (resource === undefined || resource instanceof ProjectItem) { - const creator: PythonProjectCreator | undefined = await pickCreator(pc.getProjectCreators()); - if (!creator) { - return; - } - - let results: PythonProject | PythonProject[] | undefined; - try { - results = await creator.create(); - if (results === undefined) { - return; - } - } catch (ex) { - if (ex === QuickInputButtons.Back) { - return addPythonProject(resource, wm, em, pc); - } - throw ex; - } + const creator: PythonProjectCreator | undefined = await pickCreator(pc.getProjectCreators()); + if (!creator) { + return; + } - if (!Array.isArray(results)) { - results = [results]; + let results: PythonProject | PythonProject[] | undefined; + try { + results = await creator.create(options); + if (results === undefined) { + return; } - - if (Array.isArray(results)) { - if (results.length === 0) { - return; - } + } catch (ex) { + if (ex === QuickInputButtons.Back) { + return addPythonProjectCommand(resource, wm, em, pc); } - - const projects: PythonProject[] = []; - const edits: EditProjectSettings[] = []; - - for (const result of results) { - const uri = await getAbsolutePath(result.uri.fsPath); - if (!uri) { - traceError(`Path does not belong to any opened workspace: ${result.uri.fsPath}`); - continue; - } - - const envManagerId = getDefaultEnvManagerSetting(wm, uri); - const pkgManagerId = getDefaultPkgManagerSetting( - wm, - uri, - em.getEnvironmentManager(envManagerId)?.preferredPackageManagerId, - ); - const pw = wm.create(path.basename(uri.fsPath), uri); - projects.push(pw); - edits.push({ project: pw, envManager: envManagerId, packageManager: pkgManagerId }); - } - await addPythonProjectSetting(edits); - return projects; - } else { - // If the context is not a Uri or ProjectItem, rerun function with undefined context - await addPythonProject(undefined, wm, em, pc); + throw ex; } } diff --git a/src/features/projectManager.ts b/src/features/projectManager.ts index 0316f4fb..095be208 100644 --- a/src/features/projectManager.ts +++ b/src/features/projectManager.ts @@ -9,6 +9,12 @@ import { onDidChangeWorkspaceFolders, } from '../common/workspace.apis'; import { createSimpleDebounce } from '../common/utils/debounce'; +import { + addPythonProjectSetting, + EditProjectSettings, + getDefaultEnvManagerSetting, + getDefaultPkgManagerSetting, +} from './settings/settingHelpers'; type ProjectArray = PythonProject[]; @@ -92,14 +98,24 @@ export class PythonProjectManagerImpl implements PythonProjectManager { return new PythonProjectsImpl(name, uri, options); } - add(projects: PythonProject | ProjectArray): void { + async add(projects: PythonProject | ProjectArray): Promise { const _projects = Array.isArray(projects) ? projects : [projects]; if (_projects.length === 0) { return; } + const edits: EditProjectSettings[] = []; + + const envManagerId = getDefaultEnvManagerSetting(this); + const pkgManagerId = getDefaultPkgManagerSetting(this); - _projects.forEach((w) => this._projects.set(w.uri.toString(), w)); + _projects.forEach((w) => { + edits.push({ project: w, envManager: envManagerId, packageManager: pkgManagerId }); + return this._projects.set(w.uri.toString(), w); + }); this._onDidChangeProjects.fire(Array.from(this._projects.values())); + + // handle bulk edits to avoid multiple calls to the setting + await addPythonProjectSetting(edits); } remove(projects: PythonProject | ProjectArray): void { diff --git a/src/internal.api.ts b/src/internal.api.ts index 509d48ac..668fe3fa 100644 --- a/src/internal.api.ts +++ b/src/internal.api.ts @@ -284,7 +284,7 @@ export interface PythonProjectManager extends Disposable { uri: Uri, options?: { description?: string; tooltip?: string | MarkdownString; iconPath?: IconPath }, ): PythonProject; - add(pyWorkspace: PythonProject | PythonProject[]): void; + add(pyWorkspace: PythonProject | PythonProject[]): Promise; remove(pyWorkspace: PythonProject | PythonProject[]): void; getProjects(uris?: Uri[]): ReadonlyArray; get(uri: Uri): PythonProject | undefined; From b6edc9fb8e4c332ba08163ffdd823030f8d466cb Mon Sep 17 00:00:00 2001 From: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Thu, 24 Apr 2025 11:39:04 -0700 Subject: [PATCH 2/7] fix add --- src/features/envCommands.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/features/envCommands.ts b/src/features/envCommands.ts index f9df3d32..275909c9 100644 --- a/src/features/envCommands.ts +++ b/src/features/envCommands.ts @@ -352,7 +352,8 @@ export async function addPythonProjectCommand( return; } if (resource instanceof ProjectItem) { - // If the context is a ProjectItem, project is already created. + // If the context is a ProjectItem, project is already created. Just add it to the package manager project list. + wm.add(resource.project); return; } let options: PythonProjectCreatorOptions | undefined; From fd5d686810af0443a781fa7fdd15c947100aa76c Mon Sep 17 00:00:00 2001 From: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Thu, 24 Apr 2025 11:41:36 -0700 Subject: [PATCH 3/7] support arrays --- src/features/envCommands.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/features/envCommands.ts b/src/features/envCommands.ts index 275909c9..232f266b 100644 --- a/src/features/envCommands.ts +++ b/src/features/envCommands.ts @@ -335,7 +335,7 @@ export async function setPackageManagerCommand(em: EnvironmentManagers, wm: Pyth * This function calls create on the selected creator and handles the creation process. Will return * without doing anything if the resource is a ProjectItem, as the project is already created. * - * @param resource - The resource to use for project creation (can be a Uri, ProjectItem, or undefined). + * @param resource - The resource to use for project creation (can be a Uri(s), ProjectItem(s), or undefined). * @param wm - The PythonProjectManager instance for managing projects. * @param em - The EnvironmentManagers instance for managing environments. * @param pc - The ProjectCreators instance for accessing available project creators. @@ -351,6 +351,12 @@ export async function addPythonProjectCommand( showErrorMessage('Please open a folder/project before adding a workspace'); return; } + if (resource instanceof Array) { + for (const r of resource) { + await addPythonProjectCommand(r, wm, em, pc); + return; + } + } if (resource instanceof ProjectItem) { // If the context is a ProjectItem, project is already created. Just add it to the package manager project list. wm.add(resource.project); From 6b83ba6df1f8a8254ed1ce9b03b8fe5b50f2e5d6 Mon Sep 17 00:00:00 2001 From: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Thu, 24 Apr 2025 11:47:39 -0700 Subject: [PATCH 4/7] pm add in existing creators --- src/features/creators/autoFindProjects.ts | 11 +++++++---- src/features/creators/existingProjects.ts | 5 ++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/features/creators/autoFindProjects.ts b/src/features/creators/autoFindProjects.ts index 1ee144d1..2cd8d616 100644 --- a/src/features/creators/autoFindProjects.ts +++ b/src/features/creators/autoFindProjects.ts @@ -91,16 +91,19 @@ export class AutoFindProjects implements PythonProjectCreator { traceInfo(`Found ${filtered.length} new potential projects that aren't already registered`); - const projects = await pickProjects(filtered); - if (!projects || projects.length === 0) { + const projectUris = await pickProjects(filtered); + if (!projectUris || projectUris.length === 0) { // User cancelled the selection. traceInfo('User cancelled project selection.'); return; } - return projects.map((uri) => ({ + const projects = projectUris.map((uri) => ({ name: path.basename(uri.fsPath), uri, - })); + })) as PythonProject[]; + // Add the projects to the project manager + this.pm.add(projects); + return projects; } } diff --git a/src/features/creators/existingProjects.ts b/src/features/creators/existingProjects.ts index a3e5e223..bd95c7aa 100644 --- a/src/features/creators/existingProjects.ts +++ b/src/features/creators/existingProjects.ts @@ -88,10 +88,13 @@ export class ExistingProjects implements PythonProjectCreator { } return; } else { - return resultsInWorkspace.map((uri) => ({ + const projects = resultsInWorkspace.map((uri) => ({ name: path.basename(uri.fsPath), uri, })) as PythonProject[]; + // Add the projects to the project manager + this.pm.add(projects); + return projects; } } } From 73a4a46caa6aa64d66725139a39fde0a9372ca21 Mon Sep 17 00:00:00 2001 From: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Thu, 24 Apr 2025 11:53:54 -0700 Subject: [PATCH 5/7] conflict fixes --- src/extension.ts | 1 - src/features/envCommands.ts | 50 ++++++++++++++++++------------------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 4f69001c..113658d9 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -22,7 +22,6 @@ import { ExistingProjects } from './features/creators/existingProjects'; import { ProjectCreatorsImpl } from './features/creators/projectCreators'; import { addPythonProjectCommand, - addPythonProject, copyPathToClipboard, createAnyEnvironmentCommand, createEnvironmentCommand, diff --git a/src/features/envCommands.ts b/src/features/envCommands.ts index e742c90a..79965952 100644 --- a/src/features/envCommands.ts +++ b/src/features/envCommands.ts @@ -1,12 +1,4 @@ import { QuickInputButtons, TaskExecution, TaskRevealKind, Terminal, Uri } from 'vscode'; -import { - EnvironmentManagers, - InternalEnvironmentManager, - InternalPackageManager, - ProjectCreators, - PythonProjectManager, -} from '../internal.api'; -import { traceError, traceInfo, traceVerbose } from '../common/logging'; import { CreateEnvironmentOptions, PythonEnvironment, @@ -15,30 +7,38 @@ import { PythonProjectCreator, PythonProjectCreatorOptions, } from '../api'; -import { setEnvironmentManager, setPackageManager, removePythonProjectSetting } from './settings/settingHelpers'; +import { traceError, traceInfo, traceVerbose } from '../common/logging'; +import { + EnvironmentManagers, + InternalEnvironmentManager, + InternalPackageManager, + ProjectCreators, + PythonProjectManager, +} from '../internal.api'; +import { removePythonProjectSetting, setEnvironmentManager, setPackageManager } from './settings/settingHelpers'; +import { clipboardWriteText } from '../common/env.apis'; +import {} from '../common/errors/utils'; +import { pickEnvironment } from '../common/pickers/environments'; +import { pickCreator, pickEnvironmentManager, pickPackageManager } from '../common/pickers/managers'; +import { pickProject, pickProjectMany } from '../common/pickers/projects'; +import { activeTextEditor, showErrorMessage } from '../common/window.apis'; +import { quoteArgs } from './execution/execUtils'; import { runAsTask } from './execution/runAsTask'; +import { runInTerminal } from './terminal/runInTerminal'; +import { TerminalManager } from './terminal/terminalManager'; import { EnvManagerTreeItem, - PackageRootTreeItem, - PythonEnvTreeItem, - ProjectItem, - ProjectEnvironment, - ProjectPackageRootTreeItem, - GlobalProjectItem, EnvTreeItemKind, + GlobalProjectItem, + PackageRootTreeItem, PackageTreeItem, + ProjectEnvironment, + ProjectItem, ProjectPackage, + ProjectPackageRootTreeItem, + PythonEnvTreeItem, } from './views/treeViewItems'; -import { pickEnvironment } from '../common/pickers/environments'; -import { pickEnvironmentManager, pickPackageManager, pickCreator } from '../common/pickers/managers'; -import { pickProject, pickProjectMany } from '../common/pickers/projects'; -import { TerminalManager } from './terminal/terminalManager'; -import { runInTerminal } from './terminal/runInTerminal'; -import { quoteArgs } from './execution/execUtils'; -import {} from '../common/errors/utils'; -import { activeTextEditor, showErrorMessage } from '../common/window.apis'; -import { clipboardWriteText } from '../common/env.apis'; export async function refreshManagerCommand(context: unknown): Promise { if (context instanceof EnvManagerTreeItem) { @@ -368,7 +368,7 @@ export async function addPythonProjectCommand( // Use resource as the URI for the project if it is a URI. options = { name: resource.fsPath, - uri: resource, + rootUri: resource, }; } From fadaa4ede62fa0fa9ff1524b145d82e5f8f5787d Mon Sep 17 00:00:00 2001 From: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Thu, 24 Apr 2025 15:07:31 -0700 Subject: [PATCH 6/7] minor error --- src/features/envCommands.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/features/envCommands.ts b/src/features/envCommands.ts index 52038c14..858121cf 100644 --- a/src/features/envCommands.ts +++ b/src/features/envCommands.ts @@ -3,7 +3,6 @@ import { CreateEnvironmentOptions, PythonEnvironment, PythonEnvironmentApi, - PythonProject, PythonProjectCreator, PythonProjectCreatorOptions, } from '../api'; @@ -377,10 +376,8 @@ export async function addPythonProjectCommand( return; } - - let results: PythonProject | PythonProject[] | undefined; try { - results = await creator.create(options); + const results = await creator.create(options); if (results === undefined) { return; } From 3085488f7db1f705a7350a5f8c0e6d2de14e5c48 Mon Sep 17 00:00:00 2001 From: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Thu, 24 Apr 2025 15:08:00 -0700 Subject: [PATCH 7/7] fix return --- src/features/envCommands.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/features/envCommands.ts b/src/features/envCommands.ts index 858121cf..9ec9d7b9 100644 --- a/src/features/envCommands.ts +++ b/src/features/envCommands.ts @@ -377,10 +377,7 @@ export async function addPythonProjectCommand( } try { - const results = await creator.create(options); - if (results === undefined) { - return; - } + await creator.create(options); } catch (ex) { if (ex === QuickInputButtons.Back) { return addPythonProjectCommand(resource, wm, em, pc);