mirror of
https://github.com/neovim/neovim
synced 2025-07-18 10:11:50 +00:00
Merge pull request #10959 from bfredl/resizequeue
fix crash on :!tmux split, redraw after resize in pager
This commit is contained in:
@ -9941,9 +9941,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
if (argvars[0].v_type == VAR_UNKNOWN) {
|
if (argvars[0].v_type == VAR_UNKNOWN) {
|
||||||
// getchar(): blocking wait.
|
// getchar(): blocking wait.
|
||||||
if (!(char_avail() || using_script() || input_available())) {
|
if (!(char_avail() || using_script() || input_available())) {
|
||||||
input_enable_events();
|
(void)os_inchar(NULL, 0, -1, 0, main_loop.events);
|
||||||
(void)os_inchar(NULL, 0, -1, 0);
|
|
||||||
input_disable_events();
|
|
||||||
if (!multiqueue_empty(main_loop.events)) {
|
if (!multiqueue_empty(main_loop.events)) {
|
||||||
multiqueue_process_events(main_loop.events);
|
multiqueue_process_events(main_loop.events);
|
||||||
continue;
|
continue;
|
||||||
|
@ -78,6 +78,13 @@ struct multiqueue {
|
|||||||
size_t size;
|
size_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Event event;
|
||||||
|
bool fired;
|
||||||
|
int refcount;
|
||||||
|
} SplitEvent;
|
||||||
|
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "event/multiqueue.c.generated.h"
|
# include "event/multiqueue.c.generated.h"
|
||||||
#endif
|
#endif
|
||||||
@ -245,3 +252,33 @@ static MultiQueueItem *multiqueue_node_data(QUEUE *q)
|
|||||||
{
|
{
|
||||||
return QUEUE_DATA(q, MultiQueueItem, node);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5673,7 +5673,6 @@ void ex_substitute(exarg_T *eap)
|
|||||||
}
|
}
|
||||||
|
|
||||||
block_autocmds(); // Disable events during command preview.
|
block_autocmds(); // Disable events during command preview.
|
||||||
input_disable_events();
|
|
||||||
|
|
||||||
char_u *save_eap = eap->arg;
|
char_u *save_eap = eap->arg;
|
||||||
garray_T save_view;
|
garray_T save_view;
|
||||||
@ -5716,7 +5715,6 @@ void ex_substitute(exarg_T *eap)
|
|||||||
restore_search_patterns();
|
restore_search_patterns();
|
||||||
win_size_restore(&save_view);
|
win_size_restore(&save_view);
|
||||||
ga_clear(&save_view);
|
ga_clear(&save_view);
|
||||||
input_enable_events();
|
|
||||||
unblock_autocmds();
|
unblock_autocmds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5099,10 +5099,10 @@ static void uc_list(char_u *name, size_t name_len)
|
|||||||
if (p_verbose > 0) {
|
if (p_verbose > 0) {
|
||||||
last_set_msg(cmd->uc_script_ctx);
|
last_set_msg(cmd->uc_script_ctx);
|
||||||
}
|
}
|
||||||
ui_flush();
|
line_breakcheck();
|
||||||
os_breakcheck();
|
if (got_int) {
|
||||||
if (got_int)
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (gap == &ucmds || i < gap->ga_len)
|
if (gap == &ucmds || i < gap->ga_len)
|
||||||
break;
|
break;
|
||||||
|
@ -1532,8 +1532,9 @@ int safe_vgetc(void)
|
|||||||
int c;
|
int c;
|
||||||
|
|
||||||
c = vgetc();
|
c = vgetc();
|
||||||
if (c == NUL)
|
if (c == NUL) {
|
||||||
c = get_keystroke();
|
c = get_keystroke(NULL);
|
||||||
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2447,9 +2448,10 @@ int inchar(
|
|||||||
char_u dum[DUM_LEN + 1];
|
char_u dum[DUM_LEN + 1];
|
||||||
|
|
||||||
for (;; ) {
|
for (;; ) {
|
||||||
len = os_inchar(dum, DUM_LEN, 0L, 0);
|
len = os_inchar(dum, DUM_LEN, 0L, 0, NULL);
|
||||||
if (len == 0 || (len == 1 && dum[0] == 3))
|
if (len == 0 || (len == 1 && dum[0] == 3)) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return retesc;
|
return retesc;
|
||||||
}
|
}
|
||||||
@ -2460,7 +2462,7 @@ int inchar(
|
|||||||
|
|
||||||
// Fill up to a third of the buffer, because each character may be
|
// Fill up to a third of the buffer, because each character may be
|
||||||
// tripled below.
|
// 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
|
// If the typebuf was changed further down, it is like nothing was added by
|
||||||
|
@ -57,9 +57,8 @@ struct map_arguments {
|
|||||||
};
|
};
|
||||||
typedef struct map_arguments MapArguments;
|
typedef struct map_arguments MapArguments;
|
||||||
|
|
||||||
#define KEYLEN_PART_KEY -1 /* keylen value for incomplete key-code */
|
#define KEYLEN_PART_KEY -1 // keylen value for incomplete key-code
|
||||||
#define KEYLEN_PART_MAP -2 /* keylen value for incomplete mapping */
|
#define KEYLEN_PART_MAP -2 // keylen value for incomplete mapping
|
||||||
#define KEYLEN_REMOVED 9999 /* keylen value for removed sequence */
|
|
||||||
|
|
||||||
/// Maximum number of streams to read script from
|
/// Maximum number of streams to read script from
|
||||||
enum { NSCRIPT = 15 };
|
enum { NSCRIPT = 15 };
|
||||||
|
@ -144,6 +144,8 @@ void event_init(void)
|
|||||||
{
|
{
|
||||||
log_init();
|
log_init();
|
||||||
loop_init(&main_loop, NULL);
|
loop_init(&main_loop, NULL);
|
||||||
|
resize_events = multiqueue_new_child(main_loop.events);
|
||||||
|
|
||||||
// early msgpack-rpc initialization
|
// early msgpack-rpc initialization
|
||||||
msgpack_rpc_init_method_table();
|
msgpack_rpc_init_method_table();
|
||||||
msgpack_rpc_helpers_init();
|
msgpack_rpc_helpers_init();
|
||||||
|
@ -2556,6 +2556,7 @@ static int do_more_prompt(int typed_char)
|
|||||||
int c;
|
int c;
|
||||||
int retval = FALSE;
|
int retval = FALSE;
|
||||||
int toscroll;
|
int toscroll;
|
||||||
|
bool to_redraw = false;
|
||||||
msgchunk_T *mp_last = NULL;
|
msgchunk_T *mp_last = NULL;
|
||||||
msgchunk_T *mp;
|
msgchunk_T *mp;
|
||||||
int i;
|
int i;
|
||||||
@ -2587,8 +2588,9 @@ static int do_more_prompt(int typed_char)
|
|||||||
if (used_typed_char != NUL) {
|
if (used_typed_char != NUL) {
|
||||||
c = used_typed_char; /* was typed at hit-enter prompt */
|
c = used_typed_char; /* was typed at hit-enter prompt */
|
||||||
used_typed_char = NUL;
|
used_typed_char = NUL;
|
||||||
} else
|
} else {
|
||||||
c = get_keystroke();
|
c = get_keystroke(resize_events);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
toscroll = 0;
|
toscroll = 0;
|
||||||
@ -2661,31 +2663,44 @@ static int do_more_prompt(int typed_char)
|
|||||||
lines_left = Rows - 1;
|
lines_left = Rows - 1;
|
||||||
break;
|
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 */
|
default: /* no valid response */
|
||||||
msg_moremsg(TRUE);
|
msg_moremsg(TRUE);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toscroll != 0) {
|
// code assumes we only do one at a time
|
||||||
if (toscroll < 0) {
|
assert((toscroll == 0) || !to_redraw);
|
||||||
/* go to start of last line */
|
|
||||||
if (mp_last == NULL)
|
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);
|
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);
|
mp = msg_sb_start(mp_last->sb_prev);
|
||||||
else
|
} else {
|
||||||
mp = NULL;
|
mp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* go to start of line at top of the screen */
|
/* go to start of line at top of the screen */
|
||||||
for (i = 0; i < Rows - 2 && mp != NULL && mp->sb_prev != NULL;
|
for (i = 0; i < Rows - 2 && mp != NULL && mp->sb_prev != NULL;
|
||||||
++i)
|
++i)
|
||||||
mp = msg_sb_start(mp->sb_prev);
|
mp = msg_sb_start(mp->sb_prev);
|
||||||
|
|
||||||
if (mp != NULL && mp->sb_prev != NULL) {
|
if (mp != NULL && (mp->sb_prev != NULL || to_redraw)) {
|
||||||
/* Find line to be displayed at top. */
|
// Find line to be displayed at top
|
||||||
for (i = 0; i > toscroll; --i) {
|
for (i = 0; i > toscroll; i--) {
|
||||||
if (mp == NULL || mp->sb_prev == NULL)
|
if (mp == NULL || mp->sb_prev == NULL) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
mp = msg_sb_start(mp->sb_prev);
|
mp = msg_sb_start(mp->sb_prev);
|
||||||
if (mp_last == NULL)
|
if (mp_last == NULL)
|
||||||
mp_last = msg_sb_start(last_msgchunk);
|
mp_last = msg_sb_start(last_msgchunk);
|
||||||
@ -2693,7 +2708,7 @@ static int do_more_prompt(int typed_char)
|
|||||||
mp_last = msg_sb_start(mp_last->sb_prev);
|
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_ins_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns);
|
||||||
grid_fill(&msg_grid_adj, 0, 1, 0, Columns, ' ', ' ',
|
grid_fill(&msg_grid_adj, 0, 1, 0, Columns, ' ', ' ',
|
||||||
HL_ATTR(HLF_MSG));
|
HL_ATTR(HLF_MSG));
|
||||||
@ -2709,6 +2724,7 @@ static int do_more_prompt(int typed_char)
|
|||||||
mp = disp_sb_line(i, mp);
|
mp = disp_sb_line(i, mp);
|
||||||
++msg_scrolled;
|
++msg_scrolled;
|
||||||
}
|
}
|
||||||
|
to_redraw = false;
|
||||||
}
|
}
|
||||||
toscroll = 0;
|
toscroll = 0;
|
||||||
}
|
}
|
||||||
@ -3307,8 +3323,8 @@ do_dialog (
|
|||||||
hotkeys = msg_show_console_dialog(message, buttons, dfltbutton);
|
hotkeys = msg_show_console_dialog(message, buttons, dfltbutton);
|
||||||
|
|
||||||
for (;; ) {
|
for (;; ) {
|
||||||
/* Get a typed character directly from the user. */
|
// Get a typed character directly from the user.
|
||||||
c = get_keystroke();
|
c = get_keystroke(NULL);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case CAR: /* User accepts default option */
|
case CAR: /* User accepts default option */
|
||||||
case NL:
|
case NL:
|
||||||
|
@ -560,7 +560,7 @@ int ask_yesno(const char *const str, const bool direct)
|
|||||||
// Same highlighting as for wait_return.
|
// Same highlighting as for wait_return.
|
||||||
smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str);
|
smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str);
|
||||||
if (direct) {
|
if (direct) {
|
||||||
r = get_keystroke();
|
r = get_keystroke(NULL);
|
||||||
} else {
|
} else {
|
||||||
r = plain_vgetc();
|
r = plain_vgetc();
|
||||||
}
|
}
|
||||||
@ -614,7 +614,7 @@ int is_mouse_key(int c)
|
|||||||
* Disadvantage: typeahead is ignored.
|
* Disadvantage: typeahead is ignored.
|
||||||
* Translates the interrupt character for unix to ESC.
|
* Translates the interrupt character for unix to ESC.
|
||||||
*/
|
*/
|
||||||
int get_keystroke(void)
|
int get_keystroke(MultiQueue *events)
|
||||||
{
|
{
|
||||||
char_u *buf = NULL;
|
char_u *buf = NULL;
|
||||||
int buflen = 150;
|
int buflen = 150;
|
||||||
@ -644,7 +644,7 @@ int get_keystroke(void)
|
|||||||
|
|
||||||
/* First time: blocking wait. Second time: wait up to 100ms for a
|
/* First time: blocking wait. Second time: wait up to 100ms for a
|
||||||
* terminal code to complete. */
|
* 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) {
|
if (n > 0) {
|
||||||
// Replace zero and CSI by a special key code.
|
// Replace zero and CSI by a special key code.
|
||||||
n = fix_input_buffer(buf + len, n);
|
n = fix_input_buffer(buf + len, n);
|
||||||
@ -653,18 +653,12 @@ int get_keystroke(void)
|
|||||||
} else if (len > 0)
|
} else if (len > 0)
|
||||||
++waited; /* keep track of the waiting time */
|
++waited; /* keep track of the waiting time */
|
||||||
|
|
||||||
if (n == KEYLEN_REMOVED) { /* key code removed */
|
if (n > 0) { // found a termcode: adjust length
|
||||||
if (must_redraw != 0 && !need_wait_return && (State & CMDLINE) == 0) {
|
len = n;
|
||||||
/* Redrawing was postponed, do it now. */
|
}
|
||||||
update_screen(0);
|
if (len == 0) { // nothing typed yet
|
||||||
setcursor(); /* put cursor back where it belongs */
|
|
||||||
}
|
|
||||||
continue;
|
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. */
|
/* Handle modifier and/or special key code. */
|
||||||
n = buf[0];
|
n = buf[0];
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "nvim/misc1.h"
|
#include "nvim/misc1.h"
|
||||||
#include "nvim/lib/kvec.h"
|
#include "nvim/lib/kvec.h"
|
||||||
#include "nvim/os/input.h"
|
#include "nvim/os/input.h"
|
||||||
|
#include "nvim/ui.h"
|
||||||
|
|
||||||
#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
|
#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
|
||||||
#define log_client_msg(...)
|
#define log_client_msg(...)
|
||||||
@ -355,11 +356,19 @@ static void handle_request(Channel *channel, msgpack_object *request)
|
|||||||
request_event((void **)&evdata);
|
request_event((void **)&evdata);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
multiqueue_put(channel->events, request_event, 1, evdata);
|
bool is_resize = handler.fn == handle_nvim_ui_try_resize;
|
||||||
DLOG("RPC: scheduled %.*s", method->via.bin.size, method->via.bin.ptr);
|
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:
|
/// Handles a message, depending on the type:
|
||||||
/// - Request: invokes method and writes the response (or error).
|
/// - Request: invokes method and writes the response (or error).
|
||||||
/// - Notification: invokes method (emits `nvim_error_event` on error).
|
/// - Notification: invokes method (emits `nvim_error_event` on error).
|
||||||
|
@ -38,7 +38,6 @@ static Stream read_stream = { .closed = true }; // Input before UI starts.
|
|||||||
static RBuffer *input_buffer = NULL;
|
static RBuffer *input_buffer = NULL;
|
||||||
static bool input_eof = false;
|
static bool input_eof = false;
|
||||||
static int global_fd = -1;
|
static int global_fd = -1;
|
||||||
static int events_enabled = 0;
|
|
||||||
static bool blocking = false;
|
static bool blocking = false;
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
@ -89,7 +88,7 @@ static void cursorhold_event(void **argv)
|
|||||||
did_cursorhold = true;
|
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
|
// If events are enabled and the queue has any items, this function should not
|
||||||
// have been called(inbuf_poll would return kInputAvail)
|
// 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);
|
multiqueue_put(main_loop.events, cursorhold_event, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Low level input function
|
/// Low level input function
|
||||||
int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt)
|
///
|
||||||
|
/// 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)) {
|
if (maxlen && rbuffer_size(input_buffer)) {
|
||||||
return (int)rbuffer_read(input_buffer, (char *)buf, (size_t)maxlen);
|
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;
|
InbufPollResult result;
|
||||||
if (ms >= 0) {
|
if (ms >= 0) {
|
||||||
if ((result = inbuf_poll(ms)) == kInputNone) {
|
if ((result = inbuf_poll(ms, events)) == kInputNone) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((result = inbuf_poll((int)p_ut)) == kInputNone) {
|
if ((result = inbuf_poll((int)p_ut, events)) == kInputNone) {
|
||||||
if (read_stream.closed && silent_mode) {
|
if (read_stream.closed && silent_mode) {
|
||||||
// Drained eventloop & initial input; exit silent/batch-mode (-es/-Es).
|
// Drained eventloop & initial input; exit silent/batch-mode (-es/-Es).
|
||||||
read_error_exit();
|
read_error_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trigger_cursorhold() && !typebuf_changed(tb_change_cnt)) {
|
if (trigger_cursorhold() && !typebuf_changed(tb_change_cnt)) {
|
||||||
create_cursorhold_event();
|
create_cursorhold_event(events == main_loop.events);
|
||||||
} else {
|
} else {
|
||||||
before_blocking();
|
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 there are events, return the keys directly
|
||||||
if (maxlen && pending_events()) {
|
if (maxlen && pending_events(events)) {
|
||||||
return push_event_key(buf, maxlen);
|
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
|
// Check if a character is available for reading
|
||||||
bool os_char_avail(void)
|
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.
|
// Check for CTRL-C typed by reading all available characters.
|
||||||
@ -170,15 +173,6 @@ void os_breakcheck(void)
|
|||||||
updating_screen = save_us;
|
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.
|
/// Test whether a file descriptor refers to a terminal.
|
||||||
///
|
///
|
||||||
@ -383,27 +377,37 @@ bool input_blocking(void)
|
|||||||
return blocking;
|
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) {
|
if (do_profiling == PROF_YES && ms) {
|
||||||
prof_inchar_enter();
|
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
|
// The pending input provoked a blocking wait. Do special events now. #6247
|
||||||
blocking = true;
|
blocking = true;
|
||||||
multiqueue_process_events(ch_before_blocking_events);
|
multiqueue_process_events(ch_before_blocking_events);
|
||||||
}
|
}
|
||||||
DLOG("blocking... events_enabled=%d events_pending=%d", events_enabled,
|
DLOG("blocking... events_enabled=%d events_pending=%d", events != NULL,
|
||||||
!multiqueue_empty(main_loop.events));
|
events && !multiqueue_empty(events));
|
||||||
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, input_ready() || input_eof);
|
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms,
|
||||||
|
input_ready(events) || input_eof);
|
||||||
blocking = false;
|
blocking = false;
|
||||||
|
|
||||||
if (do_profiling == PROF_YES && ms) {
|
if (do_profiling == PROF_YES && ms) {
|
||||||
prof_inchar_exit();
|
prof_inchar_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
return input_ready();
|
if (input_ready(events)) {
|
||||||
|
return kInputAvail;
|
||||||
|
} else {
|
||||||
|
return input_eof ? kInputEof : kInputNone;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void input_done(void)
|
void input_done(void)
|
||||||
@ -416,16 +420,6 @@ bool input_available(void)
|
|||||||
return rbuffer_size(input_buffer) != 0;
|
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,
|
static void input_read_cb(Stream *stream, RBuffer *buf, size_t c, void *data,
|
||||||
bool at_eof)
|
bool at_eof)
|
||||||
{
|
{
|
||||||
@ -478,11 +472,11 @@ static int push_event_key(uint8_t *buf, int maxlen)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if there's pending input
|
// 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
|
return (typebuf_was_filled // API call filled typeahead
|
||||||
|| rbuffer_size(input_buffer) // Input buffer filled
|
|| 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.
|
// Exit because of an input read error.
|
||||||
@ -495,7 +489,7 @@ static void read_error_exit(void)
|
|||||||
preserve_exit();
|
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);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
|
#include "nvim/event/multiqueue.h"
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "os/input.h.generated.h"
|
# include "os/input.h.generated.h"
|
||||||
|
@ -46,14 +46,12 @@ getkey:
|
|||||||
// Event was made available after the last multiqueue_process_events call
|
// Event was made available after the last multiqueue_process_events call
|
||||||
key = K_EVENT;
|
key = K_EVENT;
|
||||||
} else {
|
} else {
|
||||||
input_enable_events();
|
|
||||||
// Flush screen updates before blocking
|
// Flush screen updates before blocking
|
||||||
ui_flush();
|
ui_flush();
|
||||||
// Call `os_inchar` directly to block for events or user input without
|
// Call `os_inchar` directly to block for events or user input without
|
||||||
// consuming anything from `input_buffer`(os/input.c) or calling the
|
// consuming anything from `input_buffer`(os/input.c) or calling the
|
||||||
// mapping engine.
|
// mapping engine.
|
||||||
(void)os_inchar(NULL, 0, -1, 0);
|
(void)os_inchar(NULL, 0, -1, 0, main_loop.events);
|
||||||
input_disable_events();
|
|
||||||
// If an event was put into the queue, we send K_EVENT directly.
|
// If an event was put into the queue, we send K_EVENT directly.
|
||||||
key = !multiqueue_empty(main_loop.events)
|
key = !multiqueue_empty(main_loop.events)
|
||||||
? K_EVENT
|
? K_EVENT
|
||||||
|
@ -174,7 +174,7 @@ void ui_refresh(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (updating_screen) {
|
if (updating_screen) {
|
||||||
ui_schedule_refresh();
|
deferred_refresh_event(NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,11 +228,11 @@ static void ui_refresh_event(void **argv)
|
|||||||
|
|
||||||
void ui_schedule_refresh(void)
|
void ui_schedule_refresh(void)
|
||||||
{
|
{
|
||||||
// TODO(bfredl): "fast" is not optimal. UI should be refreshed only at
|
loop_schedule_fast(&main_loop, event_create(deferred_refresh_event, 0));
|
||||||
// deferred processing plus a few more blocked-on-input situtions like
|
}
|
||||||
// wait_return(), but not any os_breakcheck(). Alternatively make this
|
static void deferred_refresh_event(void **argv)
|
||||||
// defered and make wait_return() process deferred events already.
|
{
|
||||||
loop_schedule_fast(&main_loop, event_create(ui_refresh_event, 0));
|
multiqueue_put(resize_events, ui_refresh_event, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui_default_colors_set(void)
|
void ui_default_colors_set(void)
|
||||||
|
@ -66,4 +66,7 @@ struct ui_t {
|
|||||||
# include "ui.h.generated.h"
|
# include "ui.h.generated.h"
|
||||||
# include "ui_events_call.h.generated.h"
|
# include "ui_events_call.h.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
EXTERN MultiQueue *resize_events;
|
||||||
#endif // NVIM_UI_H
|
#endif // NVIM_UI_H
|
||||||
|
@ -689,6 +689,7 @@ end
|
|||||||
|
|
||||||
module.funcs = module.create_callindex(module.call)
|
module.funcs = module.create_callindex(module.call)
|
||||||
module.meths = module.create_callindex(module.nvim)
|
module.meths = module.create_callindex(module.nvim)
|
||||||
|
module.async_meths = module.create_callindex(module.nvim_async)
|
||||||
module.uimeths = module.create_callindex(ui)
|
module.uimeths = module.create_callindex(ui)
|
||||||
module.bufmeths = module.create_callindex(module.buffer)
|
module.bufmeths = module.create_callindex(module.buffer)
|
||||||
module.winmeths = module.create_callindex(module.window)
|
module.winmeths = module.create_callindex(module.window)
|
||||||
|
@ -122,10 +122,10 @@ describe('TUI', function()
|
|||||||
|
|
||||||
screen:try_resize(50,5)
|
screen:try_resize(50,5)
|
||||||
screen:expect{grid=[[
|
screen:expect{grid=[[
|
||||||
{8:FAIL 1} |
|
|
||||||
{8:FAIL 2} |
|
|
||||||
{8:FAIL 3} |
|
{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 --} |
|
{3:-- TERMINAL --} |
|
||||||
]]}
|
]]}
|
||||||
|
|
||||||
@ -144,12 +144,12 @@ describe('TUI', function()
|
|||||||
) |
|
) |
|
||||||
{8:Error detected while processing function ManyErr:} |
|
{8:Error detected while processing function ManyErr:} |
|
||||||
{11:line 2:} |
|
{11:line 2:} |
|
||||||
{10:-- More --} |
|
{8:FAIL 0} |
|
||||||
{10: }|
|
{8:FAIL 1} |
|
||||||
{10: }|
|
{8:FAIL 2} |
|
||||||
{10: }|
|
{8:FAIL 3} |
|
||||||
{10: }|
|
{8:FAIL 4} |
|
||||||
{10:-- More -- SPACE/d/j: screen/page/line down, b/u/}{12:k}|
|
{10:-- More --}{1: } |
|
||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]]}
|
]]}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ local eq = helpers.eq
|
|||||||
local command = helpers.command
|
local command = helpers.command
|
||||||
local set_method_error = helpers.set_method_error
|
local set_method_error = helpers.set_method_error
|
||||||
local meths = helpers.meths
|
local meths = helpers.meths
|
||||||
|
local async_meths = helpers.async_meths
|
||||||
local test_build_dir = helpers.test_build_dir
|
local test_build_dir = helpers.test_build_dir
|
||||||
local nvim_prog = helpers.nvim_prog
|
local nvim_prog = helpers.nvim_prog
|
||||||
local iswin = helpers.iswin
|
local iswin = helpers.iswin
|
||||||
@ -1073,6 +1074,7 @@ describe('pager', function()
|
|||||||
[9] = {foreground = tonumber('0x00000c'), background = Screen.colors.Grey100},
|
[9] = {foreground = tonumber('0x00000c'), background = Screen.colors.Grey100},
|
||||||
[10] = {background = Screen.colors.Grey100, bold = true, foreground = tonumber('0xe5e5ff')},
|
[10] = {background = Screen.colors.Grey100, bold = true, foreground = tonumber('0xe5e5ff')},
|
||||||
[11] = {background = Screen.colors.Grey100, bold = true, foreground = tonumber ('0x2b8452')},
|
[11] = {background = Screen.colors.Grey100, bold = true, foreground = tonumber ('0x2b8452')},
|
||||||
|
[12] = {bold = true, reverse = true},
|
||||||
})
|
})
|
||||||
command("set more")
|
command("set more")
|
||||||
|
|
||||||
@ -1392,4 +1394,119 @@ aliquip ex ea commodo consequat.]])
|
|||||||
{11:--}{8: }{11:More}{8: }{11:--}{8:^ }|
|
{11:--}{8: }{11:More}{8: }{11:--}{8:^ }|
|
||||||
]]}
|
]]}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('with :!cmd does not crash on resize', function()
|
||||||
|
feed(':!sleep 1<cr>')
|
||||||
|
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('<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can be resized', function()
|
||||||
|
feed(':lua error(_G.x)<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{2:E5105: Error while calling lua chun}|
|
||||||
|
{2:k: [string "<VimL compiled 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 "<VimL compiled str}|
|
||||||
|
{2:]:1: Lorem ipsum dolor sit ame}|
|
||||||
|
{2:nsectetur} |
|
||||||
|
{2:adipisicing elit, sed do eiusm}|
|
||||||
|
{2:mpore} |
|
||||||
|
{2:incididunt ut labore et dolore}|
|
||||||
|
{2: magn} |
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
{4:-- More --}^ |
|
||||||
|
]]}
|
||||||
|
|
||||||
|
-- continues in a mostly consistent state, but only new lines are
|
||||||
|
-- wrapped at the new screen size.
|
||||||
|
feed('<cr>')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{2:k: [string "<VimL compiled str}|
|
||||||
|
{2:]:1: Lorem ipsum dolor sit ame}|
|
||||||
|
{2:nsectetur} |
|
||||||
|
{2:adipisicing elit, sed do eiusm}|
|
||||||
|
{2:mpore} |
|
||||||
|
{2:incididunt ut labore et dolore}|
|
||||||
|
{2: magna aliqua.} |
|
||||||
|
{2:Ut enim ad minim veniam, quis }|
|
||||||
|
{2:nostrud xercitation} |
|
||||||
|
{2:ullamco laboris nisi ut} |
|
||||||
|
{2:aliquip ex ea commodo consequa}|
|
||||||
|
{4:-- More --}^ |
|
||||||
|
]]}
|
||||||
|
|
||||||
|
feed('q')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Reference in New Issue
Block a user