Skip to content

Commit fdd2659

Browse files
authored
Expand the ts-command-line choice paramter APIs. (#4975)
1 parent 312b8bc commit fdd2659

File tree

10 files changed

+90
-65
lines changed

10 files changed

+90
-65
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@rushstack/ts-command-line",
5+
"comment": "Expand the `alternatives` and `completions` options of `CommandLineChoiceParameter` and `CommandLineChoiceListParameter` to allow readonly arrays and sets.",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@rushstack/ts-command-line"
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@rushstack/ts-command-line",
5+
"comment": "(BREAKING API CHANGE) Change the type of the `alternatives` property of `CommandLineChoiceParameter` and `CommandLineChoiceParameter` from an array to a `ReadonlySet`.",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@rushstack/ts-command-line"
10+
}

common/reviews/api/ts-command-line.api.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ export abstract class CommandLineAction extends CommandLineParameterProvider {
3737
export class CommandLineChoiceListParameter<TChoice extends string = string> extends CommandLineParameter {
3838
// @internal
3939
constructor(definition: ICommandLineChoiceListDefinition<TChoice>);
40-
readonly alternatives: ReadonlyArray<TChoice>;
40+
readonly alternatives: ReadonlySet<TChoice>;
4141
// @override
4242
appendToArgList(argList: string[]): void;
43-
readonly completions: (() => Promise<TChoice[]>) | undefined;
43+
readonly completions: (() => Promise<ReadonlyArray<TChoice> | ReadonlySet<TChoice>>) | undefined;
4444
readonly kind: CommandLineParameterKind.ChoiceList;
4545
// @internal
4646
_setValue(data: unknown): void;
@@ -51,10 +51,10 @@ export class CommandLineChoiceListParameter<TChoice extends string = string> ext
5151
export class CommandLineChoiceParameter<TChoice extends string = string> extends CommandLineParameter {
5252
// @internal
5353
constructor(definition: ICommandLineChoiceDefinition<TChoice>);
54-
readonly alternatives: ReadonlyArray<TChoice>;
54+
readonly alternatives: ReadonlySet<TChoice>;
5555
// @override
5656
appendToArgList(argList: string[]): void;
57-
readonly completions: (() => Promise<TChoice[]>) | undefined;
57+
readonly completions: (() => Promise<ReadonlyArray<TChoice> | ReadonlySet<TChoice>>) | undefined;
5858
readonly defaultValue: TChoice | undefined;
5959
// @internal
6060
_getSupplementaryNotes(supplementaryNotes: string[]): void;
@@ -246,7 +246,7 @@ export abstract class CommandLineParameterWithArgument extends CommandLineParame
246246
// @internal
247247
constructor(definition: IBaseCommandLineDefinitionWithArgument);
248248
readonly argumentName: string;
249-
readonly completions: (() => Promise<string[]>) | undefined;
249+
readonly completions: (() => Promise<ReadonlyArray<string> | ReadonlySet<string>>) | undefined;
250250
}
251251

252252
// @public
@@ -343,7 +343,7 @@ export interface IBaseCommandLineDefinition {
343343
// @public
344344
export interface IBaseCommandLineDefinitionWithArgument extends IBaseCommandLineDefinition {
345345
argumentName: string;
346-
completions?: () => Promise<string[]>;
346+
completions?: () => Promise<ReadonlyArray<string> | ReadonlySet<string>>;
347347
}
348348

349349
// @public
@@ -355,15 +355,15 @@ export interface ICommandLineActionOptions {
355355

356356
// @public
357357
export interface ICommandLineChoiceDefinition<TChoice extends string = string> extends IBaseCommandLineDefinition {
358-
alternatives: TChoice[];
359-
completions?: () => Promise<TChoice[]>;
358+
alternatives: ReadonlyArray<TChoice> | ReadonlySet<TChoice>;
359+
completions?: () => Promise<ReadonlyArray<TChoice> | ReadonlySet<TChoice>>;
360360
defaultValue?: TChoice;
361361
}
362362

363363
// @public
364364
export interface ICommandLineChoiceListDefinition<TChoice extends string = string> extends IBaseCommandLineDefinition {
365-
alternatives: TChoice[];
366-
completions?: () => Promise<TChoice[]>;
365+
alternatives: ReadonlyArray<TChoice> | ReadonlySet<TChoice>;
366+
completions?: () => Promise<ReadonlyArray<TChoice> | ReadonlySet<TChoice>>;
367367
}
368368

369369
// @public

libraries/ts-command-line/src/parameters/BaseClasses.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ export abstract class CommandLineParameterWithArgument extends CommandLineParame
280280
public readonly argumentName: string;
281281

282282
/** {@inheritDoc IBaseCommandLineDefinitionWithArgument.completions} */
283-
public readonly completions: (() => Promise<string[]>) | undefined;
283+
public readonly completions: (() => Promise<ReadonlyArray<string> | ReadonlySet<string>>) | undefined;
284284

285285
/** @internal */
286286
public constructor(definition: IBaseCommandLineDefinitionWithArgument) {

libraries/ts-command-line/src/parameters/CommandLineChoiceListParameter.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,30 @@ export class CommandLineChoiceListParameter<
1313
TChoice extends string = string
1414
> extends CommandLineParameterBase {
1515
/** {@inheritDoc ICommandLineChoiceListDefinition.alternatives} */
16-
public readonly alternatives: ReadonlyArray<TChoice>;
16+
public readonly alternatives: ReadonlySet<TChoice>;
1717

1818
private _values: TChoice[] = [];
1919

2020
/** {@inheritDoc ICommandLineChoiceListDefinition.completions} */
21-
public readonly completions: (() => Promise<TChoice[]>) | undefined;
21+
public readonly completions: (() => Promise<ReadonlyArray<TChoice> | ReadonlySet<TChoice>>) | undefined;
2222

2323
/** {@inheritDoc CommandLineParameter.kind} */
2424
public readonly kind: CommandLineParameterKind.ChoiceList = CommandLineParameterKind.ChoiceList;
2525

2626
/** @internal */
2727
public constructor(definition: ICommandLineChoiceListDefinition<TChoice>) {
2828
super(definition);
29+
const { alternatives, completions } = definition;
2930

30-
if (definition.alternatives.length < 1) {
31+
const alternativesSet: Set<TChoice> = alternatives instanceof Set ? alternatives : new Set(alternatives);
32+
if (alternativesSet.size < 1) {
3133
throw new Error(
3234
`When defining a choice list parameter, the alternatives list must contain at least one value.`
3335
);
3436
}
3537

36-
this.alternatives = definition.alternatives;
37-
this.completions = definition.completions;
38+
this.alternatives = alternativesSet;
39+
this.completions = completions;
3840
}
3941

4042
/**
@@ -60,8 +62,8 @@ export class CommandLineChoiceListParameter<
6062
const values: string[] | undefined = EnvironmentVariableParser.parseAsList(this.environmentVariable);
6163
if (values) {
6264
for (const value of values) {
63-
if (!this.alternatives.includes(value as TChoice)) {
64-
const choices: string = '"' + this.alternatives.join('", "') + '"';
65+
if (!this.alternatives.has(value as TChoice)) {
66+
const choices: string = '"' + Array.from(this.alternatives).join('", "') + '"';
6567
throw new Error(
6668
`Invalid value "${value}" for the environment variable` +
6769
` ${this.environmentVariable}. Valid choices are: ${choices}`

libraries/ts-command-line/src/parameters/CommandLineChoiceParameter.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,39 +19,41 @@ export interface IRequiredCommandLineChoiceParameter<TChoice extends string = st
1919
*/
2020
export class CommandLineChoiceParameter<TChoice extends string = string> extends CommandLineParameterBase {
2121
/** {@inheritDoc ICommandLineChoiceDefinition.alternatives} */
22-
public readonly alternatives: ReadonlyArray<TChoice>;
22+
public readonly alternatives: ReadonlySet<TChoice>;
2323

2424
/** {@inheritDoc ICommandLineStringDefinition.defaultValue} */
2525
public readonly defaultValue: TChoice | undefined;
2626

2727
private _value: TChoice | undefined = undefined;
2828

2929
/** {@inheritDoc ICommandLineChoiceDefinition.completions} */
30-
public readonly completions: (() => Promise<TChoice[]>) | undefined;
30+
public readonly completions: (() => Promise<ReadonlyArray<TChoice> | ReadonlySet<TChoice>>) | undefined;
3131

3232
/** {@inheritDoc CommandLineParameter.kind} */
3333
public readonly kind: CommandLineParameterKind.Choice = -CommandLineParameterKind.Choice;
3434

3535
/** @internal */
3636
public constructor(definition: ICommandLineChoiceDefinition<TChoice>) {
3737
super(definition);
38+
const { alternatives, defaultValue, completions } = definition;
3839

39-
if (definition.alternatives.length < 1) {
40+
const alternativesSet: Set<TChoice> = alternatives instanceof Set ? alternatives : new Set(alternatives);
41+
if (alternativesSet.size < 1) {
4042
throw new Error(
4143
`When defining a choice parameter, the alternatives list must contain at least one value.`
4244
);
4345
}
44-
if (definition.defaultValue && definition.alternatives.indexOf(definition.defaultValue) === -1) {
46+
if (defaultValue && !alternativesSet.has(defaultValue)) {
4547
throw new Error(
46-
`The specified default value "${definition.defaultValue}"` +
47-
` is not one of the available options: ${definition.alternatives.toString()}`
48+
`The specified default value "${defaultValue}"` +
49+
` is not one of the available options: ${alternatives.toString()}`
4850
);
4951
}
5052

51-
this.alternatives = definition.alternatives;
52-
this.defaultValue = definition.defaultValue;
53+
this.alternatives = alternativesSet;
54+
this.defaultValue = defaultValue;
5355
this.validateDefaultValue(!!this.defaultValue);
54-
this.completions = definition.completions;
56+
this.completions = completions;
5557
}
5658

5759
/**
@@ -72,8 +74,8 @@ export class CommandLineChoiceParameter<TChoice extends string = string> extends
7274
// Try reading the environment variable
7375
const environmentValue: string | undefined = process.env[this.environmentVariable];
7476
if (environmentValue !== undefined && environmentValue !== '') {
75-
if (!this.alternatives.includes(environmentValue as TChoice)) {
76-
const choices: string = '"' + this.alternatives.join('", "') + '"';
77+
if (!this.alternatives.has(environmentValue as TChoice)) {
78+
const choices: string = '"' + Array.from(this.alternatives).join('", "') + '"';
7779
throw new Error(
7880
`Invalid value "${environmentValue}" for the environment variable` +
7981
` ${this.environmentVariable}. Valid choices are: ${choices}`

libraries/ts-command-line/src/parameters/CommandLineDefinition.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export interface IBaseCommandLineDefinitionWithArgument extends IBaseCommandLine
130130
*
131131
* In a future release, this will be renamed to `getCompletionsAsync`
132132
*/
133-
completions?: () => Promise<string[]>;
133+
completions?: () => Promise<ReadonlyArray<string> | ReadonlySet<string>>;
134134
}
135135

136136
/**
@@ -146,7 +146,7 @@ export interface ICommandLineChoiceDefinition<TChoice extends string = string>
146146
/**
147147
* A list of strings (which contain no spaces), of possible options which can be selected
148148
*/
149-
alternatives: TChoice[];
149+
alternatives: ReadonlyArray<TChoice> | ReadonlySet<TChoice>;
150150

151151
/**
152152
* {@inheritDoc ICommandLineStringDefinition.defaultValue}
@@ -159,7 +159,7 @@ export interface ICommandLineChoiceDefinition<TChoice extends string = string>
159159
* This option is only used when `ICommandLineParserOptions.enableTabCompletionAction`
160160
* is enabled.
161161
*/
162-
completions?: () => Promise<TChoice[]>;
162+
completions?: () => Promise<ReadonlyArray<TChoice> | ReadonlySet<TChoice>>;
163163
}
164164

165165
/**
@@ -174,15 +174,15 @@ export interface ICommandLineChoiceListDefinition<TChoice extends string = strin
174174
/**
175175
* A list of strings (which contain no spaces), of possible options which can be selected
176176
*/
177-
alternatives: TChoice[];
177+
alternatives: ReadonlyArray<TChoice> | ReadonlySet<TChoice>;
178178

179179
/**
180180
* An optional callback that provides a list of custom choices for tab completion.
181181
* @remarks
182182
* This option is only used when `ICommandLineParserOptions.enableTabCompletionAction`
183183
* is enabled.
184184
*/
185-
completions?: () => Promise<TChoice[]>;
185+
completions?: () => Promise<ReadonlyArray<TChoice> | ReadonlySet<TChoice>>;
186186
}
187187

188188
/**

libraries/ts-command-line/src/providers/CommandLineParameterProvider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -835,11 +835,11 @@ export abstract class CommandLineParameterProvider {
835835
let type: string | undefined;
836836
switch (kind) {
837837
case CommandLineParameterKind.Choice: {
838-
choices = parameter.alternatives as string[];
838+
choices = Array.from(parameter.alternatives);
839839
break;
840840
}
841841
case CommandLineParameterKind.ChoiceList: {
842-
choices = parameter.alternatives as string[];
842+
choices = Array.from(parameter.alternatives);
843843
action = 'append';
844844
break;
845845
}

libraries/ts-command-line/src/providers/TabCompletionAction.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,10 @@ export class TabCompleteAction extends CommandLineAction {
122122
if (completePartialWord) {
123123
for (const parameterName of parameterNames) {
124124
if (parameterName === secondLastToken) {
125-
const values: ReadonlyArray<string> = await this._getParameterValueCompletionsAsync(
125+
const values: ReadonlySet<string> = await this._getParameterValueCompletionsAsync(
126126
parameterNameMap.get(parameterName)!
127127
);
128-
if (values.length > 0) {
128+
if (values.size > 0) {
129129
yield* this._completeParameterValues(values, lastToken);
130130
return;
131131
}
@@ -135,10 +135,10 @@ export class TabCompleteAction extends CommandLineAction {
135135
} else {
136136
for (const parameterName of parameterNames) {
137137
if (parameterName === lastToken) {
138-
const values: ReadonlyArray<string> = await this._getParameterValueCompletionsAsync(
138+
const values: ReadonlySet<string> = await this._getParameterValueCompletionsAsync(
139139
parameterNameMap.get(parameterName)!
140140
);
141-
if (values.length > 0) {
141+
if (values.size > 0) {
142142
yield* values;
143143
return;
144144
}
@@ -174,8 +174,8 @@ export class TabCompleteAction extends CommandLineAction {
174174

175175
private async _getParameterValueCompletionsAsync(
176176
parameter: CommandLineParameter
177-
): Promise<ReadonlyArray<string>> {
178-
let choiceParameterValues: ReadonlyArray<string> = [];
177+
): Promise<ReadonlySet<string>> {
178+
let choiceParameterValues: ReadonlySet<string> | undefined;
179179
if (parameter.kind === CommandLineParameterKind.Choice) {
180180
choiceParameterValues = parameter.alternatives;
181181
} else if (parameter.kind !== CommandLineParameterKind.Flag) {
@@ -190,12 +190,12 @@ export class TabCompleteAction extends CommandLineAction {
190190
parameterWithArgumentOrChoices = parameter;
191191
}
192192

193-
if (parameterWithArgumentOrChoices?.completions) {
194-
choiceParameterValues = await parameterWithArgumentOrChoices.completions();
195-
}
193+
const completionValues: ReadonlyArray<string> | ReadonlySet<string> | undefined =
194+
await parameterWithArgumentOrChoices?.completions?.();
195+
choiceParameterValues = completionValues instanceof Set ? completionValues : new Set(completionValues);
196196
}
197197

198-
return choiceParameterValues;
198+
return choiceParameterValues ?? new Set();
199199
}
200200

201201
private _getGlobalParameterOffset(tokens: string[]): number {
@@ -215,7 +215,7 @@ export class TabCompleteAction extends CommandLineAction {
215215
}
216216

217217
private *_completeParameterValues(
218-
choiceParameterValues: ReadonlyArray<string>,
218+
choiceParameterValues: ReadonlyArray<string> | ReadonlySet<string>,
219219
lastToken: string
220220
): IterableIterator<string> {
221221
for (const choiceParameterValue of choiceParameterValues) {

vscode-extensions/rush-vscode-command-webview/src/ParameterView/ParameterForm/index.tsx

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -173,32 +173,33 @@ export const ParameterForm = (): JSX.Element => {
173173

174174
switch (parameter.kind) {
175175
case CommandLineParameterKind.Choice: {
176-
const commandLineChoiceParameter: CommandLineChoiceParameter =
176+
const { alternatives, defaultValue }: CommandLineChoiceParameter =
177177
parameter as CommandLineChoiceParameter;
178+
const options: { key: string; text: string }[] = [];
179+
for (const alternative of alternatives) {
180+
options.push({
181+
key: alternative,
182+
text: alternative
183+
});
184+
}
185+
178186
fieldNode = (
179-
<ControlledComboBox
180-
{...baseControllerProps}
181-
defaultValue={commandLineChoiceParameter.defaultValue}
182-
options={commandLineChoiceParameter.alternatives.map((alternative: string) => ({
183-
key: alternative,
184-
text: alternative
185-
}))}
186-
/>
187+
<ControlledComboBox {...baseControllerProps} defaultValue={defaultValue} options={options} />
187188
);
188189
break;
189190
}
190191
case CommandLineParameterKind.ChoiceList: {
191-
const commandLineChoiceListParameter: CommandLineChoiceListParameter =
192+
const { alternatives }: CommandLineChoiceListParameter =
192193
parameter as CommandLineChoiceListParameter;
194+
const options: { key: string; text: string }[] = [];
195+
for (const alternative of alternatives) {
196+
options.push({
197+
key: alternative,
198+
text: alternative
199+
});
200+
}
193201
fieldNode = (
194-
<ControlledComboBox
195-
{...baseControllerProps}
196-
multiSelect={true}
197-
options={commandLineChoiceListParameter.alternatives.map((alternative: string) => ({
198-
key: alternative,
199-
text: alternative
200-
}))}
201-
/>
202+
<ControlledComboBox {...baseControllerProps} multiSelect={true} options={options} />
202203
);
203204
break;
204205
}

0 commit comments

Comments
 (0)