@gjsify/readline 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/esm/index.js +423 -50
- package/lib/types/index.d.ts +19 -36
- package/package.json +4 -4
- package/src/index.ts +301 -117
- package/tsconfig.tsbuildinfo +1 -1
package/lib/esm/index.js
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
|
+
import { StringDecoder } from "node:string_decoder";
|
|
3
|
+
const LINE_END = /\r\n|\r|\n/;
|
|
2
4
|
class Interface extends EventEmitter {
|
|
3
5
|
terminal;
|
|
4
6
|
line = "";
|
|
5
7
|
cursor = 0;
|
|
6
8
|
_input;
|
|
7
9
|
_output;
|
|
10
|
+
get input() {
|
|
11
|
+
return this._input;
|
|
12
|
+
}
|
|
13
|
+
get output() {
|
|
14
|
+
return this._output;
|
|
15
|
+
}
|
|
8
16
|
_prompt;
|
|
9
17
|
_closed = false;
|
|
10
18
|
_paused = false;
|
|
@@ -13,6 +21,12 @@ class Interface extends EventEmitter {
|
|
|
13
21
|
_crlfDelay;
|
|
14
22
|
_lineBuffer = "";
|
|
15
23
|
_questionCallback = null;
|
|
24
|
+
// Per-listener refs so close() removes only our listeners, not the keypress
|
|
25
|
+
// parser's 'data' listener — which must survive across sequential prompts.
|
|
26
|
+
_boundOnData = null;
|
|
27
|
+
_boundOnEnd = null;
|
|
28
|
+
_boundOnError = null;
|
|
29
|
+
_boundOnKeypress = null;
|
|
16
30
|
constructor(input, output) {
|
|
17
31
|
super();
|
|
18
32
|
let opts;
|
|
@@ -29,21 +43,45 @@ class Interface extends EventEmitter {
|
|
|
29
43
|
this.history = [];
|
|
30
44
|
this._crlfDelay = opts.crlfDelay ?? 100;
|
|
31
45
|
if (this._input) {
|
|
32
|
-
this.
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
46
|
+
this._boundOnData = (chunk) => this._onData(chunk);
|
|
47
|
+
this._boundOnEnd = () => this._onEnd();
|
|
48
|
+
this._boundOnError = (err) => this.emit("error", err);
|
|
49
|
+
this._input.on("data", this._boundOnData);
|
|
50
|
+
this._input.on("end", this._boundOnEnd);
|
|
51
|
+
this._input.on("error", this._boundOnError);
|
|
35
52
|
if ("setEncoding" in this._input && typeof this._input.setEncoding === "function") {
|
|
36
53
|
this._input.setEncoding("utf8");
|
|
37
54
|
}
|
|
55
|
+
if (this.terminal) {
|
|
56
|
+
emitKeypressEvents(this._input, this);
|
|
57
|
+
if ("setRawMode" in this._input && typeof this._input.setRawMode === "function") {
|
|
58
|
+
if (!this._input.isRaw) this._input.setRawMode(true);
|
|
59
|
+
}
|
|
60
|
+
if ("resume" in this._input && typeof this._input.resume === "function") {
|
|
61
|
+
this._input.resume();
|
|
62
|
+
}
|
|
63
|
+
this._boundOnKeypress = (str, key) => {
|
|
64
|
+
if (!key) return;
|
|
65
|
+
if (key.name === "backspace" || key.name === "delete") {
|
|
66
|
+
if (this.cursor > 0) {
|
|
67
|
+
this.line = this.line.slice(0, this.cursor - 1) + this.line.slice(this.cursor);
|
|
68
|
+
this.cursor--;
|
|
69
|
+
}
|
|
70
|
+
} else if (str && str.length === 1 && !key.ctrl && !key.meta && key.name !== "return" && key.name !== "enter" && key.name !== "escape") {
|
|
71
|
+
this.line = this.line.slice(0, this.cursor) + str + this.line.slice(this.cursor);
|
|
72
|
+
this.cursor++;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
this._input.on("keypress", this._boundOnKeypress);
|
|
76
|
+
}
|
|
38
77
|
}
|
|
39
78
|
}
|
|
40
79
|
_onData(chunk) {
|
|
41
80
|
if (this._closed || this._paused) return;
|
|
42
81
|
const str = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
43
82
|
this._lineBuffer += str;
|
|
44
|
-
const lineEnd = /\r\n|\r|\n/;
|
|
45
83
|
let m;
|
|
46
|
-
while ((m =
|
|
84
|
+
while ((m = LINE_END.exec(this._lineBuffer)) !== null) {
|
|
47
85
|
const line = this._lineBuffer.substring(0, m.index);
|
|
48
86
|
this._lineBuffer = this._lineBuffer.substring(m.index + m[0].length);
|
|
49
87
|
this._onLine(line);
|
|
@@ -53,9 +91,7 @@ class Interface extends EventEmitter {
|
|
|
53
91
|
if (line.length > 0 && this._historySize > 0) {
|
|
54
92
|
if (this.history.length === 0 || this.history[0] !== line) {
|
|
55
93
|
this.history.unshift(line);
|
|
56
|
-
if (this.history.length > this._historySize)
|
|
57
|
-
this.history.pop();
|
|
58
|
-
}
|
|
94
|
+
if (this.history.length > this._historySize) this.history.pop();
|
|
59
95
|
}
|
|
60
96
|
}
|
|
61
97
|
if (this._questionCallback) {
|
|
@@ -72,53 +108,64 @@ class Interface extends EventEmitter {
|
|
|
72
108
|
}
|
|
73
109
|
this.close();
|
|
74
110
|
}
|
|
75
|
-
/** Set the prompt string. */
|
|
76
111
|
setPrompt(prompt) {
|
|
77
112
|
this._prompt = prompt;
|
|
78
113
|
}
|
|
79
|
-
/** Get the current prompt string. */
|
|
80
114
|
getPrompt() {
|
|
81
115
|
return this._prompt;
|
|
82
116
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
throw new Error("readline was closed");
|
|
87
|
-
}
|
|
88
|
-
if (this._output) {
|
|
89
|
-
this._output.write(this._prompt);
|
|
90
|
-
}
|
|
117
|
+
prompt(_preserveCursor) {
|
|
118
|
+
if (this._closed) throw new Error("readline was closed");
|
|
119
|
+
this._output?.write(this._prompt);
|
|
91
120
|
}
|
|
92
121
|
question(query, optionsOrCallback, callback) {
|
|
93
|
-
if (this._closed)
|
|
94
|
-
throw new Error("readline was closed");
|
|
95
|
-
}
|
|
122
|
+
if (this._closed) throw new Error("readline was closed");
|
|
96
123
|
const cb = typeof optionsOrCallback === "function" ? optionsOrCallback : callback;
|
|
97
124
|
this._questionCallback = cb;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
125
|
+
this._output?.write(query);
|
|
126
|
+
}
|
|
127
|
+
clearLine(_dir, callback) {
|
|
128
|
+
this.line = "";
|
|
129
|
+
this.cursor = 0;
|
|
130
|
+
if (callback) callback();
|
|
131
|
+
return true;
|
|
101
132
|
}
|
|
102
|
-
/** Write data to the output stream. */
|
|
103
133
|
write(data, key) {
|
|
104
134
|
if (this._closed) return;
|
|
135
|
+
if (key) {
|
|
136
|
+
if (this._input) this._input.emit("keypress", data ?? "", key);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
105
139
|
if (data !== null && data !== void 0) {
|
|
106
|
-
|
|
107
|
-
|
|
140
|
+
const str = typeof data === "string" ? data : data.toString("utf8");
|
|
141
|
+
if (str) {
|
|
142
|
+
this.line = this.line.slice(0, this.cursor) + str + this.line.slice(this.cursor);
|
|
143
|
+
this.cursor += str.length;
|
|
108
144
|
}
|
|
145
|
+
this._output?.write(data);
|
|
109
146
|
}
|
|
110
147
|
}
|
|
111
|
-
/** Close the interface. */
|
|
112
148
|
close() {
|
|
113
149
|
if (this._closed) return;
|
|
114
150
|
this._closed = true;
|
|
115
151
|
if (this._input) {
|
|
116
|
-
this._input.
|
|
117
|
-
this._input.
|
|
152
|
+
if (this._boundOnData) this._input.removeListener("data", this._boundOnData);
|
|
153
|
+
if (this._boundOnEnd) this._input.removeListener("end", this._boundOnEnd);
|
|
154
|
+
if (this._boundOnError) this._input.removeListener("error", this._boundOnError);
|
|
155
|
+
if (this._boundOnKeypress) this._input.removeListener("keypress", this._boundOnKeypress);
|
|
156
|
+
this._boundOnData = null;
|
|
157
|
+
this._boundOnEnd = null;
|
|
158
|
+
this._boundOnError = null;
|
|
159
|
+
this._boundOnKeypress = null;
|
|
160
|
+
if (this.terminal && this._input.isRaw && "setRawMode" in this._input && typeof this._input.setRawMode === "function") {
|
|
161
|
+
this._input.setRawMode(false);
|
|
162
|
+
}
|
|
163
|
+
if ("pause" in this._input && typeof this._input.pause === "function") {
|
|
164
|
+
this._input.pause();
|
|
165
|
+
}
|
|
118
166
|
}
|
|
119
167
|
this.emit("close");
|
|
120
168
|
}
|
|
121
|
-
/** Pause the input stream. */
|
|
122
169
|
pause() {
|
|
123
170
|
if (this._closed) return this;
|
|
124
171
|
this._paused = true;
|
|
@@ -128,7 +175,6 @@ class Interface extends EventEmitter {
|
|
|
128
175
|
this.emit("pause");
|
|
129
176
|
return this;
|
|
130
177
|
}
|
|
131
|
-
/** Resume the input stream. */
|
|
132
178
|
resume() {
|
|
133
179
|
if (this._closed) return this;
|
|
134
180
|
this._paused = false;
|
|
@@ -138,15 +184,16 @@ class Interface extends EventEmitter {
|
|
|
138
184
|
this.emit("resume");
|
|
139
185
|
return this;
|
|
140
186
|
}
|
|
141
|
-
/** Get the current line content. */
|
|
142
187
|
getCursorPos() {
|
|
143
|
-
|
|
188
|
+
const columns = this._output?.columns ?? 80;
|
|
189
|
+
const len = this._prompt.length + this.cursor;
|
|
190
|
+
return { rows: Math.floor(len / columns), cols: len % columns };
|
|
144
191
|
}
|
|
145
192
|
[Symbol.asyncIterator]() {
|
|
146
193
|
const lines = [];
|
|
147
194
|
let resolve = null;
|
|
148
195
|
let done = false;
|
|
149
|
-
|
|
196
|
+
const onLine = (line) => {
|
|
150
197
|
if (resolve) {
|
|
151
198
|
const r = resolve;
|
|
152
199
|
resolve = null;
|
|
@@ -154,29 +201,29 @@ class Interface extends EventEmitter {
|
|
|
154
201
|
} else {
|
|
155
202
|
lines.push(line);
|
|
156
203
|
}
|
|
157
|
-
}
|
|
158
|
-
|
|
204
|
+
};
|
|
205
|
+
const onClose = () => {
|
|
159
206
|
done = true;
|
|
160
207
|
if (resolve) {
|
|
161
208
|
const r = resolve;
|
|
162
209
|
resolve = null;
|
|
163
210
|
r({ value: void 0, done: true });
|
|
164
211
|
}
|
|
165
|
-
}
|
|
212
|
+
};
|
|
213
|
+
this.on("line", onLine);
|
|
214
|
+
this.on("close", onClose);
|
|
166
215
|
return {
|
|
167
|
-
next() {
|
|
168
|
-
if (lines.length > 0) {
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
if (done) {
|
|
172
|
-
return Promise.resolve({ value: void 0, done: true });
|
|
173
|
-
}
|
|
216
|
+
next: () => {
|
|
217
|
+
if (lines.length > 0) return Promise.resolve({ value: lines.shift(), done: false });
|
|
218
|
+
if (done) return Promise.resolve({ value: void 0, done: true });
|
|
174
219
|
return new Promise((r) => {
|
|
175
220
|
resolve = r;
|
|
176
221
|
});
|
|
177
222
|
},
|
|
178
|
-
return() {
|
|
223
|
+
return: () => {
|
|
179
224
|
done = true;
|
|
225
|
+
this.removeListener("line", onLine);
|
|
226
|
+
this.removeListener("close", onClose);
|
|
180
227
|
return Promise.resolve({ value: void 0, done: true });
|
|
181
228
|
},
|
|
182
229
|
[Symbol.asyncIterator]() {
|
|
@@ -229,13 +276,339 @@ function moveCursor(stream, dx, dy, callback) {
|
|
|
229
276
|
else if (dx < 0) code += `\x1B[${-dx}D`;
|
|
230
277
|
if (dy > 0) code += `\x1B[${dy}B`;
|
|
231
278
|
else if (dy < 0) code += `\x1B[${-dy}A`;
|
|
232
|
-
if (code)
|
|
233
|
-
return stream.write(code, callback);
|
|
234
|
-
}
|
|
279
|
+
if (code) return stream.write(code, callback);
|
|
235
280
|
if (callback) callback();
|
|
236
281
|
return true;
|
|
237
282
|
}
|
|
238
|
-
|
|
283
|
+
const ESCAPE_CODE_TIMEOUT = 500;
|
|
284
|
+
function* emitKeys(stream) {
|
|
285
|
+
while (true) {
|
|
286
|
+
let ch = yield;
|
|
287
|
+
let s = ch;
|
|
288
|
+
let escaped = false;
|
|
289
|
+
const key = { sequence: "", name: void 0, ctrl: false, meta: false, shift: false };
|
|
290
|
+
if (ch === "\x1B") {
|
|
291
|
+
escaped = true;
|
|
292
|
+
s += ch = yield;
|
|
293
|
+
if (ch === "\x1B") s += ch = yield;
|
|
294
|
+
}
|
|
295
|
+
if (escaped && (ch === "O" || ch === "[")) {
|
|
296
|
+
let code = ch;
|
|
297
|
+
let modifier = 0;
|
|
298
|
+
if (ch === "O") {
|
|
299
|
+
s += ch = yield;
|
|
300
|
+
if (ch >= "0" && ch <= "9") {
|
|
301
|
+
modifier = ch.charCodeAt(0) - 1;
|
|
302
|
+
s += ch = yield;
|
|
303
|
+
}
|
|
304
|
+
code += ch;
|
|
305
|
+
} else if (ch === "[") {
|
|
306
|
+
s += ch = yield;
|
|
307
|
+
if (ch === "[") {
|
|
308
|
+
code += ch;
|
|
309
|
+
s += ch = yield;
|
|
310
|
+
}
|
|
311
|
+
const cmdStart = s.length - 1;
|
|
312
|
+
if (ch >= "0" && ch <= "9") {
|
|
313
|
+
s += ch = yield;
|
|
314
|
+
if (ch >= "0" && ch <= "9") {
|
|
315
|
+
s += ch = yield;
|
|
316
|
+
if (ch >= "0" && ch <= "9") s += ch = yield;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (ch === ";") {
|
|
320
|
+
s += ch = yield;
|
|
321
|
+
if (ch >= "0" && ch <= "9") s += yield;
|
|
322
|
+
}
|
|
323
|
+
const cmd = s.slice(cmdStart);
|
|
324
|
+
let match;
|
|
325
|
+
if (match = /^(?:(\d\d?)(?:;(\d))?([~^$])|(\d{3}~))$/.exec(cmd)) {
|
|
326
|
+
if (match[4]) {
|
|
327
|
+
code += match[4];
|
|
328
|
+
} else {
|
|
329
|
+
code += match[1] + match[3];
|
|
330
|
+
modifier = (parseInt(match[2] ?? "1", 10) || 1) - 1;
|
|
331
|
+
}
|
|
332
|
+
} else if (match = /^((\d;)?(\d))?([A-Za-z])$/.exec(cmd)) {
|
|
333
|
+
code += match[4];
|
|
334
|
+
modifier = (parseInt(match[3] ?? "1", 10) || 1) - 1;
|
|
335
|
+
} else {
|
|
336
|
+
code += cmd;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
key.ctrl = !!(modifier & 4);
|
|
340
|
+
key.meta = !!(modifier & 10);
|
|
341
|
+
key.shift = !!(modifier & 1);
|
|
342
|
+
key.code = code;
|
|
343
|
+
switch (code) {
|
|
344
|
+
case "[P":
|
|
345
|
+
case "OP":
|
|
346
|
+
case "[11~":
|
|
347
|
+
case "[[A":
|
|
348
|
+
key.name = "f1";
|
|
349
|
+
break;
|
|
350
|
+
case "[Q":
|
|
351
|
+
case "OQ":
|
|
352
|
+
case "[12~":
|
|
353
|
+
case "[[B":
|
|
354
|
+
key.name = "f2";
|
|
355
|
+
break;
|
|
356
|
+
case "[R":
|
|
357
|
+
case "OR":
|
|
358
|
+
case "[13~":
|
|
359
|
+
case "[[C":
|
|
360
|
+
key.name = "f3";
|
|
361
|
+
break;
|
|
362
|
+
case "[S":
|
|
363
|
+
case "OS":
|
|
364
|
+
case "[14~":
|
|
365
|
+
case "[[D":
|
|
366
|
+
key.name = "f4";
|
|
367
|
+
break;
|
|
368
|
+
case "[15~":
|
|
369
|
+
case "[[E":
|
|
370
|
+
key.name = "f5";
|
|
371
|
+
break;
|
|
372
|
+
case "[17~":
|
|
373
|
+
key.name = "f6";
|
|
374
|
+
break;
|
|
375
|
+
case "[18~":
|
|
376
|
+
key.name = "f7";
|
|
377
|
+
break;
|
|
378
|
+
case "[19~":
|
|
379
|
+
key.name = "f8";
|
|
380
|
+
break;
|
|
381
|
+
case "[20~":
|
|
382
|
+
key.name = "f9";
|
|
383
|
+
break;
|
|
384
|
+
case "[21~":
|
|
385
|
+
key.name = "f10";
|
|
386
|
+
break;
|
|
387
|
+
case "[23~":
|
|
388
|
+
key.name = "f11";
|
|
389
|
+
break;
|
|
390
|
+
case "[24~":
|
|
391
|
+
key.name = "f12";
|
|
392
|
+
break;
|
|
393
|
+
case "[200~":
|
|
394
|
+
key.name = "paste-start";
|
|
395
|
+
break;
|
|
396
|
+
case "[201~":
|
|
397
|
+
key.name = "paste-end";
|
|
398
|
+
break;
|
|
399
|
+
case "[A":
|
|
400
|
+
case "OA":
|
|
401
|
+
key.name = "up";
|
|
402
|
+
break;
|
|
403
|
+
case "[B":
|
|
404
|
+
case "OB":
|
|
405
|
+
key.name = "down";
|
|
406
|
+
break;
|
|
407
|
+
case "[C":
|
|
408
|
+
case "OC":
|
|
409
|
+
key.name = "right";
|
|
410
|
+
break;
|
|
411
|
+
case "[D":
|
|
412
|
+
case "OD":
|
|
413
|
+
key.name = "left";
|
|
414
|
+
break;
|
|
415
|
+
case "[E":
|
|
416
|
+
case "OE":
|
|
417
|
+
key.name = "clear";
|
|
418
|
+
break;
|
|
419
|
+
case "[F":
|
|
420
|
+
case "OF":
|
|
421
|
+
key.name = "end";
|
|
422
|
+
break;
|
|
423
|
+
case "[H":
|
|
424
|
+
case "OH":
|
|
425
|
+
key.name = "home";
|
|
426
|
+
break;
|
|
427
|
+
case "[1~":
|
|
428
|
+
key.name = "home";
|
|
429
|
+
break;
|
|
430
|
+
case "[2~":
|
|
431
|
+
key.name = "insert";
|
|
432
|
+
break;
|
|
433
|
+
case "[3~":
|
|
434
|
+
key.name = "delete";
|
|
435
|
+
break;
|
|
436
|
+
case "[4~":
|
|
437
|
+
key.name = "end";
|
|
438
|
+
break;
|
|
439
|
+
case "[5~":
|
|
440
|
+
case "[[5~":
|
|
441
|
+
key.name = "pageup";
|
|
442
|
+
break;
|
|
443
|
+
case "[6~":
|
|
444
|
+
case "[[6~":
|
|
445
|
+
key.name = "pagedown";
|
|
446
|
+
break;
|
|
447
|
+
case "[7~":
|
|
448
|
+
key.name = "home";
|
|
449
|
+
break;
|
|
450
|
+
case "[8~":
|
|
451
|
+
key.name = "end";
|
|
452
|
+
break;
|
|
453
|
+
case "[a":
|
|
454
|
+
key.name = "up";
|
|
455
|
+
key.shift = true;
|
|
456
|
+
break;
|
|
457
|
+
case "[b":
|
|
458
|
+
key.name = "down";
|
|
459
|
+
key.shift = true;
|
|
460
|
+
break;
|
|
461
|
+
case "[c":
|
|
462
|
+
key.name = "right";
|
|
463
|
+
key.shift = true;
|
|
464
|
+
break;
|
|
465
|
+
case "[d":
|
|
466
|
+
key.name = "left";
|
|
467
|
+
key.shift = true;
|
|
468
|
+
break;
|
|
469
|
+
case "[2$":
|
|
470
|
+
key.name = "insert";
|
|
471
|
+
key.shift = true;
|
|
472
|
+
break;
|
|
473
|
+
case "[3$":
|
|
474
|
+
key.name = "delete";
|
|
475
|
+
key.shift = true;
|
|
476
|
+
break;
|
|
477
|
+
case "[5$":
|
|
478
|
+
key.name = "pageup";
|
|
479
|
+
key.shift = true;
|
|
480
|
+
break;
|
|
481
|
+
case "[6$":
|
|
482
|
+
key.name = "pagedown";
|
|
483
|
+
key.shift = true;
|
|
484
|
+
break;
|
|
485
|
+
case "[7$":
|
|
486
|
+
key.name = "home";
|
|
487
|
+
key.shift = true;
|
|
488
|
+
break;
|
|
489
|
+
case "[8$":
|
|
490
|
+
key.name = "end";
|
|
491
|
+
key.shift = true;
|
|
492
|
+
break;
|
|
493
|
+
case "Oa":
|
|
494
|
+
key.name = "up";
|
|
495
|
+
key.ctrl = true;
|
|
496
|
+
break;
|
|
497
|
+
case "Ob":
|
|
498
|
+
key.name = "down";
|
|
499
|
+
key.ctrl = true;
|
|
500
|
+
break;
|
|
501
|
+
case "Oc":
|
|
502
|
+
key.name = "right";
|
|
503
|
+
key.ctrl = true;
|
|
504
|
+
break;
|
|
505
|
+
case "Od":
|
|
506
|
+
key.name = "left";
|
|
507
|
+
key.ctrl = true;
|
|
508
|
+
break;
|
|
509
|
+
case "[2^":
|
|
510
|
+
key.name = "insert";
|
|
511
|
+
key.ctrl = true;
|
|
512
|
+
break;
|
|
513
|
+
case "[3^":
|
|
514
|
+
key.name = "delete";
|
|
515
|
+
key.ctrl = true;
|
|
516
|
+
break;
|
|
517
|
+
case "[5^":
|
|
518
|
+
key.name = "pageup";
|
|
519
|
+
key.ctrl = true;
|
|
520
|
+
break;
|
|
521
|
+
case "[6^":
|
|
522
|
+
key.name = "pagedown";
|
|
523
|
+
key.ctrl = true;
|
|
524
|
+
break;
|
|
525
|
+
case "[7^":
|
|
526
|
+
key.name = "home";
|
|
527
|
+
key.ctrl = true;
|
|
528
|
+
break;
|
|
529
|
+
case "[8^":
|
|
530
|
+
key.name = "end";
|
|
531
|
+
key.ctrl = true;
|
|
532
|
+
break;
|
|
533
|
+
case "[Z":
|
|
534
|
+
key.name = "tab";
|
|
535
|
+
key.shift = true;
|
|
536
|
+
break;
|
|
537
|
+
default:
|
|
538
|
+
key.name = "undefined";
|
|
539
|
+
break;
|
|
540
|
+
}
|
|
541
|
+
} else if (ch === "\r") {
|
|
542
|
+
key.name = "return";
|
|
543
|
+
key.meta = escaped;
|
|
544
|
+
} else if (ch === "\n") {
|
|
545
|
+
key.name = "enter";
|
|
546
|
+
key.meta = escaped;
|
|
547
|
+
} else if (ch === " ") {
|
|
548
|
+
key.name = "tab";
|
|
549
|
+
key.meta = escaped;
|
|
550
|
+
} else if (ch === "\b" || ch === "\x7F") {
|
|
551
|
+
key.name = "backspace";
|
|
552
|
+
key.meta = escaped;
|
|
553
|
+
} else if (ch === "\x1B") {
|
|
554
|
+
key.name = "escape";
|
|
555
|
+
key.meta = escaped;
|
|
556
|
+
} else if (ch === " ") {
|
|
557
|
+
key.name = "space";
|
|
558
|
+
key.meta = escaped;
|
|
559
|
+
} else if (!escaped && ch <= "") {
|
|
560
|
+
key.name = String.fromCharCode(ch.charCodeAt(0) + "a".charCodeAt(0) - 1);
|
|
561
|
+
key.ctrl = true;
|
|
562
|
+
} else if (/^[0-9A-Za-z]$/.test(ch)) {
|
|
563
|
+
key.name = ch.toLowerCase();
|
|
564
|
+
key.shift = /^[A-Z]$/.test(ch);
|
|
565
|
+
key.meta = escaped;
|
|
566
|
+
} else if (escaped) {
|
|
567
|
+
key.name = ch.length ? void 0 : "escape";
|
|
568
|
+
key.meta = true;
|
|
569
|
+
}
|
|
570
|
+
key.sequence = s;
|
|
571
|
+
if (s.length !== 0 && (key.name !== void 0 || escaped)) {
|
|
572
|
+
stream.emit("keypress", escaped ? void 0 : s, key);
|
|
573
|
+
} else if (s.length === 1) {
|
|
574
|
+
stream.emit("keypress", s, key);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const _KEYPRESS_DECODER = /* @__PURE__ */ Symbol("keypress-decoder");
|
|
579
|
+
const _ESCAPE_DECODER = /* @__PURE__ */ Symbol("escape-decoder");
|
|
580
|
+
function emitKeypressEvents(stream, iface = {}) {
|
|
581
|
+
if (stream[_KEYPRESS_DECODER]) return;
|
|
582
|
+
stream[_KEYPRESS_DECODER] = new StringDecoder("utf8");
|
|
583
|
+
stream[_ESCAPE_DECODER] = emitKeys(stream);
|
|
584
|
+
stream[_ESCAPE_DECODER].next();
|
|
585
|
+
const escTimeout = iface.escapeCodeTimeout ?? ESCAPE_CODE_TIMEOUT;
|
|
586
|
+
let timeoutId;
|
|
587
|
+
const triggerEscape = () => stream[_ESCAPE_DECODER].next("");
|
|
588
|
+
function onData(input) {
|
|
589
|
+
if (stream.listenerCount("keypress") > 0) {
|
|
590
|
+
const str = stream[_KEYPRESS_DECODER].write(
|
|
591
|
+
typeof input === "string" ? Buffer.from(input) : input
|
|
592
|
+
);
|
|
593
|
+
if (str) {
|
|
594
|
+
clearTimeout(timeoutId);
|
|
595
|
+
for (const ch of str) {
|
|
596
|
+
try {
|
|
597
|
+
stream[_ESCAPE_DECODER].next(ch);
|
|
598
|
+
if (ch === "\x1B") timeoutId = setTimeout(triggerEscape, escTimeout);
|
|
599
|
+
} catch {
|
|
600
|
+
stream[_ESCAPE_DECODER] = emitKeys(stream);
|
|
601
|
+
stream[_ESCAPE_DECODER].next();
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
} else {
|
|
606
|
+
stream.removeListener("data", onData);
|
|
607
|
+
delete stream[_KEYPRESS_DECODER];
|
|
608
|
+
delete stream[_ESCAPE_DECODER];
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
stream.on("data", onData);
|
|
239
612
|
}
|
|
240
613
|
var index_default = {
|
|
241
614
|
Interface,
|
package/lib/types/index.d.ts
CHANGED
|
@@ -12,15 +12,14 @@ export interface InterfaceOptions {
|
|
|
12
12
|
escapeCodeTimeout?: number;
|
|
13
13
|
tabSize?: number;
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
16
|
-
* readline.Interface — reads lines from a Readable stream.
|
|
17
|
-
*/
|
|
18
15
|
export declare class Interface extends EventEmitter {
|
|
19
16
|
terminal: boolean;
|
|
20
17
|
line: string;
|
|
21
18
|
cursor: number;
|
|
22
19
|
private _input;
|
|
23
20
|
private _output;
|
|
21
|
+
get input(): Readable | null;
|
|
22
|
+
get output(): Writable | null;
|
|
24
23
|
private _prompt;
|
|
25
24
|
private _closed;
|
|
26
25
|
private _paused;
|
|
@@ -29,67 +28,51 @@ export declare class Interface extends EventEmitter {
|
|
|
29
28
|
private _crlfDelay;
|
|
30
29
|
private _lineBuffer;
|
|
31
30
|
private _questionCallback;
|
|
31
|
+
private _boundOnData;
|
|
32
|
+
private _boundOnEnd;
|
|
33
|
+
private _boundOnError;
|
|
34
|
+
private _boundOnKeypress;
|
|
32
35
|
constructor(input?: Readable | InterfaceOptions, output?: Writable);
|
|
33
36
|
private _onData;
|
|
34
37
|
private _onLine;
|
|
35
38
|
private _onEnd;
|
|
36
|
-
/** Set the prompt string. */
|
|
37
39
|
setPrompt(prompt: string): void;
|
|
38
|
-
/** Get the current prompt string. */
|
|
39
40
|
getPrompt(): string;
|
|
40
|
-
|
|
41
|
-
prompt(preserveCursor?: boolean): void;
|
|
42
|
-
/**
|
|
43
|
-
* Display the query and wait for user input.
|
|
44
|
-
*/
|
|
41
|
+
prompt(_preserveCursor?: boolean): void;
|
|
45
42
|
question(query: string, callback: (answer: string) => void): void;
|
|
46
43
|
question(query: string, options: Record<string, unknown>, callback: (answer: string) => void): void;
|
|
47
|
-
|
|
44
|
+
clearLine(_dir: number, callback?: () => void): boolean;
|
|
48
45
|
write(data: string | Buffer | null, key?: {
|
|
49
46
|
ctrl?: boolean;
|
|
50
47
|
meta?: boolean;
|
|
51
48
|
shift?: boolean;
|
|
52
49
|
name?: string;
|
|
53
50
|
}): void;
|
|
54
|
-
/** Close the interface. */
|
|
55
51
|
close(): void;
|
|
56
|
-
/** Pause the input stream. */
|
|
57
52
|
pause(): this;
|
|
58
|
-
/** Resume the input stream. */
|
|
59
53
|
resume(): this;
|
|
60
|
-
/** Get the current line content. */
|
|
61
54
|
getCursorPos(): {
|
|
62
55
|
rows: number;
|
|
63
56
|
cols: number;
|
|
64
57
|
};
|
|
65
58
|
[Symbol.asyncIterator](): AsyncIterableIterator<string>;
|
|
66
59
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Create a readline Interface.
|
|
69
|
-
*/
|
|
70
60
|
export declare function createInterface(input?: Readable | InterfaceOptions, output?: Writable, completer?: InterfaceOptions['completer'], terminal?: boolean): Interface;
|
|
71
|
-
/**
|
|
72
|
-
* Clear the current line of a TTY stream.
|
|
73
|
-
* dir: -1 = to the left, 1 = to the right, 0 = entire line
|
|
74
|
-
*/
|
|
75
61
|
export declare function clearLine(stream: Writable, dir: number, callback?: () => void): boolean;
|
|
76
|
-
/**
|
|
77
|
-
* Clear from cursor to end of screen.
|
|
78
|
-
*/
|
|
79
62
|
export declare function clearScreenDown(stream: Writable, callback?: () => void): boolean;
|
|
80
|
-
/**
|
|
81
|
-
* Move cursor to the specified position.
|
|
82
|
-
*/
|
|
83
63
|
export declare function cursorTo(stream: Writable, x: number, y?: number | (() => void), callback?: () => void): boolean;
|
|
84
|
-
/**
|
|
85
|
-
* Move cursor relative to its current position.
|
|
86
|
-
*/
|
|
87
64
|
export declare function moveCursor(stream: Writable, dx: number, dy: number, callback?: () => void): boolean;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
65
|
+
export interface Key {
|
|
66
|
+
sequence: string;
|
|
67
|
+
name: string | undefined;
|
|
68
|
+
ctrl: boolean;
|
|
69
|
+
meta: boolean;
|
|
70
|
+
shift: boolean;
|
|
71
|
+
code?: string;
|
|
72
|
+
}
|
|
73
|
+
export declare function emitKeypressEvents(stream: Readable & Record<symbol, unknown>, iface?: {
|
|
74
|
+
escapeCodeTimeout?: number;
|
|
75
|
+
}): void;
|
|
93
76
|
declare const _default: {
|
|
94
77
|
Interface: typeof Interface;
|
|
95
78
|
createInterface: typeof createInterface;
|