Skip to content

Commit 5c707da

Browse files
authored
fix: UX when installing packages (#222)
* ensure skip install is only shown when relevant * ensure delete env message is modal fixes #220
1 parent b9d72c1 commit 5c707da

File tree

8 files changed

+68
-35
lines changed

8 files changed

+68
-35
lines changed

src/api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,11 @@ export interface PackageInstallOptions {
721721
* Upgrade the packages if it is already installed.
722722
*/
723723
upgrade?: boolean;
724+
725+
/**
726+
* Show option to skip package installation
727+
*/
728+
showSkipOption?: boolean;
724729
}
725730

726731
export interface PythonProcess {

src/common/window.apis.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
InputBox,
88
InputBoxOptions,
99
LogOutputChannel,
10+
MessageItem,
11+
MessageOptions,
1012
OpenDialogOptions,
1113
OutputChannel,
1214
Progress,
@@ -284,7 +286,19 @@ export async function showInputBoxWithButtons(
284286
}
285287
}
286288

287-
export function showWarningMessage(message: string, ...items: string[]): Thenable<string | undefined> {
289+
export function showWarningMessage<T extends string>(message: string, ...items: T[]): Thenable<T | undefined>;
290+
export function showWarningMessage<T extends string>(
291+
message: string,
292+
options: MessageOptions,
293+
...items: T[]
294+
): Thenable<T | undefined>;
295+
export function showWarningMessage<T extends MessageItem>(message: string, ...items: T[]): Thenable<T | undefined>;
296+
export function showWarningMessage<T extends MessageItem>(
297+
message: string,
298+
options: MessageOptions,
299+
...items: T[]
300+
): Thenable<T | undefined>;
301+
export function showWarningMessage(message: string, ...items: any[]): Thenable<string | undefined> {
288302
return window.showWarningMessage(message, ...items);
289303
}
290304

src/features/envCommands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ export async function handlePackagesCommand(
188188

189189
try {
190190
if (action === Common.install) {
191-
await packageManager.install(environment);
191+
await packageManager.install(environment, undefined, { showSkipOption: false });
192192
} else if (action === Common.uninstall) {
193193
await packageManager.uninstall(environment);
194194
}

src/managers/builtin/pipManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export class PipPackageManager implements PackageManager, Disposable {
5454

5555
if (selected.length === 0) {
5656
const projects = this.venv.getProjectsByEnvironment(environment);
57-
selected = (await getWorkspacePackagesToInstall(this.api, projects)) ?? [];
57+
selected = (await getWorkspacePackagesToInstall(this.api, options, projects)) ?? [];
5858
}
5959

6060
if (selected.length === 0) {

src/managers/builtin/pipUtils.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as tomljs from '@iarna/toml';
44
import { LogOutputChannel, ProgressLocation, QuickInputButtons, Uri } from 'vscode';
55
import { showQuickPickWithButtons, withProgress } from '../../common/window.apis';
66
import { PackageManagement, Pickers, VenvManagerStrings } from '../../common/localize';
7-
import { PythonEnvironmentApi, PythonProject } from '../../api';
7+
import { PackageInstallOptions, PythonEnvironmentApi, PythonProject } from '../../api';
88
import { findFiles } from '../../common/workspace.apis';
99
import { EXTENSION_ROOT_DIR } from '../../common/constants';
1010
import { Installable, selectFromCommonPackagesToInstall, selectFromInstallableToInstall } from '../common/pickers';
@@ -75,6 +75,7 @@ async function getCommonPackages(): Promise<Installable[]> {
7575
async function selectWorkspaceOrCommon(
7676
installable: Installable[],
7777
common: Installable[],
78+
showSkipOption: boolean,
7879
): Promise<string[] | undefined> {
7980
if (installable.length === 0 && common.length === 0) {
8081
return undefined;
@@ -95,19 +96,20 @@ async function selectWorkspaceOrCommon(
9596
});
9697
}
9798

98-
if (items.length > 0) {
99+
if (showSkipOption && items.length > 0) {
99100
items.push({ label: PackageManagement.skipPackageInstallation });
100-
} else {
101-
return undefined;
102101
}
103102

104-
const selected = await showQuickPickWithButtons(items, {
105-
placeHolder: Pickers.Packages.selectOption,
106-
ignoreFocusOut: true,
107-
showBackButton: true,
108-
matchOnDescription: false,
109-
matchOnDetail: false,
110-
});
103+
const selected =
104+
items.length === 1
105+
? items[0]
106+
: await showQuickPickWithButtons(items, {
107+
placeHolder: Pickers.Packages.selectOption,
108+
ignoreFocusOut: true,
109+
showBackButton: true,
110+
matchOnDescription: false,
111+
matchOnDetail: false,
112+
});
111113

112114
if (selected && !Array.isArray(selected)) {
113115
try {
@@ -122,7 +124,7 @@ async function selectWorkspaceOrCommon(
122124
// eslint-disable-next-line @typescript-eslint/no-explicit-any
123125
} catch (ex: any) {
124126
if (ex === QuickInputButtons.Back) {
125-
return selectWorkspaceOrCommon(installable, common);
127+
return selectWorkspaceOrCommon(installable, common, showSkipOption);
126128
}
127129
}
128130
}
@@ -131,11 +133,12 @@ async function selectWorkspaceOrCommon(
131133

132134
export async function getWorkspacePackagesToInstall(
133135
api: PythonEnvironmentApi,
136+
options?: PackageInstallOptions,
134137
project?: PythonProject[],
135138
): Promise<string[] | undefined> {
136139
const installable = (await getProjectInstallable(api, project)) ?? [];
137140
const common = await getCommonPackages();
138-
return selectWorkspaceOrCommon(installable, common);
141+
return selectWorkspaceOrCommon(installable, common, !!options?.showSkipOption);
139142
}
140143

141144
export async function getProjectInstallable(

src/managers/builtin/venvUtils.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,11 @@ export async function createPythonVenv(
400400
os.platform() === 'win32' ? path.join(envPath, 'Scripts', 'python.exe') : path.join(envPath, 'bin', 'python');
401401

402402
const project = api.getPythonProject(venvRoot);
403-
const packages = await getWorkspacePackagesToInstall(api, project ? [project] : undefined);
403+
const packages = await getWorkspacePackagesToInstall(
404+
api,
405+
{ showSkipOption: true },
406+
project ? [project] : undefined,
407+
);
404408

405409
return await withProgress(
406410
{
@@ -455,10 +459,13 @@ export async function removeVenv(environment: PythonEnvironment, log: LogOutputC
455459

456460
const confirm = await showWarningMessage(
457461
l10n.t('Are you sure you want to remove {0}?', envPath),
458-
Common.yes,
459-
Common.no,
462+
{
463+
modal: true,
464+
},
465+
{ title: Common.yes },
466+
{ title: Common.no, isCloseAffordance: true },
460467
);
461-
if (confirm === Common.yes) {
468+
if (confirm?.title === Common.yes) {
462469
await withProgress(
463470
{
464471
location: ProgressLocation.Notification,

src/managers/conda/condaPackageManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export class CondaPackageManager implements PackageManager, Disposable {
5656
let selected: string[] = packages ?? [];
5757

5858
if (selected.length === 0) {
59-
selected = (await getCommonCondaPackagesToInstall()) ?? [];
59+
selected = (await getCommonCondaPackagesToInstall(options)) ?? [];
6060
}
6161

6262
if (selected.length === 0) {

src/managers/conda/condaUtils.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,10 @@ async function getCommonPackages(): Promise<Installable[]> {
761761
}
762762
}
763763

764-
async function selectCommonPackagesOrSkip(common: Installable[]): Promise<string[] | undefined> {
764+
async function selectCommonPackagesOrSkip(
765+
common: Installable[],
766+
showSkipOption: boolean,
767+
): Promise<string[] | undefined> {
765768
if (common.length === 0) {
766769
return undefined;
767770
}
@@ -774,19 +777,20 @@ async function selectCommonPackagesOrSkip(common: Installable[]): Promise<string
774777
});
775778
}
776779

777-
if (items.length > 0) {
780+
if (showSkipOption && items.length > 0) {
778781
items.push({ label: PackageManagement.skipPackageInstallation });
779-
} else {
780-
return undefined;
781782
}
782783

783-
const selected = await showQuickPickWithButtons(items, {
784-
placeHolder: Pickers.Packages.selectOption,
785-
ignoreFocusOut: true,
786-
showBackButton: true,
787-
matchOnDescription: false,
788-
matchOnDetail: false,
789-
});
784+
const selected =
785+
items.length === 1
786+
? items[0]
787+
: await showQuickPickWithButtons(items, {
788+
placeHolder: Pickers.Packages.selectOption,
789+
ignoreFocusOut: true,
790+
showBackButton: true,
791+
matchOnDescription: false,
792+
matchOnDetail: false,
793+
});
790794

791795
if (selected && !Array.isArray(selected)) {
792796
try {
@@ -799,15 +803,15 @@ async function selectCommonPackagesOrSkip(common: Installable[]): Promise<string
799803
// eslint-disable-next-line @typescript-eslint/no-explicit-any
800804
} catch (ex: any) {
801805
if (ex === QuickInputButtons.Back) {
802-
return selectCommonPackagesOrSkip(common);
806+
return selectCommonPackagesOrSkip(common, showSkipOption);
803807
}
804808
}
805809
}
806810
return undefined;
807811
}
808812

809-
export async function getCommonCondaPackagesToInstall(): Promise<string[] | undefined> {
813+
export async function getCommonCondaPackagesToInstall(options?: PackageInstallOptions): Promise<string[] | undefined> {
810814
const common = await getCommonPackages();
811-
const selected = await selectCommonPackagesOrSkip(common);
815+
const selected = await selectCommonPackagesOrSkip(common, !!options?.showSkipOption);
812816
return selected;
813817
}

0 commit comments

Comments
 (0)