-
Notifications
You must be signed in to change notification settings - Fork 934
/
Copy pathInputCustomPrompt.ts
116 lines (98 loc) Β· 2.96 KB
/
InputCustomPrompt.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
/// <reference path="./inquirer.d.ts" />
import chalk from 'chalk';
import inquirer, {type Answers, type InputCustomOptions} from 'inquirer';
import InputPrompt from 'inquirer/lib/prompts/input.js';
import observe from 'inquirer/lib/utils/events.js';
import type {Interface as ReadlineInterface, Key} from 'readline';
import type {Subscription} from 'rxjs';
import SuccessfulPromptStateData = inquirer.prompts.SuccessfulPromptStateData;
interface KeyDescriptor {
value: string;
key: Key;
}
export default class InputCustomPrompt<
TQuestion extends InputCustomOptions = InputCustomOptions
> extends InputPrompt<TQuestion> {
private lineSubscription: Subscription;
private readonly tabCompletion: string[];
constructor(
question: TQuestion,
readLine: ReadlineInterface,
answers: Answers
) {
super(question, readLine, answers);
if (this.opt.log) {
this.rl.write(this.opt.log(answers));
}
if (!this.opt.maxLength) {
this.throwParamError('maxLength');
}
const events = observe(this.rl);
this.lineSubscription = events.keypress.subscribe(
this.onKeyPress2.bind(this)
);
this.tabCompletion = (this.opt.tabCompletion || [])
.map((item) => item.value)
.sort((a, b) => a.localeCompare(b));
}
onEnd(state: SuccessfulPromptStateData): void {
this.lineSubscription.unsubscribe();
super.onEnd(state);
}
/**
* @see https://nodejs.org/api/readline.html#readline_rl_write_data_key
* @see https://nodejs.org/api/readline.html#readline_rl_line
*/
updateLine(line: string): void {
this.rl.write(null as any, {ctrl: true, name: 'b'});
this.rl.write(null as any, {ctrl: true, name: 'd'});
this.rl.write(line.substr(this.rl.line.length));
}
onKeyPress2(e: KeyDescriptor): void {
if (e.key.name === 'tab' && this.tabCompletion.length > 0) {
let line = this.rl.line.trim();
if (line.length > 0) {
for (const item of this.tabCompletion) {
if (item.startsWith(line) && item !== line) {
line = item;
break;
}
}
}
this.updateLine(line);
}
}
measureInput(input: string): number {
if (this.opt.filter) {
return this.opt.filter(input, this.answers).length;
}
return input.length;
}
render(error?: string): void {
const answered = this.status === 'answered';
let message = this.getQuestion();
const length = this.measureInput(this.rl.line);
if (answered) {
message += chalk.cyan(this.answer);
} else if (this.opt.transformer) {
message += this.opt.transformer(this.rl.line, this.answers, {});
}
let bottomContent = '';
if (error) {
bottomContent = chalk.red('>> ') + error;
} else if (!answered) {
const maxLength = this.opt.maxLength(this.answers);
if (maxLength < Infinity) {
const lengthRemaining = maxLength - length;
const color =
lengthRemaining <= 5
? chalk.red
: lengthRemaining <= 10
? chalk.yellow
: chalk.grey;
bottomContent = color(`${lengthRemaining} characters left`);
}
}
this.screen.render(message, bottomContent);
}
}