From e6b7613e89cc274198eec014180da6034442b3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Linse?= Date: Fri, 6 Sep 2019 20:10:56 +0200 Subject: [PATCH 1/5] refactor: allow us to process a child queue only while waiting on input --- src/nvim/eval.c | 4 +-- src/nvim/ex_cmds.c | 2 -- src/nvim/getchar.c | 12 ++++---- src/nvim/getchar.h | 5 ++-- src/nvim/main.c | 2 ++ src/nvim/message.c | 10 ++++--- src/nvim/misc1.c | 20 +++++-------- src/nvim/os/input.c | 72 +++++++++++++++++++++------------------------ src/nvim/os/input.h | 1 + src/nvim/state.c | 4 +-- src/nvim/ui.c | 12 ++++---- src/nvim/ui.h | 3 ++ 12 files changed, 69 insertions(+), 78 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8e848483a7..0aa0e8c165 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9941,9 +9941,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_UNKNOWN) { // getchar(): blocking wait. if (!(char_avail() || using_script() || input_available())) { - input_enable_events(); - (void)os_inchar(NULL, 0, -1, 0); - input_disable_events(); + (void)os_inchar(NULL, 0, -1, 0, main_loop.events); if (!multiqueue_empty(main_loop.events)) { multiqueue_process_events(main_loop.events); continue; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index a0fbde008b..c3e1e69a63 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -5673,7 +5673,6 @@ void ex_substitute(exarg_T *eap) } block_autocmds(); // Disable events during command preview. - input_disable_events(); char_u *save_eap = eap->arg; garray_T save_view; @@ -5716,7 +5715,6 @@ void ex_substitute(exarg_T *eap) restore_search_patterns(); win_size_restore(&save_view); ga_clear(&save_view); - input_enable_events(); unblock_autocmds(); } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 318b36860e..2469bb5baa 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1532,8 +1532,9 @@ int safe_vgetc(void) int c; c = vgetc(); - if (c == NUL) - c = get_keystroke(); + if (c == NUL) { + c = get_keystroke(NULL); + } return c; } @@ -2447,9 +2448,10 @@ int inchar( char_u dum[DUM_LEN + 1]; for (;; ) { - len = os_inchar(dum, DUM_LEN, 0L, 0); - if (len == 0 || (len == 1 && dum[0] == 3)) + len = os_inchar(dum, DUM_LEN, 0L, 0, NULL); + if (len == 0 || (len == 1 && dum[0] == 3)) { break; + } } return retesc; } @@ -2460,7 +2462,7 @@ int inchar( // Fill up to a third of the buffer, because each character may be // tripled below. - len = os_inchar(buf, maxlen / 3, (int)wait_time, tb_change_cnt); + len = os_inchar(buf, maxlen / 3, (int)wait_time, tb_change_cnt, NULL); } // If the typebuf was changed further down, it is like nothing was added by diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h index a40ea7730a..01f60ccf49 100644 --- a/src/nvim/getchar.h +++ b/src/nvim/getchar.h @@ -57,9 +57,8 @@ struct map_arguments { }; typedef struct map_arguments MapArguments; -#define KEYLEN_PART_KEY -1 /* keylen value for incomplete key-code */ -#define KEYLEN_PART_MAP -2 /* keylen value for incomplete mapping */ -#define KEYLEN_REMOVED 9999 /* keylen value for removed sequence */ +#define KEYLEN_PART_KEY -1 // keylen value for incomplete key-code +#define KEYLEN_PART_MAP -2 // keylen value for incomplete mapping /// Maximum number of streams to read script from enum { NSCRIPT = 15 }; diff --git a/src/nvim/main.c b/src/nvim/main.c index 50e495c1e6..4335dab1f9 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -144,6 +144,8 @@ void event_init(void) { log_init(); loop_init(&main_loop, NULL); + resize_events = multiqueue_new_child(main_loop.events); + // early msgpack-rpc initialization msgpack_rpc_init_method_table(); msgpack_rpc_helpers_init(); diff --git a/src/nvim/message.c b/src/nvim/message.c index c499aa5f0c..5b5f14b99d 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -2587,8 +2587,10 @@ static int do_more_prompt(int typed_char) if (used_typed_char != NUL) { c = used_typed_char; /* was typed at hit-enter prompt */ used_typed_char = NUL; - } else - c = get_keystroke(); + } else { + c = get_keystroke(resize_events); + multiqueue_process_events(resize_events); + } toscroll = 0; @@ -3307,8 +3309,8 @@ do_dialog ( hotkeys = msg_show_console_dialog(message, buttons, dfltbutton); for (;; ) { - /* Get a typed character directly from the user. */ - c = get_keystroke(); + // Get a typed character directly from the user. + c = get_keystroke(NULL); switch (c) { case CAR: /* User accepts default option */ case NL: diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index bb95cd5737..ab3520dd73 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -560,7 +560,7 @@ int ask_yesno(const char *const str, const bool direct) // Same highlighting as for wait_return. smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str); if (direct) { - r = get_keystroke(); + r = get_keystroke(NULL); } else { r = plain_vgetc(); } @@ -614,7 +614,7 @@ int is_mouse_key(int c) * Disadvantage: typeahead is ignored. * Translates the interrupt character for unix to ESC. */ -int get_keystroke(void) +int get_keystroke(MultiQueue *events) { char_u *buf = NULL; int buflen = 150; @@ -644,7 +644,7 @@ int get_keystroke(void) /* First time: blocking wait. Second time: wait up to 100ms for a * terminal code to complete. */ - n = os_inchar(buf + len, maxlen, len == 0 ? -1L : 100L, 0); + n = os_inchar(buf + len, maxlen, len == 0 ? -1L : 100L, 0, events); if (n > 0) { // Replace zero and CSI by a special key code. n = fix_input_buffer(buf + len, n); @@ -653,18 +653,12 @@ int get_keystroke(void) } else if (len > 0) ++waited; /* keep track of the waiting time */ - if (n == KEYLEN_REMOVED) { /* key code removed */ - if (must_redraw != 0 && !need_wait_return && (State & CMDLINE) == 0) { - /* Redrawing was postponed, do it now. */ - update_screen(0); - setcursor(); /* put cursor back where it belongs */ - } + if (n > 0) { // found a termcode: adjust length + len = n; + } + if (len == 0) { // nothing typed yet continue; } - if (n > 0) /* found a termcode: adjust length */ - len = n; - if (len == 0) /* nothing typed yet */ - continue; /* Handle modifier and/or special key code. */ n = buf[0]; diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 83ac3dfa62..c1580c5fc3 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -38,7 +38,6 @@ static Stream read_stream = { .closed = true }; // Input before UI starts. static RBuffer *input_buffer = NULL; static bool input_eof = false; static int global_fd = -1; -static int events_enabled = 0; static bool blocking = false; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -89,7 +88,7 @@ static void cursorhold_event(void **argv) did_cursorhold = true; } -static void create_cursorhold_event(void) +static void create_cursorhold_event(bool events_enabled) { // If events are enabled and the queue has any items, this function should not // have been called(inbuf_poll would return kInputAvail) @@ -99,8 +98,12 @@ static void create_cursorhold_event(void) multiqueue_put(main_loop.events, cursorhold_event, 0); } -// Low level input function -int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt) +/// Low level input function +/// +/// wait until either the input buffer is non-empty or , if `events` is not NULL +/// until `events` is non-empty. +int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, + MultiQueue *events) { if (maxlen && rbuffer_size(input_buffer)) { return (int)rbuffer_read(input_buffer, (char *)buf, (size_t)maxlen); @@ -108,21 +111,21 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt) InbufPollResult result; if (ms >= 0) { - if ((result = inbuf_poll(ms)) == kInputNone) { + if ((result = inbuf_poll(ms, events)) == kInputNone) { return 0; } } else { - if ((result = inbuf_poll((int)p_ut)) == kInputNone) { + if ((result = inbuf_poll((int)p_ut, events)) == kInputNone) { if (read_stream.closed && silent_mode) { // Drained eventloop & initial input; exit silent/batch-mode (-es/-Es). read_error_exit(); } if (trigger_cursorhold() && !typebuf_changed(tb_change_cnt)) { - create_cursorhold_event(); + create_cursorhold_event(events == main_loop.events); } else { before_blocking(); - result = inbuf_poll(-1); + result = inbuf_poll(-1, events); } } } @@ -139,7 +142,7 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt) } // If there are events, return the keys directly - if (maxlen && pending_events()) { + if (maxlen && pending_events(events)) { return push_event_key(buf, maxlen); } @@ -153,7 +156,7 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt) // Check if a character is available for reading bool os_char_avail(void) { - return inbuf_poll(0) == kInputAvail; + return inbuf_poll(0, NULL) == kInputAvail; } // Check for CTRL-C typed by reading all available characters. @@ -170,15 +173,6 @@ void os_breakcheck(void) updating_screen = save_us; } -void input_enable_events(void) -{ - events_enabled++; -} - -void input_disable_events(void) -{ - events_enabled--; -} /// Test whether a file descriptor refers to a terminal. /// @@ -383,27 +377,37 @@ bool input_blocking(void) return blocking; } -static bool input_poll(int ms) +// This is a replacement for the old `WaitForChar` function in os_unix.c +static InbufPollResult inbuf_poll(int ms, MultiQueue *events) { + if (input_ready(events)) { + return kInputAvail; + } + if (do_profiling == PROF_YES && ms) { prof_inchar_enter(); } - if ((ms == - 1 || ms > 0) && !events_enabled && !input_eof) { + if ((ms == - 1 || ms > 0) && events == NULL && !input_eof) { // The pending input provoked a blocking wait. Do special events now. #6247 blocking = true; multiqueue_process_events(ch_before_blocking_events); } - DLOG("blocking... events_enabled=%d events_pending=%d", events_enabled, - !multiqueue_empty(main_loop.events)); - LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, input_ready() || input_eof); + DLOG("blocking... events_enabled=%d events_pending=%d", events != NULL, + events && !multiqueue_empty(events)); + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, + input_ready(events) || input_eof); blocking = false; if (do_profiling == PROF_YES && ms) { prof_inchar_exit(); } - return input_ready(); + if (input_ready(events)) { + return kInputAvail; + } else { + return input_eof ? kInputEof : kInputNone; + } } void input_done(void) @@ -416,16 +420,6 @@ bool input_available(void) return rbuffer_size(input_buffer) != 0; } -// This is a replacement for the old `WaitForChar` function in os_unix.c -static InbufPollResult inbuf_poll(int ms) -{ - if (input_ready() || input_poll(ms)) { - return kInputAvail; - } - - return input_eof ? kInputEof : kInputNone; -} - static void input_read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, bool at_eof) { @@ -478,11 +472,11 @@ static int push_event_key(uint8_t *buf, int maxlen) } // Check if there's pending input -static bool input_ready(void) +static bool input_ready(MultiQueue *events) { return (typebuf_was_filled // API call filled typeahead || rbuffer_size(input_buffer) // Input buffer filled - || pending_events()); // Events must be processed + || pending_events(events)); // Events must be processed } // Exit because of an input read error. @@ -495,7 +489,7 @@ static void read_error_exit(void) preserve_exit(); } -static bool pending_events(void) +static bool pending_events(MultiQueue *events) { - return events_enabled && !multiqueue_empty(main_loop.events); + return events && !multiqueue_empty(events); } diff --git a/src/nvim/os/input.h b/src/nvim/os/input.h index 5902677a35..d571965408 100644 --- a/src/nvim/os/input.h +++ b/src/nvim/os/input.h @@ -5,6 +5,7 @@ #include #include "nvim/api/private/defs.h" +#include "nvim/event/multiqueue.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/input.h.generated.h" diff --git a/src/nvim/state.c b/src/nvim/state.c index dbf04eebec..81bc078a88 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -46,14 +46,12 @@ getkey: // Event was made available after the last multiqueue_process_events call key = K_EVENT; } else { - input_enable_events(); // Flush screen updates before blocking ui_flush(); // Call `os_inchar` directly to block for events or user input without // consuming anything from `input_buffer`(os/input.c) or calling the // mapping engine. - (void)os_inchar(NULL, 0, -1, 0); - input_disable_events(); + (void)os_inchar(NULL, 0, -1, 0, main_loop.events); // If an event was put into the queue, we send K_EVENT directly. key = !multiqueue_empty(main_loop.events) ? K_EVENT diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 94fae0a774..d8c9a3e780 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -174,7 +174,7 @@ void ui_refresh(void) } if (updating_screen) { - ui_schedule_refresh(); + deferred_refresh_event(NULL); return; } @@ -228,11 +228,11 @@ static void ui_refresh_event(void **argv) void ui_schedule_refresh(void) { - // TODO(bfredl): "fast" is not optimal. UI should be refreshed only at - // deferred processing plus a few more blocked-on-input situtions like - // wait_return(), but not any os_breakcheck(). Alternatively make this - // defered and make wait_return() process deferred events already. - loop_schedule_fast(&main_loop, event_create(ui_refresh_event, 0)); + loop_schedule_fast(&main_loop, event_create(deferred_refresh_event, 0)); +} +static void deferred_refresh_event(void **argv) +{ + multiqueue_put(resize_events, ui_refresh_event, 0); } void ui_default_colors_set(void) diff --git a/src/nvim/ui.h b/src/nvim/ui.h index e1dd18a289..1f549160db 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -66,4 +66,7 @@ struct ui_t { # include "ui.h.generated.h" # include "ui_events_call.h.generated.h" #endif + + +EXTERN MultiQueue *resize_events; #endif // NVIM_UI_H From f72c7b0b3aa71aae47994112ea1d2a3697c7a1dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Linse?= Date: Sat, 7 Sep 2019 12:17:29 +0200 Subject: [PATCH 2/5] messages: batch draw :map --- src/nvim/ex_docmd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d524c3d035..154bf9f5f6 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5099,10 +5099,10 @@ static void uc_list(char_u *name, size_t name_len) if (p_verbose > 0) { last_set_msg(cmd->uc_script_ctx); } - ui_flush(); - os_breakcheck(); - if (got_int) + line_breakcheck(); + if (got_int) { break; + } } if (gap == &ucmds || i < gap->ga_len) break; From bf9ff5148a57c64c26dd3786a4028418a6047e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Linse?= Date: Sat, 7 Sep 2019 12:23:13 +0200 Subject: [PATCH 3/5] messages: redraw after resize in pager note: does not "return" space at the bottom to the caller --- src/nvim/message.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/nvim/message.c b/src/nvim/message.c index 5b5f14b99d..30e906cd5f 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -2556,6 +2556,7 @@ static int do_more_prompt(int typed_char) int c; int retval = FALSE; int toscroll; + bool to_redraw = false; msgchunk_T *mp_last = NULL; msgchunk_T *mp; int i; @@ -2589,7 +2590,6 @@ static int do_more_prompt(int typed_char) used_typed_char = NUL; } else { c = get_keystroke(resize_events); - multiqueue_process_events(resize_events); } @@ -2663,31 +2663,44 @@ static int do_more_prompt(int typed_char) lines_left = Rows - 1; break; + case K_EVENT: + // only resize_events are processed here + // Attempt to redraw the screen. sb_text doesn't support reflow + // so this only really works for vertical resize. + multiqueue_process_events(resize_events); + to_redraw = true; + break; + default: /* no valid response */ msg_moremsg(TRUE); continue; } - if (toscroll != 0) { - if (toscroll < 0) { - /* go to start of last line */ - if (mp_last == NULL) + // code assumes we only do one at a time + assert((toscroll == 0) || !to_redraw); + + if (toscroll != 0 || to_redraw) { + if (toscroll < 0 || to_redraw) { + // go to start of last line + if (mp_last == NULL) { mp = msg_sb_start(last_msgchunk); - else if (mp_last->sb_prev != NULL) + } else if (mp_last->sb_prev != NULL) { mp = msg_sb_start(mp_last->sb_prev); - else + } else { mp = NULL; + } /* go to start of line at top of the screen */ for (i = 0; i < Rows - 2 && mp != NULL && mp->sb_prev != NULL; ++i) mp = msg_sb_start(mp->sb_prev); - if (mp != NULL && mp->sb_prev != NULL) { - /* Find line to be displayed at top. */ - for (i = 0; i > toscroll; --i) { - if (mp == NULL || mp->sb_prev == NULL) + if (mp != NULL && (mp->sb_prev != NULL || to_redraw)) { + // Find line to be displayed at top + for (i = 0; i > toscroll; i--) { + if (mp == NULL || mp->sb_prev == NULL) { break; + } mp = msg_sb_start(mp->sb_prev); if (mp_last == NULL) mp_last = msg_sb_start(last_msgchunk); @@ -2695,7 +2708,7 @@ static int do_more_prompt(int typed_char) mp_last = msg_sb_start(mp_last->sb_prev); } - if (toscroll == -1) { + if (toscroll == -1 && !to_redraw) { grid_ins_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns); grid_fill(&msg_grid_adj, 0, 1, 0, Columns, ' ', ' ', HL_ATTR(HLF_MSG)); @@ -2711,6 +2724,7 @@ static int do_more_prompt(int typed_char) mp = disp_sb_line(i, mp); ++msg_scrolled; } + to_redraw = false; } toscroll = 0; } From 19993bca4afaade5d0fbaf132ff66064370bacb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Linse?= Date: Sat, 7 Sep 2019 13:00:51 +0200 Subject: [PATCH 4/5] rpc: allow handling of nvim_ui_try_resize at the pager This makes external UI behave consistenly with TUI w.r.t resizes. Which will be needed anyway as TUI will use the external UI protocol soon. --- src/nvim/event/multiqueue.c | 37 ++++++++++++++++++++++++++++++++++ src/nvim/msgpack_rpc/channel.c | 13 ++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c index ef9f3f1870..a1b75f66a5 100644 --- a/src/nvim/event/multiqueue.c +++ b/src/nvim/event/multiqueue.c @@ -78,6 +78,13 @@ struct multiqueue { size_t size; }; +typedef struct { + Event event; + bool fired; + int refcount; +} SplitEvent; + + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/multiqueue.c.generated.h" #endif @@ -245,3 +252,33 @@ static MultiQueueItem *multiqueue_node_data(QUEUE *q) { return QUEUE_DATA(q, MultiQueueItem, node); } + +/// Allow an event to be processed by multiple child queues to the main queue +/// +/// The handler will be fired once by the _first_ queue that processes the +/// event. Later processing will do nothing (just memory cleanup). +/// +/// @param ev the event +/// @param num number of queues that the split event will be put on +/// @return an Event that is safe to put onto `num` queues +Event event_split(Event ev, int num) +{ + SplitEvent *data = xmalloc(sizeof(*data)); + data->event = ev; + data->fired = false; + data->refcount = num; + return event_create(split_event, 1, data); +} +static void split_event(void ** argv) +{ + SplitEvent *data = argv[0]; + if (!data->fired) { + data->fired = true; + if (data->event.handler) { + data->event.handler(data->event.argv); + } + } + if ((--data->refcount) == 0) { + xfree(data); + } +} diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 81c9f1e3f4..19f626c63b 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -31,6 +31,7 @@ #include "nvim/misc1.h" #include "nvim/lib/kvec.h" #include "nvim/os/input.h" +#include "nvim/ui.h" #if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL #define log_client_msg(...) @@ -355,11 +356,19 @@ static void handle_request(Channel *channel, msgpack_object *request) request_event((void **)&evdata); } } else { - multiqueue_put(channel->events, request_event, 1, evdata); - DLOG("RPC: scheduled %.*s", method->via.bin.size, method->via.bin.ptr); + bool is_resize = handler.fn == handle_nvim_ui_try_resize; + if (is_resize) { + Event ev = event_split(event_create(request_event, 1, evdata), 2); + multiqueue_put_event(channel->events, ev); + multiqueue_put_event(resize_events, ev); + } else { + multiqueue_put(channel->events, request_event, 1, evdata); + DLOG("RPC: scheduled %.*s", method->via.bin.size, method->via.bin.ptr); + } } } + /// Handles a message, depending on the type: /// - Request: invokes method and writes the response (or error). /// - Notification: invokes method (emits `nvim_error_event` on error). From c705e3fb0b70887aaebe4973ce02acc6be45eb97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Linse?= Date: Sun, 8 Sep 2019 14:57:45 +0200 Subject: [PATCH 5/5] update tests for new resize behavior (resize at pager, but not at :!cmd) --- test/functional/helpers.lua | 1 + test/functional/terminal/tui_spec.lua | 18 ++-- test/functional/ui/messages_spec.lua | 117 ++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 9 deletions(-) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index d50ecd2e6f..131ce7ed0b 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -689,6 +689,7 @@ end module.funcs = module.create_callindex(module.call) module.meths = module.create_callindex(module.nvim) +module.async_meths = module.create_callindex(module.nvim_async) module.uimeths = module.create_callindex(ui) module.bufmeths = module.create_callindex(module.buffer) module.winmeths = module.create_callindex(module.window) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 3225ce5162..56c1cc0d7b 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -122,10 +122,10 @@ describe('TUI', function() screen:try_resize(50,5) screen:expect{grid=[[ - {8:FAIL 1} | - {8:FAIL 2} | {8:FAIL 3} | - {10:-- More -- SPACE/d/j: screen/page/line down, b/u/}{12:k}| + {8:FAIL 4} | + {8:FAIL 5} | + {10:-- More --}{1: } | {3:-- TERMINAL --} | ]]} @@ -144,12 +144,12 @@ describe('TUI', function() ) | {8:Error detected while processing function ManyErr:} | {11:line 2:} | - {10:-- More --} | - {10: }| - {10: }| - {10: }| - {10: }| - {10:-- More -- SPACE/d/j: screen/page/line down, b/u/}{12:k}| + {8:FAIL 0} | + {8:FAIL 1} | + {8:FAIL 2} | + {8:FAIL 3} | + {8:FAIL 4} | + {10:-- More --}{1: } | {3:-- TERMINAL --} | ]]} diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 90874a92ef..875e4092a6 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -6,6 +6,7 @@ local eq = helpers.eq local command = helpers.command local set_method_error = helpers.set_method_error local meths = helpers.meths +local async_meths = helpers.async_meths local test_build_dir = helpers.test_build_dir local nvim_prog = helpers.nvim_prog local iswin = helpers.iswin @@ -1073,6 +1074,7 @@ describe('pager', function() [9] = {foreground = tonumber('0x00000c'), background = Screen.colors.Grey100}, [10] = {background = Screen.colors.Grey100, bold = true, foreground = tonumber('0xe5e5ff')}, [11] = {background = Screen.colors.Grey100, bold = true, foreground = tonumber ('0x2b8452')}, + [12] = {bold = true, reverse = true}, }) command("set more") @@ -1392,4 +1394,119 @@ aliquip ex ea commodo consequat.]]) {11:--}{8: }{11:More}{8: }{11:--}{8:^ }| ]]} end) + + it('with :!cmd does not crash on resize', function() + feed(':!sleep 1') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {12: }| + :!sleep 1 | + | + ]]} + + -- not processed while command is executing + async_meths.ui_try_resize(35, 5) + + -- TODO(bfredl): ideally it should be processed just + -- before the "press ENTER" prompt though + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {12: }| + :!sleep 1 | + | + {4:Press ENTER or type command to cont}| + {4:inue}^ | + ]]} + + feed('') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('can be resized', function() + feed(':lua error(_G.x)') + screen:expect{grid=[[ + {2:E5105: Error while calling lua chun}| + {2:k: [string ""}| + {2:]:1: Lorem ipsum dolor sit amet, co}| + {2:nsectetur} | + {2:adipisicing elit, sed do eiusmod te}| + {2:mpor} | + {2:incididunt ut labore et dolore magn}| + {4:-- More --}^ | + ]]} + + -- responds to resize, but text is not reflown + screen:try_resize(45, 5) + screen:expect{grid=[[ + {2:nsectetur} | + {2:adipisicing elit, sed do eiusmod te} | + {2:mpor} | + {2:incididunt ut labore et dolore magn} | + {4:-- More --}^ | + ]]} + + -- can create empty space, as the command hasn't output the text below yet. + -- text is not reflown; existing lines get cut + screen:try_resize(30, 12) + screen:expect{grid=[[ + {2:E5105: Error while calling lua}| + {2:k: [string "') + screen:expect{grid=[[ + {2:k: [string "