MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

rewrite readline to use proper EventEmitter, add async readline

+583 -422
+14
examples/spec/events.js
··· 111 111 test('separate instances are isolated (A)', aVal, 1); 112 112 test('separate instances are isolated (B)', bVal, 0); 113 113 114 + const eeC = new EventEmitter(); 115 + eeC.on('a', () => {}); 116 + eeC.on('b', () => {}); 117 + eeC.removeAllListeners(); 118 + test('removeAllListeners without event clears all listeners', eeC.listenerCount('a') + eeC.listenerCount('b'), 0); 119 + 120 + const eeD = new EventEmitter(); 121 + let listenerThis = null; 122 + eeD.on('ctx', function () { 123 + listenerThis = this; 124 + }); 125 + eeD.emit('ctx'); 126 + test('EventEmitter listeners receive emitter as this', listenerThis, eeD); 127 + 114 128 console.log('\nEventTarget Tests\n'); 115 129 116 130 const et = new EventTarget();
+21 -1
examples/spec/readline.js
··· 1 - import { test, summary } from './helpers.js'; 1 + import { test, testDeep, summary } from './helpers.js'; 2 2 import * as readline from 'node:readline'; 3 3 import * as readlinePromises from 'node:readline/promises'; 4 4 ··· 192 192 test('ctrl+u clears line', rl5.line, ''); 193 193 194 194 rl5.close(); 195 + 196 + const rlAsync = readlinePromises.createInterface({ 197 + input: process.stdin, 198 + output: process.stdout 199 + }); 200 + 201 + const asyncLines = []; 202 + const consumeAsyncLines = (async () => { 203 + for await (const line of rlAsync) { 204 + asyncLines.push(line); 205 + if (asyncLines.length === 2) break; 206 + } 207 + })(); 208 + 209 + rlAsync.write('alpha\n'); 210 + rlAsync.write('beta\n'); 211 + await consumeAsyncLines; 212 + 213 + testDeep('async iterator yields lines', asyncLines, ['alpha', 'beta']); 214 + test('async iterator closes interface after break', rlAsync.closed, true); 195 215 196 216 summary();
+23
include/modules/events.h
··· 5 5 #include "types.h" 6 6 7 7 ant_value_t events_library(ant_t *js); 8 + ant_value_t eventemitter_prototype(ant_t *js); 8 9 9 10 void init_events_module(void); 10 11 void js_dispatch_global_event(ant_t *js, ant_value_t event_obj); ··· 31 32 ant_t *js, 32 33 ant_value_t target, ant_value_t key, 33 34 ant_value_t *args, int nargs 35 + ); 36 + 37 + bool eventemitter_remove_listener( 38 + ant_t *js, 39 + ant_value_t target, const char *event_type, 40 + ant_value_t listener 41 + ); 42 + 43 + bool eventemitter_remove_listener_val( 44 + ant_t *js, 45 + ant_value_t target, ant_value_t key, 46 + ant_value_t listener 47 + ); 48 + 49 + ant_offset_t eventemitter_listener_count( 50 + ant_t *js, 51 + ant_value_t target, const char *event_type 52 + ); 53 + 54 + ant_offset_t eventemitter_listener_count_val( 55 + ant_t *js, 56 + ant_value_t target, ant_value_t key 34 57 ); 35 58 36 59 #endif
+100 -9
src/modules/events.c
··· 26 26 } event_data_t; 27 27 28 28 static ant_value_t g_isTrusted_getter = 0; 29 + static ant_value_t g_eventemitter_ctor = 0; 30 + static ant_value_t g_eventemitter_proto = 0; 29 31 static ant_value_t g_event_proto = 0; 30 32 static ant_value_t g_customevent_proto = 0; 31 33 static ant_value_t g_errorevent_proto = 0; ··· 637 639 return true; 638 640 } 639 641 642 + static bool eventemitter_remove_listener_impl( 643 + ant_t *js, 644 + ant_value_t target, ant_value_t key, 645 + ant_value_t listener 646 + ) { 647 + EventType *evt = NULL; 648 + uint8_t t = 0; 649 + 650 + if (!is_object_type(target) || !key) return false; 651 + t = vtype(listener); 652 + if (t != T_FUNC && t != T_CFUNC) return false; 653 + 654 + evt = find_emitter_event_type(js, target, key); 655 + if (!evt) return false; 656 + 657 + for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) { 658 + EventListenerEntry *entry = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); 659 + if (entry->callback == listener) { 660 + utarray_erase(evt->listeners, i, 1); 661 + return true; 662 + }} 663 + 664 + return false; 665 + } 666 + 667 + static ant_offset_t eventemitter_listener_count_impl( 668 + ant_t *js, 669 + ant_value_t target, ant_value_t key 670 + ) { 671 + EventType *evt = NULL; 672 + 673 + if (!is_object_type(target) || !key) return 0; 674 + evt = find_emitter_event_type(js, target, key); 675 + if (!evt) return 0; 676 + 677 + return (ant_offset_t)utarray_len(evt->listeners); 678 + } 679 + 640 680 static ant_value_t js_eventemitter_off(ant_t *js, ant_value_t *args, int nargs) { 641 681 if (nargs < 2) return js_mkerr(js, "off requires 2 arguments (event, listener)"); 642 682 ant_value_t key = evt_key_from_arg(args[0]); ··· 674 714 if (vtype(entry->signal) != T_UNDEF && abort_signal_is_aborted(entry->signal)) continue; 675 715 if (vtype(cb) != T_FUNC && vtype(cb) != T_CFUNC) continue; 676 716 677 - ant_value_t result = sv_vm_call(js->vm, js, cb, js_mkundef(), args, nargs, NULL, false); 717 + ant_value_t result = sv_vm_call(js->vm, js, cb, target, args, nargs, NULL, false); 678 718 if (vtype(result) == T_ERR) { 679 719 if (vtype(key) == T_STR) fprintf(stderr, "Error in event listener for '%s': %s\n", js_str(js, key), js_str(js, result)); 680 720 else fprintf(stderr, "Error in event listener: %s\n", js_str(js, result)); ··· 727 767 return eventemitter_add_listener_val(js, target, js_mkstr(js, event_type, strlen(event_type)), listener, once); 728 768 } 729 769 770 + bool eventemitter_remove_listener_val( 771 + ant_t *js, 772 + ant_value_t target, ant_value_t key, 773 + ant_value_t listener 774 + ) { 775 + return eventemitter_remove_listener_impl(js, target, key, listener); 776 + } 777 + 778 + bool eventemitter_remove_listener( 779 + ant_t *js, 780 + ant_value_t target, const char *event_type, 781 + ant_value_t listener 782 + ) { 783 + return eventemitter_remove_listener_val(js, target, js_mkstr(js, event_type, strlen(event_type)), listener); 784 + } 785 + 786 + ant_offset_t eventemitter_listener_count_val( 787 + ant_t *js, 788 + ant_value_t target, ant_value_t key 789 + ) { 790 + return eventemitter_listener_count_impl(js, target, key); 791 + } 792 + 793 + ant_offset_t eventemitter_listener_count( 794 + ant_t *js, 795 + ant_value_t target, const char *event_type 796 + ) { 797 + return eventemitter_listener_count_val(js, target, js_mkstr(js, event_type, strlen(event_type))); 798 + } 799 + 730 800 static ant_value_t js_eventemitter_add(ant_t *js, ant_value_t *args, int nargs, bool once, bool prepend) { 731 801 if (nargs < 2) return js_mkerr(js, "requires 2 arguments (event, listener)"); 732 802 ant_value_t key = evt_key_from_arg(args[0]); ··· 756 826 757 827 static ant_value_t js_eventemitter_removeAllListeners(ant_t *js, ant_value_t *args, int nargs) { 758 828 ant_value_t this_obj = js_getthis(js); 759 - if (nargs < 1) return this_obj; 760 - 829 + EventType **events = NULL; 830 + 831 + if (nargs < 1 || vtype(args[0]) == T_UNDEF) { 832 + events = get_or_create_emitter_events(js, this_obj); 833 + if (!events || !*events) return this_obj; 834 + 835 + EventType *evt, *tmp; 836 + HASH_ITER(hh, *events, evt, tmp) utarray_clear(evt->listeners); 837 + return this_obj; 838 + } 839 + 761 840 ant_value_t key = evt_key_from_arg(args[0]); 762 841 if (!key) return this_obj; 763 - 842 + 764 843 EventType *evt = find_emitter_event_type(js, this_obj, key); 765 844 if (evt) utarray_clear(evt->listeners); 766 845 ··· 794 873 795 874 ant_value_t events_library(ant_t *js) { 796 875 ant_value_t lib = js_mkobj(js); 797 - ant_value_t eventemitter_ctor = js_mkobj(js); 876 + 877 + eventemitter_prototype(js); 878 + js_set_module_default(js, lib, g_eventemitter_ctor, "EventEmitter"); 879 + js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "events", 6)); 880 + 881 + return lib; 882 + } 883 + 884 + ant_value_t eventemitter_prototype(ant_t *js) { 885 + if (g_eventemitter_proto) return g_eventemitter_proto; 886 + 887 + ant_value_t eventemitter_ctor = js_mkobj(js); 798 888 ant_value_t eventemitter_proto = js_mkobj(js); 799 889 800 890 js_set(js, eventemitter_proto, "on", js_mkfun(js_eventemitter_on)); ··· 814 904 js_mkprop_fast(js, eventemitter_ctor, "name", 4, ANT_STRING("EventEmitter")); 815 905 js_set_descriptor(js, eventemitter_ctor, "name", 4, 0); 816 906 817 - ant_value_t ctor_fn = js_obj_to_func_ex(eventemitter_ctor, SV_CALL_IS_DEFAULT_CTOR); 818 - js_set_module_default(js, lib, ctor_fn, "EventEmitter"); 819 - js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "events", 6)); 907 + g_eventemitter_proto = eventemitter_proto; 908 + g_eventemitter_ctor = js_obj_to_func_ex(eventemitter_ctor, SV_CALL_IS_DEFAULT_CTOR); 820 909 821 - return lib; 910 + return g_eventemitter_proto; 822 911 } 823 912 824 913 void init_events_module(void) { ··· 903 992 if (*reg->events) mark_event_type_listeners(js, mark, *reg->events); 904 993 } 905 994 if (g_isTrusted_getter) mark(js, g_isTrusted_getter); 995 + if (g_eventemitter_ctor) mark(js, g_eventemitter_ctor); 996 + if (g_eventemitter_proto) mark(js, g_eventemitter_proto); 906 997 if (g_event_proto) mark(js, g_event_proto); 907 998 if (g_customevent_proto) mark(js, g_customevent_proto); 908 999 if (g_errorevent_proto) mark(js, g_errorevent_proto);
+425 -410
src/modules/readline.c
··· 33 33 #include "silver/engine.h" 34 34 35 35 #include "gc/modules.h" 36 + #include "modules/events.h" 36 37 #include "modules/readline.h" 37 38 #include "modules/process.h" 38 39 #include "modules/symbol.h" ··· 42 43 #define DEFAULT_PROMPT "> " 43 44 #define DEFAULT_HISTORY_SIZE 30 44 45 #define DEFAULT_TAB_SIZE 8 45 - #define MAX_INTERFACES 64 46 - 47 - typedef struct { 48 - ant_value_t listener; 49 - bool once; 50 - } RLEventListener; 51 - 52 - #define MAX_LISTENERS_PER_EVENT 16 53 - 54 - typedef struct { 55 - char *event_type; 56 - RLEventListener listeners[MAX_LISTENERS_PER_EVENT]; 57 - int listener_count; 58 - UT_hash_handle hh; 59 - } RLEventType; 60 46 61 47 typedef struct { 62 48 char **lines; ··· 87 73 int tab_size; 88 74 ant_value_t pending_question_resolve; 89 75 ant_value_t pending_question_reject; 90 - RLEventType *events; 91 76 uv_tty_t tty_in; 92 77 uv_tty_t tty_out; 93 78 bool tty_initialized; ··· 104 89 #endif 105 90 } rl_interface_t; 106 91 107 - static rl_interface_t *interfaces = NULL; 108 92 static uint64_t next_interface_id = 1; 93 + static rl_interface_t *interfaces = NULL; 94 + static ant_value_t g_rl_async_iter_proto = 0; 95 + static ant_value_t g_rl_interface_proto = 0; 109 96 110 97 static void rl_history_init(rl_history_t *hist, int capacity) { 111 98 hist->capacity = capacity > 0 ? capacity : DEFAULT_HISTORY_SIZE; ··· 114 101 hist->current = -1; 115 102 } 116 103 104 + static void rl_history_remove_at(rl_history_t *hist, int index) { 105 + if (!hist || index < 0 || index >= hist->count) return; 106 + 107 + free(hist->lines[index]); 108 + if (index < hist->count - 1) memmove( 109 + hist->lines + index, 110 + hist->lines + index + 1, 111 + sizeof(char *) * (size_t)(hist->count - index - 1) 112 + ); 113 + 114 + hist->count--; 115 + } 116 + 117 117 static void rl_history_add(rl_history_t *hist, const char *line, bool remove_duplicates) { 118 - if (!line || strlen(line) == 0) return; 119 - 118 + int duplicate_index = -1; 119 + 120 + if (!line || line[0] == '\0') return; 121 + if (!remove_duplicates && hist->count > 0 && strcmp(hist->lines[hist->count - 1], line) == 0) return; 122 + 120 123 if (remove_duplicates) { 121 - for (int i = 0; i < hist->count; i++) { 122 - if (strcmp(hist->lines[i], line) == 0) { 123 - free(hist->lines[i]); 124 - for (int j = i; j < hist->count - 1; j++) { 125 - hist->lines[j] = hist->lines[j + 1]; 126 - } 127 - hist->count--; break; 128 - } 129 - } 130 - } else if (hist->count > 0 && strcmp(hist->lines[hist->count - 1], line) == 0) { 131 - return; 132 - } 133 - 134 - if (hist->count >= hist->capacity) { 135 - free(hist->lines[0]); 136 - memmove(hist->lines, hist->lines + 1, sizeof(char*) * (hist->capacity - 1)); 137 - hist->count--; 138 - } 139 - 124 + for (int i = 0; i < hist->count; i++) { 125 + if (strcmp(hist->lines[i], line) != 0) continue; 126 + duplicate_index = i; 127 + break; 128 + }} 129 + 130 + if (duplicate_index >= 0) rl_history_remove_at(hist, duplicate_index); 131 + if (hist->count >= hist->capacity) rl_history_remove_at(hist, 0); 132 + 140 133 hist->lines[hist->count++] = strdup(line); 141 134 hist->current = hist->count; 142 135 } ··· 169 162 hist->current = -1; 170 163 } 171 164 172 - static RLEventType *find_or_create_event_type(rl_interface_t *iface, const char *event_type) { 173 - RLEventType *evt = NULL; 174 - HASH_FIND_STR(iface->events, event_type, evt); 175 - 176 - if (evt == NULL) { 177 - evt = malloc(sizeof(RLEventType)); 178 - evt->event_type = strdup(event_type); 179 - evt->listener_count = 0; 180 - HASH_ADD_KEYPTR(hh, iface->events, evt->event_type, strlen(evt->event_type), evt); 181 - } 182 - 183 - return evt; 184 - } 185 - 186 - static void emit_event(ant_t *js, rl_interface_t *iface, const char *event_type, ant_value_t *args, int nargs) { 187 - RLEventType *evt = NULL; 188 - HASH_FIND_STR(iface->events, event_type, evt); 189 - 190 - if (evt == NULL || evt->listener_count == 0) return; 191 - 192 - int i = 0; 193 - while (i < evt->listener_count) { 194 - RLEventListener *listener = &evt->listeners[i]; 195 - sv_vm_call(js->vm, js, listener->listener, js_mkundef(), args, nargs, NULL, false); 196 - 197 - if (listener->once) { 198 - for (int j = i; j < evt->listener_count - 1; j++) { 199 - evt->listeners[j] = evt->listeners[j + 1]; 200 - } 201 - evt->listener_count--; 202 - } else i++; 203 - } 204 - } 205 - 206 - static bool rl_has_event_listener(rl_interface_t *iface, const char *event_type) { 207 - RLEventType *evt = NULL; 208 - if (!iface || !event_type) return false; 209 - HASH_FIND_STR(iface->events, event_type, evt); 210 - return evt != NULL && evt->listener_count > 0; 211 - } 212 - 213 - static ant_value_t get_history_array(ant_t *js, rl_interface_t *iface) { 214 - ant_value_t arr = js_mkarr(js); 215 - for (int i = 0; i < iface->history.count; i++) js_arr_push( 216 - js, arr, js_mkstr(js, iface->history.lines[i], strlen(iface->history.lines[i])) 217 - ); 218 - return arr; 219 - } 220 - 221 - static void emit_history_event(ant_t *js, rl_interface_t *iface) { 222 - ant_value_t history_arr = get_history_array(js, iface); 223 - emit_event(js, iface, "history", &history_arr, 1); 224 - } 225 - 226 - static void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); 227 - static void on_stdin_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf); 228 - 229 165 #ifndef _WIN32 230 166 static void enter_raw_mode(rl_interface_t *iface) { 231 167 if (iface->raw_mode) return; ··· 412 348 413 349 static void handle_escape_sequence(rl_interface_t *iface, const char *seq, int len) { 414 350 if (len >= 2 && seq[0] == '[') { 415 - switch (seq[1]) { 416 - case 'A': handle_history_up(iface); break; 417 - case 'B': handle_history_down(iface); break; 418 - case 'C': 419 - if (iface->line_pos < iface->line_len) { 420 - iface->line_pos++; 421 - printf("\033[C"); 422 - fflush(stdout); 423 - } 424 - break; 425 - case 'D': 426 - if (iface->line_pos > 0) { 427 - iface->line_pos--; 428 - printf("\033[D"); 429 - fflush(stdout); 430 - } 431 - break; 432 - case 'H': 433 - iface->line_pos = 0; 434 - refresh_line(iface); 435 - break; 436 - case 'F': 437 - iface->line_pos = iface->line_len; 438 - refresh_line(iface); 439 - break; 440 - case '3': 441 - if (len >= 3 && seq[2] == '~') { 442 - handle_delete(iface); 443 - } 444 - break; 445 - } 446 - } 351 + switch (seq[1]) { 352 + case 'A': handle_history_up(iface); break; 353 + case 'B': handle_history_down(iface); break; 354 + case 'C': 355 + if (iface->line_pos < iface->line_len) { 356 + iface->line_pos++; 357 + printf("\033[C"); 358 + fflush(stdout); 359 + } 360 + break; 361 + case 'D': 362 + if (iface->line_pos > 0) { 363 + iface->line_pos--; 364 + printf("\033[D"); 365 + fflush(stdout); 366 + } 367 + break; 368 + case 'H': 369 + iface->line_pos = 0; 370 + refresh_line(iface); 371 + break; 372 + case 'F': 373 + iface->line_pos = iface->line_len; 374 + refresh_line(iface); 375 + break; 376 + case '3': 377 + if (len >= 3 && seq[2] == '~') { 378 + handle_delete(iface); 379 + } 380 + break; 381 + }} 447 382 } 448 383 449 384 static void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { ··· 451 386 buf->len = suggested_size; 452 387 } 453 388 389 + static void emit_event(ant_t *js, rl_interface_t *iface, const char *event_type, ant_value_t *args, int nargs) { 390 + if (!iface || vtype(iface->js_obj) == T_UNDEF) return; 391 + eventemitter_emit_args(js, iface->js_obj, event_type, args, nargs); 392 + } 393 + 394 + static ant_value_t get_history_array(ant_t *js, rl_interface_t *iface) { 395 + ant_value_t arr = js_mkarr(js); 396 + for (int i = 0; i < iface->history.count; i++) js_arr_push( 397 + js, arr, js_mkstr(js, iface->history.lines[i], strlen(iface->history.lines[i])) 398 + ); 399 + return arr; 400 + } 401 + 402 + static void emit_history_event(ant_t *js, rl_interface_t *iface) { 403 + ant_value_t history_arr = get_history_array(js, iface); 404 + emit_event(js, iface, "history", &history_arr, 1); 405 + } 406 + 407 + 454 408 static void stop_reading(rl_interface_t *iface) { 455 409 if (!iface->reading) return; 456 410 ··· 464 418 #endif 465 419 } 466 420 421 + static bool rl_has_event_listener(ant_t *js, rl_interface_t *iface, const char *event_type) { 422 + if (!iface || !event_type || vtype(iface->js_obj) == T_UNDEF) return false; 423 + return eventemitter_listener_count(js, iface->js_obj, event_type) > 0; 424 + } 425 + 426 + static bool rl_add_listener(ant_t *js, rl_interface_t *iface, const char *event_type, ant_value_t listener, bool once) { 427 + if (!iface || !event_type || vtype(iface->js_obj) == T_UNDEF) return false; 428 + return eventemitter_add_listener(js, iface->js_obj, event_type, listener, once); 429 + } 430 + 431 + static void rl_remove_listener(ant_t *js, rl_interface_t *iface, const char *event_type, ant_value_t listener) { 432 + if (!iface || !event_type || vtype(iface->js_obj) == T_UNDEF) return; 433 + eventemitter_remove_listener(js, iface->js_obj, event_type, listener); 434 + } 435 + 436 + static ant_value_t rl_async_iter_state(ant_t *js, ant_value_t iterator) { 437 + ant_value_t state = is_object_type(iterator) ? js_get_slot(iterator, SLOT_DATA) : js_mkundef(); 438 + return is_object_type(state) ? state : js_mkundef(); 439 + } 440 + 441 + static ant_value_t rl_async_iter_queue(ant_t *js, ant_value_t state, const char *queue_key) { 442 + ant_value_t queue = is_object_type(state) ? js_get(js, state, queue_key) : js_mkundef(); 443 + return vtype(queue) == T_ARR ? queue : js_mkundef(); 444 + } 445 + 446 + static ant_offset_t rl_async_iter_queue_head(ant_t *js, ant_value_t state, const char *head_key) { 447 + ant_value_t head = is_object_type(state) ? js_get(js, state, head_key) : js_mkundef(); 448 + return vtype(head) == T_NUM ? (ant_offset_t)js_getnum(head) : 0; 449 + } 450 + 451 + static void rl_async_iter_set_queue_head(ant_t *js, ant_value_t state, const char *head_key, ant_offset_t head) { 452 + if (is_object_type(state)) js_set(js, state, head_key, js_mknum((double)head)); 453 + } 454 + 455 + static void rl_close_interface(ant_t *js, rl_interface_t *iface) { 456 + if (!iface || iface->closed) return; 457 + stop_reading(iface); 458 + 459 + if (iface->tty_initialized) { 460 + uv_close((uv_handle_t *)&iface->tty_in, NULL); 461 + #ifndef _WIN32 462 + if (!iface->sigint_watcher_active && uv_is_active((uv_handle_t *)&iface->sigint_watcher)) { 463 + uv_close((uv_handle_t *)&iface->sigint_watcher, NULL); 464 + } 465 + exit_raw_mode(iface); 466 + #endif 467 + iface->tty_initialized = false; 468 + } 469 + 470 + iface->closed = true; 471 + emit_event(js, iface, "close", NULL, 0); 472 + } 473 + 467 474 static void process_line(ant_t *js, rl_interface_t *iface) { 468 475 char *line = strdup(iface->line_buffer); 469 476 ··· 548 555 rl_interface_t *iface = (rl_interface_t *)handle->data; 549 556 ant_t *js = rt->js; 550 557 551 - if (rl_has_event_listener(iface, "SIGINT")) { 558 + if (rl_has_event_listener(js, iface, "SIGINT")) { 552 559 emit_event(js, iface, "SIGINT", NULL, 0); 553 560 } else if (process_has_event_listeners("SIGINT")) { 554 561 ant_value_t sig_arg = js_mkstr(js, "SIGINT", 6); ··· 597 604 return iface; 598 605 } 599 606 600 - static ant_value_t rl_interface_on(ant_t *js, ant_value_t *args, int nargs) { 601 - ant_value_t this_obj = js_getthis(js); 602 - rl_interface_t *iface = get_interface(js, this_obj); 603 - 604 - if (!iface) return js_mkerr(js, "Invalid Interface"); 605 - if (nargs < 2) return js_mkerr(js, "on requires 2 arguments"); 606 - 607 - char *event = js_getstr(js, args[0], NULL); 608 - if (!event) return js_mkerr(js, "event must be a string"); 609 - int t = vtype(args[1]); 610 - if (t != T_FUNC && t != T_CFUNC) return js_mkerr(js, "listener must be a function"); 611 - 612 - RLEventType *evt = find_or_create_event_type(iface, event); 613 - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { 614 - return js_mkerr(js, "maximum listeners reached"); 615 - } 616 - 617 - evt->listeners[evt->listener_count].listener = args[1]; 618 - evt->listeners[evt->listener_count].once = false; 619 - evt->listener_count++; 620 - 621 - return this_obj; 622 - } 623 - 624 - static ant_value_t rl_interface_once(ant_t *js, ant_value_t *args, int nargs) { 625 - ant_value_t this_obj = js_getthis(js); 626 - rl_interface_t *iface = get_interface(js, this_obj); 627 - 628 - if (!iface) return js_mkerr(js, "Invalid Interface"); 629 - if (nargs < 2) return js_mkerr(js, "once requires 2 arguments"); 630 - 631 - char *event = js_getstr(js, args[0], NULL); 632 - if (!event) return js_mkerr(js, "event must be a string"); 633 - int t = vtype(args[1]); 634 - if (t != T_FUNC && t != T_CFUNC) return js_mkerr(js, "listener must be a function"); 635 - 636 - RLEventType *evt = find_or_create_event_type(iface, event); 637 - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { 638 - return js_mkerr(js, "maximum listeners reached"); 639 - } 640 - 641 - evt->listeners[evt->listener_count].listener = args[1]; 642 - evt->listeners[evt->listener_count].once = true; 643 - evt->listener_count++; 644 - 645 - return this_obj; 646 - } 647 - 648 - static ant_value_t rl_interface_off(ant_t *js, ant_value_t *args, int nargs) { 649 - ant_value_t this_obj = js_getthis(js); 650 - rl_interface_t *iface = get_interface(js, this_obj); 651 - 652 - if (!iface) return js_mkerr(js, "Invalid Interface"); 653 - if (nargs < 2) return this_obj; 654 - 655 - char *event = js_getstr(js, args[0], NULL); 656 - if (!event) return this_obj; 657 - 658 - RLEventType *evt = NULL; 659 - HASH_FIND_STR(iface->events, event, evt); 660 - if (!evt) return this_obj; 661 - 662 - for (int i = 0; i < evt->listener_count; i++) { 663 - if (evt->listeners[i].listener == args[1]) { 664 - for (int j = i; j < evt->listener_count - 1; j++) { 665 - evt->listeners[j] = evt->listeners[j + 1]; 666 - } 667 - evt->listener_count--; 668 - break; 669 - } 670 - } 671 - 672 - return this_obj; 673 - } 674 - 675 - static ant_value_t rl_interface_remove_all_listeners(ant_t *js, ant_value_t *args, int nargs) { 676 - ant_value_t this_obj = js_getthis(js); 677 - rl_interface_t *iface = get_interface(js, this_obj); 678 - 679 - RLEventType *evt = NULL; 680 - RLEventType *tmp = NULL; 681 - char *event = NULL; 682 - 683 - if (!iface) return js_mkerr(js, "Invalid Interface"); 684 - if (nargs < 1 || vtype(args[0]) == T_UNDEF) { 685 - HASH_ITER(hh, iface->events, evt, tmp) { 686 - HASH_DEL(iface->events, evt); 687 - free(evt->event_type); 688 - free(evt); 689 - } 690 - return this_obj; 691 - } 692 - 693 - event = js_getstr(js, args[0], NULL); 694 - if (!event) return this_obj; 695 - 696 - HASH_FIND_STR(iface->events, event, evt); 697 - if (!evt) return this_obj; 698 - 699 - HASH_DEL(iface->events, evt); 700 - free(evt->event_type); 701 - free(evt); 702 - 703 - return this_obj; 704 - } 705 - 706 - static ant_value_t rl_interface_emit(ant_t *js, ant_value_t *args, int nargs) { 707 - ant_value_t this_obj = js_getthis(js); 708 - rl_interface_t *iface = get_interface(js, this_obj); 709 - 710 - if (!iface) return js_mkerr(js, "Invalid Interface"); 711 - if (nargs < 1) return js_false; 712 - 713 - char *event = js_getstr(js, args[0], NULL); 714 - if (!event) return js_false; 715 - 716 - emit_event(js, iface, event, nargs > 1 ? &args[1] : NULL, nargs - 1); 717 - return js_true; 718 - } 719 - 720 607 static ant_value_t rl_interface_close(ant_t *js, ant_value_t *args, int nargs) { 721 - (void)args; (void)nargs; 722 608 ant_value_t this_obj = js_getthis(js); 723 609 rl_interface_t *iface = get_interface(js, this_obj); 724 610 725 611 if (!iface || iface->closed) return js_mkundef(); 726 - 727 - stop_reading(iface); 728 - 729 - if (iface->tty_initialized) { 730 - uv_close((uv_handle_t *)&iface->tty_in, NULL); 731 - #ifndef _WIN32 732 - if (!iface->sigint_watcher_active && uv_is_active((uv_handle_t *)&iface->sigint_watcher)) { 733 - uv_close((uv_handle_t *)&iface->sigint_watcher, NULL); 734 - } 735 - exit_raw_mode(iface); 736 - #endif 737 - iface->tty_initialized = false; 738 - } 739 - 740 - iface->closed = true; 741 - emit_event(js, iface, "close", NULL, 0); 612 + rl_close_interface(js, iface); 742 613 743 614 return js_mkundef(); 744 615 } 745 616 746 617 static ant_value_t rl_interface_pause(ant_t *js, ant_value_t *args, int nargs) { 747 - (void)args; (void)nargs; 748 618 ant_value_t this_obj = js_getthis(js); 749 619 rl_interface_t *iface = get_interface(js, this_obj); 750 - 751 620 if (!iface) return js_mkerr(js, "Invalid Interface"); 752 621 753 622 if (!iface->paused) { ··· 760 629 } 761 630 762 631 static ant_value_t rl_interface_resume(ant_t *js, ant_value_t *args, int nargs) { 763 - (void)args; (void)nargs; 764 632 ant_value_t this_obj = js_getthis(js); 765 633 rl_interface_t *iface = get_interface(js, this_obj); 766 - 767 634 if (!iface) return js_mkerr(js, "Invalid Interface"); 768 635 769 636 if (iface->paused) { ··· 822 689 } 823 690 824 691 static ant_value_t rl_interface_get_prompt(ant_t *js, ant_value_t *args, int nargs) { 825 - (void)args; (void)nargs; 826 692 ant_value_t this_obj = js_getthis(js); 827 693 rl_interface_t *iface = get_interface(js, this_obj); 828 - 829 694 if (!iface) return js_mkerr(js, "Invalid Interface"); 830 695 831 696 return js_mkstr(js, iface->prompt, strlen(iface->prompt)); ··· 928 793 } 929 794 930 795 static ant_value_t rl_interface_line_getter(ant_t *js, ant_value_t *args, int nargs) { 931 - (void)args; (void)nargs; 932 796 ant_value_t this_obj = js_getthis(js); 933 797 rl_interface_t *iface = get_interface(js, this_obj); 934 798 ··· 937 801 } 938 802 939 803 static ant_value_t rl_interface_cursor_getter(ant_t *js, ant_value_t *args, int nargs) { 940 - (void)args; (void)nargs; 941 804 ant_value_t this_obj = js_getthis(js); 942 805 rl_interface_t *iface = get_interface(js, this_obj); 943 806 ··· 962 825 } 963 826 964 827 write_output(iface, query); 965 - 966 - RLEventType *evt = find_or_create_event_type(iface, "line"); 967 - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { 968 - return js_mkerr(js, "maximum listeners reached"); 969 - } 970 - 971 - evt->listeners[evt->listener_count].listener = args[1]; 972 - evt->listeners[evt->listener_count].once = true; 973 - evt->listener_count++; 828 + if (!rl_add_listener(js, iface, "line", args[1], true)) return js_mkerr(js, "listener must be a function"); 974 829 975 830 return js_mkundef(); 976 831 } ··· 997 852 } 998 853 999 854 static ant_value_t rl_interface_get_cursor_pos(ant_t *js, ant_value_t *args, int nargs) { 1000 - (void)args; (void)nargs; 1001 855 ant_value_t this_obj = js_getthis(js); 1002 856 rl_interface_t *iface = get_interface(js, this_obj); 1003 857 ··· 1034 888 } 1035 889 1036 890 static ant_value_t rl_interface_closed_getter(ant_t *js, ant_value_t *args, int nargs) { 1037 - (void)args; (void)nargs; 1038 891 ant_value_t this_obj = js_getthis(js); 1039 892 rl_interface_t *iface = get_interface(js, this_obj); 1040 893 ··· 1042 895 return js_bool(iface->closed); 1043 896 } 1044 897 1045 - static ant_value_t rl_interface_async_iterator(ant_t *js, ant_value_t *args, int nargs) { 1046 - (void)args; (void)nargs; 1047 - ant_value_t this_obj = js_getthis(js); 1048 - rl_interface_t *iface = get_interface(js, this_obj); 1049 - 1050 - if (!iface) return js_mkerr(js, "Invalid Interface"); 1051 - 1052 - ant_value_t iterator = js_mkobj(js); 1053 - js_set(js, iterator, "_rl_id", js_mknum((double)iface->id)); 1054 - js_set(js, iterator, "_lines", js_mkarr(js)); 1055 - js_set(js, iterator, "_done", js_false); 1056 - 1057 - return iterator; 1058 - } 1059 - 1060 898 static void free_interface(rl_interface_t *iface) { 1061 899 if (!iface) return; 1062 900 ··· 1065 903 free(iface->prompt); 1066 904 free(iface->line_buffer); 1067 905 rl_history_free(&iface->history); 906 + free(iface); 907 + } 908 + 909 + static ant_value_t rl_clear_line(ant_t *js, ant_value_t *args, int nargs) { 910 + int dir = 0; 911 + if (!tty_ctrl_parse_clear_line_dir(args, nargs, 1, &dir)) return js_false; 912 + 913 + size_t seq_len = 0; 914 + const char *seq = tty_ctrl_clear_line_seq(dir, &seq_len); 915 + return tty_ctrl_bool_result(js, tty_ctrl_write_stream(stdout, seq, seq_len, true)); 916 + } 917 + 918 + static ant_value_t rl_clear_screen_down(ant_t *js, ant_value_t *args, int nargs) { 919 + size_t seq_len = 0; 920 + const char *seq = tty_ctrl_clear_screen_down_seq(&seq_len); 921 + return tty_ctrl_bool_result(js, tty_ctrl_write_stream(stdout, seq, seq_len, true)); 922 + } 923 + 924 + static ant_value_t rl_cursor_to(ant_t *js, ant_value_t *args, int nargs) { 925 + tty_ctrl_cursor_to_args_t parsed; 926 + if (!tty_ctrl_parse_cursor_to_args(args, nargs, 1, 2, &parsed)) return js_false; 927 + 928 + char seq[64]; 929 + size_t seq_len = 0; 930 + bool ok = tty_ctrl_build_cursor_to( 931 + seq, sizeof(seq), 932 + parsed.x, parsed.has_y, parsed.y, 933 + &seq_len 934 + ); 935 + if (!ok) return js_false; 936 + 937 + return tty_ctrl_bool_result(js, tty_ctrl_write_stream(stdout, seq, seq_len, true)); 938 + } 939 + 940 + static ant_value_t rl_move_cursor(ant_t *js, ant_value_t *args, int nargs) { 941 + int dx = 0; 942 + int dy = 0; 943 + if (!tty_ctrl_parse_move_cursor_args(args, nargs, 1, 2, &dx, &dy)) return js_false; 944 + 945 + bool ok = true; 946 + if (dx != 0) { 947 + char seq_x[32]; 948 + size_t len_x = 0; 949 + ok = tty_ctrl_build_move_cursor_axis(seq_x, sizeof(seq_x), dx, true, &len_x); 950 + if (ok) ok = tty_ctrl_write_stream(stdout, seq_x, len_x, false); 951 + } 952 + 953 + if (ok && dy != 0) { 954 + char seq_y[32]; 955 + size_t len_y = 0; 956 + ok = tty_ctrl_build_move_cursor_axis(seq_y, sizeof(seq_y), dy, false, &len_y); 957 + if (ok) ok = tty_ctrl_write_stream(stdout, seq_y, len_y, false); 958 + } 959 + 960 + if (!ok) return js_false; 961 + return tty_ctrl_bool_result(js, fflush(stdout) == 0); 962 + } 963 + 964 + static ant_value_t rl_emit_keypress_events(ant_t *js, ant_value_t *args, int nargs) { 965 + if (nargs > 0) { 966 + ant_value_t stdin_obj = js_get(js, js_get(js, js_glob(js), "process"), "stdin"); 967 + if (stdin_obj != args[0]) { 968 + return js_mkerr(js, "emitKeypressEvents only supports process.stdin"); 969 + } 970 + } 971 + process_enable_keypress_events(); 972 + return js_mkundef(); 973 + } 974 + 975 + bool has_active_readline_interfaces(void) { 976 + rl_interface_t *iface, *tmp; 977 + HASH_ITER(hh, interfaces, iface, tmp) { 978 + if (!iface->closed && iface->reading) return true; 979 + } 980 + return false; 981 + } 982 + 983 + static void rl_async_iter_compact_queue(ant_t *js, ant_value_t state, const char *queue_key, const char *head_key) { 984 + ant_value_t queue = rl_async_iter_queue(js, state, queue_key); 985 + ant_offset_t head = rl_async_iter_queue_head(js, state, head_key); 986 + ant_offset_t len = vtype(queue) == T_ARR ? js_arr_len(js, queue) : 0; 987 + ant_value_t compact = 0; 988 + 989 + if (vtype(queue) != T_ARR || head == 0) return; 990 + 991 + if (head >= len) { 992 + js_set(js, state, queue_key, js_mkarr(js)); 993 + js_set(js, state, head_key, js_mknum(0)); 994 + return; 995 + } 996 + 997 + if (head <= 32 && head * 2 < len) return; 998 + 999 + compact = js_mkarr(js); 1000 + for (ant_offset_t i = head; i < len; i++) js_arr_push(js, compact, js_arr_get(js, queue, i)); 1001 + js_set(js, state, queue_key, compact); 1002 + js_set(js, state, head_key, js_mknum(0)); 1003 + } 1004 + 1005 + static void rl_async_iter_queue_push(ant_t *js, ant_value_t state, const char *queue_key, ant_value_t value) { 1006 + ant_value_t queue = rl_async_iter_queue(js, state, queue_key); 1007 + if (vtype(queue) == T_ARR) js_arr_push(js, queue, value); 1008 + } 1009 + 1010 + static ant_value_t rl_async_iter_queue_shift(ant_t *js, ant_value_t state, const char *queue_key, const char *head_key) { 1011 + ant_value_t queue = rl_async_iter_queue(js, state, queue_key); 1012 + ant_offset_t head = rl_async_iter_queue_head(js, state, head_key); 1013 + ant_offset_t len = vtype(queue) == T_ARR ? js_arr_len(js, queue) : 0; 1014 + ant_value_t value = js_mkundef(); 1015 + 1016 + if (vtype(queue) != T_ARR || head >= len) return js_mkundef(); 1017 + value = js_arr_get(js, queue, head); 1018 + rl_async_iter_set_queue_head(js, state, head_key, head + 1); 1019 + rl_async_iter_compact_queue(js, state, queue_key, head_key); 1068 1020 1069 - RLEventType *evt, *tmp; 1070 - HASH_ITER(hh, iface->events, evt, tmp) { 1071 - HASH_DEL(iface->events, evt); 1072 - free(evt->event_type); 1073 - free(evt); 1021 + return value; 1022 + } 1023 + 1024 + static void rl_async_iter_cleanup(ant_t *js, ant_value_t state) { 1025 + ant_value_t iface_obj = is_object_type(state) ? js_get(js, state, "iface") : js_mkundef(); 1026 + rl_interface_t *iface = get_interface(js, iface_obj); 1027 + if (!iface) return; 1028 + 1029 + rl_remove_listener(js, iface, "line", js_get(js, state, "onLine")); 1030 + rl_remove_listener(js, iface, "close", js_get(js, state, "onClose")); 1031 + } 1032 + 1033 + static void rl_async_iter_finish(ant_t *js, ant_value_t state) { 1034 + if (!is_object_type(state)) return; 1035 + 1036 + js_set(js, state, "done", js_true); 1037 + rl_async_iter_cleanup(js, state); 1038 + 1039 + for (;;) { 1040 + ant_value_t pending = rl_async_iter_queue_shift(js, state, "pending", "pendingHead"); 1041 + if (vtype(pending) != T_PROMISE) break; 1042 + js_resolve_promise(js, pending, js_iter_result(js, false, js_mkundef())); 1043 + } 1044 + } 1045 + 1046 + static ant_value_t rl_async_iter_on_line(ant_t *js, ant_value_t *args, int nargs) { 1047 + ant_value_t state = rl_async_iter_state(js, js_getcurrentfunc(js)); 1048 + ant_value_t line = nargs > 0 ? args[0] : js_mkundef(); 1049 + ant_value_t pending = 0; 1050 + 1051 + if (!is_object_type(state)) return js_mkundef(); 1052 + if (js_truthy(js, js_get(js, state, "done"))) return js_mkundef(); 1053 + 1054 + pending = rl_async_iter_queue_shift(js, state, "pending", "pendingHead"); 1055 + if (vtype(pending) == T_PROMISE) { 1056 + js_resolve_promise(js, pending, js_iter_result(js, true, line)); 1057 + } else rl_async_iter_queue_push(js, state, "buffer", line); 1058 + 1059 + return js_mkundef(); 1060 + } 1061 + 1062 + static ant_value_t rl_async_iter_on_close(ant_t *js, ant_value_t *args, int nargs) { 1063 + ant_value_t state = rl_async_iter_state(js, js_getcurrentfunc(js)); 1064 + rl_async_iter_finish(js, state); 1065 + return js_mkundef(); 1066 + } 1067 + 1068 + static ant_value_t rl_async_iter_next(ant_t *js, ant_value_t *args, int nargs) { 1069 + ant_value_t state = rl_async_iter_state(js, js_getthis(js)); 1070 + ant_value_t promise = js_mkpromise(js); 1071 + ant_value_t value = 0; 1072 + 1073 + if (!is_object_type(state)) { 1074 + js_resolve_promise(js, promise, js_iter_result(js, false, js_mkundef())); 1075 + return promise; 1076 + } 1077 + 1078 + value = rl_async_iter_queue_shift(js, state, "buffer", "bufferHead"); 1079 + if (vtype(value) != T_UNDEF) { 1080 + js_resolve_promise(js, promise, js_iter_result(js, true, value)); 1081 + return promise; 1082 + } 1083 + 1084 + if (js_truthy(js, js_get(js, state, "done"))) { 1085 + js_resolve_promise(js, promise, js_iter_result(js, false, js_mkundef())); 1086 + return promise; 1087 + } 1088 + 1089 + rl_async_iter_queue_push(js, state, "pending", promise); 1090 + return promise; 1091 + } 1092 + 1093 + static ant_value_t rl_async_iter_return(ant_t *js, ant_value_t *args, int nargs) { 1094 + ant_value_t state = rl_async_iter_state(js, js_getthis(js)); 1095 + ant_value_t promise = js_mkpromise(js); 1096 + 1097 + if (!is_object_type(state)) { 1098 + js_resolve_promise(js, promise, js_iter_result(js, false, js_mkundef())); 1099 + return promise; 1074 1100 } 1101 + 1102 + if (!js_truthy(js, js_get(js, state, "done"))) { 1103 + ant_value_t iface_obj = js_get(js, state, "iface"); 1104 + rl_interface_t *iface = get_interface(js, iface_obj); 1105 + 1106 + if (iface && !iface->closed) { 1107 + ant_value_t old_this = js_getthis(js); 1108 + js_setthis(js, iface_obj); 1109 + rl_interface_close(js, NULL, 0); 1110 + js_setthis(js, old_this); 1111 + } else rl_async_iter_finish(js, state); 1112 + } 1113 + 1114 + js_resolve_promise(js, promise, js_iter_result(js, false, js_mkundef())); 1115 + return promise; 1116 + } 1117 + 1118 + static ant_value_t rl_get_async_iter_proto(ant_t *js) { 1119 + if (is_object_type(g_rl_async_iter_proto)) return g_rl_async_iter_proto; 1120 + 1121 + g_rl_async_iter_proto = js_mkobj(js); 1122 + js_set(js, g_rl_async_iter_proto, "next", js_mkfun(rl_async_iter_next)); 1123 + js_set(js, g_rl_async_iter_proto, "return", js_mkfun(rl_async_iter_return)); 1124 + js_set_sym(js, g_rl_async_iter_proto, get_asyncIterator_sym(), js_mkfun(sym_this_cb)); 1125 + js_set_sym(js, g_rl_async_iter_proto, get_toStringTag_sym(), js_mkstr(js, "AsyncIterator", 13)); 1126 + 1127 + return g_rl_async_iter_proto; 1128 + } 1129 + 1130 + static ant_value_t rl_interface_async_iterator(ant_t *js, ant_value_t *args, int nargs) { 1131 + ant_value_t this_obj = js_getthis(js); 1132 + rl_interface_t *iface = get_interface(js, this_obj); 1075 1133 1076 - free(iface); 1134 + ant_value_t iterator = 0; 1135 + ant_value_t state = 0; 1136 + ant_value_t on_line = 0; 1137 + ant_value_t on_close = 0; 1138 + 1139 + if (!iface) return js_mkerr(js, "Invalid Interface"); 1140 + 1141 + iterator = js_mkobj(js); 1142 + state = js_mkobj(js); 1143 + 1144 + js_set_proto_init(iterator, rl_get_async_iter_proto(js)); 1145 + js_set_slot_wb(js, iterator, SLOT_DATA, state); 1146 + 1147 + js_set(js, state, "iface", this_obj); 1148 + js_set(js, state, "buffer", js_mkarr(js)); 1149 + js_set(js, state, "bufferHead", js_mknum(0)); 1150 + js_set(js, state, "pending", js_mkarr(js)); 1151 + js_set(js, state, "pendingHead", js_mknum(0)); 1152 + js_set(js, state, "done", js_bool(iface->closed)); 1153 + 1154 + if (!iface->closed) { 1155 + on_line = js_heavy_mkfun(js, rl_async_iter_on_line, state); 1156 + on_close = js_heavy_mkfun(js, rl_async_iter_on_close, state); 1157 + js_set(js, state, "onLine", on_line); 1158 + js_set(js, state, "onClose", on_close); 1159 + 1160 + if (!rl_add_listener(js, iface, "line", on_line, false) || !rl_add_listener(js, iface, "close", on_close, false)) { 1161 + rl_remove_listener(js, iface, "line", on_line); 1162 + rl_remove_listener(js, iface, "close", on_close); 1163 + return js_mkerr(js, "listener must be a function"); 1164 + }} 1165 + 1166 + return iterator; 1167 + } 1168 + 1169 + static ant_value_t rl_get_interface_proto(ant_t *js) { 1170 + if (is_object_type(g_rl_interface_proto)) return g_rl_interface_proto; 1171 + 1172 + g_rl_interface_proto = js_mkobj(js); 1173 + js_set_proto_init(g_rl_interface_proto, eventemitter_prototype(js)); 1174 + 1175 + js_set(js, g_rl_interface_proto, "close", js_mkfun(rl_interface_close)); 1176 + js_set(js, g_rl_interface_proto, "pause", js_mkfun(rl_interface_pause)); 1177 + js_set(js, g_rl_interface_proto, "resume", js_mkfun(rl_interface_resume)); 1178 + js_set(js, g_rl_interface_proto, "prompt", js_mkfun(rl_interface_prompt)); 1179 + js_set(js, g_rl_interface_proto, "setPrompt", js_mkfun(rl_interface_set_prompt)); 1180 + js_set(js, g_rl_interface_proto, "getPrompt", js_mkfun(rl_interface_get_prompt)); 1181 + js_set(js, g_rl_interface_proto, "write", js_mkfun(rl_interface_write)); 1182 + js_set(js, g_rl_interface_proto, "question", js_mkfun(rl_interface_question_callback)); 1183 + js_set(js, g_rl_interface_proto, "getCursorPos", js_mkfun(rl_interface_get_cursor_pos)); 1184 + 1185 + js_set_getter_desc(js, g_rl_interface_proto, "line", 4, js_mkfun(rl_interface_line_getter), JS_DESC_E | JS_DESC_C); 1186 + js_set_getter_desc(js, g_rl_interface_proto, "cursor", 6, js_mkfun(rl_interface_cursor_getter), JS_DESC_E | JS_DESC_C); 1187 + js_set_getter_desc(js, g_rl_interface_proto, "closed", 6, js_mkfun(rl_interface_closed_getter), JS_DESC_E | JS_DESC_C); 1188 + js_set_sym(js, g_rl_interface_proto, get_asyncIterator_sym(), js_mkfun(rl_interface_async_iterator)); 1189 + js_set_sym(js, g_rl_interface_proto, get_toStringTag_sym(), js_mkstr(js, "Interface", 9)); 1190 + 1191 + return g_rl_interface_proto; 1077 1192 } 1078 1193 1079 1194 static ant_value_t rl_create_interface(ant_t *js, ant_value_t *args, int nargs) { ··· 1095 1210 iface->reading = false; 1096 1211 iface->pending_question_resolve = js_mkundef(); 1097 1212 iface->pending_question_reject = js_mkundef(); 1098 - iface->events = NULL; 1099 1213 iface->tty_initialized = false; 1100 1214 iface->escape_state = 0; 1101 1215 iface->escape_len = 0; ··· 1161 1275 HASH_ADD(hh, interfaces, id, sizeof(uint64_t), iface); 1162 1276 1163 1277 ant_value_t obj = js_mkobj(js); 1278 + js_set_proto_init(obj, rl_get_interface_proto(js)); 1279 + iface->js_obj = obj; 1164 1280 js_set(js, obj, "_rl_id", js_mknum((double)iface->id)); 1165 - 1166 - js_set(js, obj, "on", js_mkfun(rl_interface_on)); 1167 - js_set(js, obj, "once", js_mkfun(rl_interface_once)); 1168 - js_set(js, obj, "off", js_mkfun(rl_interface_off)); 1169 - js_set(js, obj, "addListener", js_mkfun(rl_interface_on)); 1170 - js_set(js, obj, "removeListener", js_mkfun(rl_interface_off)); 1171 - js_set(js, obj, "removeAllListeners", js_mkfun(rl_interface_remove_all_listeners)); 1172 - js_set(js, obj, "emit", js_mkfun(rl_interface_emit)); 1173 - 1174 - js_set(js, obj, "close", js_mkfun(rl_interface_close)); 1175 - js_set(js, obj, "pause", js_mkfun(rl_interface_pause)); 1176 - js_set(js, obj, "resume", js_mkfun(rl_interface_resume)); 1177 - js_set(js, obj, "prompt", js_mkfun(rl_interface_prompt)); 1178 - js_set(js, obj, "setPrompt", js_mkfun(rl_interface_set_prompt)); 1179 - js_set(js, obj, "getPrompt", js_mkfun(rl_interface_get_prompt)); 1180 - js_set(js, obj, "write", js_mkfun(rl_interface_write)); 1181 - js_set(js, obj, "question", js_mkfun(rl_interface_question_callback)); 1182 - js_set(js, obj, "getCursorPos", js_mkfun(rl_interface_get_cursor_pos)); 1183 - 1184 - js_set_getter_desc(js, obj, "line", 4, js_mkfun(rl_interface_line_getter), JS_DESC_E | JS_DESC_C); 1185 - js_set_getter_desc(js, obj, "cursor", 6, js_mkfun(rl_interface_cursor_getter), JS_DESC_E | JS_DESC_C); 1186 - js_set_getter_desc(js, obj, "closed", 6, js_mkfun(rl_interface_closed_getter), JS_DESC_E | JS_DESC_C); 1187 - 1188 1281 js_set(js, obj, "terminal", js_bool(iface->terminal)); 1189 - js_set_sym(js, obj, get_asyncIterator_sym(), js_mkfun(rl_interface_async_iterator)); 1190 - js_set_sym(js, obj, get_toStringTag_sym(), js_mkstr(js, "Interface", 9)); 1191 1282 1192 1283 start_reading(iface); 1193 1284 ··· 1202 1293 return iface_obj; 1203 1294 } 1204 1295 1205 - static ant_value_t rl_clear_line(ant_t *js, ant_value_t *args, int nargs) { 1206 - int dir = 0; 1207 - if (!tty_ctrl_parse_clear_line_dir(args, nargs, 1, &dir)) return js_false; 1208 - 1209 - size_t seq_len = 0; 1210 - const char *seq = tty_ctrl_clear_line_seq(dir, &seq_len); 1211 - return tty_ctrl_bool_result(js, tty_ctrl_write_stream(stdout, seq, seq_len, true)); 1212 - } 1213 - 1214 - static ant_value_t rl_clear_screen_down(ant_t *js, ant_value_t *args, int nargs) { 1215 - size_t seq_len = 0; 1216 - const char *seq = tty_ctrl_clear_screen_down_seq(&seq_len); 1217 - return tty_ctrl_bool_result(js, tty_ctrl_write_stream(stdout, seq, seq_len, true)); 1218 - } 1219 - 1220 - static ant_value_t rl_cursor_to(ant_t *js, ant_value_t *args, int nargs) { 1221 - tty_ctrl_cursor_to_args_t parsed; 1222 - if (!tty_ctrl_parse_cursor_to_args(args, nargs, 1, 2, &parsed)) return js_false; 1223 - 1224 - char seq[64]; 1225 - size_t seq_len = 0; 1226 - bool ok = tty_ctrl_build_cursor_to( 1227 - seq, sizeof(seq), 1228 - parsed.x, parsed.has_y, parsed.y, 1229 - &seq_len 1230 - ); 1231 - if (!ok) return js_false; 1232 - 1233 - return tty_ctrl_bool_result(js, tty_ctrl_write_stream(stdout, seq, seq_len, true)); 1234 - } 1235 - 1236 - static ant_value_t rl_move_cursor(ant_t *js, ant_value_t *args, int nargs) { 1237 - int dx = 0; 1238 - int dy = 0; 1239 - if (!tty_ctrl_parse_move_cursor_args(args, nargs, 1, 2, &dx, &dy)) return js_false; 1240 - 1241 - bool ok = true; 1242 - if (dx != 0) { 1243 - char seq_x[32]; 1244 - size_t len_x = 0; 1245 - ok = tty_ctrl_build_move_cursor_axis(seq_x, sizeof(seq_x), dx, true, &len_x); 1246 - if (ok) ok = tty_ctrl_write_stream(stdout, seq_x, len_x, false); 1247 - } 1248 - 1249 - if (ok && dy != 0) { 1250 - char seq_y[32]; 1251 - size_t len_y = 0; 1252 - ok = tty_ctrl_build_move_cursor_axis(seq_y, sizeof(seq_y), dy, false, &len_y); 1253 - if (ok) ok = tty_ctrl_write_stream(stdout, seq_y, len_y, false); 1254 - } 1255 - 1256 - if (!ok) return js_false; 1257 - return tty_ctrl_bool_result(js, fflush(stdout) == 0); 1258 - } 1259 - 1260 - static ant_value_t rl_emit_keypress_events(ant_t *js, ant_value_t *args, int nargs) { 1261 - if (nargs > 0) { 1262 - ant_value_t stdin_obj = js_get(js, js_get(js, js_glob(js), "process"), "stdin"); 1263 - if (stdin_obj != args[0]) { 1264 - return js_mkerr(js, "emitKeypressEvents only supports process.stdin"); 1265 - } 1266 - } 1267 - process_enable_keypress_events(); 1268 - return js_mkundef(); 1269 - } 1270 - 1271 - bool has_active_readline_interfaces(void) { 1272 - rl_interface_t *iface, *tmp; 1273 - HASH_ITER(hh, interfaces, iface, tmp) { 1274 - if (!iface->closed && iface->reading) return true; 1275 - } 1276 - return false; 1277 - } 1278 - 1279 - 1280 1296 ant_value_t readline_library(ant_t *js) { 1281 1297 ant_value_t lib = js_mkobj(js); 1282 1298 ··· 1306 1322 } 1307 1323 1308 1324 void gc_mark_readline(ant_t *js, gc_mark_fn mark) { 1325 + if (g_rl_async_iter_proto) mark(js, g_rl_async_iter_proto); 1326 + if (g_rl_interface_proto) mark(js, g_rl_interface_proto); 1327 + 1309 1328 rl_interface_t *iface, *tmp; 1310 1329 HASH_ITER(hh, interfaces, iface, tmp) { 1311 1330 mark(js, iface->input_stream); ··· 1314 1333 mark(js, iface->js_obj); 1315 1334 mark(js, iface->pending_question_resolve); 1316 1335 mark(js, iface->pending_question_reject); 1317 - 1318 - RLEventType *evt, *evt_tmp; 1319 - HASH_ITER(hh, iface->events, evt, evt_tmp) for (int i = 0; i < evt->listener_count; i++) 1320 - mark(js, evt->listeners[i].listener); 1321 1336 } 1322 1337 }
-2
src/modules/tty.c
··· 37 37 #include "modules/symbol.h" 38 38 #include "modules/tty.h" 39 39 40 - 41 - 42 40 static bool parse_fd(ant_value_t value, int *fd_out) { 43 41 int fd = 0; 44 42 if (!tty_ctrl_parse_int_value(value, &fd)) return false;