Skip to content

Commit 57b187d

Browse files
karthiknadigeleanorjboyd
authored andcommitted
fix: handling installables selection (#315)
fixes: #304 fixes: #301
1 parent b99c452 commit 57b187d

File tree

6 files changed

+91
-64
lines changed

6 files changed

+91
-64
lines changed

src/managers/builtin/pipManager.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export class PipPackageManager implements PackageManager, Disposable {
6363

6464
if (toInstall.length === 0 && toUninstall.length === 0) {
6565
const projects = this.venv.getProjectsByEnvironment(environment);
66-
const result = await getWorkspacePackagesToInstall(this.api, options, projects, environment);
66+
const result = await getWorkspacePackagesToInstall(this.api, options, projects, environment, this.log);
6767
if (result) {
6868
toInstall = result.install;
6969
toUninstall = result.uninstall;

src/managers/builtin/pipUtils.ts

+25-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as fse from 'fs-extra';
22
import * as path from 'path';
33
import * as tomljs from '@iarna/toml';
4-
import { LogOutputChannel, ProgressLocation, QuickInputButtons, Uri } from 'vscode';
4+
import { LogOutputChannel, ProgressLocation, QuickInputButtons, QuickPickItem, Uri } from 'vscode';
55
import { showQuickPickWithButtons, withProgress } from '../../common/window.apis';
66
import { PackageManagement, Pickers, VenvManagerStrings } from '../../common/localize';
77
import { PackageManagementOptions, PythonEnvironment, PythonEnvironmentApi, PythonProject } from '../../api';
@@ -10,6 +10,7 @@ import { EXTENSION_ROOT_DIR } from '../../common/constants';
1010
import { selectFromCommonPackagesToInstall, selectFromInstallableToInstall } from '../common/pickers';
1111
import { traceInfo } from '../../common/logging';
1212
import { Installable, mergePackages } from '../common/utils';
13+
import { refreshPipPackages } from './utils';
1314

1415
async function tomlParse(fsPath: string, log?: LogOutputChannel): Promise<tomljs.JsonMap> {
1516
try {
@@ -83,7 +84,7 @@ async function selectWorkspaceOrCommon(
8384
return undefined;
8485
}
8586

86-
const items = [];
87+
const items: QuickPickItem[] = [];
8788
if (installable.length > 0) {
8889
items.push({
8990
label: PackageManagement.workspaceDependencies,
@@ -102,27 +103,32 @@ async function selectWorkspaceOrCommon(
102103
items.push({ label: PackageManagement.skipPackageInstallation });
103104
}
104105

105-
const selected =
106-
items.length === 1
107-
? items[0]
108-
: await showQuickPickWithButtons(items, {
109-
placeHolder: Pickers.Packages.selectOption,
110-
ignoreFocusOut: true,
111-
showBackButton: true,
112-
matchOnDescription: false,
113-
matchOnDetail: false,
114-
});
106+
let showBackButton = true;
107+
let selected: QuickPickItem[] | QuickPickItem | undefined = undefined;
108+
if (items.length === 1) {
109+
selected = items[0];
110+
showBackButton = false;
111+
} else {
112+
selected = await showQuickPickWithButtons(items, {
113+
placeHolder: Pickers.Packages.selectOption,
114+
ignoreFocusOut: true,
115+
showBackButton: true,
116+
matchOnDescription: false,
117+
matchOnDetail: false,
118+
});
119+
}
115120

116121
if (selected && !Array.isArray(selected)) {
117122
try {
118123
if (selected.label === PackageManagement.workspaceDependencies) {
119-
const installArgs = await selectFromInstallableToInstall(installable);
120-
return { install: installArgs ?? [], uninstall: [] };
124+
return await selectFromInstallableToInstall(installable, undefined, { showBackButton });
121125
} else if (selected.label === PackageManagement.searchCommonPackages) {
122-
return await selectFromCommonPackagesToInstall(common, installed);
123-
} else {
126+
return await selectFromCommonPackagesToInstall(common, installed, undefined, { showBackButton });
127+
} else if (selected.label === PackageManagement.skipPackageInstallation) {
124128
traceInfo('Package Installer: user selected skip package installation');
125129
return undefined;
130+
} else {
131+
return undefined;
126132
}
127133
// eslint-disable-next-line @typescript-eslint/no-explicit-any
128134
} catch (ex: any) {
@@ -144,12 +150,13 @@ export async function getWorkspacePackagesToInstall(
144150
options: PackageManagementOptions,
145151
project?: PythonProject[],
146152
environment?: PythonEnvironment,
153+
log?: LogOutputChannel,
147154
): Promise<PipPackages | undefined> {
148155
const installable = (await getProjectInstallable(api, project)) ?? [];
149156
let common = await getCommonPackages();
150157
let installed: string[] | undefined;
151158
if (environment) {
152-
installed = (await api.getPackages(environment))?.map((pkg) => pkg.name);
159+
installed = (await refreshPipPackages(environment, log, { showProgress: true }))?.map((pkg) => pkg.name);
153160
common = mergePackages(common, installed ?? []);
154161
}
155162
return selectWorkspaceOrCommon(installable, common, !!options.showSkipOption, installed ?? []);
@@ -166,7 +173,7 @@ export async function getProjectInstallable(
166173
const installable: Installable[] = [];
167174
await withProgress(
168175
{
169-
location: ProgressLocation.Window,
176+
location: ProgressLocation.Notification,
170177
title: VenvManagerStrings.searchingDependencies,
171178
},
172179
async (_progress, token) => {

src/managers/builtin/utils.ts

+36-25
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CancellationToken, l10n, LogOutputChannel, QuickPickItem, ThemeIcon, Uri, window } from 'vscode';
1+
import { CancellationToken, LogOutputChannel, ProgressLocation, QuickPickItem, ThemeIcon, Uri, window } from 'vscode';
22
import {
33
EnvironmentManager,
44
Package,
@@ -18,7 +18,8 @@ import { showErrorMessage } from '../../common/errors/utils';
1818
import { shortVersion, sortEnvironments } from '../common/utils';
1919
import { SysManagerStrings } from '../../common/localize';
2020
import { isUvInstalled, runUV, runPython } from './helpers';
21-
import { parsePipList } from './pipListUtils';
21+
import { parsePipList, PipPackage } from './pipListUtils';
22+
import { withProgress } from '../../common/window.apis';
2223

2324
function asPackageQuickPickItem(name: string, version?: string): QuickPickItem {
2425
return {
@@ -138,39 +139,49 @@ export async function refreshPythons(
138139
return sortEnvironments(collection);
139140
}
140141

141-
export async function refreshPackages(
142-
environment: PythonEnvironment,
143-
api: PythonEnvironmentApi,
144-
manager: PackageManager,
145-
): Promise<Package[]> {
146-
if (!environment.execInfo) {
147-
manager.log?.error(`No executable found for python: ${environment.environmentPath.fsPath}`);
148-
showErrorMessage(
149-
l10n.t('No executable found for python: {0}', environment.environmentPath.fsPath),
150-
manager.log,
151-
);
152-
return [];
142+
async function refreshPipPackagesRaw(environment: PythonEnvironment, log?: LogOutputChannel): Promise<string> {
143+
const useUv = await isUvInstalled();
144+
if (useUv) {
145+
return await runUV(['pip', 'list', '--python', environment.execInfo.run.executable], undefined, log);
153146
}
147+
return await runPython(environment.execInfo.run.executable, ['-m', 'pip', 'list'], undefined, log);
148+
}
154149

150+
export async function refreshPipPackages(
151+
environment: PythonEnvironment,
152+
log?: LogOutputChannel,
153+
options?: { showProgress: boolean },
154+
): Promise<PipPackage[] | undefined> {
155155
let data: string;
156156
try {
157-
const useUv = await isUvInstalled();
158-
if (useUv) {
159-
data = await runUV(
160-
['pip', 'list', '--python', environment.execInfo.run.executable],
161-
undefined,
162-
manager.log,
157+
if (options?.showProgress) {
158+
data = await withProgress(
159+
{
160+
location: ProgressLocation.Notification,
161+
},
162+
async () => {
163+
return await refreshPipPackagesRaw(environment, log);
164+
},
163165
);
164166
} else {
165-
data = await runPython(environment.execInfo.run.executable, ['-m', 'pip', 'list'], undefined, manager.log);
167+
data = await refreshPipPackagesRaw(environment, log);
166168
}
169+
170+
return parsePipList(data);
167171
} catch (e) {
168-
manager.log?.error('Error refreshing packages', e);
169-
showErrorMessage(SysManagerStrings.packageRefreshError, manager.log);
170-
return [];
172+
log?.error('Error refreshing packages', e);
173+
showErrorMessage(SysManagerStrings.packageRefreshError, log);
174+
return undefined;
171175
}
176+
}
172177

173-
return parsePipList(data).map((pkg) => api.createPackageItem(pkg, environment, manager));
178+
export async function refreshPackages(
179+
environment: PythonEnvironment,
180+
api: PythonEnvironmentApi,
181+
manager: PackageManager,
182+
): Promise<Package[]> {
183+
const data = await refreshPipPackages(environment, manager.log);
184+
return (data ?? []).map((pkg) => api.createPackageItem(pkg, environment, manager));
174185
}
175186

176187
export async function managePackages(

src/managers/builtin/venvUtils.ts

+2
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,8 @@ export async function createPythonVenv(
527527
api,
528528
{ showSkipOption: true, install: [] },
529529
project ? [project] : undefined,
530+
undefined,
531+
log,
530532
);
531533
const allPackages = [];
532534
allPackages.push(...(packages?.install ?? []), ...(options.additionalPackages ?? []));

src/managers/common/pickers.ts

+10-8
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,12 @@ function groupByInstalled(items: PackageQuickPickItem[], installed?: string[]):
105105
};
106106
}
107107

108-
export interface CommonPackagesResult {
108+
interface PackagesPickerResult {
109109
install: string[];
110110
uninstall: string[];
111111
}
112112

113-
function selectionsToResult(selections: string[], installed: string[]): CommonPackagesResult {
113+
function selectionsToResult(selections: string[], installed: string[]): PackagesPickerResult {
114114
const install: string[] = selections;
115115
const uninstall: string[] = [];
116116
installed.forEach((i) => {
@@ -128,7 +128,8 @@ export async function selectFromCommonPackagesToInstall(
128128
common: Installable[],
129129
installed: string[],
130130
preSelected?: PackageQuickPickItem[] | undefined,
131-
): Promise<CommonPackagesResult | undefined> {
131+
options?: { showBackButton?: boolean } | undefined,
132+
): Promise<PackagesPickerResult | undefined> {
132133
const { installedItems, items } = groupByInstalled(common.map(installableToQuickPickItem), installed);
133134
const preSelectedItems = items.filter((i) => (preSelected ?? installedItems).some((s) => s.id === i.id));
134135
let selected: PackageQuickPickItem | PackageQuickPickItem[] | undefined;
@@ -139,7 +140,7 @@ export async function selectFromCommonPackagesToInstall(
139140
placeHolder: PackageManagement.selectPackagesToInstall,
140141
ignoreFocusOut: true,
141142
canPickMany: true,
142-
showBackButton: true,
143+
showBackButton: options?.showBackButton,
143144
buttons: [EDIT_ARGUMENTS_BUTTON],
144145
selected: preSelectedItems,
145146
},
@@ -232,7 +233,8 @@ function getGroupedItems(items: Installable[]): PackageQuickPickItem[] {
232233
export async function selectFromInstallableToInstall(
233234
installable: Installable[],
234235
preSelected?: PackageQuickPickItem[],
235-
): Promise<string[] | undefined> {
236+
options?: { showBackButton?: boolean } | undefined,
237+
): Promise<PackagesPickerResult | undefined> {
236238
const items: PackageQuickPickItem[] = [];
237239

238240
if (installable && installable.length > 0) {
@@ -252,7 +254,7 @@ export async function selectFromInstallableToInstall(
252254
placeHolder: PackageManagement.selectPackagesToInstall,
253255
ignoreFocusOut: true,
254256
canPickMany: true,
255-
showBackButton: true,
257+
showBackButton: options?.showBackButton,
256258
selected: preSelectedItems,
257259
},
258260
undefined,
@@ -263,9 +265,9 @@ export async function selectFromInstallableToInstall(
263265

264266
if (selected) {
265267
if (Array.isArray(selected)) {
266-
return selected.flatMap((s) => s.args ?? []);
268+
return { install: selected.flatMap((s) => s.args ?? []), uninstall: [] };
267269
} else {
268-
return selected.args ?? [];
270+
return { install: selected.args ?? [], uninstall: [] };
269271
}
270272
}
271273
return undefined;

src/managers/conda/condaUtils.ts

+17-12
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
LogOutputChannel,
2121
ProgressLocation,
2222
QuickInputButtons,
23+
QuickPickItem,
2324
Uri,
2425
} from 'vscode';
2526
import { ENVS_EXTENSION_ID, EXTENSION_ROOT_DIR } from '../../common/constants';
@@ -786,7 +787,7 @@ async function selectCommonPackagesOrSkip(
786787
return undefined;
787788
}
788789

789-
const items = [];
790+
const items: QuickPickItem[] = [];
790791
if (common.length > 0) {
791792
items.push({
792793
label: PackageManagement.searchCommonPackages,
@@ -798,21 +799,25 @@ async function selectCommonPackagesOrSkip(
798799
items.push({ label: PackageManagement.skipPackageInstallation });
799800
}
800801

801-
const selected =
802-
items.length === 1
803-
? items[0]
804-
: await showQuickPickWithButtons(items, {
805-
placeHolder: Pickers.Packages.selectOption,
806-
ignoreFocusOut: true,
807-
showBackButton: true,
808-
matchOnDescription: false,
809-
matchOnDetail: false,
810-
});
802+
let showBackButton = true;
803+
let selected: QuickPickItem[] | QuickPickItem | undefined = undefined;
804+
if (items.length === 1) {
805+
selected = items[0];
806+
showBackButton = false;
807+
} else {
808+
selected = await showQuickPickWithButtons(items, {
809+
placeHolder: Pickers.Packages.selectOption,
810+
ignoreFocusOut: true,
811+
showBackButton: true,
812+
matchOnDescription: false,
813+
matchOnDetail: false,
814+
});
815+
}
811816

812817
if (selected && !Array.isArray(selected)) {
813818
try {
814819
if (selected.label === PackageManagement.searchCommonPackages) {
815-
return await selectFromCommonPackagesToInstall(common, installed);
820+
return await selectFromCommonPackagesToInstall(common, installed, undefined, { showBackButton });
816821
} else {
817822
traceInfo('Package Installer: user selected skip package installation');
818823
return undefined;

0 commit comments

Comments
 (0)