visaljs 0.0.9 → 0.0.11
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/README.md +12 -4
- package/THIRD_PARTY_LICENSES +31 -1
- package/cli.js +3745 -890
- package/node/include/js_node.h +12 -1
- package/node/prelude/_http_common.js +29 -0
- package/node/prelude/async_hooks.js +50 -11
- package/node/prelude/child_process.js +31 -4
- package/node/prelude/console.js +311 -143
- package/node/prelude/crypto.js +6 -4
- package/node/prelude/dgram.js +1169 -0
- package/node/prelude/diagnostics_channel.js +3 -1
- package/node/prelude/dns.js +250 -45
- package/node/prelude/dns_promises.js +8 -2
- package/node/prelude/fs_promises.js +9 -1
- package/node/prelude/http.js +405 -94
- package/node/prelude/https.js +13 -4
- package/node/prelude/internal/console_class.js +7 -0
- package/node/prelude/internal/console_global.js +25 -0
- package/node/prelude/internal/crypto/key_encryption.js +217 -0
- package/node/prelude/internal/crypto/keys.js +161 -26
- package/node/prelude/internal/crypto/random.js +24 -0
- package/node/prelude/internal/crypto/util.js +2 -1
- package/node/prelude/internal/crypto_x509.js +161 -0
- package/node/prelude/internal/dns_classes.js +6 -0
- package/node/prelude/internal/dns_resolver.js +371 -0
- package/node/prelude/internal/errors.js +9 -0
- package/node/prelude/internal/events_on.js +82 -37
- package/node/prelude/internal/features.js +8 -4
- package/node/prelude/internal/fs_filehandle.js +10 -4
- package/node/prelude/internal/http_chars.ts +22 -0
- package/node/prelude/internal/perf_histogram.js +1 -1
- package/node/prelude/internal/process_more.js +32 -17
- package/node/prelude/internal/process_signals.js +8 -2
- package/node/prelude/internal/punycode.ts +163 -0
- package/node/prelude/internal/readline.js +648 -274
- package/node/prelude/internal/readline_editor.js +1240 -0
- package/node/prelude/internal/string_width.js +43 -0
- package/node/prelude/internal/tls/common.js +145 -33
- package/node/prelude/internal/tls/wrap.js +182 -49
- package/node/prelude/internal/tls/x509.js +4 -1
- package/node/prelude/internal/udp.js +177 -0
- package/node/prelude/net.js +30 -14
- package/node/prelude/os.js +24 -6
- package/node/prelude/perf_hooks.js +37 -2
- package/node/prelude/punycode.js +2 -130
- package/node/prelude/readline.js +113 -4
- package/node/prelude/readline_promises.js +56 -32
- package/node/prelude/tls.js +21 -4
- package/node/prelude/util.js +3 -16
- package/node/src/host_dns.c +623 -0
- package/node/src/host_http.c +12 -0
- package/node/src/host_net.c +46 -0
- package/node/src/host_os.c +1 -6
- package/node/src/host_sys.c +79 -0
- package/node/src/host_udp.c +418 -0
- package/node/third_party/c-ares/LICENSE.md +24 -0
- package/node/third_party/c-ares/config/darwin/ares_config.h +573 -0
- package/node/third_party/c-ares/config/freebsd/ares_config.h +573 -0
- package/node/third_party/c-ares/config/linux/ares_config.h +573 -0
- package/node/third_party/c-ares/config/openbsd/ares_config.h +583 -0
- package/node/third_party/c-ares/include/ares.h +1237 -0
- package/node/third_party/c-ares/include/ares_build.h +202 -0
- package/node/third_party/c-ares/include/ares_dns.h +127 -0
- package/node/third_party/c-ares/include/ares_dns_record.h +1119 -0
- package/node/third_party/c-ares/include/ares_nameser.h +510 -0
- package/node/third_party/c-ares/include/ares_version.h +47 -0
- package/node/third_party/c-ares/src/lib/ares_addrinfo2hostent.c +305 -0
- package/node/third_party/c-ares/src/lib/ares_addrinfo_localhost.c +245 -0
- package/node/third_party/c-ares/src/lib/ares_android.c +496 -0
- package/node/third_party/c-ares/src/lib/ares_android.h +38 -0
- package/node/third_party/c-ares/src/lib/ares_cancel.c +87 -0
- package/node/third_party/c-ares/src/lib/ares_close_sockets.c +137 -0
- package/node/third_party/c-ares/src/lib/ares_conn.c +511 -0
- package/node/third_party/c-ares/src/lib/ares_conn.h +202 -0
- package/node/third_party/c-ares/src/lib/ares_cookie.c +461 -0
- package/node/third_party/c-ares/src/lib/ares_data.c +168 -0
- package/node/third_party/c-ares/src/lib/ares_data.h +92 -0
- package/node/third_party/c-ares/src/lib/ares_destroy.c +159 -0
- package/node/third_party/c-ares/src/lib/ares_free_hostent.c +54 -0
- package/node/third_party/c-ares/src/lib/ares_free_string.c +33 -0
- package/node/third_party/c-ares/src/lib/ares_freeaddrinfo.c +67 -0
- package/node/third_party/c-ares/src/lib/ares_getaddrinfo.c +806 -0
- package/node/third_party/c-ares/src/lib/ares_getenv.c +38 -0
- package/node/third_party/c-ares/src/lib/ares_getenv.h +34 -0
- package/node/third_party/c-ares/src/lib/ares_gethostbyaddr.c +229 -0
- package/node/third_party/c-ares/src/lib/ares_gethostbyname.c +341 -0
- package/node/third_party/c-ares/src/lib/ares_getnameinfo.c +437 -0
- package/node/third_party/c-ares/src/lib/ares_hosts_file.c +960 -0
- package/node/third_party/c-ares/src/lib/ares_inet_net_pton.h +31 -0
- package/node/third_party/c-ares/src/lib/ares_init.c +631 -0
- package/node/third_party/c-ares/src/lib/ares_ipv6.h +115 -0
- package/node/third_party/c-ares/src/lib/ares_library_init.c +190 -0
- package/node/third_party/c-ares/src/lib/ares_metrics.c +261 -0
- package/node/third_party/c-ares/src/lib/ares_options.c +492 -0
- package/node/third_party/c-ares/src/lib/ares_parse_into_addrinfo.c +179 -0
- package/node/third_party/c-ares/src/lib/ares_private.h +648 -0
- package/node/third_party/c-ares/src/lib/ares_process.c +1649 -0
- package/node/third_party/c-ares/src/lib/ares_qcache.c +442 -0
- package/node/third_party/c-ares/src/lib/ares_query.c +151 -0
- package/node/third_party/c-ares/src/lib/ares_search.c +623 -0
- package/node/third_party/c-ares/src/lib/ares_send.c +300 -0
- package/node/third_party/c-ares/src/lib/ares_set_socket_functions.c +590 -0
- package/node/third_party/c-ares/src/lib/ares_setup.h +224 -0
- package/node/third_party/c-ares/src/lib/ares_socket.c +427 -0
- package/node/third_party/c-ares/src/lib/ares_socket.h +163 -0
- package/node/third_party/c-ares/src/lib/ares_sortaddrinfo.c +447 -0
- package/node/third_party/c-ares/src/lib/ares_strerror.c +91 -0
- package/node/third_party/c-ares/src/lib/ares_sysconfig.c +642 -0
- package/node/third_party/c-ares/src/lib/ares_sysconfig_files.c +851 -0
- package/node/third_party/c-ares/src/lib/ares_sysconfig_mac.c +373 -0
- package/node/third_party/c-ares/src/lib/ares_sysconfig_win.c +673 -0
- package/node/third_party/c-ares/src/lib/ares_timeout.c +162 -0
- package/node/third_party/c-ares/src/lib/ares_update_servers.c +1376 -0
- package/node/third_party/c-ares/src/lib/ares_version.c +36 -0
- package/node/third_party/c-ares/src/lib/config-dos.h +114 -0
- package/node/third_party/c-ares/src/lib/config-win32.h +396 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_array.c +409 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable.c +451 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable.h +174 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable_asvp.c +224 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable_dict.c +230 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable_strvp.c +210 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable_szvp.c +188 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable_vpstr.c +186 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable_vpvp.c +194 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_llist.c +385 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_slist.c +479 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_slist.h +207 -0
- package/node/third_party/c-ares/src/lib/event/ares_event.h +191 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_configchg.c +746 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_epoll.c +192 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_kqueue.c +248 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_poll.c +140 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_select.c +159 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_thread.c +575 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_wake_pipe.c +172 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_win32.c +981 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_win32.h +161 -0
- package/node/third_party/c-ares/src/lib/include/ares_array.h +276 -0
- package/node/third_party/c-ares/src/lib/include/ares_buf.h +732 -0
- package/node/third_party/c-ares/src/lib/include/ares_htable_asvp.h +130 -0
- package/node/third_party/c-ares/src/lib/include/ares_htable_dict.h +123 -0
- package/node/third_party/c-ares/src/lib/include/ares_htable_strvp.h +130 -0
- package/node/third_party/c-ares/src/lib/include/ares_htable_szvp.h +118 -0
- package/node/third_party/c-ares/src/lib/include/ares_htable_vpstr.h +111 -0
- package/node/third_party/c-ares/src/lib/include/ares_htable_vpvp.h +128 -0
- package/node/third_party/c-ares/src/lib/include/ares_llist.h +239 -0
- package/node/third_party/c-ares/src/lib/include/ares_mem.h +41 -0
- package/node/third_party/c-ares/src/lib/include/ares_str.h +261 -0
- package/node/third_party/c-ares/src/lib/inet_net_pton.c +451 -0
- package/node/third_party/c-ares/src/lib/inet_ntop.c +219 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_create_query.c +78 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_expand_name.c +99 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_expand_string.c +107 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_fds.c +80 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_getsock.c +85 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_a_reply.c +107 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_aaaa_reply.c +109 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_caa_reply.c +137 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_mx_reply.c +110 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_naptr_reply.c +132 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_ns_reply.c +154 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_ptr_reply.c +213 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_soa_reply.c +115 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_srv_reply.c +114 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_txt_reply.c +144 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_uri_reply.c +113 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_mapping.c +982 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_multistring.c +309 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_multistring.h +72 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_name.c +736 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_parse.c +1360 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_private.h +273 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_record.c +1701 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_write.c +1229 -0
- package/node/third_party/c-ares/src/lib/str/ares_buf.c +1510 -0
- package/node/third_party/c-ares/src/lib/str/ares_str.c +562 -0
- package/node/third_party/c-ares/src/lib/str/ares_strsplit.c +90 -0
- package/node/third_party/c-ares/src/lib/str/ares_strsplit.h +51 -0
- package/node/third_party/c-ares/src/lib/thirdparty/apple/dnsinfo.h +122 -0
- package/node/third_party/c-ares/src/lib/util/ares_iface_ips.c +640 -0
- package/node/third_party/c-ares/src/lib/util/ares_iface_ips.h +140 -0
- package/node/third_party/c-ares/src/lib/util/ares_math.c +167 -0
- package/node/third_party/c-ares/src/lib/util/ares_math.h +58 -0
- package/node/third_party/c-ares/src/lib/util/ares_rand.c +389 -0
- package/node/third_party/c-ares/src/lib/util/ares_rand.h +36 -0
- package/node/third_party/c-ares/src/lib/util/ares_threads.c +613 -0
- package/node/third_party/c-ares/src/lib/util/ares_threads.h +60 -0
- package/node/third_party/c-ares/src/lib/util/ares_time.h +49 -0
- package/node/third_party/c-ares/src/lib/util/ares_timeval.c +95 -0
- package/node/third_party/c-ares/src/lib/util/ares_uri.c +1627 -0
- package/node/third_party/c-ares/src/lib/util/ares_uri.h +252 -0
- package/node/third_party/c-ares/src/lib/windows_port.c +29 -0
- package/package.json +1 -1
- package/prebuilt/darwin-arm64/cares-a6286c65ca9374a3.a +0 -0
- package/prebuilt/darwin-x64/cares-a6286c65ca9374a3.a +0 -0
- package/prebuilt/linux-arm64/cares-4ba05ac8e948af8e.a +0 -0
- package/prebuilt/linux-arm64-musl/cares-4ba05ac8e948af8e.a +0 -0
- package/prebuilt/linux-x64/cares-4ba05ac8e948af8e.a +0 -0
- package/prebuilt/linux-x64-musl/cares-4ba05ac8e948af8e.a +0 -0
- package/prebuilt/win32-x64/boringssl-ce573858472b7fc9.a +0 -0
- package/prebuilt/win32-x64/brotli-20d86a61f30baeb3.a +0 -0
- package/prebuilt/win32-x64/cares-49c9ec95162b912d.a +0 -0
- package/prebuilt/win32-x64/uv-b11bae65d7fced82.a +0 -0
- package/prebuilt/win32-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/win32-x64/zstd-015315ac7c784c73.a +0 -0
- package/runtime/include/js_buffer.h +185 -0
- package/runtime/include/js_builtins.h +58 -0
- package/runtime/include/js_crypto.h +11 -0
- package/runtime/include/js_exception.h +3 -0
- package/runtime/include/js_function.h +1 -1
- package/runtime/include/js_gc.h +13 -10
- package/runtime/include/js_jobs.h +2 -0
- package/runtime/include/js_object.h +79 -3
- package/runtime/include/js_ops.h +27 -0
- package/runtime/include/js_runtime.h +1 -0
- package/runtime/include/js_web.h +0 -3
- package/runtime/include/jscontext.h +8 -0
- package/runtime/src/builtins.c +17 -4
- package/runtime/src/crypto_internal.h +1 -0
- package/runtime/src/crypto_subtle.c +66 -7
- package/runtime/src/crypto_tls.c +265 -19
- package/runtime/src/function.c +37 -10
- package/runtime/src/gc.c +76 -93
- package/runtime/src/inspect.c +18 -1
- package/runtime/src/iterator.c +5 -1
- package/runtime/src/jobs.c +37 -22
- package/runtime/src/object.c +91 -8
- package/runtime/src/os.c +16 -6
- package/runtime/src/os.h +2 -0
- package/runtime/src/process.c +97 -10
- package/runtime/src/typedarray.c +23 -0
|
@@ -0,0 +1,1240 @@
|
|
|
1
|
+
// node:readline's terminal line editor (Node's lib/internal/readline/interface.js, the editing
|
|
2
|
+
// half; utils.js's emitKeys; emitKeypressEvents.js): keys read in raw mode, the line edited and
|
|
3
|
+
// redrawn, completion, the kill ring, undo, history. A feature module (internal/features.js),
|
|
4
|
+
// linked into programs that may read a terminal; it gives the Interface its methods.
|
|
5
|
+
// Ported from Node.js (Copyright Node.js contributors, MIT): node/third_party/nodejs/LICENSE.
|
|
6
|
+
import { Interface as LegacyInterface } from "../readline.js";
|
|
7
|
+
import { StringDecoder } from "../string_decoder.js";
|
|
8
|
+
import { inspect } from "../util.js";
|
|
9
|
+
import { features } from "./features.js";
|
|
10
|
+
import {
|
|
11
|
+
abortError, charLengthAt, charLengthLeft, clearScreenDown, CSI, cursorTo, Interface, kAddHistory, kAddNewLineOnTTY, kBeforeEdit, kDeleteLeft,
|
|
12
|
+
kDeleteLineLeft, kDeleteLineRight, kDeleteRight, kDeleteWordLeft, kDeleteWordRight, kGetDisplayPos, kHistoryNext, kHistoryPrev,
|
|
13
|
+
kInsertString, kIsMultiline, kKillRing, kKillRingCursor, kLine, kMoveCursor, kMoveDownOrHistoryNext, kMoveUpOrHistoryPrev,
|
|
14
|
+
kMultilineMove, kMultilinePrompt, kOnLine, kPreviousCursorCols, kPreviousKey, kPrompt, kPushToKillRing, kQuestionReject, kRedo,
|
|
15
|
+
kRedoStack, kRefreshLine, kRestorePreviousState, kSavePreviousState, kSawKeyPress, kSawReturnAt, kSetLine, kSetRawMode,
|
|
16
|
+
kSubstringSearch, kTabComplete, kTabCompleter, kTtyWrite, kUndo, kUndoStack, kWordLeft, kWordRight, kWriteToOutput, kYank,
|
|
17
|
+
kYanking, kYankPop, moveCursor,
|
|
18
|
+
} from "./readline.js";
|
|
19
|
+
import { getStringWidth } from "./string_width.js";
|
|
20
|
+
|
|
21
|
+
const kEscape = CSI.kEscape;
|
|
22
|
+
const kEmptyObject = Object.freeze({ __proto__: null });
|
|
23
|
+
const kMaxLengthOfKillRing = 32;
|
|
24
|
+
/* \r\n, \n, \r, and the Unicode line separators: a paste's lines */
|
|
25
|
+
const lineEnding = /\r?\n|\r(?!\n)|\u2028|\u2029/g;
|
|
26
|
+
|
|
27
|
+
/* ---- keys (Node's internal/readline/utils.js) ------------------------------------------------ */
|
|
28
|
+
|
|
29
|
+
function* emitKeys(stream) {
|
|
30
|
+
while (true) {
|
|
31
|
+
let ch = yield;
|
|
32
|
+
let s = ch;
|
|
33
|
+
let escaped = false;
|
|
34
|
+
const key = {
|
|
35
|
+
sequence: null,
|
|
36
|
+
name: undefined,
|
|
37
|
+
ctrl: false,
|
|
38
|
+
meta: false,
|
|
39
|
+
shift: false,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
if (ch === kEscape) {
|
|
43
|
+
escaped = true;
|
|
44
|
+
s += (ch = yield);
|
|
45
|
+
|
|
46
|
+
if (ch === kEscape) {
|
|
47
|
+
s += (ch = yield);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (escaped && (ch === 'O' || ch === '[')) {
|
|
52
|
+
// ANSI escape sequence
|
|
53
|
+
let code = ch;
|
|
54
|
+
let modifier = 0;
|
|
55
|
+
|
|
56
|
+
if (ch === 'O') {
|
|
57
|
+
// ESC O letter
|
|
58
|
+
// ESC O modifier letter
|
|
59
|
+
s += (ch = yield);
|
|
60
|
+
|
|
61
|
+
if (ch >= '0' && ch <= '9') {
|
|
62
|
+
modifier = (ch >> 0) - 1;
|
|
63
|
+
s += (ch = yield);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
code += ch;
|
|
67
|
+
} else if (ch === '[') {
|
|
68
|
+
// ESC [ letter
|
|
69
|
+
// ESC [ modifier letter
|
|
70
|
+
// ESC [ [ modifier letter
|
|
71
|
+
// ESC [ [ num char
|
|
72
|
+
s += (ch = yield);
|
|
73
|
+
|
|
74
|
+
if (ch === '[') {
|
|
75
|
+
// \x1b[[A
|
|
76
|
+
// ^--- escape codes might have a second bracket
|
|
77
|
+
code += ch;
|
|
78
|
+
s += (ch = yield);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/*
|
|
82
|
+
* Here and later we try to buffer just enough data to get
|
|
83
|
+
* a complete ascii sequence.
|
|
84
|
+
*
|
|
85
|
+
* We have basically two classes of ascii characters to process:
|
|
86
|
+
*
|
|
87
|
+
*
|
|
88
|
+
* 1. `\x1b[24;5~` should be parsed as { code: '[24~', modifier: 5 }
|
|
89
|
+
*
|
|
90
|
+
* This particular example is featuring Ctrl+F12 in xterm.
|
|
91
|
+
*
|
|
92
|
+
* - `;5` part is optional, e.g. it could be `\x1b[24~`
|
|
93
|
+
* - first part can contain one or two digits
|
|
94
|
+
* - there is also special case when there can be 3 digits
|
|
95
|
+
* but without modifier. They are the case of paste bracket mode
|
|
96
|
+
*
|
|
97
|
+
* So the generic regexp is like /^(?:\d\d?(;\d)?[~^$]|\d{3}~)$/
|
|
98
|
+
*
|
|
99
|
+
*
|
|
100
|
+
* 2. `\x1b[1;5H` should be parsed as { code: '[H', modifier: 5 }
|
|
101
|
+
*
|
|
102
|
+
* This particular example is featuring Ctrl+Home in xterm.
|
|
103
|
+
*
|
|
104
|
+
* - `1;5` part is optional, e.g. it could be `\x1b[H`
|
|
105
|
+
* - `1;` part is optional, e.g. it could be `\x1b[5H`
|
|
106
|
+
*
|
|
107
|
+
* So the generic regexp is like /^((\d;)?\d)?[A-Za-z]$/
|
|
108
|
+
*
|
|
109
|
+
*/
|
|
110
|
+
const cmdStart = s.length - 1;
|
|
111
|
+
|
|
112
|
+
// Skip one or two leading digits
|
|
113
|
+
if (ch >= '0' && ch <= '9') {
|
|
114
|
+
s += (ch = yield);
|
|
115
|
+
|
|
116
|
+
if (ch >= '0' && ch <= '9') {
|
|
117
|
+
s += (ch = yield);
|
|
118
|
+
|
|
119
|
+
if (ch >= '0' && ch <= '9') {
|
|
120
|
+
s += (ch = yield);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// skip modifier
|
|
126
|
+
if (ch === ';') {
|
|
127
|
+
s += (ch = yield);
|
|
128
|
+
|
|
129
|
+
if (ch >= '0' && ch <= '9') {
|
|
130
|
+
s += yield;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* We buffered enough data, now trying to extract code
|
|
136
|
+
* and modifier from it
|
|
137
|
+
*/
|
|
138
|
+
const cmd = s.slice(cmdStart);
|
|
139
|
+
let match;
|
|
140
|
+
|
|
141
|
+
if ((match = (/^(?:(\d\d?)(?:;(\d))?([~^$])|(\d{3}~))$/).exec(cmd))) {
|
|
142
|
+
if (match[4]) {
|
|
143
|
+
code += match[4];
|
|
144
|
+
} else {
|
|
145
|
+
code += match[1] + match[3];
|
|
146
|
+
modifier = (match[2] || 1) - 1;
|
|
147
|
+
}
|
|
148
|
+
} else if (
|
|
149
|
+
(match = (/^((\d;)?(\d))?([A-Za-z])$/).exec(cmd))
|
|
150
|
+
) {
|
|
151
|
+
code += match[4];
|
|
152
|
+
modifier = (match[3] || 1) - 1;
|
|
153
|
+
} else {
|
|
154
|
+
code += cmd;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Parse the key modifier
|
|
159
|
+
key.ctrl = !!(modifier & 4);
|
|
160
|
+
key.meta = !!(modifier & 10);
|
|
161
|
+
key.shift = !!(modifier & 1);
|
|
162
|
+
key.code = code;
|
|
163
|
+
|
|
164
|
+
// Parse the key itself
|
|
165
|
+
switch (code) {
|
|
166
|
+
/* xterm/gnome ESC [ letter (with modifier) */
|
|
167
|
+
case '[P': key.name = 'f1'; break;
|
|
168
|
+
case '[Q': key.name = 'f2'; break;
|
|
169
|
+
case '[R': key.name = 'f3'; break;
|
|
170
|
+
case '[S': key.name = 'f4'; break;
|
|
171
|
+
|
|
172
|
+
/* xterm/gnome ESC O letter (without modifier) */
|
|
173
|
+
case 'OP': key.name = 'f1'; break;
|
|
174
|
+
case 'OQ': key.name = 'f2'; break;
|
|
175
|
+
case 'OR': key.name = 'f3'; break;
|
|
176
|
+
case 'OS': key.name = 'f4'; break;
|
|
177
|
+
|
|
178
|
+
/* xterm/rxvt ESC [ number ~ */
|
|
179
|
+
case '[11~': key.name = 'f1'; break;
|
|
180
|
+
case '[12~': key.name = 'f2'; break;
|
|
181
|
+
case '[13~': key.name = 'f3'; break;
|
|
182
|
+
case '[14~': key.name = 'f4'; break;
|
|
183
|
+
|
|
184
|
+
/* paste bracket mode */
|
|
185
|
+
case '[200~': key.name = 'paste-start'; break;
|
|
186
|
+
case '[201~': key.name = 'paste-end'; break;
|
|
187
|
+
|
|
188
|
+
/* from Cygwin and used in libuv */
|
|
189
|
+
case '[[A': key.name = 'f1'; break;
|
|
190
|
+
case '[[B': key.name = 'f2'; break;
|
|
191
|
+
case '[[C': key.name = 'f3'; break;
|
|
192
|
+
case '[[D': key.name = 'f4'; break;
|
|
193
|
+
case '[[E': key.name = 'f5'; break;
|
|
194
|
+
|
|
195
|
+
/* common */
|
|
196
|
+
case '[15~': key.name = 'f5'; break;
|
|
197
|
+
case '[17~': key.name = 'f6'; break;
|
|
198
|
+
case '[18~': key.name = 'f7'; break;
|
|
199
|
+
case '[19~': key.name = 'f8'; break;
|
|
200
|
+
case '[20~': key.name = 'f9'; break;
|
|
201
|
+
case '[21~': key.name = 'f10'; break;
|
|
202
|
+
case '[23~': key.name = 'f11'; break;
|
|
203
|
+
case '[24~': key.name = 'f12'; break;
|
|
204
|
+
|
|
205
|
+
/* xterm ESC [ letter */
|
|
206
|
+
case '[A': key.name = 'up'; break;
|
|
207
|
+
case '[B': key.name = 'down'; break;
|
|
208
|
+
case '[C': key.name = 'right'; break;
|
|
209
|
+
case '[D': key.name = 'left'; break;
|
|
210
|
+
case '[E': key.name = 'clear'; break;
|
|
211
|
+
case '[F': key.name = 'end'; break;
|
|
212
|
+
case '[H': key.name = 'home'; break;
|
|
213
|
+
|
|
214
|
+
/* xterm/gnome ESC O letter */
|
|
215
|
+
case 'OA': key.name = 'up'; break;
|
|
216
|
+
case 'OB': key.name = 'down'; break;
|
|
217
|
+
case 'OC': key.name = 'right'; break;
|
|
218
|
+
case 'OD': key.name = 'left'; break;
|
|
219
|
+
case 'OE': key.name = 'clear'; break;
|
|
220
|
+
case 'OF': key.name = 'end'; break;
|
|
221
|
+
case 'OH': key.name = 'home'; break;
|
|
222
|
+
|
|
223
|
+
/* xterm/rxvt ESC [ number ~ */
|
|
224
|
+
case '[1~': key.name = 'home'; break;
|
|
225
|
+
case '[2~': key.name = 'insert'; break;
|
|
226
|
+
case '[3~': key.name = 'delete'; break;
|
|
227
|
+
case '[4~': key.name = 'end'; break;
|
|
228
|
+
case '[5~': key.name = 'pageup'; break;
|
|
229
|
+
case '[6~': key.name = 'pagedown'; break;
|
|
230
|
+
|
|
231
|
+
/* putty */
|
|
232
|
+
case '[[5~': key.name = 'pageup'; break;
|
|
233
|
+
case '[[6~': key.name = 'pagedown'; break;
|
|
234
|
+
|
|
235
|
+
/* rxvt */
|
|
236
|
+
case '[7~': key.name = 'home'; break;
|
|
237
|
+
case '[8~': key.name = 'end'; break;
|
|
238
|
+
|
|
239
|
+
/* rxvt keys with modifiers */
|
|
240
|
+
case '[a': key.name = 'up'; key.shift = true; break;
|
|
241
|
+
case '[b': key.name = 'down'; key.shift = true; break;
|
|
242
|
+
case '[c': key.name = 'right'; key.shift = true; break;
|
|
243
|
+
case '[d': key.name = 'left'; key.shift = true; break;
|
|
244
|
+
case '[e': key.name = 'clear'; key.shift = true; break;
|
|
245
|
+
|
|
246
|
+
case '[2$': key.name = 'insert'; key.shift = true; break;
|
|
247
|
+
case '[3$': key.name = 'delete'; key.shift = true; break;
|
|
248
|
+
case '[5$': key.name = 'pageup'; key.shift = true; break;
|
|
249
|
+
case '[6$': key.name = 'pagedown'; key.shift = true; break;
|
|
250
|
+
case '[7$': key.name = 'home'; key.shift = true; break;
|
|
251
|
+
case '[8$': key.name = 'end'; key.shift = true; break;
|
|
252
|
+
|
|
253
|
+
case 'Oa': key.name = 'up'; key.ctrl = true; break;
|
|
254
|
+
case 'Ob': key.name = 'down'; key.ctrl = true; break;
|
|
255
|
+
case 'Oc': key.name = 'right'; key.ctrl = true; break;
|
|
256
|
+
case 'Od': key.name = 'left'; key.ctrl = true; break;
|
|
257
|
+
case 'Oe': key.name = 'clear'; key.ctrl = true; break;
|
|
258
|
+
|
|
259
|
+
case '[2^': key.name = 'insert'; key.ctrl = true; break;
|
|
260
|
+
case '[3^': key.name = 'delete'; key.ctrl = true; break;
|
|
261
|
+
case '[5^': key.name = 'pageup'; key.ctrl = true; break;
|
|
262
|
+
case '[6^': key.name = 'pagedown'; key.ctrl = true; break;
|
|
263
|
+
case '[7^': key.name = 'home'; key.ctrl = true; break;
|
|
264
|
+
case '[8^': key.name = 'end'; key.ctrl = true; break;
|
|
265
|
+
|
|
266
|
+
/* misc. */
|
|
267
|
+
case '[Z': key.name = 'tab'; key.shift = true; break;
|
|
268
|
+
default: key.name = 'undefined'; break;
|
|
269
|
+
}
|
|
270
|
+
} else if (ch === '\r') {
|
|
271
|
+
// carriage return
|
|
272
|
+
key.name = 'return';
|
|
273
|
+
key.meta = escaped;
|
|
274
|
+
} else if (ch === '\n') {
|
|
275
|
+
// Enter, should have been called linefeed
|
|
276
|
+
key.name = 'enter';
|
|
277
|
+
key.meta = escaped;
|
|
278
|
+
} else if (ch === '\t') {
|
|
279
|
+
// tab
|
|
280
|
+
key.name = 'tab';
|
|
281
|
+
key.meta = escaped;
|
|
282
|
+
} else if (ch === '\b' || ch === '\x7f') {
|
|
283
|
+
// backspace or ctrl+h
|
|
284
|
+
key.name = 'backspace';
|
|
285
|
+
key.meta = escaped;
|
|
286
|
+
} else if (ch === kEscape) {
|
|
287
|
+
// escape key
|
|
288
|
+
key.name = 'escape';
|
|
289
|
+
key.meta = escaped;
|
|
290
|
+
} else if (ch === ' ') {
|
|
291
|
+
key.name = 'space';
|
|
292
|
+
key.meta = escaped;
|
|
293
|
+
} else if (!escaped && ch <= '\x1a') {
|
|
294
|
+
// ctrl+letter
|
|
295
|
+
key.name = String.fromCharCode(
|
|
296
|
+
ch.charCodeAt() + ('a').charCodeAt() - 1,
|
|
297
|
+
);
|
|
298
|
+
key.ctrl = true;
|
|
299
|
+
} else if ((/^[0-9A-Za-z]$/).exec(ch) !== null) {
|
|
300
|
+
// Letter, number, shift+letter
|
|
301
|
+
key.name = ch.toLowerCase();
|
|
302
|
+
key.shift = (/^[A-Z]$/).exec(ch) !== null;
|
|
303
|
+
key.meta = escaped;
|
|
304
|
+
} else if (escaped) {
|
|
305
|
+
// Escape sequence timeout
|
|
306
|
+
key.name = ch.length ? undefined : 'escape';
|
|
307
|
+
key.meta = true;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
key.sequence = s;
|
|
311
|
+
|
|
312
|
+
if (s.length !== 0 && (key.name !== undefined || escaped)) {
|
|
313
|
+
/* Named character or sequence */
|
|
314
|
+
stream.emit('keypress', escaped ? undefined : s, key);
|
|
315
|
+
} else if (charLengthAt(s, 0) === s.length) {
|
|
316
|
+
/* Single unnamed character, e.g. "." */
|
|
317
|
+
stream.emit('keypress', s, key);
|
|
318
|
+
}
|
|
319
|
+
/* Unrecognized or broken escape sequence, don't emit anything */
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// This runs in O(n log n).
|
|
324
|
+
function commonPrefix(strings) {
|
|
325
|
+
if (strings.length === 0) {
|
|
326
|
+
return '';
|
|
327
|
+
}
|
|
328
|
+
if (strings.length === 1) {
|
|
329
|
+
return strings[0];
|
|
330
|
+
}
|
|
331
|
+
const sorted = strings.toSorted();
|
|
332
|
+
const min = sorted[0];
|
|
333
|
+
const max = sorted[sorted.length - 1];
|
|
334
|
+
for (let i = 0; i < min.length; i++) {
|
|
335
|
+
if (min[i] !== max[i]) {
|
|
336
|
+
return min.slice(0, i);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return min;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/* ---- emitKeypressEvents (Node's internal/readline/emitKeypressEvents.js) ------------------------ */
|
|
343
|
+
|
|
344
|
+
const KEYPRESS_DECODER = Symbol('keypress-decoder');
|
|
345
|
+
const ESCAPE_DECODER = Symbol('escape-decoder');
|
|
346
|
+
|
|
347
|
+
// GNU readline library - keyseq-timeout is 500ms (default)
|
|
348
|
+
const ESCAPE_CODE_TIMEOUT = 500;
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* accepts a readable Stream instance and makes it emit "keypress" events
|
|
352
|
+
*/
|
|
353
|
+
|
|
354
|
+
function emitKeypressEvents(stream, iface = {}) {
|
|
355
|
+
if (stream[KEYPRESS_DECODER]) return;
|
|
356
|
+
|
|
357
|
+
stream[KEYPRESS_DECODER] = new StringDecoder('utf8');
|
|
358
|
+
|
|
359
|
+
stream[ESCAPE_DECODER] = emitKeys(stream);
|
|
360
|
+
stream[ESCAPE_DECODER].next();
|
|
361
|
+
|
|
362
|
+
const triggerEscape = () => stream[ESCAPE_DECODER].next('');
|
|
363
|
+
const { escapeCodeTimeout = ESCAPE_CODE_TIMEOUT } = iface;
|
|
364
|
+
let timeoutId;
|
|
365
|
+
|
|
366
|
+
function onData(input) {
|
|
367
|
+
if (stream.listenerCount('keypress') > 0) {
|
|
368
|
+
const string = stream[KEYPRESS_DECODER].write(input);
|
|
369
|
+
if (string) {
|
|
370
|
+
clearTimeout(timeoutId);
|
|
371
|
+
|
|
372
|
+
// This supports characters of length 2.
|
|
373
|
+
iface[kSawKeyPress] = charLengthAt(string, 0) === string.length;
|
|
374
|
+
iface.isCompletionEnabled = false;
|
|
375
|
+
|
|
376
|
+
let length = 0;
|
|
377
|
+
for (const character of string) {
|
|
378
|
+
length += character.length;
|
|
379
|
+
if (length === string.length) {
|
|
380
|
+
iface.isCompletionEnabled = true;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
try {
|
|
384
|
+
stream[ESCAPE_DECODER].next(character);
|
|
385
|
+
// Escape letter at the tail position
|
|
386
|
+
if (length === string.length && character === kEscape) {
|
|
387
|
+
timeoutId = setTimeout(triggerEscape, escapeCodeTimeout);
|
|
388
|
+
}
|
|
389
|
+
} catch (err) {
|
|
390
|
+
// If the generator throws (it could happen in the `keypress`
|
|
391
|
+
// event), we need to restart it.
|
|
392
|
+
stream[ESCAPE_DECODER] = emitKeys(stream);
|
|
393
|
+
stream[ESCAPE_DECODER].next();
|
|
394
|
+
throw err;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
} else {
|
|
399
|
+
// Nobody's watching anyway
|
|
400
|
+
stream.removeListener('data', onData);
|
|
401
|
+
stream.on('newListener', onNewListener);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function onNewListener(event) {
|
|
406
|
+
if (event === 'keypress') {
|
|
407
|
+
stream.on('data', onData);
|
|
408
|
+
stream.removeListener('newListener', onNewListener);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (stream.listenerCount('keypress') > 0) {
|
|
413
|
+
stream.on('data', onData);
|
|
414
|
+
} else {
|
|
415
|
+
stream.on('newListener', onNewListener);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/* ---- the editing methods, Interface's ------------------------------------------------------- */
|
|
420
|
+
|
|
421
|
+
class Editor {
|
|
422
|
+
[kInsertString](c) {
|
|
423
|
+
this[kBeforeEdit](this.line, this.cursor);
|
|
424
|
+
if (!this.isCompletionEnabled) {
|
|
425
|
+
if (this.cursor < this.line.length) {
|
|
426
|
+
const beg = this.line.slice(0, this.cursor);
|
|
427
|
+
const end = this.line.slice(this.cursor, this.line.length);
|
|
428
|
+
this.line = beg + c + end;
|
|
429
|
+
} else {
|
|
430
|
+
this.line += c;
|
|
431
|
+
}
|
|
432
|
+
this.cursor += c.length;
|
|
433
|
+
this[kWriteToOutput](c);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (this.cursor < this.line.length) {
|
|
437
|
+
const beg = this.line.slice(0, this.cursor);
|
|
438
|
+
const end = this.line.slice(this.cursor, this.line.length);
|
|
439
|
+
this[kSetLine](beg + c + end);
|
|
440
|
+
this.cursor += c.length;
|
|
441
|
+
this[kRefreshLine]();
|
|
442
|
+
} else {
|
|
443
|
+
const oldPos = this.getCursorPos();
|
|
444
|
+
this.line += c;
|
|
445
|
+
this.cursor += c.length;
|
|
446
|
+
const newPos = this.getCursorPos();
|
|
447
|
+
|
|
448
|
+
if (oldPos.rows < newPos.rows) {
|
|
449
|
+
this[kRefreshLine]();
|
|
450
|
+
} else {
|
|
451
|
+
this[kWriteToOutput](c);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async [kTabComplete](lastKeypressWasTab) {
|
|
457
|
+
this.pause();
|
|
458
|
+
const string = this.line.slice(0, this.cursor);
|
|
459
|
+
let value;
|
|
460
|
+
try {
|
|
461
|
+
value = await this.completer(string);
|
|
462
|
+
} catch (err) {
|
|
463
|
+
this[kWriteToOutput](`Tab completion error: ${inspect(err)}`);
|
|
464
|
+
return;
|
|
465
|
+
} finally {
|
|
466
|
+
this.resume();
|
|
467
|
+
}
|
|
468
|
+
this[kTabCompleter](lastKeypressWasTab, value);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
[kTabCompleter](lastKeypressWasTab, { 0: completions, 1: completeOn }) {
|
|
472
|
+
// Result and the text that was completed.
|
|
473
|
+
|
|
474
|
+
if (!completions || completions.length === 0) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// If there is a common prefix to all matches, then apply that portion.
|
|
479
|
+
const prefix = commonPrefix(
|
|
480
|
+
completions.filter((e) => e !== ''),
|
|
481
|
+
);
|
|
482
|
+
if (prefix.startsWith(completeOn) &&
|
|
483
|
+
prefix.length > completeOn.length) {
|
|
484
|
+
this[kInsertString](prefix.slice(completeOn.length));
|
|
485
|
+
return;
|
|
486
|
+
} else if (!completeOn.startsWith(prefix)) {
|
|
487
|
+
this[kSetLine](this.line.slice(0, this.cursor - completeOn.length) +
|
|
488
|
+
prefix +
|
|
489
|
+
this.line.slice(this.cursor, this.line.length));
|
|
490
|
+
this.cursor = this.cursor - completeOn.length + prefix.length;
|
|
491
|
+
this[kRefreshLine]();
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (!lastKeypressWasTab) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
this[kBeforeEdit](this.line, this.cursor);
|
|
500
|
+
|
|
501
|
+
// Apply/show completions.
|
|
502
|
+
const completionsWidth = completions.map((e) =>
|
|
503
|
+
getStringWidth(e));
|
|
504
|
+
const width = Math.max(...completionsWidth) + 2; // 2 space padding
|
|
505
|
+
let maxColumns = Math.floor(this.columns / width) || 1;
|
|
506
|
+
if (maxColumns === Infinity) {
|
|
507
|
+
maxColumns = 1;
|
|
508
|
+
}
|
|
509
|
+
let output = '\r\n';
|
|
510
|
+
let lineIndex = 0;
|
|
511
|
+
let whitespace = 0;
|
|
512
|
+
for (let i = 0; i < completions.length; i++) {
|
|
513
|
+
const completion = completions[i];
|
|
514
|
+
if (completion === '' || lineIndex === maxColumns) {
|
|
515
|
+
output += '\r\n';
|
|
516
|
+
lineIndex = 0;
|
|
517
|
+
whitespace = 0;
|
|
518
|
+
} else {
|
|
519
|
+
output += (' ').repeat(whitespace);
|
|
520
|
+
}
|
|
521
|
+
if (completion !== '') {
|
|
522
|
+
output += completion;
|
|
523
|
+
whitespace = width - completionsWidth[i];
|
|
524
|
+
lineIndex++;
|
|
525
|
+
} else {
|
|
526
|
+
output += '\r\n';
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (lineIndex !== 0) {
|
|
530
|
+
output += '\r\n\r\n';
|
|
531
|
+
}
|
|
532
|
+
this[kWriteToOutput](output);
|
|
533
|
+
this[kRefreshLine]();
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
[kWordLeft]() {
|
|
537
|
+
if (this.cursor > 0) {
|
|
538
|
+
// Reverse the string and match a word near beginning
|
|
539
|
+
// to avoid quadratic time complexity
|
|
540
|
+
const leading = this.line.slice(0, this.cursor);
|
|
541
|
+
const reversed = ((Array.from(leading)).reverse()).join('');
|
|
542
|
+
const match = (/^\s*(?:[^\w\s]+|\w+)?/).exec(reversed);
|
|
543
|
+
this[kMoveCursor](-match[0].length);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
[kWordRight]() {
|
|
548
|
+
if (this.cursor < this.line.length) {
|
|
549
|
+
const trailing = this.line.slice(this.cursor);
|
|
550
|
+
const match = (/^(?:\s+|[^\w\s]+|\w+)\s*/).exec(trailing);
|
|
551
|
+
this[kMoveCursor](match[0].length);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
[kDeleteLeft]() {
|
|
556
|
+
if (this.cursor > 0 && this.line.length > 0) {
|
|
557
|
+
this[kBeforeEdit](this.line, this.cursor);
|
|
558
|
+
// The number of UTF-16 units comprising the character to the left
|
|
559
|
+
const charSize = charLengthLeft(this.line, this.cursor);
|
|
560
|
+
this.line =
|
|
561
|
+
this.line.slice(0, this.cursor - charSize) +
|
|
562
|
+
this.line.slice(this.cursor, this.line.length);
|
|
563
|
+
|
|
564
|
+
this.cursor -= charSize;
|
|
565
|
+
this[kRefreshLine]();
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
[kDeleteRight]() {
|
|
570
|
+
if (this.cursor < this.line.length) {
|
|
571
|
+
this[kBeforeEdit](this.line, this.cursor);
|
|
572
|
+
// The number of UTF-16 units comprising the character to the left
|
|
573
|
+
const charSize = charLengthAt(this.line, this.cursor);
|
|
574
|
+
this.line =
|
|
575
|
+
this.line.slice(0, this.cursor) +
|
|
576
|
+
this.line.slice(this.cursor + charSize, this.line.length);
|
|
577
|
+
this[kRefreshLine]();
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
[kDeleteWordLeft]() {
|
|
582
|
+
if (this.cursor > 0) {
|
|
583
|
+
this[kBeforeEdit](this.line, this.cursor);
|
|
584
|
+
// Reverse the string and match a word near beginning
|
|
585
|
+
// to avoid quadratic time complexity
|
|
586
|
+
let leading = this.line.slice(0, this.cursor);
|
|
587
|
+
const reversed = ((Array.from(leading)).reverse()).join('');
|
|
588
|
+
const match = (/^\s*(?:[^\w\s]+|\w+)?/).exec(reversed);
|
|
589
|
+
leading = leading.slice(0, leading.length - match[0].length);
|
|
590
|
+
this.line =
|
|
591
|
+
leading +
|
|
592
|
+
this.line.slice(this.cursor, this.line.length);
|
|
593
|
+
this.cursor = leading.length;
|
|
594
|
+
this[kRefreshLine]();
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
[kDeleteWordRight]() {
|
|
599
|
+
if (this.cursor < this.line.length) {
|
|
600
|
+
this[kBeforeEdit](this.line, this.cursor);
|
|
601
|
+
const trailing = this.line.slice(this.cursor);
|
|
602
|
+
const match = (/^(?:\s+|\W+|\w+)\s*/).exec(trailing);
|
|
603
|
+
this.line =
|
|
604
|
+
this.line.slice(0, this.cursor) +
|
|
605
|
+
trailing.slice(match[0].length);
|
|
606
|
+
this[kRefreshLine]();
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
[kDeleteLineLeft]() {
|
|
611
|
+
this[kBeforeEdit](this.line, this.cursor);
|
|
612
|
+
const del = this.line.slice(0, this.cursor);
|
|
613
|
+
this[kSetLine](this.line.slice(this.cursor));
|
|
614
|
+
this.cursor = 0;
|
|
615
|
+
this[kPushToKillRing](del);
|
|
616
|
+
this[kRefreshLine]();
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
[kDeleteLineRight]() {
|
|
620
|
+
this[kBeforeEdit](this.line, this.cursor);
|
|
621
|
+
const del = this.line.slice(this.cursor);
|
|
622
|
+
this[kSetLine](this.line.slice(0, this.cursor));
|
|
623
|
+
this[kPushToKillRing](del);
|
|
624
|
+
this[kRefreshLine]();
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
[kPushToKillRing](del) {
|
|
628
|
+
if (!del || del === this[kKillRing][0]) return;
|
|
629
|
+
this[kKillRing].unshift(del);
|
|
630
|
+
this[kKillRingCursor] = 0;
|
|
631
|
+
while (this[kKillRing].length > kMaxLengthOfKillRing)
|
|
632
|
+
this[kKillRing].pop();
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
[kYank]() {
|
|
636
|
+
if (this[kKillRing].length > 0) {
|
|
637
|
+
this[kYanking] = true;
|
|
638
|
+
this[kInsertString](this[kKillRing][this[kKillRingCursor]]);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
[kYankPop]() {
|
|
643
|
+
if (!this[kYanking]) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (this[kKillRing].length > 1) {
|
|
647
|
+
const lastYank = this[kKillRing][this[kKillRingCursor]];
|
|
648
|
+
this[kKillRingCursor]++;
|
|
649
|
+
if (this[kKillRingCursor] >= this[kKillRing].length) {
|
|
650
|
+
this[kKillRingCursor] = 0;
|
|
651
|
+
}
|
|
652
|
+
const currentYank = this[kKillRing][this[kKillRingCursor]];
|
|
653
|
+
const head =
|
|
654
|
+
this.line.slice(0, this.cursor - lastYank.length);
|
|
655
|
+
const tail =
|
|
656
|
+
this.line.slice(this.cursor);
|
|
657
|
+
this[kSetLine](head + currentYank + tail);
|
|
658
|
+
this.cursor = head.length + currentYank.length;
|
|
659
|
+
this[kRefreshLine]();
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
[kLine]() {
|
|
664
|
+
this[kSavePreviousState]();
|
|
665
|
+
const line = this[kAddHistory]();
|
|
666
|
+
this[kUndoStack] = [];
|
|
667
|
+
this[kRedoStack] = [];
|
|
668
|
+
this.clearLine();
|
|
669
|
+
this[kOnLine](line);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
// TODO(puskin94): edit [kTtyWrite] to make call this function on a new key combination
|
|
674
|
+
// to make it add a new line in the middle of a "complete" multiline.
|
|
675
|
+
// I tried with shift + enter but it is not detected. Find a new one.
|
|
676
|
+
// Make sure to call this[kSavePreviousState](); && this.clearLine();
|
|
677
|
+
// before calling this[kAddNewLineOnTTY] to simulate what [kLine] is doing.
|
|
678
|
+
|
|
679
|
+
// When this function is called, the actual cursor is at the very end of the whole string,
|
|
680
|
+
// No matter where the new line was entered.
|
|
681
|
+
// This function should only be used when the output is a TTY
|
|
682
|
+
[kAddNewLineOnTTY]() {
|
|
683
|
+
// Restore terminal state and store current line
|
|
684
|
+
this[kRestorePreviousState]();
|
|
685
|
+
const originalLine = this.line;
|
|
686
|
+
|
|
687
|
+
// Split the line at the current cursor position
|
|
688
|
+
const beforeCursor = this.line.slice(0, this.cursor);
|
|
689
|
+
let afterCursor = this.line.slice(this.cursor, this.line.length);
|
|
690
|
+
|
|
691
|
+
// Add the new line where the cursor is at
|
|
692
|
+
this[kSetLine](`${beforeCursor}\n${afterCursor}`);
|
|
693
|
+
|
|
694
|
+
// To account for the new line
|
|
695
|
+
this.cursor += 1;
|
|
696
|
+
|
|
697
|
+
const hasContentAfterCursor = afterCursor.length > 0;
|
|
698
|
+
const cursorIsNotOnFirstLine = this.prevRows > 0;
|
|
699
|
+
let needsRewriteFirstLine = false;
|
|
700
|
+
|
|
701
|
+
// Handle cursor positioning based on different scenarios
|
|
702
|
+
if (hasContentAfterCursor) {
|
|
703
|
+
const splitBeg = beforeCursor.split('\n');
|
|
704
|
+
// Determine if we need to rewrite the first line
|
|
705
|
+
needsRewriteFirstLine = splitBeg.length < 2;
|
|
706
|
+
|
|
707
|
+
// If the cursor is not on the first line
|
|
708
|
+
if (cursorIsNotOnFirstLine) {
|
|
709
|
+
const splitEnd = afterCursor.split('\n');
|
|
710
|
+
|
|
711
|
+
// If the cursor when I pressed enter was at least on the second line
|
|
712
|
+
// I need to completely erase the line where the cursor was pressed because it is possible
|
|
713
|
+
// That it was pressed in the middle of the line, hence I need to write the whole line.
|
|
714
|
+
// To achieve that, I need to reach the line above the current line coming from the end
|
|
715
|
+
const dy = splitEnd.length + 1;
|
|
716
|
+
|
|
717
|
+
// Calculate how many Xs we need to move on the right to get to the end of the line
|
|
718
|
+
const dxEndOfLineAbove = (splitBeg[splitBeg.length - 2] || '').length + kMultilinePrompt.description.length;
|
|
719
|
+
moveCursor(this.output, dxEndOfLineAbove, -dy);
|
|
720
|
+
|
|
721
|
+
// This is the line that was split in the middle
|
|
722
|
+
// Just add it to the rest of the line that will be printed later
|
|
723
|
+
afterCursor = `${splitBeg[splitBeg.length - 1]}\n${afterCursor}`;
|
|
724
|
+
} else {
|
|
725
|
+
// Otherwise, go to the very beginning of the first line and erase everything
|
|
726
|
+
const dy = originalLine.split('\n').length;
|
|
727
|
+
moveCursor(this.output, 0, -dy);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Erase from the cursor to the end of the line
|
|
731
|
+
clearScreenDown(this.output);
|
|
732
|
+
|
|
733
|
+
if (cursorIsNotOnFirstLine) {
|
|
734
|
+
this[kWriteToOutput]('\n');
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (needsRewriteFirstLine) {
|
|
739
|
+
this[kWriteToOutput](`${this[kPrompt]}${beforeCursor}\n${kMultilinePrompt.description}`);
|
|
740
|
+
} else {
|
|
741
|
+
this[kWriteToOutput](kMultilinePrompt.description);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Write the rest and restore the cursor to where the user left it
|
|
745
|
+
if (hasContentAfterCursor) {
|
|
746
|
+
// Save the cursor pos, we need to come back here
|
|
747
|
+
const oldCursor = this.getCursorPos();
|
|
748
|
+
|
|
749
|
+
// Write everything after the cursor which has been deleted by clearScreenDown
|
|
750
|
+
const formattedEndContent = afterCursor.replaceAll('\n', `\n${kMultilinePrompt.description}`);
|
|
751
|
+
|
|
752
|
+
this[kWriteToOutput](formattedEndContent);
|
|
753
|
+
|
|
754
|
+
const newCursor = this[kGetDisplayPos](this.line);
|
|
755
|
+
|
|
756
|
+
// Go back to where the cursor was, with relative movement
|
|
757
|
+
moveCursor(this.output, oldCursor.cols - newCursor.cols, oldCursor.rows - newCursor.rows);
|
|
758
|
+
|
|
759
|
+
// Setting how many rows we have on top of the cursor
|
|
760
|
+
// Necessary for kRefreshLine
|
|
761
|
+
this.prevRows = oldCursor.rows;
|
|
762
|
+
} else {
|
|
763
|
+
// Setting how many rows we have on top of the cursor
|
|
764
|
+
// Necessary for kRefreshLine
|
|
765
|
+
this.prevRows = this.line.split('\n').length - 1;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
[kUndo]() {
|
|
770
|
+
if (this[kUndoStack].length <= 0) return;
|
|
771
|
+
|
|
772
|
+
this[kRedoStack].push({ text: this.line, cursor: this.cursor });
|
|
773
|
+
|
|
774
|
+
const entry = this[kUndoStack].pop();
|
|
775
|
+
this[kSetLine](entry.text);
|
|
776
|
+
this.cursor = entry.cursor;
|
|
777
|
+
|
|
778
|
+
this[kRefreshLine]();
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
[kRedo]() {
|
|
782
|
+
if (this[kRedoStack].length <= 0) return;
|
|
783
|
+
|
|
784
|
+
this[kUndoStack].push({ text: this.line, cursor: this.cursor });
|
|
785
|
+
|
|
786
|
+
const entry = this[kRedoStack].pop();
|
|
787
|
+
this[kSetLine](entry.text);
|
|
788
|
+
this.cursor = entry.cursor;
|
|
789
|
+
|
|
790
|
+
this[kRefreshLine]();
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
[kMultilineMove](direction, splitLines, { rows, cols }) {
|
|
794
|
+
const curr = splitLines[rows];
|
|
795
|
+
const down = direction === 1;
|
|
796
|
+
const adj = splitLines[rows + direction];
|
|
797
|
+
const promptLen = kMultilinePrompt.description.length;
|
|
798
|
+
let amountToMove;
|
|
799
|
+
// Clamp distance to end of current + prompt + next/prev line + newline
|
|
800
|
+
const clamp = down ?
|
|
801
|
+
curr.length - cols + promptLen + adj.length + 1 :
|
|
802
|
+
-cols + 1;
|
|
803
|
+
const shouldClamp = cols > adj.length + 1;
|
|
804
|
+
|
|
805
|
+
if (shouldClamp) {
|
|
806
|
+
if (this[kPreviousCursorCols] === -1) {
|
|
807
|
+
this[kPreviousCursorCols] = cols;
|
|
808
|
+
}
|
|
809
|
+
amountToMove = clamp;
|
|
810
|
+
} else {
|
|
811
|
+
if (down) {
|
|
812
|
+
amountToMove = curr.length + 1;
|
|
813
|
+
} else {
|
|
814
|
+
amountToMove = -adj.length - 1;
|
|
815
|
+
}
|
|
816
|
+
if (this[kPreviousCursorCols] !== -1) {
|
|
817
|
+
if (this[kPreviousCursorCols] <= adj.length) {
|
|
818
|
+
amountToMove += this[kPreviousCursorCols] - cols;
|
|
819
|
+
this[kPreviousCursorCols] = -1;
|
|
820
|
+
} else {
|
|
821
|
+
amountToMove = clamp;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
this[kMoveCursor](amountToMove);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
[kMoveDownOrHistoryNext]() {
|
|
830
|
+
const cursorPos = this.getCursorPos();
|
|
831
|
+
const splitLines = this.line.split('\n');
|
|
832
|
+
if (this[kIsMultiline] && cursorPos.rows < splitLines.length - 1) {
|
|
833
|
+
this[kMultilineMove](1, splitLines, cursorPos);
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
this[kPreviousCursorCols] = -1;
|
|
837
|
+
this[kHistoryNext]();
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// TODO(BridgeAR): Add underscores to the search part and a red background in
|
|
841
|
+
// case no match is found. This should only be the visual part and not the
|
|
842
|
+
// actual line content!
|
|
843
|
+
// TODO(BridgeAR): In case the substring based search is active and the end is
|
|
844
|
+
// reached, show a comment how to search the history as before. E.g., using
|
|
845
|
+
// <ctrl> + N. Only show this after two/three UPs or DOWNs, not on the first
|
|
846
|
+
// one.
|
|
847
|
+
[kHistoryNext]() {
|
|
848
|
+
if (!this.historyManager.canNavigateToNext()) { return; }
|
|
849
|
+
|
|
850
|
+
this[kBeforeEdit](this.line, this.cursor);
|
|
851
|
+
this[kSetLine](this.historyManager.navigateToNext(this[kSubstringSearch]));
|
|
852
|
+
this.cursor = this.line.length; // Set cursor to end of line.
|
|
853
|
+
this[kRefreshLine]();
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
[kMoveUpOrHistoryPrev]() {
|
|
857
|
+
const cursorPos = this.getCursorPos();
|
|
858
|
+
if (this[kIsMultiline] && cursorPos.rows > 0) {
|
|
859
|
+
const splitLines = this.line.split('\n');
|
|
860
|
+
this[kMultilineMove](-1, splitLines, cursorPos);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
this[kPreviousCursorCols] = -1;
|
|
864
|
+
this[kHistoryPrev]();
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
[kHistoryPrev]() {
|
|
868
|
+
if (!this.historyManager.canNavigateToPrevious()) { return; }
|
|
869
|
+
|
|
870
|
+
this[kBeforeEdit](this.line, this.cursor);
|
|
871
|
+
this[kSetLine](this.historyManager.navigateToPrevious(this[kSubstringSearch]));
|
|
872
|
+
this.cursor = this.line.length; // Set cursor to end of line.
|
|
873
|
+
this[kRefreshLine]();
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// Returns the last character's display position of the given string
|
|
877
|
+
[kTtyWrite](s, key) {
|
|
878
|
+
const previousKey = this[kPreviousKey];
|
|
879
|
+
key ||= kEmptyObject;
|
|
880
|
+
this[kPreviousKey] = key;
|
|
881
|
+
let shouldResetPreviousCursorCols = true;
|
|
882
|
+
|
|
883
|
+
if (!key.meta || key.name !== 'y') {
|
|
884
|
+
// Reset yanking state unless we are doing yank pop.
|
|
885
|
+
this[kYanking] = false;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Activate or deactivate substring search.
|
|
889
|
+
if (
|
|
890
|
+
(key.name === 'up' || key.name === 'down') &&
|
|
891
|
+
!key.ctrl &&
|
|
892
|
+
!key.meta &&
|
|
893
|
+
!key.shift
|
|
894
|
+
) {
|
|
895
|
+
if (this[kSubstringSearch] === null && !this[kIsMultiline]) {
|
|
896
|
+
this[kSubstringSearch] = this.line.slice(0, this.cursor);
|
|
897
|
+
}
|
|
898
|
+
} else if (this[kSubstringSearch] !== null) {
|
|
899
|
+
this[kSubstringSearch] = null;
|
|
900
|
+
// Reset the index in case there's no match.
|
|
901
|
+
if (this.history.length === this.historyIndex) {
|
|
902
|
+
this.historyIndex = -1;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// Undo & Redo
|
|
907
|
+
if (typeof key.sequence === 'string') {
|
|
908
|
+
switch (key.sequence.codePointAt(0)) {
|
|
909
|
+
case 0x1f:
|
|
910
|
+
this[kUndo]();
|
|
911
|
+
return;
|
|
912
|
+
case 0x1e:
|
|
913
|
+
this[kRedo]();
|
|
914
|
+
return;
|
|
915
|
+
default:
|
|
916
|
+
break;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// Ignore escape key, fixes
|
|
921
|
+
// https://github.com/nodejs/node-v0.x-archive/issues/2876.
|
|
922
|
+
if (key.name === 'escape') return;
|
|
923
|
+
|
|
924
|
+
if (key.ctrl && key.shift) {
|
|
925
|
+
/* Control and shift pressed */
|
|
926
|
+
switch (key.name) {
|
|
927
|
+
// TODO(BridgeAR): The transmitted escape sequence is `\b` and that is
|
|
928
|
+
// identical to <ctrl>-h. It should have a unique escape sequence.
|
|
929
|
+
case 'backspace':
|
|
930
|
+
this[kDeleteLineLeft]();
|
|
931
|
+
break;
|
|
932
|
+
|
|
933
|
+
case 'delete':
|
|
934
|
+
this[kDeleteLineRight]();
|
|
935
|
+
break;
|
|
936
|
+
}
|
|
937
|
+
} else if (key.ctrl) {
|
|
938
|
+
/* Control key pressed */
|
|
939
|
+
|
|
940
|
+
switch (key.name) {
|
|
941
|
+
case 'c':
|
|
942
|
+
if (this.listenerCount('SIGINT') > 0) {
|
|
943
|
+
this.emit('SIGINT');
|
|
944
|
+
} else {
|
|
945
|
+
// This readline instance is finished
|
|
946
|
+
this.close();
|
|
947
|
+
this[kQuestionReject]?.(abortError('Aborted with Ctrl+C'));
|
|
948
|
+
}
|
|
949
|
+
break;
|
|
950
|
+
|
|
951
|
+
case 'h': // delete left
|
|
952
|
+
this[kDeleteLeft]();
|
|
953
|
+
break;
|
|
954
|
+
|
|
955
|
+
case 'd': // delete right or EOF
|
|
956
|
+
if (this.cursor === 0 && this.line.length === 0) {
|
|
957
|
+
// This readline instance is finished
|
|
958
|
+
this.close();
|
|
959
|
+
this[kQuestionReject]?.(abortError('Aborted with Ctrl+D'));
|
|
960
|
+
} else if (this.cursor < this.line.length) {
|
|
961
|
+
this[kDeleteRight]();
|
|
962
|
+
}
|
|
963
|
+
break;
|
|
964
|
+
|
|
965
|
+
case 'u': // Delete from current to start of line
|
|
966
|
+
this[kDeleteLineLeft]();
|
|
967
|
+
break;
|
|
968
|
+
|
|
969
|
+
case 'k': // Delete from current to end of line
|
|
970
|
+
this[kDeleteLineRight]();
|
|
971
|
+
break;
|
|
972
|
+
|
|
973
|
+
case 'a': // Go to the start of the line
|
|
974
|
+
this[kMoveCursor](-Infinity);
|
|
975
|
+
break;
|
|
976
|
+
|
|
977
|
+
case 'e': // Go to the end of the line
|
|
978
|
+
this[kMoveCursor](+Infinity);
|
|
979
|
+
break;
|
|
980
|
+
|
|
981
|
+
case 'b': // back one character
|
|
982
|
+
this[kMoveCursor](-charLengthLeft(this.line, this.cursor));
|
|
983
|
+
break;
|
|
984
|
+
|
|
985
|
+
case 'f': // Forward one character
|
|
986
|
+
this[kMoveCursor](+charLengthAt(this.line, this.cursor));
|
|
987
|
+
break;
|
|
988
|
+
|
|
989
|
+
case 'l': // Clear the whole screen
|
|
990
|
+
cursorTo(this.output, 0, 0);
|
|
991
|
+
clearScreenDown(this.output);
|
|
992
|
+
this[kRefreshLine]();
|
|
993
|
+
break;
|
|
994
|
+
|
|
995
|
+
case 'n': // next history item
|
|
996
|
+
this[kHistoryNext]();
|
|
997
|
+
break;
|
|
998
|
+
|
|
999
|
+
case 'p': // Previous history item
|
|
1000
|
+
this[kHistoryPrev]();
|
|
1001
|
+
break;
|
|
1002
|
+
|
|
1003
|
+
case 'y': // Yank killed string
|
|
1004
|
+
this[kYank]();
|
|
1005
|
+
break;
|
|
1006
|
+
|
|
1007
|
+
case 'z':
|
|
1008
|
+
if (process.platform === 'win32') break;
|
|
1009
|
+
if (this.listenerCount('SIGTSTP') > 0) {
|
|
1010
|
+
this.emit('SIGTSTP');
|
|
1011
|
+
} else {
|
|
1012
|
+
process.once('SIGCONT', () => {
|
|
1013
|
+
// Don't raise events if stream has already been abandoned.
|
|
1014
|
+
if (!this.paused) {
|
|
1015
|
+
// Stream must be paused and resumed after SIGCONT to catch
|
|
1016
|
+
// SIGINT, SIGTSTP, and EOF.
|
|
1017
|
+
this.pause();
|
|
1018
|
+
this.emit('SIGCONT');
|
|
1019
|
+
}
|
|
1020
|
+
// Explicitly re-enable "raw mode" and move the cursor to
|
|
1021
|
+
// the correct position.
|
|
1022
|
+
// See https://github.com/joyent/node/issues/3295.
|
|
1023
|
+
this[kSetRawMode](true);
|
|
1024
|
+
this[kRefreshLine]();
|
|
1025
|
+
});
|
|
1026
|
+
this[kSetRawMode](false);
|
|
1027
|
+
process.kill(process.pid, 'SIGTSTP');
|
|
1028
|
+
}
|
|
1029
|
+
break;
|
|
1030
|
+
|
|
1031
|
+
case 'w': // Delete backwards to a word boundary
|
|
1032
|
+
// TODO(BridgeAR): The transmitted escape sequence is `\b` and that is
|
|
1033
|
+
// identical to <ctrl>-h. It should have a unique escape sequence.
|
|
1034
|
+
// Falls through
|
|
1035
|
+
case 'backspace':
|
|
1036
|
+
this[kDeleteWordLeft]();
|
|
1037
|
+
break;
|
|
1038
|
+
|
|
1039
|
+
case 'delete': // Delete forward to a word boundary
|
|
1040
|
+
this[kDeleteWordRight]();
|
|
1041
|
+
break;
|
|
1042
|
+
|
|
1043
|
+
case 'left':
|
|
1044
|
+
this[kWordLeft]();
|
|
1045
|
+
break;
|
|
1046
|
+
|
|
1047
|
+
case 'right':
|
|
1048
|
+
this[kWordRight]();
|
|
1049
|
+
break;
|
|
1050
|
+
}
|
|
1051
|
+
} else if (key.meta) {
|
|
1052
|
+
/* Meta key pressed */
|
|
1053
|
+
|
|
1054
|
+
switch (key.name) {
|
|
1055
|
+
case 'b': // backward word
|
|
1056
|
+
this[kWordLeft]();
|
|
1057
|
+
break;
|
|
1058
|
+
|
|
1059
|
+
case 'f': // forward word
|
|
1060
|
+
this[kWordRight]();
|
|
1061
|
+
break;
|
|
1062
|
+
|
|
1063
|
+
case 'd': // delete forward word
|
|
1064
|
+
case 'delete':
|
|
1065
|
+
this[kDeleteWordRight]();
|
|
1066
|
+
break;
|
|
1067
|
+
|
|
1068
|
+
case 'backspace': // Delete backwards to a word boundary
|
|
1069
|
+
this[kDeleteWordLeft]();
|
|
1070
|
+
break;
|
|
1071
|
+
|
|
1072
|
+
case 'y': // Doing yank pop
|
|
1073
|
+
this[kYankPop]();
|
|
1074
|
+
break;
|
|
1075
|
+
}
|
|
1076
|
+
} else {
|
|
1077
|
+
/* No modifier keys used */
|
|
1078
|
+
|
|
1079
|
+
// \r bookkeeping is only relevant if a \n comes right after.
|
|
1080
|
+
if (this[kSawReturnAt] && key.name !== 'enter') this[kSawReturnAt] = 0;
|
|
1081
|
+
|
|
1082
|
+
switch (key.name) {
|
|
1083
|
+
case 'return': // Carriage return, i.e. \r
|
|
1084
|
+
this[kSawReturnAt] = Date.now();
|
|
1085
|
+
this[kLine]();
|
|
1086
|
+
break;
|
|
1087
|
+
|
|
1088
|
+
case 'enter':
|
|
1089
|
+
// When key interval > crlfDelay
|
|
1090
|
+
if (
|
|
1091
|
+
this[kSawReturnAt] === 0 ||
|
|
1092
|
+
Date.now() - this[kSawReturnAt] > this.crlfDelay
|
|
1093
|
+
) {
|
|
1094
|
+
this[kLine]();
|
|
1095
|
+
}
|
|
1096
|
+
this[kSawReturnAt] = 0;
|
|
1097
|
+
break;
|
|
1098
|
+
|
|
1099
|
+
case 'backspace':
|
|
1100
|
+
this[kDeleteLeft]();
|
|
1101
|
+
break;
|
|
1102
|
+
|
|
1103
|
+
case 'delete':
|
|
1104
|
+
this[kDeleteRight]();
|
|
1105
|
+
break;
|
|
1106
|
+
|
|
1107
|
+
case 'left':
|
|
1108
|
+
// Obtain the code point to the left
|
|
1109
|
+
this[kMoveCursor](-charLengthLeft(this.line, this.cursor));
|
|
1110
|
+
break;
|
|
1111
|
+
|
|
1112
|
+
case 'right':
|
|
1113
|
+
this[kMoveCursor](+charLengthAt(this.line, this.cursor));
|
|
1114
|
+
break;
|
|
1115
|
+
|
|
1116
|
+
case 'home':
|
|
1117
|
+
this[kMoveCursor](-Infinity);
|
|
1118
|
+
break;
|
|
1119
|
+
|
|
1120
|
+
case 'end':
|
|
1121
|
+
this[kMoveCursor](+Infinity);
|
|
1122
|
+
break;
|
|
1123
|
+
|
|
1124
|
+
case 'up':
|
|
1125
|
+
shouldResetPreviousCursorCols = false;
|
|
1126
|
+
this[kMoveUpOrHistoryPrev]();
|
|
1127
|
+
break;
|
|
1128
|
+
|
|
1129
|
+
case 'down':
|
|
1130
|
+
shouldResetPreviousCursorCols = false;
|
|
1131
|
+
this[kMoveDownOrHistoryNext]();
|
|
1132
|
+
break;
|
|
1133
|
+
|
|
1134
|
+
case 'tab':
|
|
1135
|
+
// If tab completion enabled, do that...
|
|
1136
|
+
if (
|
|
1137
|
+
typeof this.completer === 'function' &&
|
|
1138
|
+
this.isCompletionEnabled
|
|
1139
|
+
) {
|
|
1140
|
+
const lastKeypressWasTab =
|
|
1141
|
+
previousKey && previousKey.name === 'tab';
|
|
1142
|
+
this[kTabComplete](lastKeypressWasTab);
|
|
1143
|
+
break;
|
|
1144
|
+
}
|
|
1145
|
+
// falls through
|
|
1146
|
+
default:
|
|
1147
|
+
if (typeof s === 'string' && s) {
|
|
1148
|
+
// Erase state of previous searches.
|
|
1149
|
+
lineEnding.lastIndex = 0;
|
|
1150
|
+
let nextMatch;
|
|
1151
|
+
// Keep track of the end of the last match.
|
|
1152
|
+
let lastIndex = 0;
|
|
1153
|
+
while ((nextMatch = lineEnding.exec(s)) !== null) {
|
|
1154
|
+
this[kInsertString](s.slice(lastIndex, nextMatch.index));
|
|
1155
|
+
({ lastIndex } = lineEnding);
|
|
1156
|
+
this[kLine]();
|
|
1157
|
+
// Restore lastIndex as the call to kLine could have mutated it.
|
|
1158
|
+
lineEnding.lastIndex = lastIndex;
|
|
1159
|
+
}
|
|
1160
|
+
// This ensures that the last line is written if it doesn't end in a newline.
|
|
1161
|
+
// Note that the last line may be the first line, in which case this still works.
|
|
1162
|
+
this[kInsertString](s.slice(lastIndex));
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
if (shouldResetPreviousCursorCols) {
|
|
1167
|
+
this[kPreviousCursorCols] = -1;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
for (const key of Reflect.ownKeys(Editor.prototype)) {
|
|
1174
|
+
if (key !== "constructor") Object.defineProperty(Interface.prototype, key, Object.getOwnPropertyDescriptor(Editor.prototype, key));
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/* node:readline's Interface: the old names of the editing methods (Node's lib/readline.js) */
|
|
1178
|
+
const legacy = LegacyInterface.prototype;
|
|
1179
|
+
legacy._insertString = Interface.prototype[kInsertString];
|
|
1180
|
+
legacy._tabComplete = function (lastKeypressWasTab) {
|
|
1181
|
+
this.pause();
|
|
1182
|
+
const string = this.line.slice(0, this.cursor);
|
|
1183
|
+
this.completer(string, (err, value) => {
|
|
1184
|
+
this.resume();
|
|
1185
|
+
if (err) {
|
|
1186
|
+
this._writeToOutput(`Tab completion error: ${inspect(err)}`);
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
this[kTabCompleter](lastKeypressWasTab, value);
|
|
1190
|
+
});
|
|
1191
|
+
};
|
|
1192
|
+
legacy._wordLeft = Interface.prototype[kWordLeft];
|
|
1193
|
+
legacy._wordRight = Interface.prototype[kWordRight];
|
|
1194
|
+
legacy._deleteLeft = Interface.prototype[kDeleteLeft];
|
|
1195
|
+
legacy._deleteRight = Interface.prototype[kDeleteRight];
|
|
1196
|
+
legacy._deleteWordLeft = Interface.prototype[kDeleteWordLeft];
|
|
1197
|
+
legacy._deleteWordRight = Interface.prototype[kDeleteWordRight];
|
|
1198
|
+
legacy._deleteLineLeft = Interface.prototype[kDeleteLineLeft];
|
|
1199
|
+
legacy._deleteLineRight = Interface.prototype[kDeleteLineRight];
|
|
1200
|
+
legacy._line = Interface.prototype[kLine];
|
|
1201
|
+
legacy._historyNext = Interface.prototype[kHistoryNext];
|
|
1202
|
+
legacy._historyPrev = Interface.prototype[kHistoryPrev];
|
|
1203
|
+
legacy._ttyWrite = Interface.prototype[kTtyWrite];
|
|
1204
|
+
|
|
1205
|
+
/* a TERM=dumb terminal: no escapes, the line as typed */
|
|
1206
|
+
function _ttyWriteDumb(s, key) {
|
|
1207
|
+
key ||= kEmptyObject;
|
|
1208
|
+
if (key.name === "escape") return;
|
|
1209
|
+
if (this[kSawReturnAt] && key.name !== "enter") this[kSawReturnAt] = 0;
|
|
1210
|
+
if (key.ctrl) {
|
|
1211
|
+
if (key.name === "c") {
|
|
1212
|
+
if (this.listenerCount("SIGINT") > 0) this.emit("SIGINT");
|
|
1213
|
+
else this.close();
|
|
1214
|
+
return;
|
|
1215
|
+
} else if (key.name === "d") {
|
|
1216
|
+
this.close();
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
switch (key.name) {
|
|
1221
|
+
case "return":
|
|
1222
|
+
this[kSawReturnAt] = Date.now();
|
|
1223
|
+
this._line();
|
|
1224
|
+
break;
|
|
1225
|
+
case "enter":
|
|
1226
|
+
if (this[kSawReturnAt] === 0 || Date.now() - this[kSawReturnAt] > this.crlfDelay) this._line();
|
|
1227
|
+
this[kSawReturnAt] = 0;
|
|
1228
|
+
break;
|
|
1229
|
+
default:
|
|
1230
|
+
if (typeof s === "string" && s) {
|
|
1231
|
+
this.line += s;
|
|
1232
|
+
this.cursor += s.length;
|
|
1233
|
+
this._writeToOutput(s);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
features.readlineEditor = true;
|
|
1239
|
+
features.ttyWriteDumb = _ttyWriteDumb;
|
|
1240
|
+
features.emitKeypressEvents = emitKeypressEvents;
|