-
Notifications
You must be signed in to change notification settings - Fork 213
/
Copy pathui.ts
524 lines (469 loc) · 15.8 KB
/
ui.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { Result } from "neverthrow";
import { FxError } from "../error";
import { Inputs, OptionItem } from "../types";
import { Colors } from "./../utils/log";
import { LocalFunc, OnSelectionChangeFunc, StaticOptions } from "./question";
/**
* A base structure of user interaction (UI) configuration
*/
export interface UIConfig<T> {
/**
* name is the identifier of the UI
*/
name: string;
/**
* human readable meaningful display name of the UI
*/
title: string;
/**
* placeholder in the UI
*/
placeholder?: string;
/**
* prompt text providing some ask or explanation to the user
*/
prompt?: string;
/**
* `step` and `totalSteps` are used to describe the progress in question flow
* `step` is the sequence number of current question
*/
step?: number;
/**
* `totalStep` is the number of questions totally
*/
totalSteps?: number;
/**
* default input value
*/
default?: T | (() => Promise<T>) | string;
/**
* A function that will be called to validate input and to give a hint to the user.
*
* @param input The current value of the input to be validated.
* @return A human-readable string which is presented as diagnostic message.
* Return `undefined` when 'value' is valid.
*/
validation?: (input: T) => string | undefined | Promise<string | undefined>;
/**
* Actions that can be made within the question.
* @param An array of actions
* @param `icon` is the icon id of the action item
* @param `tooltip` is the hint of the action item
* @param `command` is the command name that will be executed when current action triggered
*/
buttons?: { icon: string; tooltip: string; command: string }[];
/**
* `innerStep` and `innerTotalStep` are used to describe the inner step of a group of questions
* `innerStep` is the sequence number of the current question in the group.
* VSC will display the innerStep and innerTotalStep in the question title.
*/
innerStep?: number;
/**
* `innerTotalStep` is the number of questions in the group in total
*/
innerTotalStep?: number;
}
export interface ConfirmConfig extends UIConfig<boolean> {
/**
* display text for option true or false
*/
transformer?: (value: boolean) => string;
}
/**
* single selection UI config
*/
export interface SingleSelectConfig extends UIConfig<string> {
/**
* option array or a callback function which returns option array
*/
options: StaticOptions | (() => Promise<StaticOptions>);
default?: string | (() => Promise<string>);
/**
* This config only works for option items with `OptionItem[]` type. If `returnObject` is true, the answer value is an `OptionItem` object; otherwise, the answer value is the `id` string of the `OptionItem`.
* In case of option items with `string[]` type, whether `returnObject` is true or false, the returned answer value is always a string.
*/
returnObject?: boolean;
/**
* whether skip selection if there is only one option, default is false
*/
skipSingleOption?: boolean;
}
/**
* multiple selection UI config
*/
export interface MultiSelectConfig extends UIConfig<string[]> {
/**
* option array or a callback function which returns option array
*/
options: StaticOptions | (() => Promise<StaticOptions>);
default?: string[] | (() => Promise<string[]>) | "none" | "all";
/**
* This config only works for option items with `OptionItem[]` type. If `returnObject` is true, the answer value is an array of `OptionItem` objects; otherwise, the answer value is an array of `id` strings.
* In case of option items with `string[]` type, whether `returnObject` is true or false, the returned answer value is always a string array.
*/
returnObject?: boolean;
/**
* a callback function which is triggered when the selected values change, which can change the final selected values.
* @param currentSelectedIds current selected option ids
* @param previousSelectedIds previous selected option ids
* @returns the final selected option ids
*/
onDidChangeSelection?: OnSelectionChangeFunc;
/**
* whether skip selection if there is only one option, default is false
*/
skipSingleOption?: boolean;
}
/**
* text input UI config
*/
export interface InputTextConfig extends UIConfig<string> {
/**
* If the input value should be hidden. Defaults to false.
*/
password?: boolean;
default?: string | (() => Promise<string>);
/**
* A function that will be called to validate the input that user accepted.
*
* @param input The current value of the input to be validated.
* @return A human-readable string which is presented as diagnostic message.
* Return `undefined` when 'value' is valid.
*/
additionalValidationOnAccept?: (
input: string
) => string | undefined | Promise<string | undefined>;
}
/**
* single file selector config
*/
export type SelectFileConfig = UIConfig<string> & {
/**
* This will only take effect in VSC.
* A set of file filters that are used by the dialog. Each entry is a human-readable label,
* like "TypeScript", and an array of extensions, e.g.
* ```ts
* {
* 'Images': ['png', 'jpg']
* 'TypeScript': ['ts', 'tsx']
* }
* ```
*/
filters?: { [name: string]: string[] };
default?: string | (() => Promise<string>);
/**
* Possible files that will be listed for users to select.
* The id cannot be "default" or "browse" as they are reserved for default and browse options.
*/
possibleFiles?: {
id: string;
label: string;
description?: string;
}[];
/**
* Default Uri when open file selector window.
*/
defaultFolder?: string | (() => Promise<string>);
};
/**
* multiple files selector config
*/
export type SelectFilesConfig = UIConfig<string[]> & {
/**
* This will only take effect in VSC.
* A set of file filters that are used by the dialog. Each entry is a human-readable label,
* like "TypeScript", and an array of extensions, e.g.
* ```ts
* {
* 'Images': ['png', 'jpg']
* 'TypeScript': ['ts', 'tsx']
* }
* ```
*/
filters?: { [name: string]: string[] };
default?: string[] | (() => Promise<string[]>);
};
/**
* folder selector config
*/
export type SelectFolderConfig = UIConfig<string> & {
default?: string | (() => Promise<string>);
};
/**
* func execution config
*/
export interface ExecuteFuncConfig extends UIConfig<string> {
func: LocalFunc<any>;
inputs: Inputs;
}
export interface SingleFileOrInputConfig extends UIConfig<string> {
/**
* An item shown in the list in VSC that user can click to input text.
*/
inputOptionItem: OptionItem;
/**
* Config for the input box.
*/
inputBoxConfig: UIConfig<string>;
/**
* This will only take effect in VSC.
* A set of file filters that are used by the dialog. Each entry is a human-readable label,
* like "TypeScript", and an array of extensions, e.g.
* ```ts
* {
* 'Images': ['png', 'jpg']
* 'TypeScript': ['ts', 'tsx']
* }
* ```
*/
filters?: { [name: string]: string[] };
}
/**
* a wrapper of user input result
*/
export interface InputResult<T> {
/**
* `success`: the returned answer value is successfully collected when user click predefined confirm button/key, user will continue to answer the next question if available
* `skip`: the answer value is automatically selected when `skipSingleOption` is true for single/multiple selection list, user will continue to answer the next question if available
* `back`: the returned answer is undefined because user click the go-back button/key and will go back to re-answer the previous question in the question flow
*/
type: "success" | "skip" | "back";
/**
* answer value
*/
result?: T;
/**
* resolved option list
*/
options?: StaticOptions;
}
export type ConfirmResult = InputResult<boolean>;
export type SingleSelectResult = InputResult<string | OptionItem>;
export type MultiSelectResult = InputResult<StaticOptions>;
export type InputTextResult = InputResult<string>;
export type SelectFileResult = InputResult<string>;
export type SelectFilesResult = InputResult<string[]>;
export type SelectFolderResult = InputResult<string>;
/**
* Definition of user interaction, which is platform independent
*/
export interface UserInteraction {
/**
* Shows confirm dialog
* @param config confirm config
* @returns A promise that resolves to the confirm result wrapper or FxError
* @throws FxError
*/
confirm?: (config: ConfirmConfig) => Promise<Result<ConfirmResult, FxError>>;
/**
* Shows a single selection list
* @param config single selection config
* @returns A promise that resolves to the single select result wrapper or FxError
* @throws FxError
*/
selectOption: (config: SingleSelectConfig) => Promise<Result<SingleSelectResult, FxError>>;
/**
* Shows a multiple selection list
* @param config multiple selection config
* @returns A promise that resolves to the multiple select result wrapper or FxError
* @throws FxError
*/
selectOptions: (config: MultiSelectConfig) => Promise<Result<MultiSelectResult, FxError>>;
/**
* Opens an input box to ask the user for input.
* @param config text input config
* @returns A promise that resolves to the text input result wrapper or FxError
* @throws FxError
*/
inputText: (config: InputTextConfig) => Promise<Result<InputTextResult, FxError>>;
/**
* Shows a file open dialog to the user which allows to select a single file
* @param config file selector config
* @returns A promise that resolves to the file selector result wrapper or FxError
* @throws FxError
*/
selectFile: (config: SelectFileConfig) => Promise<Result<SelectFileResult, FxError>>;
/**
* Shows a file open dialog to the user which allows to select multiple files
* @param config multiple files selector config
* @returns A promise that resolves to the multiple files selector result wrapper or FxError
* @throws FxError
*/
selectFiles: (config: SelectFilesConfig) => Promise<Result<SelectFilesResult, FxError>>;
/**
* Shows a file open dialog to the user which allows to select a folder
* @param config folder selector config
* @returns A promise that resolves to the folder selector result wrapper or FxError
* @throws FxError
*/
selectFolder: (config: SelectFolderConfig) => Promise<Result<SelectFolderResult, FxError>>;
/**
* Opens a link externally in the browser.
* @param link The uri that should be opened.
* @returns A promise indicating if open was successful.
*/
openUrl(link: string): Promise<Result<boolean, FxError>>;
/**
* Show an information/warning/error message to users. Optionally provide an array of items which will be presented as clickable buttons.
* @param level message level
* @param message The message to show.
* @param items A set of items that will be rendered as actions in the message.
* @returns A promise that resolves to the selected item or `undefined` when being dismissed.
*/
showMessage(
level: "info" | "warn" | "error",
message: string,
modal: boolean,
...items: string[]
): Promise<Result<string | undefined, FxError>>;
/**
* Show an information/warning/error message with different colors to users, which only works for CLI.
* @param level message level
* @param message The message with color to show. The color only works for CLI.
* @param items A set of items that will be rendered as actions in the message.
* @returns A promise that resolves to the selected item or `undefined` when being dismissed.
*/
showMessage(
level: "info" | "warn" | "error",
message: Array<{ content: string; color: Colors }>,
modal: boolean,
...items: string[]
): Promise<Result<string | undefined, FxError>>;
/**
* Create a new progress bar with the specified title and the number of steps. It will
* return a progress handler and you can use this handler to control the detail message
* of it.
* ${currentStep} will increase from 0 to ${totalSteps}.
* @param title the title of this progress bar.
* @param totalSteps the number of steps.
* @returns the handler of a progress bar
*/
createProgressBar: (title: string, totalSteps: number) => IProgressHandler;
/**
* Reload window to update user interface. (Only works for VS Code)
* @returns A promise indicating if reload is successful.
*/
reload?(): Promise<Result<boolean, FxError>>;
/**
* Execute a function. User interface can decide what the UX is.
* @param config execute function configurations
*/
executeFunction?(config: ExecuteFuncConfig): any | Promise<any>;
/**
* Opens a file.
* @param filePath The path of the file that should be opened.
* @returns A promise indicating if open was successful.
*/
openFile?(filePath: string): Promise<Result<boolean, FxError>>;
/**
* run a user defined command in terminals of UI
* @param args
*/
runCommand?(args: {
cmd: string;
workingDirectory?: string;
shell?: string;
timeout?: number;
env?: { [k: string]: string };
shellName?: string;
iconPath?: string;
}): Promise<Result<string, FxError>>;
/**
* In VSC, it shows two options to user, one will open a dialog to the user which allows to select a single file, another one will show an input box asking to enter a value.
* If CLI, it will directly asks user to enter a value.
* @param config config to select local file or enter a value
* @returns A promise that resolves to the local file path or the value entered by user or FxError
* @throws FxError
*/
selectFileOrInput?(
config: SingleFileOrInputConfig
): Promise<Result<InputResult<string>, FxError>>;
/**
* Supports in VSC only for now. Show diagnostic message in editor.
*/
showDiagnosticInfo?(diagnostics: IDiagnosticInfo[]): void;
}
export interface IProgressHandler {
/**
* Start this progress bar. After calling it, the progress bar will be seen to users with
* ${currentStep} = 0 and ${detail} = detail.
* @param detail the detail message of the next work.
*/
start: (detail?: string) => Promise<void>;
/**
* Update the progress bar's message. After calling it, the progress bar will be seen to
* users with ${currentStep}++ and ${detail} = detail.
* This func must be called after calling start().
* @param detail the detail message of the next work.
*/
next: (detail?: string) => Promise<void>;
/**
* End the progress bar and tell if success. After calling it, the progress bar will disappear. This handler
* can be reused after calling end().
*/
end: (success: boolean, hideAfterFinish?: boolean) => Promise<void>;
}
export enum DiagnosticSeverity {
/**
* Something not allowed by the rules of a language or other means.
*/
Error = 0,
/**
* Something suspicious but allowed.
*/
Warning = 1,
/**
* Something to inform about but not a problem.
*/
Information = 2,
/**
* Something to hint to a better way of doing it, like proposing
* a refactoring.
*/
Hint = 3,
}
export interface IDiagnosticInfo {
/**
* Path of file where diagnostic shows.
*/
filePath: string;
/**
* Line number where diagnostic info starts.
*/
startLine: number;
/**
* Index of the beginning character where diagnostic info shows
*/
startIndex: number;
/**
* Line number where diagnostic info ends.
*/
endLine: number;
/**
* Index of the end character where diagnostic info ends.
*/
endIndex: number;
/**
* Message.
*/
message: string;
/**
* Severity.
*/
severity: DiagnosticSeverity;
/**
* A code or identifier for this diagnostic.
*/
code?: {
/**
* Value.
*/
value: string;
/**
* Link to open with more information about the diagnostic error.
*/
link: string;
};
}