diff --git a/examples/sample1/src/api.ts b/examples/sample1/src/api.ts index 627cb96d..2cad951b 100644 --- a/examples/sample1/src/api.ts +++ b/examples/sample1/src/api.ts @@ -48,23 +48,6 @@ export interface PythonCommandRunConfiguration { args?: string[]; } -export enum TerminalShellType { - powershell = 'powershell', - powershellCore = 'powershellCore', - commandPrompt = 'commandPrompt', - gitbash = 'gitbash', - bash = 'bash', - zsh = 'zsh', - ksh = 'ksh', - fish = 'fish', - cshell = 'cshell', - tcshell = 'tcshell', - nushell = 'nushell', - wsl = 'wsl', - xonsh = 'xonsh', - unknown = 'unknown', -} - /** * Contains details on how to use a particular python environment * @@ -73,7 +56,7 @@ export enum TerminalShellType { * 2. If {@link PythonEnvironmentExecutionInfo.activatedRun} is not provided, then: * - If {@link PythonEnvironmentExecutionInfo.shellActivation} is provided and shell type is known, then that will be used. * - If {@link PythonEnvironmentExecutionInfo.shellActivation} is provided and shell type is not known, then: - * - {@link TerminalShellType.unknown} will be used if provided. + * - 'unknown' will be used if provided. * - {@link PythonEnvironmentExecutionInfo.activation} will be used otherwise. * - If {@link PythonEnvironmentExecutionInfo.shellActivation} is not provided, then {@link PythonEnvironmentExecutionInfo.activation} will be used. * - If {@link PythonEnvironmentExecutionInfo.activation} is not provided, then {@link PythonEnvironmentExecutionInfo.run} will be used. @@ -82,7 +65,7 @@ export enum TerminalShellType { * 1. If {@link PythonEnvironmentExecutionInfo.shellActivation} is provided and shell type is known, then that will be used. * 2. If {@link PythonEnvironmentExecutionInfo.shellActivation} is provided and shell type is not known, then {@link PythonEnvironmentExecutionInfo.activation} will be used. * 3. If {@link PythonEnvironmentExecutionInfo.shellActivation} is not provided, then: - * - {@link TerminalShellType.unknown} will be used if provided. + * - 'unknown' will be used if provided. * - {@link PythonEnvironmentExecutionInfo.activation} will be used otherwise. * 4. If {@link PythonEnvironmentExecutionInfo.activation} is not provided, then {@link PythonEnvironmentExecutionInfo.run} will be used. * @@ -107,11 +90,11 @@ export interface PythonEnvironmentExecutionInfo { /** * Details on how to activate an environment using a shell specific command. * If set this will override the {@link PythonEnvironmentExecutionInfo.activation}. - * {@link TerminalShellType.unknown} is used if shell type is not known. - * If {@link TerminalShellType.unknown} is not provided and shell type is not known then + * 'unknown' is used if shell type is not known. + * If 'unknown' is not provided and shell type is not known then * {@link PythonEnvironmentExecutionInfo.activation} if set. */ - shellActivation?: Map; + shellActivation?: Map; /** * Details on how to deactivate an environment. @@ -121,11 +104,11 @@ export interface PythonEnvironmentExecutionInfo { /** * Details on how to deactivate an environment using a shell specific command. * If set this will override the {@link PythonEnvironmentExecutionInfo.deactivation} property. - * {@link TerminalShellType.unknown} is used if shell type is not known. - * If {@link TerminalShellType.unknown} is not provided and shell type is not known then + * 'unknown' is used if shell type is not known. + * If 'unknown' is not provided and shell type is not known then * {@link PythonEnvironmentExecutionInfo.deactivation} if set. */ - shellDeactivation?: Map; + shellDeactivation?: Map; } /** @@ -592,20 +575,12 @@ export interface PackageManager { log?: LogOutputChannel; /** - * Installs packages in the specified Python environment. + * Installs/Uninstall packages in the specified Python environment. * @param environment - The Python environment in which to install packages. * @param packages - The packages to install. * @returns A promise that resolves when the installation is complete. */ - install(environment: PythonEnvironment, packages?: string[], options?: PackageInstallOptions): Promise; - - /** - * Uninstalls packages from the specified Python environment. - * @param environment - The Python environment from which to uninstall packages. - * @param packages - The packages to uninstall, which can be an array of packages or strings. - * @returns A promise that resolves when the uninstall is complete. - */ - uninstall(environment: PythonEnvironment, packages?: Package[] | string[]): Promise; + manage(environment: PythonEnvironment, options: PackageManagementOptions): Promise; /** * Refreshes the package list for the specified Python environment. @@ -730,15 +705,47 @@ export interface DidChangePythonProjectsEventArgs { removed: PythonProject[]; } -/** - * Options for package installation. - */ -export interface PackageInstallOptions { - /** - * Upgrade the packages if it is already installed. - */ - upgrade?: boolean; -} +export type PackageManagementOptions = + | { + /** + * Upgrade the packages if it is already installed. + */ + upgrade?: boolean; + + /** + * Show option to skip package installation + */ + showSkipOption?: boolean; + /** + * The list of packages to install. + */ + install: string[]; + + /** + * The list of packages to uninstall. + */ + uninstall?: string[]; + } + | { + /** + * Upgrade the packages if it is already installed. + */ + upgrade?: boolean; + + /** + * Show option to skip package installation + */ + showSkipOption?: boolean; + /** + * The list of packages to install. + */ + install?: string[]; + + /** + * The list of packages to uninstall. + */ + uninstall: string[]; + }; export interface PythonProcess { /** @@ -921,21 +928,13 @@ export interface PythonPackageItemApi { export interface PythonPackageManagementApi { /** - * Install packages into a Python Environment. + * Install/Uninstall packages into a Python Environment. * * @param environment The Python Environment into which packages are to be installed. * @param packages The packages to install. * @param options Options for installing packages. */ - installPackages(environment: PythonEnvironment, packages: string[], options?: PackageInstallOptions): Promise; - - /** - * Uninstall packages from a Python Environment. - * - * @param environment The Python Environment from which packages are to be uninstalled. - * @param packages The packages to uninstall. - */ - uninstallPackages(environment: PythonEnvironment, packages: PackageInfo[] | string[]): Promise; + managePackages(environment: PythonEnvironment, options: PackageManagementOptions): Promise; } export interface PythonPackageManagerApi diff --git a/package-lock.json b/package-lock.json index 65755ba0..346bf966 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "@types/node": "20.2.5", "@types/sinon": "^17.0.3", "@types/stack-trace": "0.0.29", - "@types/vscode": "^1.97.0", + "@types/vscode": "^1.99.0", "@types/which": "^3.0.4", "@typescript-eslint/eslint-plugin": "^8.16.0", "@typescript-eslint/parser": "^8.16.0", @@ -42,7 +42,7 @@ "webpack-cli": "^5.1.1" }, "engines": { - "vscode": "^1.99.0-20250317" + "vscode": "^1.100.0-20250407" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -715,9 +715,9 @@ "dev": true }, "node_modules/@types/vscode": { - "version": "1.97.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.97.0.tgz", - "integrity": "sha512-ueE73loeOTe7olaVyqP9mrRI54kVPJifUPjblZo9fYcv1CuVLPOEKEkqW0GkqPC454+nCEoigLWnC2Pp7prZ9w==", + "version": "1.99.1", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.99.1.tgz", + "integrity": "sha512-cQlqxHZ040ta6ovZXnXRxs3fJiTmlurkIWOfZVcLSZPcm9J4ikFpXuB7gihofGn5ng+kDVma5EmJIclfk0trPQ==", "dev": true, "license": "MIT" }, @@ -5997,9 +5997,9 @@ "dev": true }, "@types/vscode": { - "version": "1.97.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.97.0.tgz", - "integrity": "sha512-ueE73loeOTe7olaVyqP9mrRI54kVPJifUPjblZo9fYcv1CuVLPOEKEkqW0GkqPC454+nCEoigLWnC2Pp7prZ9w==", + "version": "1.99.1", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.99.1.tgz", + "integrity": "sha512-cQlqxHZ040ta6ovZXnXRxs3fJiTmlurkIWOfZVcLSZPcm9J4ikFpXuB7gihofGn5ng+kDVma5EmJIclfk0trPQ==", "dev": true }, "@types/which": { diff --git a/package.json b/package.json index ce1be620..f552f1fc 100644 --- a/package.json +++ b/package.json @@ -6,13 +6,12 @@ "publisher": "ms-python", "preview": true, "engines": { - "vscode": "^1.99.0-20250317" + "vscode": "^1.100.0-20250407" }, "categories": [ "Other" ], "enabledApiProposals": [ - "terminalShellType", "terminalShellEnv" ], "capabilities": { @@ -142,7 +141,6 @@ { "command": "python-envs.reset", "title": "%python-envs.reset.title%", - "shortTitle": "Reset Environment", "category": "Python", "icon": "$(sync)" }, @@ -174,7 +172,6 @@ { "command": "python-envs.packages", "title": "%python-envs.packages.title%", - "shortTitle": "Modify Packages", "category": "Python", "icon": "$(package)" }, @@ -534,7 +531,7 @@ "@types/node": "20.2.5", "@types/sinon": "^17.0.3", "@types/stack-trace": "0.0.29", - "@types/vscode": "^1.97.0", + "@types/vscode": "^1.99.0", "@types/which": "^3.0.4", "@typescript-eslint/eslint-plugin": "^8.16.0", "@typescript-eslint/parser": "^8.16.0", diff --git a/package.nls.json b/package.nls.json index f7547a6e..f170c241 100644 --- a/package.nls.json +++ b/package.nls.json @@ -16,12 +16,12 @@ "python-envs.createAny.title": "Create Environment", "python-envs.set.title": "Set Workspace Environment", "python-envs.setEnv.title": "Set As Workspace Environment", - "python-envs.reset.title": "Reset Environment Selection to Default", + "python-envs.reset.title": "Reset to Default", "python-envs.remove.title": "Delete Environment", "python-envs.refreshAllManagers.title": "Refresh All Environment Managers", "python-envs.refreshManager.title": "Refresh Environments List", "python-envs.refreshPackages.title": "Refresh Packages List", - "python-envs.packages.title": "Install or Remove Packages", + "python-envs.packages.title": "Manage Packages", "python-envs.clearCache.title": "Clear Cache", "python-envs.runInTerminal.title": "Run in Terminal", "python-envs.createTerminal.title": "Create Python Terminal", diff --git a/src/api.ts b/src/api.ts index 50e804ea..2cad951b 100644 --- a/src/api.ts +++ b/src/api.ts @@ -575,20 +575,12 @@ export interface PackageManager { log?: LogOutputChannel; /** - * Installs packages in the specified Python environment. + * Installs/Uninstall packages in the specified Python environment. * @param environment - The Python environment in which to install packages. * @param packages - The packages to install. * @returns A promise that resolves when the installation is complete. */ - install(environment: PythonEnvironment, packages?: string[], options?: PackageInstallOptions): Promise; - - /** - * Uninstalls packages from the specified Python environment. - * @param environment - The Python environment from which to uninstall packages. - * @param packages - The packages to uninstall, which can be an array of packages or strings. - * @returns A promise that resolves when the uninstall is complete. - */ - uninstall(environment: PythonEnvironment, packages?: Package[] | string[]): Promise; + manage(environment: PythonEnvironment, options: PackageManagementOptions): Promise; /** * Refreshes the package list for the specified Python environment. @@ -713,20 +705,47 @@ export interface DidChangePythonProjectsEventArgs { removed: PythonProject[]; } -/** - * Options for package installation. - */ -export interface PackageInstallOptions { - /** - * Upgrade the packages if it is already installed. - */ - upgrade?: boolean; +export type PackageManagementOptions = + | { + /** + * Upgrade the packages if it is already installed. + */ + upgrade?: boolean; - /** - * Show option to skip package installation - */ - showSkipOption?: boolean; -} + /** + * Show option to skip package installation + */ + showSkipOption?: boolean; + /** + * The list of packages to install. + */ + install: string[]; + + /** + * The list of packages to uninstall. + */ + uninstall?: string[]; + } + | { + /** + * Upgrade the packages if it is already installed. + */ + upgrade?: boolean; + + /** + * Show option to skip package installation + */ + showSkipOption?: boolean; + /** + * The list of packages to install. + */ + install?: string[]; + + /** + * The list of packages to uninstall. + */ + uninstall: string[]; + }; export interface PythonProcess { /** @@ -909,21 +928,13 @@ export interface PythonPackageItemApi { export interface PythonPackageManagementApi { /** - * Install packages into a Python Environment. + * Install/Uninstall packages into a Python Environment. * * @param environment The Python Environment into which packages are to be installed. * @param packages The packages to install. * @param options Options for installing packages. */ - installPackages(environment: PythonEnvironment, packages: string[], options?: PackageInstallOptions): Promise; - - /** - * Uninstall packages from a Python Environment. - * - * @param environment The Python Environment from which packages are to be uninstalled. - * @param packages The packages to uninstall. - */ - uninstallPackages(environment: PythonEnvironment, packages: PackageInfo[] | string[]): Promise; + managePackages(environment: PythonEnvironment, options: PackageManagementOptions): Promise; } export interface PythonPackageManagerApi diff --git a/src/common/localize.ts b/src/common/localize.ts index 57df9447..755508c3 100644 --- a/src/common/localize.ts +++ b/src/common/localize.ts @@ -20,10 +20,14 @@ export namespace Interpreter { } export namespace PackageManagement { + export const install = l10n.t('Install'); + export const uninstall = l10n.t('Uninstall'); + export const installed = l10n.t('Installed'); + export const commonPackages = l10n.t('Common Packages'); export const selectPackagesToInstall = l10n.t('Select packages to install'); export const enterPackageNames = l10n.t('Enter package names'); - export const commonPackages = l10n.t('Search common `PyPI` packages'); - export const commonPackagesDescription = l10n.t('Search and Install common `PyPI` packages'); + export const searchCommonPackages = l10n.t('Search common `PyPI` packages'); + export const searchCommonPackagesDescription = l10n.t('Search and Install common `PyPI` packages'); export const workspaceDependencies = l10n.t('Install workspace dependencies'); export const workspaceDependenciesDescription = l10n.t('Install dependencies found in the current workspace.'); export const selectPackagesToUninstall = l10n.t('Select packages to uninstall'); @@ -40,8 +44,6 @@ export namespace Pickers { export namespace Packages { export const selectOption = l10n.t('Select an option'); - export const installPackages = l10n.t('Install packages'); - export const uninstallPackages = l10n.t('Uninstall packages'); } export namespace Managers { diff --git a/src/common/pickers/packages.ts b/src/common/pickers/packages.ts deleted file mode 100644 index 80102248..00000000 --- a/src/common/pickers/packages.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Common, Pickers } from '../localize'; -import { showQuickPick } from '../window.apis'; - -export async function pickPackageOptions(): Promise { - const items = [ - { - label: Common.install, - description: Pickers.Packages.installPackages, - }, - { - label: Common.uninstall, - description: Pickers.Packages.uninstallPackages, - }, - ]; - const selected = await showQuickPick(items, { - placeHolder: Pickers.Packages.selectOption, - ignoreFocusOut: true, - matchOnDescription: false, - matchOnDetail: false, - }); - return selected?.label; -} diff --git a/src/extension.ts b/src/extension.ts index ac5a1b34..004a9832 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -8,7 +8,6 @@ import { createEnvironmentCommand, createTerminalCommand, getPackageCommandOptions, - handlePackagesCommand, refreshManagerCommand, removeEnvironmentCommand, removePythonProject, @@ -138,7 +137,7 @@ export async function activate(context: ExtensionContext): Promise { await handlePackageUninstall(context, envManagers); diff --git a/src/features/envCommands.ts b/src/features/envCommands.ts index 9c3d413a..6838c0a0 100644 --- a/src/features/envCommands.ts +++ b/src/features/envCommands.ts @@ -33,10 +33,8 @@ import { PackageTreeItem, ProjectPackage, } from './views/treeViewItems'; -import { Common } from '../common/localize'; import { pickEnvironment } from '../common/pickers/environments'; import { pickEnvironmentManager, pickPackageManager, pickCreator } from '../common/pickers/managers'; -import { pickPackageOptions } from '../common/pickers/packages'; import { pickProject, pickProjectMany } from '../common/pickers/projects'; import { TerminalManager } from './terminal/terminalManager'; import { runInTerminal } from './terminal/runInTerminal'; @@ -180,32 +178,12 @@ export async function removeEnvironmentCommand(context: unknown, managers: Envir } } -export async function handlePackagesCommand( - packageManager: InternalPackageManager, - environment: PythonEnvironment, -): Promise { - const action = await pickPackageOptions(); - - try { - if (action === Common.install) { - await packageManager.install(environment, undefined, { showSkipOption: false }); - } else if (action === Common.uninstall) { - await packageManager.uninstall(environment); - } - } catch (ex) { - if (ex === QuickInputButtons.Back) { - return handlePackagesCommand(packageManager, environment); - } - throw ex; - } -} - export async function handlePackageUninstall(context: unknown, em: EnvironmentManagers) { if (context instanceof PackageTreeItem || context instanceof ProjectPackage) { const moduleName = context.pkg.name; const environment = context.parent.environment; const packageManager = em.getPackageManager(environment); - await packageManager?.uninstall(environment, [moduleName]); + await packageManager?.manage(environment, { uninstall: [moduleName], install: [] }); return; } traceError(`Invalid context for uninstall command: ${typeof context}`); diff --git a/src/features/pythonApi.ts b/src/features/pythonApi.ts index 780413ef..53aad43a 100644 --- a/src/features/pythonApi.ts +++ b/src/features/pythonApi.ts @@ -21,7 +21,7 @@ import { PackageId, PythonProjectCreator, ResolveEnvironmentContext, - PackageInstallOptions, + PackageManagementOptions, PythonProcess, PythonTaskExecutionOptions, PythonTerminalExecutionOptions, @@ -216,19 +216,12 @@ class PythonEnvironmentApiImpl implements PythonEnvironmentApi { } return new Disposable(() => disposables.forEach((d) => d.dispose())); } - installPackages(context: PythonEnvironment, packages: string[], options: PackageInstallOptions): Promise { + managePackages(context: PythonEnvironment, options: PackageManagementOptions): Promise { const manager = this.envManagers.getPackageManager(context); if (!manager) { return Promise.reject(new Error('No package manager found')); } - return manager.install(context, packages, options); - } - uninstallPackages(context: PythonEnvironment, packages: Package[] | string[]): Promise { - const manager = this.envManagers.getPackageManager(context); - if (!manager) { - return Promise.reject(new Error('No package manager found')); - } - return manager.uninstall(context, packages); + return manager.manage(context, options); } refreshPackages(context: PythonEnvironment): Promise { const manager = this.envManagers.getPackageManager(context); diff --git a/src/internal.api.ts b/src/internal.api.ts index fb38d49d..2a45c6dd 100644 --- a/src/internal.api.ts +++ b/src/internal.api.ts @@ -22,7 +22,7 @@ import { PackageInfo, PythonProjectCreator, ResolveEnvironmentContext, - PackageInstallOptions, + PackageManagementOptions, EnvironmentGroupInfo, } from './api'; import { CreateEnvironmentNotSupported, RemoveEnvironmentNotSupported } from './common/errors/NotSupportedError'; @@ -225,15 +225,14 @@ export class InternalPackageManager implements PackageManager { return this.manager.log; } - install(environment: PythonEnvironment, packages?: string[], options?: PackageInstallOptions): Promise { - return this.manager.install(environment, packages, options); - } - uninstall(environment: PythonEnvironment, packages?: Package[] | string[]): Promise { - return this.manager.uninstall(environment, packages); + manage(environment: PythonEnvironment, options: PackageManagementOptions): Promise { + return this.manager.manage(environment, options); } + refresh(environment: PythonEnvironment): Promise { return this.manager.refresh(environment); } + getPackages(environment: PythonEnvironment): Promise { return this.manager.getPackages(environment); } @@ -241,6 +240,7 @@ export class InternalPackageManager implements PackageManager { onDidChangePackages(handler: (e: DidChangePackagesEventArgs) => void): Disposable { return this.manager.onDidChangePackages ? this.manager.onDidChangePackages(handler) : new Disposable(() => {}); } + equals(other: PackageManager): boolean { return this.manager === other; } diff --git a/src/managers/builtin/pipManager.ts b/src/managers/builtin/pipManager.ts index 45753d81..93cf55ae 100644 --- a/src/managers/builtin/pipManager.ts +++ b/src/managers/builtin/pipManager.ts @@ -4,16 +4,15 @@ import { IconPath, Package, PackageChangeKind, - PackageInstallOptions, + PackageManagementOptions, PackageManager, PythonEnvironment, PythonEnvironmentApi, } from '../../api'; -import { installPackages, refreshPackages, uninstallPackages } from './utils'; +import { managePackages, refreshPackages } from './utils'; import { Disposable } from 'vscode-jsonrpc'; import { VenvManager } from './venvManager'; import { getWorkspacePackagesToInstall } from './pipUtils'; -import { getPackagesToUninstall } from '../common/utils'; function getChanges(before: Package[], after: Package[]): { kind: PackageChangeKind; pkg: Package }[] { const changes: { kind: PackageChangeKind; pkg: Package }[] = []; @@ -49,76 +48,43 @@ export class PipPackageManager implements PackageManager, Disposable { readonly tooltip?: string | MarkdownString; readonly iconPath?: IconPath; - async install(environment: PythonEnvironment, packages?: string[], options?: PackageInstallOptions): Promise { - let selected: string[] = packages ?? []; + async manage(environment: PythonEnvironment, options: PackageManagementOptions): Promise { + let toInstall: string[] = [...(options.install ?? [])]; + let toUninstall: string[] = [...(options.uninstall ?? [])]; - if (selected.length === 0) { + if (toInstall.length === 0 && toUninstall.length === 0) { const projects = this.venv.getProjectsByEnvironment(environment); - selected = (await getWorkspacePackagesToInstall(this.api, options, projects, environment)) ?? []; - } - - if (selected.length === 0) { - return; - } - - const installOptions = options ?? { upgrade: false }; - await window.withProgress( - { - location: ProgressLocation.Notification, - title: 'Installing packages', - cancellable: true, - }, - async (_progress, token) => { - try { - const before = this.packages.get(environment.envId.id) ?? []; - const after = await installPackages(environment, selected, installOptions, this.api, this, token); - const changes = getChanges(before, after); - this.packages.set(environment.envId.id, after); - this._onDidChangePackages.fire({ environment, manager: this, changes }); - } catch (e) { - this.log.error('Error installing packages', e); - setImmediate(async () => { - const result = await window.showErrorMessage('Error installing packages', 'View Output'); - if (result === 'View Output') { - this.log.show(); - } - }); - } - }, - ); - } - - async uninstall(environment: PythonEnvironment, packages?: Package[] | string[]): Promise { - let selected: Package[] | string[] = packages ?? []; - if (selected.length === 0) { - const installed = await this.getPackages(environment); - if (!installed) { + const result = await getWorkspacePackagesToInstall(this.api, options, projects, environment); + if (result) { + toInstall = result.install; + toUninstall = result.uninstall; + } else { return; } - selected = (await getPackagesToUninstall(installed)) ?? []; - } - - if (selected.length === 0) { - return; } + const manageOptions = { + ...options, + install: toInstall, + uninstall: toUninstall, + }; await window.withProgress( { location: ProgressLocation.Notification, - title: 'Uninstalling packages', + title: 'Installing packages', cancellable: true, }, async (_progress, token) => { try { const before = this.packages.get(environment.envId.id) ?? []; - const after = await uninstallPackages(environment, this.api, this, selected, token); + const after = await managePackages(environment, manageOptions, this.api, this, token); const changes = getChanges(before, after); this.packages.set(environment.envId.id, after); - this._onDidChangePackages.fire({ environment: environment, manager: this, changes }); + this._onDidChangePackages.fire({ environment, manager: this, changes }); } catch (e) { - this.log.error('Error uninstalling packages', e); + this.log.error('Error managing packages', e); setImmediate(async () => { - const result = await window.showErrorMessage('Error installing packages', 'View Output'); + const result = await window.showErrorMessage('Error managing packages', 'View Output'); if (result === 'View Output') { this.log.show(); } diff --git a/src/managers/builtin/pipUtils.ts b/src/managers/builtin/pipUtils.ts index 3bbd6c87..c6912ab1 100644 --- a/src/managers/builtin/pipUtils.ts +++ b/src/managers/builtin/pipUtils.ts @@ -4,11 +4,12 @@ import * as tomljs from '@iarna/toml'; import { LogOutputChannel, ProgressLocation, QuickInputButtons, Uri } from 'vscode'; import { showQuickPickWithButtons, withProgress } from '../../common/window.apis'; import { PackageManagement, Pickers, VenvManagerStrings } from '../../common/localize'; -import { PackageInstallOptions, PythonEnvironment, PythonEnvironmentApi, PythonProject } from '../../api'; +import { PackageManagementOptions, PythonEnvironment, PythonEnvironmentApi, PythonProject } from '../../api'; import { findFiles } from '../../common/workspace.apis'; import { EXTENSION_ROOT_DIR } from '../../common/constants'; -import { Installable, selectFromCommonPackagesToInstall, selectFromInstallableToInstall } from '../common/pickers'; +import { selectFromCommonPackagesToInstall, selectFromInstallableToInstall } from '../common/pickers'; import { traceInfo } from '../../common/logging'; +import { Installable, mergePackages } from '../common/utils'; async function tomlParse(fsPath: string, log?: LogOutputChannel): Promise { try { @@ -76,8 +77,8 @@ async function selectWorkspaceOrCommon( installable: Installable[], common: Installable[], showSkipOption: boolean, - installed?: string[], -): Promise { + installed: string[], +): Promise { if (installable.length === 0 && common.length === 0) { return undefined; } @@ -92,8 +93,8 @@ async function selectWorkspaceOrCommon( if (common.length > 0) { items.push({ - label: PackageManagement.commonPackages, - description: PackageManagement.commonPackagesDescription, + label: PackageManagement.searchCommonPackages, + description: PackageManagement.searchCommonPackagesDescription, }); } @@ -115,8 +116,9 @@ async function selectWorkspaceOrCommon( if (selected && !Array.isArray(selected)) { try { if (selected.label === PackageManagement.workspaceDependencies) { - return await selectFromInstallableToInstall(installable); - } else if (selected.label === PackageManagement.commonPackages) { + const installArgs = await selectFromInstallableToInstall(installable); + return { install: installArgs ?? [], uninstall: [] }; + } else if (selected.label === PackageManagement.searchCommonPackages) { return await selectFromCommonPackagesToInstall(common, installed); } else { traceInfo('Package Installer: user selected skip package installation'); @@ -125,26 +127,32 @@ async function selectWorkspaceOrCommon( // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (ex: any) { if (ex === QuickInputButtons.Back) { - return selectWorkspaceOrCommon(installable, common, showSkipOption); + return selectWorkspaceOrCommon(installable, common, showSkipOption, installed); } } } return undefined; } +export interface PipPackages { + install: string[]; + uninstall: string[]; +} + export async function getWorkspacePackagesToInstall( api: PythonEnvironmentApi, - options?: PackageInstallOptions, + options: PackageManagementOptions, project?: PythonProject[], environment?: PythonEnvironment, -): Promise { +): Promise { const installable = (await getProjectInstallable(api, project)) ?? []; - const common = await getCommonPackages(); + let common = await getCommonPackages(); let installed: string[] | undefined; if (environment) { installed = (await api.getPackages(environment))?.map((pkg) => pkg.name); + common = mergePackages(common, installed ?? []); } - return selectWorkspaceOrCommon(installable, common, !!options?.showSkipOption, installed); + return selectWorkspaceOrCommon(installable, common, !!options.showSkipOption, installed ?? []); } export async function getProjectInstallable( diff --git a/src/managers/builtin/utils.ts b/src/managers/builtin/utils.ts index fab88a79..9c19e8a5 100644 --- a/src/managers/builtin/utils.ts +++ b/src/managers/builtin/utils.ts @@ -2,7 +2,7 @@ import { CancellationToken, l10n, LogOutputChannel, QuickPickItem, ThemeIcon, Ur import { EnvironmentManager, Package, - PackageInstallOptions, + PackageManagementOptions, PackageManager, PythonEnvironment, PythonEnvironmentApi, @@ -173,10 +173,9 @@ export async function refreshPackages( return parsePipList(data).map((pkg) => api.createPackageItem(pkg, environment, manager)); } -export async function installPackages( +export async function managePackages( environment: PythonEnvironment, - packages: string[], - options: PackageInstallOptions, + options: PackageManagementOptions, api: PythonEnvironmentApi, manager: PackageManager, token?: CancellationToken, @@ -185,73 +184,36 @@ export async function installPackages( throw new Error('Python 2.* is not supported (deprecated)'); } - if (environment.execInfo) { - if (packages.length === 0) { - throw new Error('No packages selected to install'); - } - - const useUv = await isUvInstalled(); - - const installArgs = ['pip', 'install']; - if (options.upgrade) { - installArgs.push('--upgrade'); - } + const useUv = await isUvInstalled(); + const uninstallArgs = ['pip', 'uninstall']; + if (options.uninstall && options.uninstall.length > 0) { if (useUv) { await runUV( - [...installArgs, '--python', environment.execInfo.run.executable, ...packages], + [...uninstallArgs, '--python', environment.execInfo.run.executable, ...options.uninstall], undefined, manager.log, token, ); } else { + uninstallArgs.push('--yes'); await runPython( environment.execInfo.run.executable, - ['-m', ...installArgs, ...packages], + ['-m', ...uninstallArgs, ...options.uninstall], undefined, manager.log, token, ); } - - return refreshPackages(environment, api, manager); } - throw new Error(`No executable found for python: ${environment.environmentPath.fsPath}`); -} -export async function uninstallPackages( - environment: PythonEnvironment, - api: PythonEnvironmentApi, - manager: PackageManager, - packages: string[] | Package[], - token?: CancellationToken, -): Promise { - if (environment.version.startsWith('2.')) { - throw new Error('Python 2.* is not supported (deprecated)'); + const installArgs = ['pip', 'install']; + if (options.upgrade) { + installArgs.push('--upgrade'); } - - if (environment.execInfo) { - const remove = []; - for (let pkg of packages) { - if (typeof pkg === 'string') { - remove.push(pkg); - } else { - remove.push(pkg.name); - } - } - if (remove.length === 0) { - const installed = await manager.getPackages(environment); - if (installed) { - const packages = await pickPackages(true, installed); - if (packages.length === 0) { - throw new Error('No packages selected to uninstall'); - } - } - } - - const useUv = await isUvInstalled(); + if (options.install && options.install.length > 0) { if (useUv) { await runUV( - ['pip', 'uninstall', '--python', environment.execInfo.run.executable, ...remove], + [...installArgs, '--python', environment.execInfo.run.executable, ...options.install], undefined, manager.log, token, @@ -259,15 +221,15 @@ export async function uninstallPackages( } else { await runPython( environment.execInfo.run.executable, - ['-m', 'pip', 'uninstall', '-y', ...remove], + ['-m', ...installArgs, ...options.install], undefined, manager.log, token, ); } - return refreshPackages(environment, api, manager); } - throw new Error(`No executable found for python: ${environment.environmentPath.fsPath}`); + + return refreshPackages(environment, api, manager); } export async function resolveSystemPythonEnvironmentPath( diff --git a/src/managers/builtin/venvUtils.ts b/src/managers/builtin/venvUtils.ts index fd279732..9e4b8a2f 100644 --- a/src/managers/builtin/venvUtils.ts +++ b/src/managers/builtin/venvUtils.ts @@ -31,7 +31,7 @@ import { import { showErrorMessage } from '../../common/errors/utils'; import { Common, VenvManagerStrings } from '../../common/localize'; import { isUvInstalled, runUV, runPython } from './helpers'; -import { getProjectInstallable, getWorkspacePackagesToInstall } from './pipUtils'; +import { getProjectInstallable, getWorkspacePackagesToInstall, PipPackages } from './pipUtils'; import { sendTelemetryEvent } from '../../common/telemetry/sender'; import { EventNames } from '../../common/telemetry/constants'; @@ -384,7 +384,7 @@ async function createWithProgress( basePython: PythonEnvironment, venvRoot: Uri, envPath: string, - packages?: string[], + packages?: PipPackages, ) { const pythonPath = os.platform() === 'win32' ? path.join(envPath, 'Scripts', 'python.exe') : path.join(envPath, 'bin', 'python'); @@ -420,8 +420,12 @@ async function createWithProgress( const resolved = await nativeFinder.resolve(pythonPath); const env = api.createPythonEnvironmentItem(await getPythonInfo(resolved), manager); - if (packages && packages?.length > 0) { - await api.installPackages(env, packages, { upgrade: false }); + if (packages && (packages.install.length > 0 || packages.uninstall.length > 0)) { + await api.managePackages(env, { + upgrade: false, + install: packages?.install, + uninstall: packages?.uninstall ?? [], + }); } return env; } catch (e) { @@ -474,7 +478,7 @@ export async function createPythonVenv( sortedEnvs[0], venvRoot, path.join(venvRoot.fsPath, '.venv'), - installables?.flatMap((i) => i.args ?? []), + { install: installables?.flatMap((i) => i.args ?? []), uninstall: [] }, ); } else { sendTelemetryEvent(EventNames.VENV_CREATION, undefined, { creationType: 'custom' }); @@ -508,7 +512,7 @@ export async function createPythonVenv( const packages = await getWorkspacePackagesToInstall( api, - { showSkipOption: true }, + { showSkipOption: true, install: [] }, project ? [project] : undefined, ); diff --git a/src/managers/common/pickers.ts b/src/managers/common/pickers.ts index 81499562..beb099a4 100644 --- a/src/managers/common/pickers.ts +++ b/src/managers/common/pickers.ts @@ -2,6 +2,7 @@ import { QuickInputButtons, QuickPickItem, QuickPickItemButtonEvent, QuickPickIt import { Common, PackageManagement } from '../../common/localize'; import { launchBrowser } from '../../common/env.apis'; import { showInputBoxWithButtons, showQuickPickWithButtons, showTextDocument } from '../../common/window.apis'; +import { Installable } from './utils'; const OPEN_BROWSER_BUTTON = { iconPath: new ThemeIcon('globe'), @@ -18,50 +19,6 @@ const EDIT_ARGUMENTS_BUTTON = { tooltip: PackageManagement.editArguments, }; -export interface Installable { - /** - * The name of the package, requirements, lock files, or step name. - */ - readonly name: string; - - /** - * The name of the package, requirements, pyproject.toml or any other project file, etc. - */ - readonly displayName: string; - - /** - * Arguments passed to the package manager to install the package. - * - * @example - * ['debugpy==1.8.7'] for `pip install debugpy==1.8.7`. - * ['--pre', 'debugpy'] for `pip install --pre debugpy`. - * ['-r', 'requirements.txt'] for `pip install -r requirements.txt`. - */ - readonly args?: string[]; - - /** - * Installable group name, this will be used to group installable items in the UI. - * - * @example - * `Requirements` for any requirements file. - * `Packages` for any package. - */ - readonly group?: string; - - /** - * Description about the installable item. This can also be path to the requirements, - * version of the package, or any other project file path. - */ - readonly description?: string; - - /** - * External Uri to the package on pypi or docs. - * @example - * https://pypi.org/project/debugpy/ for `debugpy`. - */ - readonly uri?: Uri; -} - function handleItemButton(uri?: Uri) { if (uri) { if (uri.scheme.toLowerCase().startsWith('http')) { @@ -117,20 +74,67 @@ async function enterPackageManually(filler?: string): Promise { + if (installed?.find((p) => i.id === p)) { + installedItems.push(i); + } else { + result.push(i); + } + }); + const installedSeparator: PackageQuickPickItem = { + id: 'installed-sep', + label: PackageManagement.installed, + kind: QuickPickItemKind.Separator, + }; + const commonPackages: PackageQuickPickItem = { + id: 'common-packages-sep', + label: PackageManagement.commonPackages, + kind: QuickPickItemKind.Separator, + }; + return { + items: [installedSeparator, ...installedItems, commonPackages, ...result], + installedItems, + }; +} + +export interface CommonPackagesResult { + install: string[]; + uninstall: string[]; +} + +function selectionsToResult(selections: string[], installed: string[]): CommonPackagesResult { + const install: string[] = selections; + const uninstall: string[] = []; + installed.forEach((i) => { + if (!selections.find((s) => i === s)) { + uninstall.push(i); + } + }); + return { + install, + uninstall, + }; +} + export async function selectFromCommonPackagesToInstall( common: Installable[], - installed?: string[], + installed: string[], preSelected?: PackageQuickPickItem[] | undefined, -): Promise { - const items: PackageQuickPickItem[] = common.map(installableToQuickPickItem); - const preSelectedItems = items - .filter((i) => i.kind !== QuickPickItemKind.Separator) - .filter((i) => installed?.find((p) => i.id === p) || preSelected?.find((s) => s.id === i.id)); - +): Promise { + const { installedItems, items } = groupByInstalled(common.map(installableToQuickPickItem), installed); + const preSelectedItems = items.filter((i) => (preSelected ?? installedItems).some((s) => s.id === i.id)); let selected: PackageQuickPickItem | PackageQuickPickItem[] | undefined; try { selected = await showQuickPickWithButtons( - items, + items as PackageQuickPickItem[], { placeHolder: PackageManagement.selectPackagesToInstall, ignoreFocusOut: true, @@ -163,21 +167,25 @@ export async function selectFromCommonPackagesToInstall( if (selected && Array.isArray(selected)) { if (selected.find((s) => s.label === PackageManagement.enterPackageNames)) { - const filler = selected - .filter((s) => s.label !== PackageManagement.enterPackageNames) - .map((s) => s.id) - .join(' '); + const filtered = selected.filter((s) => s.label !== PackageManagement.enterPackageNames); + const filler = filtered.map((s) => s.id).join(' '); try { - const result = await enterPackageManually(filler); - return result; + const selections = await enterPackageManually(filler); + if (selections) { + return selectionsToResult(selections, installed); + } + return undefined; } catch (ex) { if (ex === QuickInputButtons.Back) { - return selectFromCommonPackagesToInstall(common, installed, selected); + return selectFromCommonPackagesToInstall(common, installed, filtered); } return undefined; } } else { - return selected.map((s) => s.id); + return selectionsToResult( + selected.map((s) => s.id), + installed, + ); } } } diff --git a/src/managers/common/utils.ts b/src/managers/common/utils.ts index 500ce19b..11bcd9c0 100644 --- a/src/managers/common/utils.ts +++ b/src/managers/common/utils.ts @@ -1,7 +1,50 @@ import * as os from 'os'; -import { Package, PythonEnvironment } from '../../api'; -import { showQuickPick } from '../../common/window.apis'; -import { PackageManagement } from '../../common/localize'; +import { PythonEnvironment } from '../../api'; +import { Uri } from 'vscode'; + +export interface Installable { + /** + * The name of the package, requirements, lock files, or step name. + */ + readonly name: string; + + /** + * The name of the package, requirements, pyproject.toml or any other project file, etc. + */ + readonly displayName: string; + + /** + * Arguments passed to the package manager to install the package. + * + * @example + * ['debugpy==1.8.7'] for `pip install debugpy==1.8.7`. + * ['--pre', 'debugpy'] for `pip install --pre debugpy`. + * ['-r', 'requirements.txt'] for `pip install -r requirements.txt`. + */ + readonly args?: string[]; + + /** + * Installable group name, this will be used to group installable items in the UI. + * + * @example + * `Requirements` for any requirements file. + * `Packages` for any package. + */ + readonly group?: string; + + /** + * Description about the installable item. This can also be path to the requirements, + * version of the package, or any other project file path. + */ + readonly description?: string; + + /** + * External Uri to the package on pypi or docs. + * @example + * https://pypi.org/project/debugpy/ for `debugpy`. + */ + readonly uri?: Uri; +} export function isWindows(): boolean { return process.platform === 'win32'; @@ -89,16 +132,9 @@ export function getLatest(collection: PythonEnvironment[]): PythonEnvironment | return latest; } -export async function getPackagesToUninstall(packages: Package[]): Promise { - const items = packages.map((p) => ({ - label: p.name, - description: p.version, - p, - })); - const selected = await showQuickPick(items, { - placeHolder: PackageManagement.selectPackagesToUninstall, - ignoreFocusOut: true, - canPickMany: true, - }); - return Array.isArray(selected) ? selected?.map((s) => s.p) : undefined; +export function mergePackages(common: Installable[], installed: string[]): Installable[] { + const notInCommon = installed.filter((pkg) => !common.some((c) => c.name === pkg)); + return common + .concat(notInCommon.map((pkg) => ({ name: pkg, displayName: pkg }))) + .sort((a, b) => a.name.localeCompare(b.name)); } diff --git a/src/managers/conda/condaPackageManager.ts b/src/managers/conda/condaPackageManager.ts index 7125dfe5..b5b1023c 100644 --- a/src/managers/conda/condaPackageManager.ts +++ b/src/managers/conda/condaPackageManager.ts @@ -12,16 +12,15 @@ import { IconPath, Package, PackageChangeKind, - PackageInstallOptions, + PackageManagementOptions, PackageManager, PythonEnvironment, PythonEnvironmentApi, } from '../../api'; -import { getCommonCondaPackagesToInstall, installPackages, refreshPackages, uninstallPackages } from './condaUtils'; +import { getCommonCondaPackagesToInstall, managePackages, refreshPackages } from './condaUtils'; import { withProgress } from '../../common/window.apis'; import { showErrorMessage } from '../../common/errors/utils'; import { CondaStrings } from '../../common/localize'; -import { getPackagesToUninstall } from '../common/utils'; function getChanges(before: Package[], after: Package[]): { kind: PackageChangeKind; pkg: Package }[] { const changes: { kind: PackageChangeKind; pkg: Package }[] = []; @@ -52,18 +51,25 @@ export class CondaPackageManager implements PackageManager, Disposable { tooltip?: string | MarkdownString; iconPath?: IconPath; - async install(environment: PythonEnvironment, packages?: string[], options?: PackageInstallOptions): Promise { - let selected: string[] = packages ?? []; + async manage(environment: PythonEnvironment, options: PackageManagementOptions): Promise { + let toInstall: string[] = [...(options.install ?? [])]; + let toUninstall: string[] = [...(options.uninstall ?? [])]; - if (selected.length === 0) { - selected = (await getCommonCondaPackagesToInstall(options)) ?? []; - } - - if (selected.length === 0) { - return; + if (toInstall.length === 0 && toUninstall.length === 0) { + const result = await getCommonCondaPackagesToInstall(environment, options, this.api); + if (result) { + toInstall = result.install; + toUninstall = result.uninstall; + } else { + return; + } } - const installOptions = options ?? { upgrade: false }; + const manageOptions = { + ...options, + install: toInstall, + uninstall: toUninstall, + }; await withProgress( { location: ProgressLocation.Notification, @@ -73,7 +79,7 @@ export class CondaPackageManager implements PackageManager, Disposable { async (_progress, token) => { try { const before = this.packages.get(environment.envId.id) ?? []; - const after = await installPackages(environment, selected, installOptions, this.api, this, token); + const after = await managePackages(environment, manageOptions, this.api, this, token); const changes = getChanges(before, after); this.packages.set(environment.envId.id, after); this._onDidChangePackages.fire({ environment: environment, manager: this, changes }); @@ -91,46 +97,6 @@ export class CondaPackageManager implements PackageManager, Disposable { ); } - async uninstall(environment: PythonEnvironment, packages?: Package[] | string[]): Promise { - let selected: Package[] | string[] = packages ?? []; - if (selected.length === 0) { - const installed = await this.getPackages(environment); - if (!installed) { - return; - } - selected = (await getPackagesToUninstall(installed)) ?? []; - } - - if (selected.length === 0) { - return; - } - - await withProgress( - { - location: ProgressLocation.Notification, - title: CondaStrings.condaUninstallingPackages, - cancellable: true, - }, - async (_progress, token) => { - try { - const before = this.packages.get(environment.envId.id) ?? []; - const after = await uninstallPackages(environment, selected, this.api, this, token); - const changes = getChanges(before, after); - this.packages.set(environment.envId.id, after); - this._onDidChangePackages.fire({ environment: environment, manager: this, changes }); - } catch (e) { - if (e instanceof CancellationError) { - return; - } - - this.log.error('Error uninstalling packages', e); - setImmediate(async () => { - await showErrorMessage(CondaStrings.condaUninstallError, this.log); - }); - } - }, - ); - } async refresh(environment: PythonEnvironment): Promise { await withProgress( { diff --git a/src/managers/conda/condaUtils.ts b/src/managers/conda/condaUtils.ts index c240fbf8..2647ac16 100644 --- a/src/managers/conda/condaUtils.ts +++ b/src/managers/conda/condaUtils.ts @@ -2,8 +2,7 @@ import * as ch from 'child_process'; import { EnvironmentManager, Package, - PackageInfo, - PackageInstallOptions, + PackageManagementOptions, PackageManager, PythonCommandRunConfiguration, PythonEnvironment, @@ -34,12 +33,12 @@ import { import { getConfiguration } from '../../common/workspace.apis'; import { getGlobalPersistentState, getWorkspacePersistentState } from '../../common/persistentState'; import which from 'which'; -import { isWindows, shortVersion, sortEnvironments, untildify } from '../common/utils'; +import { Installable, isWindows, shortVersion, sortEnvironments, untildify } from '../common/utils'; import { pickProject } from '../../common/pickers/projects'; import { CondaStrings, PackageManagement, Pickers } from '../../common/localize'; import { showErrorMessage } from '../../common/errors/utils'; import { showInputBox, showQuickPick, showQuickPickWithButtons, withProgress } from '../../common/window.apis'; -import { Installable, selectFromCommonPackagesToInstall } from '../common/pickers'; +import { selectFromCommonPackagesToInstall } from '../common/pickers'; import { quoteArgs } from '../../features/execution/execUtils'; import { traceInfo } from '../../common/logging'; @@ -695,50 +694,27 @@ export async function refreshPackages( return packages; } -export async function installPackages( +export async function managePackages( environment: PythonEnvironment, - packages: string[], - options: PackageInstallOptions, + options: PackageManagementOptions, api: PythonEnvironmentApi, manager: PackageManager, token: CancellationToken, ): Promise { - if (!packages || packages.length === 0) { - // TODO: Ask user to pick packages - throw new Error('No packages to install'); - } - - const args = ['install', '--prefix', environment.environmentPath.fsPath, '--yes']; - if (options.upgrade) { - args.push('--update-all'); + if (options.uninstall && options.uninstall.length > 0) { + await runConda( + ['remove', '--prefix', environment.environmentPath.fsPath, '--yes', ...options.uninstall], + token, + ); } - args.push(...packages); - - await runConda(args, token); - return refreshPackages(environment, api, manager); -} - -export async function uninstallPackages( - environment: PythonEnvironment, - packages: PackageInfo[] | string[], - api: PythonEnvironmentApi, - manager: PackageManager, - token: CancellationToken, -): Promise { - const remove = []; - for (let pkg of packages) { - if (typeof pkg === 'string') { - remove.push(pkg); - } else { - remove.push(pkg.name); + if (options.install && options.install.length > 0) { + const args = ['install', '--prefix', environment.environmentPath.fsPath, '--yes']; + if (options.upgrade) { + args.push('--update-all'); } + args.push(...options.install); + await runConda(args, token); } - if (remove.length === 0) { - throw new Error('No packages to remove'); - } - - await runConda(['remove', '--prefix', environment.environmentPath.fsPath, '--yes', ...remove], token); - return refreshPackages(environment, api, manager); } @@ -761,10 +737,16 @@ async function getCommonPackages(): Promise { } } +interface CondaPackagesResult { + install: string[]; + uninstall: string[]; +} + async function selectCommonPackagesOrSkip( common: Installable[], + installed: string[], showSkipOption: boolean, -): Promise { +): Promise { if (common.length === 0) { return undefined; } @@ -772,8 +754,8 @@ async function selectCommonPackagesOrSkip( const items = []; if (common.length > 0) { items.push({ - label: PackageManagement.commonPackages, - description: PackageManagement.commonPackagesDescription, + label: PackageManagement.searchCommonPackages, + description: PackageManagement.searchCommonPackagesDescription, }); } @@ -794,8 +776,8 @@ async function selectCommonPackagesOrSkip( if (selected && !Array.isArray(selected)) { try { - if (selected.label === PackageManagement.commonPackages) { - return await selectFromCommonPackagesToInstall(common); + if (selected.label === PackageManagement.searchCommonPackages) { + return await selectFromCommonPackagesToInstall(common, installed); } else { traceInfo('Package Installer: user selected skip package installation'); return undefined; @@ -803,15 +785,20 @@ async function selectCommonPackagesOrSkip( // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (ex: any) { if (ex === QuickInputButtons.Back) { - return selectCommonPackagesOrSkip(common, showSkipOption); + return selectCommonPackagesOrSkip(common, installed, showSkipOption); } } } return undefined; } -export async function getCommonCondaPackagesToInstall(options?: PackageInstallOptions): Promise { +export async function getCommonCondaPackagesToInstall( + environment: PythonEnvironment, + options: PackageManagementOptions, + api: PythonEnvironmentApi, +): Promise { const common = await getCommonPackages(); - const selected = await selectCommonPackagesOrSkip(common, !!options?.showSkipOption); + const installed = (await api.getPackages(environment))?.map((p) => p.name); + const selected = await selectCommonPackagesOrSkip(common, installed ?? [], !!options.showSkipOption); return selected; } diff --git a/src/vscode.proposed.terminalShellType.d.ts b/src/vscode.proposed.terminalShellType.d.ts deleted file mode 100644 index 75e15460..00000000 --- a/src/vscode.proposed.terminalShellType.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - // https://github.com/microsoft/vscode/issues/230165 - - // Part of TerminalState since the shellType can change multiple times and this comes with an event. - export interface TerminalState { - /** - * The detected shell type of the {@link Terminal}. This will be `undefined` when there is - * not a clear signal as to what the shell is, or the shell is not supported yet. This - * value should change to the shell type of a sub-shell when launched (for example, running - * `bash` inside `zsh`). - * - * Note that the possible values are currently defined as any of the following: - * 'bash', 'cmd', 'csh', 'fish', 'gitbash', 'julia', 'ksh', 'node', 'nu', 'pwsh', 'python', - * 'sh', 'wsl', 'zsh'. - */ - readonly shell: string | undefined; - } -}