Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 17 additions & 19 deletions packages/common/src/ide/types/QuickPickOptions.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
export interface UnknownValuesOptions {
allowed: true;
export interface QuickPickOptions {
/**
* An optional string that represents the title of the quick pick.
*/
title?: string;

/**
* An optional string that represents the default value to be selected in the quick pick.
*/
defaultValue?: string;

/**
* An optional template to use when displaying the new option to the user. The
* template must include `{}` as a substring, which will be replaced with the
* user's input when displaying an option to add the unknown item.
* Indicates whether the quick pick should allow unknown values, and if so,
* how to handle them.
*
* If provided as a string: An optional template to use when displaying the new
* option to the user. The template must include `{}` as a substring, which will
* be replaced with the user's input when displaying an option to add the unknown item.
*
* For example:
*
Expand All @@ -16,18 +27,5 @@ export interface UnknownValuesOptions {
*
* "Add new value 'foo' →"
*/
newValueTemplate?: string;
}

export interface QuickPickOptions {
/**
* An optional string that represents the title of the quick pick.
*/
title?: string;

/**
* Indicates whether the quick pick should allow unknown values, and if so,
* how to handle them.
*/
unknownValues?: UnknownValuesOptions;
unknownValues?: boolean | string;
}
4 changes: 2 additions & 2 deletions packages/cursorless-vscode/src/ide/vscode/VscodeIDE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ export class VscodeIDE implements IDE {
this.editorMap = new WeakMap<vscode.TextEditor, VscodeTextEditorImpl>();
}

async showQuickPick(
showQuickPick(
items: readonly string[],
options?: QuickPickOptions,
): Promise<string | undefined> {
return await vscodeShowQuickPick(items, options);
return vscodeShowQuickPick(items, options);
}

setHighlightRanges(
Expand Down
88 changes: 41 additions & 47 deletions packages/cursorless-vscode/src/ide/vscode/vscodeShowQuickPick.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,5 @@
import type { QuickPickOptions } from "@cursorless/common";
import * as vscode from "vscode";
import type {
QuickPickOptions,
UnknownValuesOptions,
} from "@cursorless/common";

export async function vscodeShowQuickPick(
items: readonly string[],
options: QuickPickOptions | undefined,
) {
if (options?.unknownValues == null) {
return await vscode.window.showQuickPick(items, options);
}

const { unknownValues, ...rest } = options;
return await showQuickPickAllowingUnknown(items, unknownValues, rest);
}

interface CustomQuickPickItem extends vscode.QuickPickItem {
value: string;
}

const DEFAULT_NEW_VALUE_TEMPLATE = "Add new value '{}' →";

/**
* Show a quick pick that allows the user to enter a new value. We do this by
Expand All @@ -29,43 +8,58 @@ const DEFAULT_NEW_VALUE_TEMPLATE = "Add new value '{}' →";
* detect that it is an unknown value and handle that case.
*
* Based on https://stackoverflow.com/a/69842249
* @param items
* @param options
*/
function showQuickPickAllowingUnknown(
choices: readonly string[],
unknownValues: UnknownValuesOptions,
options: vscode.QuickPickOptions,

interface CustomQuickPickItem extends vscode.QuickPickItem {
value: string;
}

export function vscodeShowQuickPick(
items: readonly string[],
options: QuickPickOptions | undefined,
) {
return new Promise<string | undefined>((resolve, _reject) => {
const quickPick = vscode.window.createQuickPick<CustomQuickPickItem>();
const quickPickItems = choices.map((choice) => ({
label: choice,
value: choice,

const quickPickItems = items.map((item) => ({
label: item,
value: item,
}));

quickPick.items = quickPickItems;

if (options.title != null) {
if (options?.title != null) {
quickPick.title = options.title;
}

const { newValueTemplate = DEFAULT_NEW_VALUE_TEMPLATE } = unknownValues;
if (options?.defaultValue != null) {
quickPick.activeItems = quickPickItems.filter(
(item) => item.value === options.defaultValue,
);
}

if (options?.unknownValues) {
const newValueTemplate =
typeof options.unknownValues === "string"
? options.unknownValues
: "Add new value '{}' →";

quickPick.onDidChangeValue(() => {
quickPick.items = [
...quickPickItems,
quickPick.onDidChangeValue(() => {
quickPick.items = [
...quickPickItems,

// INJECT user values into proposed values
...(choices.includes(quickPick.value)
? []
: [
{
label: newValueTemplate.replace("{}", quickPick.value),
value: quickPick.value,
},
]),
];
});
// INJECT user values into proposed values
...(items.includes(quickPick.value) || quickPick.value.trim() === ""
? []
: [
{
label: newValueTemplate.replace("{}", quickPick.value),
value: quickPick.value,
},
]),
];
});
}

quickPick.onDidAccept(() => {
const selection = quickPick.activeItems[0];
Expand Down
1 change: 1 addition & 0 deletions packages/test-case-recorder/src/ScopeTestRecorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export class ScopeTestRecorder {
languageIds.sort();
return this.ide.showQuickPick(languageIds, {
title: "Select language to record scope tests for",
defaultValue: this.ide.activeTextEditor?.document.languageId,
});
}
}
Expand Down
5 changes: 1 addition & 4 deletions packages/test-case-recorder/src/TestCaseRecorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,10 +418,7 @@ export class TestCaseRecorder {
walkDirsSync(this.fixtureRoot).concat("/"),
{
title: "Select directory for new test cases",
unknownValues: {
allowed: true,
newValueTemplate: "Create new directory '{}' →",
},
unknownValues: "Create new directory '{}' →",
},
);

Expand Down
Loading