diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/ex_cmds.c | 5 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 2 | ||||
-rw-r--r-- | src/nvim/getchar.c | 29 | ||||
-rw-r--r-- | src/nvim/keymap.c | 1 | ||||
-rw-r--r-- | src/nvim/keymap.h | 3 | ||||
-rw-r--r-- | src/nvim/os/input.c | 9 | ||||
-rw-r--r-- | src/nvim/rbuffer.c | 18 | ||||
-rw-r--r-- | src/nvim/rbuffer.h | 2 | ||||
-rw-r--r-- | src/nvim/tui/input.c | 106 | ||||
-rw-r--r-- | src/nvim/tui/input.h | 4 |
11 files changed, 120 insertions, 61 deletions
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 6cb379f0b4..81abf2fa63 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1246,8 +1246,9 @@ do_shell ( // 1" command to the terminal. ui_cursor_goto(msg_row, msg_col); (void)call_shell(cmd, flags, NULL); - did_check_timestamps = FALSE; - need_check_timestamps = TRUE; + msg_didout = true; + did_check_timestamps = false; + need_check_timestamps = true; // put the message cursor at the end of the screen, avoids wait_return() // to overwrite the text that the external command showed diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 3c09a3a2f8..f7162896ff 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7624,7 +7624,7 @@ void update_topline_cursor(void) */ static void ex_normal(exarg_T *eap) { - if (curbuf->terminal) { + if (curbuf->terminal && State & TERM_FOCUS) { EMSG("Can't re-enter normal mode from terminal mode"); return; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a9d371d0eb..9739090d7c 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -302,7 +302,7 @@ getcmdline ( input_enable_events(); do { c = safe_vgetc(); - } while (c == K_IGNORE); + } while (c == K_IGNORE || c == K_PASTE); input_disable_events(); if (c == K_EVENT) { diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 604425b3ba..990d0fb8e2 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -153,6 +153,7 @@ static char_u typebuf_init[TYPELEN_INIT]; /* initial typebuf.tb_buf */ static char_u noremapbuf_init[TYPELEN_INIT]; /* initial typebuf.tb_noremap */ static int last_recorded_len = 0; /* number of last recorded chars */ +static const uint8_t ui_toggle[] = { K_SPECIAL, KS_EXTRA, KE_PASTE, 0 }; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "getchar.c.generated.h" @@ -1873,14 +1874,15 @@ static int vgetorpeek(int advance) } } - /* Check for match with 'pastetoggle' */ - if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) { - for (mlen = 0; mlen < typebuf.tb_len && p_pt[mlen]; - ++mlen) - if (p_pt[mlen] != typebuf.tb_buf[typebuf.tb_off - + mlen]) - break; - if (p_pt[mlen] == NUL) { /* match */ + // Check for a key that can toggle the 'paste' option + if (mp == NULL && (State & (INSERT|NORMAL))) { + bool match = typebuf_match_len(ui_toggle, &mlen); + if (!match && mlen != typebuf.tb_len && *p_pt != NUL) { + // didn't match ui_toggle_key and didn't try the whole typebuf, + // check the 'pastetoggle' + match = typebuf_match_len(p_pt, &mlen); + } + if (match) { /* write chars to script file(s) */ if (mlen > typebuf.tb_maplen) gotchars(typebuf.tb_buf + typebuf.tb_off @@ -4238,3 +4240,14 @@ static char_u * translate_mapping ( ga_append(&ga, NUL); return (char_u *)(ga.ga_data); } + +static bool typebuf_match_len(const uint8_t *str, int *mlen) +{ + int i; + for (i = 0; i < typebuf.tb_len && str[i]; i++) { + if (str[i] != typebuf.tb_buf[typebuf.tb_off + i]) + break; + } + *mlen = i; + return str[i] == NUL; // matched the whole string +} diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 0c5c24184e..bf4f5e8c4d 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -284,6 +284,7 @@ static struct key_name_entry { {K_SNR, (char_u *)"SNR"}, {K_PLUG, (char_u *)"Plug"}, {K_CURSORHOLD, (char_u *)"CursorHold"}, + {K_PASTE, (char_u *)"Paste"}, {0, NULL} }; diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index 38b96b1b8c..119bff943a 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -247,6 +247,8 @@ enum key_extra { , KE_FOCUSGAINED /* focus gained */ , KE_FOCUSLOST /* focus lost */ , KE_EVENT // event + , KE_PASTE // special key to toggle the 'paste' option. + // sent only by UIs }; /* @@ -437,6 +439,7 @@ enum key_extra { #define K_CURSORHOLD TERMCAP2KEY(KS_EXTRA, KE_CURSORHOLD) #define K_EVENT TERMCAP2KEY(KS_EXTRA, KE_EVENT) +#define K_PASTE TERMCAP2KEY(KS_EXTRA, KE_PASTE) /* Bits for modifier mask */ /* 0x01 cannot be used, because the modifier must be 0x02 or higher */ diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 09f162f79d..e2cff2f9c0 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -171,10 +171,17 @@ size_t input_enqueue(String keys) } if (*ptr == '<') { - // Invalid key sequence, skip until the next '>' or until *end + char *old_ptr = ptr; + // Invalid or incomplete key sequence, skip until the next '>' or until + // *end do { ptr++; } while (ptr < end && *ptr != '>'); + if (*ptr != '>') { + // Incomplete key sequence, return without consuming. + ptr = old_ptr; + break; + } ptr++; continue; } diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c index 0a04ba1954..b3805a3a28 100644 --- a/src/nvim/rbuffer.c +++ b/src/nvim/rbuffer.c @@ -24,11 +24,13 @@ RBuffer *rbuffer_new(size_t capacity) rv->size = 0; rv->write_ptr = rv->read_ptr = rv->start_ptr; rv->end_ptr = rv->start_ptr + capacity; + rv->temp = NULL; return rv; } void rbuffer_free(RBuffer *buf) { + xfree(buf->temp); xfree(buf); } @@ -69,12 +71,20 @@ char *rbuffer_write_ptr(RBuffer *buf, size_t *write_count) FUNC_ATTR_NONNULL_ALL return buf->write_ptr; } -// Set read and write pointer for an empty RBuffer to the beginning of the -// buffer. +// Reset an RBuffer so read_ptr is at the beginning of the memory. If +// necessary, this moves existing data by allocating temporary memory. void rbuffer_reset(RBuffer *buf) FUNC_ATTR_NONNULL_ALL { - if (buf->size == 0) { - buf->write_ptr = buf->read_ptr = buf->start_ptr; + size_t temp_size; + if ((temp_size = rbuffer_size(buf))) { + if (buf->temp == NULL) { + buf->temp = xmalloc(rbuffer_capacity(buf)); + } + rbuffer_read(buf, buf->temp, buf->size); + } + buf->read_ptr = buf->write_ptr = buf->start_ptr; + if (temp_size) { + rbuffer_write(buf, buf->temp, temp_size); } } diff --git a/src/nvim/rbuffer.h b/src/nvim/rbuffer.h index b205db0b5a..35fb16508e 100644 --- a/src/nvim/rbuffer.h +++ b/src/nvim/rbuffer.h @@ -72,6 +72,8 @@ struct rbuffer { rbuffer_callback full_cb, nonfull_cb; void *data; size_t size; + // helper memory used to by rbuffer_reset if required + char *temp; char *end_ptr, *read_ptr, *write_ptr; char start_ptr[]; }; diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 6c362540d0..32b3f1583c 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -9,7 +9,8 @@ #include "nvim/os/input.h" #include "nvim/event/rstream.h" -#define PASTETOGGLE_KEY "<f37>" +#define PASTETOGGLE_KEY "<Paste>" +#define KEY_BUFFER_SIZE 0xfff #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/input.c.generated.h" @@ -20,6 +21,9 @@ void term_input_init(TermInput *input, Loop *loop) input->loop = loop; input->paste_enabled = false; input->in_fd = 0; + input->key_buffer = rbuffer_new(KEY_BUFFER_SIZE); + uv_mutex_init(&input->key_buffer_mutex); + uv_cond_init(&input->key_buffer_cond); const char *term = os_getenv("TERM"); if (!term) { @@ -34,15 +38,13 @@ void term_input_init(TermInput *input, Loop *loop) rstream_init_fd(loop, &input->read_stream, input->in_fd, 0xfff, input); // initialize a timer handle for handling ESC with libtermkey time_watcher_init(loop, &input->timer_handle, input); - // Set the pastetoggle option to a special key that will be sent when - // \e[20{0,1}~/ are received - Error err = ERROR_INIT; - vim_set_option(cstr_as_string("pastetoggle"), - STRING_OBJ(cstr_as_string(PASTETOGGLE_KEY)), &err); } void term_input_destroy(TermInput *input) { + rbuffer_free(input->key_buffer); + uv_mutex_destroy(&input->key_buffer_mutex); + uv_cond_destroy(&input->key_buffer_cond); time_watcher_close(&input->timer_handle, NULL); stream_close(&input->read_stream, NULL); termkey_destroy(input->tk); @@ -59,25 +61,56 @@ void term_input_stop(TermInput *input) time_watcher_stop(&input->timer_handle); } -static void input_enqueue_event(void **argv) +static void input_done_event(void **argv) { - char *buf = argv[0]; - input_enqueue(cstr_as_string(buf)); - xfree(buf); + input_done(); } -static void input_done_event(void **argv) +static void wait_input_enqueue(void **argv) { - input_done(); + TermInput *input = argv[0]; + RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) { + size_t consumed = input_enqueue((String){.data = buf, .size = len}); + if (consumed) { + rbuffer_consumed(input->key_buffer, consumed); + } + rbuffer_reset(input->key_buffer); + if (consumed < len) { + break; + } + } + uv_mutex_lock(&input->key_buffer_mutex); + input->waiting = false; + uv_cond_signal(&input->key_buffer_cond); + uv_mutex_unlock(&input->key_buffer_mutex); } -static void enqueue_input(char *buf, size_t size) +static void flush_input(TermInput *input, bool wait_until_empty) { - loop_schedule(&loop, event_create(1, input_enqueue_event, 1, - xstrndup(buf, size))); + size_t drain_boundary = wait_until_empty ? 0 : 0xff; + do { + uv_mutex_lock(&input->key_buffer_mutex); + loop_schedule(&loop, event_create(1, wait_input_enqueue, 1, input)); + input->waiting = true; + while (input->waiting) { + uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex); + } + uv_mutex_unlock(&input->key_buffer_mutex); + } while (rbuffer_size(input->key_buffer) > drain_boundary); } -static void forward_simple_utf8(TermKeyKey *key) +static void enqueue_input(TermInput *input, char *buf, size_t size) +{ + if (rbuffer_size(input->key_buffer) > + rbuffer_capacity(input->key_buffer) - 0xff) { + // don't ever let the buffer get too full or we risk putting incomplete keys + // into it + flush_input(input, false); + } + rbuffer_write(input->key_buffer, buf, size); +} + +static void forward_simple_utf8(TermInput *input, TermKeyKey *key) { size_t len = 0; char buf[64]; @@ -92,11 +125,10 @@ static void forward_simple_utf8(TermKeyKey *key) ptr++; } - buf[len] = 0; - enqueue_input(buf, len); + enqueue_input(input, buf, len); } -static void forward_modified_utf8(TermKey *tk, TermKeyKey *key) +static void forward_modified_utf8(TermInput *input, TermKeyKey *key) { size_t len; char buf[64]; @@ -105,19 +137,19 @@ static void forward_modified_utf8(TermKey *tk, TermKeyKey *key) && key->code.sym == TERMKEY_SYM_ESCAPE) { len = (size_t)snprintf(buf, sizeof(buf), "<Esc>"); } else { - len = termkey_strfkey(tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM); + len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM); } - enqueue_input(buf, len); + enqueue_input(input, buf, len); } -static void forward_mouse_event(TermKey *tk, TermKeyKey *key) +static void forward_mouse_event(TermInput *input, TermKeyKey *key) { char buf[64]; size_t len = 0; int button, row, col; TermKeyMouseEvent ev; - termkey_interpret_mouse(tk, key, &ev, &button, &row, &col); + termkey_interpret_mouse(input->tk, key, &ev, &button, &row, &col); if (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG) { return; @@ -159,7 +191,7 @@ static void forward_mouse_event(TermKey *tk, TermKeyKey *key) } len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row); - enqueue_input(buf, len); + enqueue_input(input, buf, len); } static TermKeyResult tk_getkey(TermKey *tk, TermKeyKey *key, bool force) @@ -189,17 +221,17 @@ static void tk_getkeys(TermInput *input, bool force) while ((result = tk_getkey(input->tk, &key, force)) == TERMKEY_RES_KEY) { if (key.type == TERMKEY_TYPE_UNICODE && !key.modifiers) { - forward_simple_utf8(&key); + forward_simple_utf8(input, &key); } else if (key.type == TERMKEY_TYPE_UNICODE || key.type == TERMKEY_TYPE_FUNCTION || key.type == TERMKEY_TYPE_KEYSYM) { - forward_modified_utf8(input->tk, &key); + forward_modified_utf8(input, &key); } else if (key.type == TERMKEY_TYPE_MOUSE) { - forward_mouse_event(input->tk, &key); + forward_mouse_event(input, &key); } } - if (result != TERMKEY_RES_AGAIN) { + if (result != TERMKEY_RES_AGAIN || input->paste_enabled) { return; } @@ -230,21 +262,7 @@ static bool handle_bracketed_paste(TermInput *input) if (input->paste_enabled == enable) { return true; } - if (enable) { - // Get the current mode - int state = get_real_state(); - if (state & NORMAL) { - // Enter insert mode - enqueue_input("i", 1); - } else if (state & VISUAL) { - // Remove the selected text and enter insert mode - enqueue_input("c", 1); - } else if (!(state & INSERT)) { - // Don't mess with the paste option - return true; - } - } - enqueue_input(PASTETOGGLE_KEY, sizeof(PASTETOGGLE_KEY) - 1); + enqueue_input(input, PASTETOGGLE_KEY, sizeof(PASTETOGGLE_KEY) - 1); input->paste_enabled = enable; return true; } @@ -326,7 +344,7 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, } } } while (rbuffer_size(input->read_stream.buffer)); - + flush_input(input, true); // Make sure the next input escape sequence fits into the ring buffer // without wrap around, otherwise it could be misinterpreted. rbuffer_reset(input->read_stream.buffer); diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h index 033f53b4e2..d7ee2b9e52 100644 --- a/src/nvim/tui/input.h +++ b/src/nvim/tui/input.h @@ -10,10 +10,14 @@ typedef struct term_input { int in_fd; bool paste_enabled; + bool waiting; TermKey *tk; TimeWatcher timer_handle; Loop *loop; Stream read_stream; + RBuffer *key_buffer; + uv_mutex_t key_buffer_mutex; + uv_cond_t key_buffer_cond; } TermInput; #ifdef INCLUDE_GENERATED_DECLARATIONS |