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.
Files changed (232) hide show
  1. package/README.md +12 -4
  2. package/THIRD_PARTY_LICENSES +31 -1
  3. package/cli.js +3745 -890
  4. package/node/include/js_node.h +12 -1
  5. package/node/prelude/_http_common.js +29 -0
  6. package/node/prelude/async_hooks.js +50 -11
  7. package/node/prelude/child_process.js +31 -4
  8. package/node/prelude/console.js +311 -143
  9. package/node/prelude/crypto.js +6 -4
  10. package/node/prelude/dgram.js +1169 -0
  11. package/node/prelude/diagnostics_channel.js +3 -1
  12. package/node/prelude/dns.js +250 -45
  13. package/node/prelude/dns_promises.js +8 -2
  14. package/node/prelude/fs_promises.js +9 -1
  15. package/node/prelude/http.js +405 -94
  16. package/node/prelude/https.js +13 -4
  17. package/node/prelude/internal/console_class.js +7 -0
  18. package/node/prelude/internal/console_global.js +25 -0
  19. package/node/prelude/internal/crypto/key_encryption.js +217 -0
  20. package/node/prelude/internal/crypto/keys.js +161 -26
  21. package/node/prelude/internal/crypto/random.js +24 -0
  22. package/node/prelude/internal/crypto/util.js +2 -1
  23. package/node/prelude/internal/crypto_x509.js +161 -0
  24. package/node/prelude/internal/dns_classes.js +6 -0
  25. package/node/prelude/internal/dns_resolver.js +371 -0
  26. package/node/prelude/internal/errors.js +9 -0
  27. package/node/prelude/internal/events_on.js +82 -37
  28. package/node/prelude/internal/features.js +8 -4
  29. package/node/prelude/internal/fs_filehandle.js +10 -4
  30. package/node/prelude/internal/http_chars.ts +22 -0
  31. package/node/prelude/internal/perf_histogram.js +1 -1
  32. package/node/prelude/internal/process_more.js +32 -17
  33. package/node/prelude/internal/process_signals.js +8 -2
  34. package/node/prelude/internal/punycode.ts +163 -0
  35. package/node/prelude/internal/readline.js +648 -274
  36. package/node/prelude/internal/readline_editor.js +1240 -0
  37. package/node/prelude/internal/string_width.js +43 -0
  38. package/node/prelude/internal/tls/common.js +145 -33
  39. package/node/prelude/internal/tls/wrap.js +182 -49
  40. package/node/prelude/internal/tls/x509.js +4 -1
  41. package/node/prelude/internal/udp.js +177 -0
  42. package/node/prelude/net.js +30 -14
  43. package/node/prelude/os.js +24 -6
  44. package/node/prelude/perf_hooks.js +37 -2
  45. package/node/prelude/punycode.js +2 -130
  46. package/node/prelude/readline.js +113 -4
  47. package/node/prelude/readline_promises.js +56 -32
  48. package/node/prelude/tls.js +21 -4
  49. package/node/prelude/util.js +3 -16
  50. package/node/src/host_dns.c +623 -0
  51. package/node/src/host_http.c +12 -0
  52. package/node/src/host_net.c +46 -0
  53. package/node/src/host_os.c +1 -6
  54. package/node/src/host_sys.c +79 -0
  55. package/node/src/host_udp.c +418 -0
  56. package/node/third_party/c-ares/LICENSE.md +24 -0
  57. package/node/third_party/c-ares/config/darwin/ares_config.h +573 -0
  58. package/node/third_party/c-ares/config/freebsd/ares_config.h +573 -0
  59. package/node/third_party/c-ares/config/linux/ares_config.h +573 -0
  60. package/node/third_party/c-ares/config/openbsd/ares_config.h +583 -0
  61. package/node/third_party/c-ares/include/ares.h +1237 -0
  62. package/node/third_party/c-ares/include/ares_build.h +202 -0
  63. package/node/third_party/c-ares/include/ares_dns.h +127 -0
  64. package/node/third_party/c-ares/include/ares_dns_record.h +1119 -0
  65. package/node/third_party/c-ares/include/ares_nameser.h +510 -0
  66. package/node/third_party/c-ares/include/ares_version.h +47 -0
  67. package/node/third_party/c-ares/src/lib/ares_addrinfo2hostent.c +305 -0
  68. package/node/third_party/c-ares/src/lib/ares_addrinfo_localhost.c +245 -0
  69. package/node/third_party/c-ares/src/lib/ares_android.c +496 -0
  70. package/node/third_party/c-ares/src/lib/ares_android.h +38 -0
  71. package/node/third_party/c-ares/src/lib/ares_cancel.c +87 -0
  72. package/node/third_party/c-ares/src/lib/ares_close_sockets.c +137 -0
  73. package/node/third_party/c-ares/src/lib/ares_conn.c +511 -0
  74. package/node/third_party/c-ares/src/lib/ares_conn.h +202 -0
  75. package/node/third_party/c-ares/src/lib/ares_cookie.c +461 -0
  76. package/node/third_party/c-ares/src/lib/ares_data.c +168 -0
  77. package/node/third_party/c-ares/src/lib/ares_data.h +92 -0
  78. package/node/third_party/c-ares/src/lib/ares_destroy.c +159 -0
  79. package/node/third_party/c-ares/src/lib/ares_free_hostent.c +54 -0
  80. package/node/third_party/c-ares/src/lib/ares_free_string.c +33 -0
  81. package/node/third_party/c-ares/src/lib/ares_freeaddrinfo.c +67 -0
  82. package/node/third_party/c-ares/src/lib/ares_getaddrinfo.c +806 -0
  83. package/node/third_party/c-ares/src/lib/ares_getenv.c +38 -0
  84. package/node/third_party/c-ares/src/lib/ares_getenv.h +34 -0
  85. package/node/third_party/c-ares/src/lib/ares_gethostbyaddr.c +229 -0
  86. package/node/third_party/c-ares/src/lib/ares_gethostbyname.c +341 -0
  87. package/node/third_party/c-ares/src/lib/ares_getnameinfo.c +437 -0
  88. package/node/third_party/c-ares/src/lib/ares_hosts_file.c +960 -0
  89. package/node/third_party/c-ares/src/lib/ares_inet_net_pton.h +31 -0
  90. package/node/third_party/c-ares/src/lib/ares_init.c +631 -0
  91. package/node/third_party/c-ares/src/lib/ares_ipv6.h +115 -0
  92. package/node/third_party/c-ares/src/lib/ares_library_init.c +190 -0
  93. package/node/third_party/c-ares/src/lib/ares_metrics.c +261 -0
  94. package/node/third_party/c-ares/src/lib/ares_options.c +492 -0
  95. package/node/third_party/c-ares/src/lib/ares_parse_into_addrinfo.c +179 -0
  96. package/node/third_party/c-ares/src/lib/ares_private.h +648 -0
  97. package/node/third_party/c-ares/src/lib/ares_process.c +1649 -0
  98. package/node/third_party/c-ares/src/lib/ares_qcache.c +442 -0
  99. package/node/third_party/c-ares/src/lib/ares_query.c +151 -0
  100. package/node/third_party/c-ares/src/lib/ares_search.c +623 -0
  101. package/node/third_party/c-ares/src/lib/ares_send.c +300 -0
  102. package/node/third_party/c-ares/src/lib/ares_set_socket_functions.c +590 -0
  103. package/node/third_party/c-ares/src/lib/ares_setup.h +224 -0
  104. package/node/third_party/c-ares/src/lib/ares_socket.c +427 -0
  105. package/node/third_party/c-ares/src/lib/ares_socket.h +163 -0
  106. package/node/third_party/c-ares/src/lib/ares_sortaddrinfo.c +447 -0
  107. package/node/third_party/c-ares/src/lib/ares_strerror.c +91 -0
  108. package/node/third_party/c-ares/src/lib/ares_sysconfig.c +642 -0
  109. package/node/third_party/c-ares/src/lib/ares_sysconfig_files.c +851 -0
  110. package/node/third_party/c-ares/src/lib/ares_sysconfig_mac.c +373 -0
  111. package/node/third_party/c-ares/src/lib/ares_sysconfig_win.c +673 -0
  112. package/node/third_party/c-ares/src/lib/ares_timeout.c +162 -0
  113. package/node/third_party/c-ares/src/lib/ares_update_servers.c +1376 -0
  114. package/node/third_party/c-ares/src/lib/ares_version.c +36 -0
  115. package/node/third_party/c-ares/src/lib/config-dos.h +114 -0
  116. package/node/third_party/c-ares/src/lib/config-win32.h +396 -0
  117. package/node/third_party/c-ares/src/lib/dsa/ares_array.c +409 -0
  118. package/node/third_party/c-ares/src/lib/dsa/ares_htable.c +451 -0
  119. package/node/third_party/c-ares/src/lib/dsa/ares_htable.h +174 -0
  120. package/node/third_party/c-ares/src/lib/dsa/ares_htable_asvp.c +224 -0
  121. package/node/third_party/c-ares/src/lib/dsa/ares_htable_dict.c +230 -0
  122. package/node/third_party/c-ares/src/lib/dsa/ares_htable_strvp.c +210 -0
  123. package/node/third_party/c-ares/src/lib/dsa/ares_htable_szvp.c +188 -0
  124. package/node/third_party/c-ares/src/lib/dsa/ares_htable_vpstr.c +186 -0
  125. package/node/third_party/c-ares/src/lib/dsa/ares_htable_vpvp.c +194 -0
  126. package/node/third_party/c-ares/src/lib/dsa/ares_llist.c +385 -0
  127. package/node/third_party/c-ares/src/lib/dsa/ares_slist.c +479 -0
  128. package/node/third_party/c-ares/src/lib/dsa/ares_slist.h +207 -0
  129. package/node/third_party/c-ares/src/lib/event/ares_event.h +191 -0
  130. package/node/third_party/c-ares/src/lib/event/ares_event_configchg.c +746 -0
  131. package/node/third_party/c-ares/src/lib/event/ares_event_epoll.c +192 -0
  132. package/node/third_party/c-ares/src/lib/event/ares_event_kqueue.c +248 -0
  133. package/node/third_party/c-ares/src/lib/event/ares_event_poll.c +140 -0
  134. package/node/third_party/c-ares/src/lib/event/ares_event_select.c +159 -0
  135. package/node/third_party/c-ares/src/lib/event/ares_event_thread.c +575 -0
  136. package/node/third_party/c-ares/src/lib/event/ares_event_wake_pipe.c +172 -0
  137. package/node/third_party/c-ares/src/lib/event/ares_event_win32.c +981 -0
  138. package/node/third_party/c-ares/src/lib/event/ares_event_win32.h +161 -0
  139. package/node/third_party/c-ares/src/lib/include/ares_array.h +276 -0
  140. package/node/third_party/c-ares/src/lib/include/ares_buf.h +732 -0
  141. package/node/third_party/c-ares/src/lib/include/ares_htable_asvp.h +130 -0
  142. package/node/third_party/c-ares/src/lib/include/ares_htable_dict.h +123 -0
  143. package/node/third_party/c-ares/src/lib/include/ares_htable_strvp.h +130 -0
  144. package/node/third_party/c-ares/src/lib/include/ares_htable_szvp.h +118 -0
  145. package/node/third_party/c-ares/src/lib/include/ares_htable_vpstr.h +111 -0
  146. package/node/third_party/c-ares/src/lib/include/ares_htable_vpvp.h +128 -0
  147. package/node/third_party/c-ares/src/lib/include/ares_llist.h +239 -0
  148. package/node/third_party/c-ares/src/lib/include/ares_mem.h +41 -0
  149. package/node/third_party/c-ares/src/lib/include/ares_str.h +261 -0
  150. package/node/third_party/c-ares/src/lib/inet_net_pton.c +451 -0
  151. package/node/third_party/c-ares/src/lib/inet_ntop.c +219 -0
  152. package/node/third_party/c-ares/src/lib/legacy/ares_create_query.c +78 -0
  153. package/node/third_party/c-ares/src/lib/legacy/ares_expand_name.c +99 -0
  154. package/node/third_party/c-ares/src/lib/legacy/ares_expand_string.c +107 -0
  155. package/node/third_party/c-ares/src/lib/legacy/ares_fds.c +80 -0
  156. package/node/third_party/c-ares/src/lib/legacy/ares_getsock.c +85 -0
  157. package/node/third_party/c-ares/src/lib/legacy/ares_parse_a_reply.c +107 -0
  158. package/node/third_party/c-ares/src/lib/legacy/ares_parse_aaaa_reply.c +109 -0
  159. package/node/third_party/c-ares/src/lib/legacy/ares_parse_caa_reply.c +137 -0
  160. package/node/third_party/c-ares/src/lib/legacy/ares_parse_mx_reply.c +110 -0
  161. package/node/third_party/c-ares/src/lib/legacy/ares_parse_naptr_reply.c +132 -0
  162. package/node/third_party/c-ares/src/lib/legacy/ares_parse_ns_reply.c +154 -0
  163. package/node/third_party/c-ares/src/lib/legacy/ares_parse_ptr_reply.c +213 -0
  164. package/node/third_party/c-ares/src/lib/legacy/ares_parse_soa_reply.c +115 -0
  165. package/node/third_party/c-ares/src/lib/legacy/ares_parse_srv_reply.c +114 -0
  166. package/node/third_party/c-ares/src/lib/legacy/ares_parse_txt_reply.c +144 -0
  167. package/node/third_party/c-ares/src/lib/legacy/ares_parse_uri_reply.c +113 -0
  168. package/node/third_party/c-ares/src/lib/record/ares_dns_mapping.c +982 -0
  169. package/node/third_party/c-ares/src/lib/record/ares_dns_multistring.c +309 -0
  170. package/node/third_party/c-ares/src/lib/record/ares_dns_multistring.h +72 -0
  171. package/node/third_party/c-ares/src/lib/record/ares_dns_name.c +736 -0
  172. package/node/third_party/c-ares/src/lib/record/ares_dns_parse.c +1360 -0
  173. package/node/third_party/c-ares/src/lib/record/ares_dns_private.h +273 -0
  174. package/node/third_party/c-ares/src/lib/record/ares_dns_record.c +1701 -0
  175. package/node/third_party/c-ares/src/lib/record/ares_dns_write.c +1229 -0
  176. package/node/third_party/c-ares/src/lib/str/ares_buf.c +1510 -0
  177. package/node/third_party/c-ares/src/lib/str/ares_str.c +562 -0
  178. package/node/third_party/c-ares/src/lib/str/ares_strsplit.c +90 -0
  179. package/node/third_party/c-ares/src/lib/str/ares_strsplit.h +51 -0
  180. package/node/third_party/c-ares/src/lib/thirdparty/apple/dnsinfo.h +122 -0
  181. package/node/third_party/c-ares/src/lib/util/ares_iface_ips.c +640 -0
  182. package/node/third_party/c-ares/src/lib/util/ares_iface_ips.h +140 -0
  183. package/node/third_party/c-ares/src/lib/util/ares_math.c +167 -0
  184. package/node/third_party/c-ares/src/lib/util/ares_math.h +58 -0
  185. package/node/third_party/c-ares/src/lib/util/ares_rand.c +389 -0
  186. package/node/third_party/c-ares/src/lib/util/ares_rand.h +36 -0
  187. package/node/third_party/c-ares/src/lib/util/ares_threads.c +613 -0
  188. package/node/third_party/c-ares/src/lib/util/ares_threads.h +60 -0
  189. package/node/third_party/c-ares/src/lib/util/ares_time.h +49 -0
  190. package/node/third_party/c-ares/src/lib/util/ares_timeval.c +95 -0
  191. package/node/third_party/c-ares/src/lib/util/ares_uri.c +1627 -0
  192. package/node/third_party/c-ares/src/lib/util/ares_uri.h +252 -0
  193. package/node/third_party/c-ares/src/lib/windows_port.c +29 -0
  194. package/package.json +1 -1
  195. package/prebuilt/darwin-arm64/cares-a6286c65ca9374a3.a +0 -0
  196. package/prebuilt/darwin-x64/cares-a6286c65ca9374a3.a +0 -0
  197. package/prebuilt/linux-arm64/cares-4ba05ac8e948af8e.a +0 -0
  198. package/prebuilt/linux-arm64-musl/cares-4ba05ac8e948af8e.a +0 -0
  199. package/prebuilt/linux-x64/cares-4ba05ac8e948af8e.a +0 -0
  200. package/prebuilt/linux-x64-musl/cares-4ba05ac8e948af8e.a +0 -0
  201. package/prebuilt/win32-x64/boringssl-ce573858472b7fc9.a +0 -0
  202. package/prebuilt/win32-x64/brotli-20d86a61f30baeb3.a +0 -0
  203. package/prebuilt/win32-x64/cares-49c9ec95162b912d.a +0 -0
  204. package/prebuilt/win32-x64/uv-b11bae65d7fced82.a +0 -0
  205. package/prebuilt/win32-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
  206. package/prebuilt/win32-x64/zstd-015315ac7c784c73.a +0 -0
  207. package/runtime/include/js_buffer.h +185 -0
  208. package/runtime/include/js_builtins.h +58 -0
  209. package/runtime/include/js_crypto.h +11 -0
  210. package/runtime/include/js_exception.h +3 -0
  211. package/runtime/include/js_function.h +1 -1
  212. package/runtime/include/js_gc.h +13 -10
  213. package/runtime/include/js_jobs.h +2 -0
  214. package/runtime/include/js_object.h +79 -3
  215. package/runtime/include/js_ops.h +27 -0
  216. package/runtime/include/js_runtime.h +1 -0
  217. package/runtime/include/js_web.h +0 -3
  218. package/runtime/include/jscontext.h +8 -0
  219. package/runtime/src/builtins.c +17 -4
  220. package/runtime/src/crypto_internal.h +1 -0
  221. package/runtime/src/crypto_subtle.c +66 -7
  222. package/runtime/src/crypto_tls.c +265 -19
  223. package/runtime/src/function.c +37 -10
  224. package/runtime/src/gc.c +76 -93
  225. package/runtime/src/inspect.c +18 -1
  226. package/runtime/src/iterator.c +5 -1
  227. package/runtime/src/jobs.c +37 -22
  228. package/runtime/src/object.c +91 -8
  229. package/runtime/src/os.c +16 -6
  230. package/runtime/src/os.h +2 -0
  231. package/runtime/src/process.c +97 -10
  232. 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;