diff options
Diffstat (limited to 'src/nvim/getchar.c')
-rw-r--r-- | src/nvim/getchar.c | 341 |
1 files changed, 231 insertions, 110 deletions
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 9b19644b7e..472bc3a850 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -13,12 +13,14 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/vim.h" #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -104,10 +106,10 @@ static buffheader_T readbuf1 = { { NULL, { NUL } }, NULL, 0, 0 }; static buffheader_T readbuf2 = { { NULL, { NUL } }, NULL, 0, 0 }; /// Buffer used to store typed characters for vim.on_key(). -static kvec_withinit_t(char, MAXMAPLEN) on_key_buf = KVI_INITIAL_VALUE(on_key_buf); +static kvec_withinit_t(char, MAXMAPLEN + 1) on_key_buf = KVI_INITIAL_VALUE(on_key_buf); /// Number of following bytes that should not be stored for vim.on_key(). -static size_t no_on_key_len = 0; +static size_t on_key_ignore_len = 0; static int typeahead_char = 0; ///< typeahead char that's not flushed @@ -267,17 +269,12 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle } buf->bh_index = 0; - size_t len; if (buf->bh_space >= (size_t)slen) { - len = strlen(buf->bh_curr->b_str); + size_t len = strlen(buf->bh_curr->b_str); xmemcpyz(buf->bh_curr->b_str + len, s, (size_t)slen); buf->bh_space -= (size_t)slen; } else { - if (slen < MINIMAL_SIZE) { - len = MINIMAL_SIZE; - } else { - len = (size_t)slen; - } + size_t len = MAX(MINIMAL_SIZE, (size_t)slen); buffblock_T *p = xmalloc(offsetof(buffblock_T, b_str) + len + 1); buf->bh_space = len - (size_t)slen; xmemcpyz(p->b_str, s, (size_t)slen); @@ -312,6 +309,24 @@ static void add_num_buff(buffheader_T *buf, int n) add_buff(buf, number, -1); } +/// Add byte or special key 'c' to buffer "buf". +/// Translates special keys, NUL and K_SPECIAL. +static void add_byte_buff(buffheader_T *buf, int c) +{ + char temp[4]; + if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL) { + // Translate special key code into three byte sequence. + temp[0] = (char)K_SPECIAL; + temp[1] = (char)K_SECOND(c); + temp[2] = (char)K_THIRD(c); + temp[3] = NUL; + } else { + temp[0] = (char)c; + temp[1] = NUL; + } + add_buff(buf, temp, -1); +} + /// Add character 'c' to buffer "buf". /// Translates special keys, NUL, K_SPECIAL and multibyte characters. static void add_char_buff(buffheader_T *buf, int c) @@ -329,19 +344,7 @@ static void add_char_buff(buffheader_T *buf, int c) if (!IS_SPECIAL(c)) { c = bytes[i]; } - - char temp[4]; - if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL) { - // Translate special key code into three byte sequence. - temp[0] = (char)K_SPECIAL; - temp[1] = (char)K_SECOND(c); - temp[2] = (char)K_THIRD(c); - temp[3] = NUL; - } else { - temp[0] = (char)c; - temp[1] = NUL; - } - add_buff(buf, temp, -1); + add_byte_buff(buf, c); } } @@ -1009,18 +1012,18 @@ int ins_typebuf(char *str, int noremap, int offset, bool nottyped, bool silent) /// Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to /// the char. /// -/// @param no_on_key don't store these bytes for vim.on_key() +/// @param on_key_ignore don't store these bytes for vim.on_key() /// /// @return the length of what was inserted -int ins_char_typebuf(int c, int modifiers, bool no_on_key) +int ins_char_typebuf(int c, int modifiers, bool on_key_ignore) { char buf[MB_MAXBYTES * 3 + 4]; unsigned len = special_to_buf(c, modifiers, true, buf); assert(len < sizeof(buf)); buf[len] = NUL; ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); - if (KeyTyped && no_on_key) { - no_on_key_len += len; + if (KeyTyped && on_key_ignore) { + on_key_ignore_len += len; } return (int)len; } @@ -1188,22 +1191,21 @@ static void gotchars(const uint8_t *chars, size_t len) updatescript(state.buf[i]); } - state.buf[state.buflen] = NUL; + if (state.buflen > on_key_ignore_len) { + kvi_concat_len(on_key_buf, (char *)state.buf + on_key_ignore_len, + state.buflen - on_key_ignore_len); + on_key_ignore_len = 0; + } else { + on_key_ignore_len -= state.buflen; + } if (reg_recording != 0) { + state.buf[state.buflen] = NUL; add_buff(&recordbuff, (char *)state.buf, (ptrdiff_t)state.buflen); // remember how many chars were last recorded last_recorded_len += state.buflen; } - if (state.buflen > no_on_key_len) { - vim_unescape_ks((char *)state.buf + no_on_key_len); - kvi_concat(on_key_buf, (char *)state.buf + no_on_key_len); - no_on_key_len = 0; - } else { - no_on_key_len -= state.buflen; - } - state.buflen = 0; } @@ -1221,7 +1223,7 @@ static void gotchars(const uint8_t *chars, size_t len) void gotchars_ignore(void) { uint8_t nop_buf[3] = { K_SPECIAL, KS_EXTRA, KE_IGNORE }; - no_on_key_len += 3; + on_key_ignore_len += 3; gotchars(nop_buf, 3); } @@ -1634,8 +1636,52 @@ int vgetc(void) c = TO_SPECIAL(c2, c); } - // a keypad or special function key was not mapped, use it like - // its ASCII equivalent + // For a multi-byte character get all the bytes and return the + // converted character. + // Note: This will loop until enough bytes are received! + int n; + if ((n = MB_BYTE2LEN_CHECK(c)) > 1) { + no_mapping++; + buf[0] = (uint8_t)c; + for (int i = 1; i < n; i++) { + buf[i] = (uint8_t)vgetorpeek(true); + if (buf[i] == K_SPECIAL) { + // Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence, + // which represents a K_SPECIAL (0x80). + vgetorpeek(true); // skip KS_SPECIAL + vgetorpeek(true); // skip KE_FILLER + } + } + no_mapping--; + c = utf_ptr2char((char *)buf); + } + + // If mappings are enabled (i.e., not i_CTRL-V) and the user directly typed + // something with MOD_MASK_ALT (<M-/<A- modifier) that was not mapped, interpret + // <M-x> as <Esc>x rather than as an unbound <M-x> keypress. #8213 + // In Terminal mode, however, this is not desirable. #16202 #16220 + // Also do not do this for mouse keys, as terminals encode mouse events as + // CSI sequences, and MOD_MASK_ALT has a meaning even for unmapped mouse keys. + if (!no_mapping && KeyTyped && mod_mask == MOD_MASK_ALT + && !(State & MODE_TERMINAL) && !is_mouse_key(c)) { + mod_mask = 0; + int len = ins_char_typebuf(c, 0, false); + ins_char_typebuf(ESC, 0, false); + int old_len = len + 3; // K_SPECIAL KS_MODIFIER MOD_MASK_ALT takes 3 more bytes + ungetchars(old_len); + if (on_key_buf.size >= (size_t)old_len) { + on_key_buf.size -= (size_t)old_len; + } + continue; + } + + if (vgetc_char == 0) { + vgetc_mod_mask = mod_mask; + vgetc_char = c; + } + + // A keypad or special function key was not mapped, use it like + // its ASCII equivalent. switch (c) { case K_KPLUS: c = '+'; break; @@ -1713,50 +1759,6 @@ int vgetc(void) c = K_RIGHT; break; } - // For a multi-byte character get all the bytes and return the - // converted character. - // Note: This will loop until enough bytes are received! - int n; - if ((n = MB_BYTE2LEN_CHECK(c)) > 1) { - no_mapping++; - buf[0] = (uint8_t)c; - for (int i = 1; i < n; i++) { - buf[i] = (uint8_t)vgetorpeek(true); - if (buf[i] == K_SPECIAL) { - // Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence, - // which represents a K_SPECIAL (0x80). - vgetorpeek(true); // skip KS_SPECIAL - vgetorpeek(true); // skip KE_FILLER - } - } - no_mapping--; - c = utf_ptr2char((char *)buf); - } - - if (vgetc_char == 0) { - vgetc_mod_mask = mod_mask; - vgetc_char = c; - } - - // If mappings are enabled (i.e., not i_CTRL-V) and the user directly typed something with - // MOD_MASK_ALT (<M-/<A- modifier) that was not mapped, interpret <M-x> as <Esc>x rather - // than as an unbound <M-x> keypress. #8213 - // In Terminal mode, however, this is not desirable. #16202 #16220 - // Also do not do this for mouse keys, as terminals encode mouse events as CSI sequences, and - // MOD_MASK_ALT has a meaning even for unmapped mouse keys. - if (!no_mapping && KeyTyped && mod_mask == MOD_MASK_ALT && !(State & MODE_TERMINAL) - && !is_mouse_key(c)) { - mod_mask = 0; - int len = ins_char_typebuf(c, 0, false); - ins_char_typebuf(ESC, 0, false); - int old_len = len + 3; // K_SPECIAL KS_MODIFIER MOD_MASK_ALT takes 3 more bytes - ungetchars(old_len); - if (on_key_buf.size >= (size_t)old_len) { - on_key_buf.size -= (size_t)old_len; - } - continue; - } - break; } @@ -1769,7 +1771,8 @@ int vgetc(void) may_garbage_collect = false; // Execute Lua on_key callbacks. - nlua_execute_on_key(c, on_key_buf.items, on_key_buf.size); + kvi_push(on_key_buf, NUL); + nlua_execute_on_key(c, on_key_buf.items); kvi_destroy(on_key_buf); kvi_init(on_key_buf); @@ -1862,7 +1865,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) if (!char_avail()) { // Flush screen updates before blocking. ui_flush(); - os_inchar(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events); + input_get(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events); if (!multiqueue_empty(main_loop.events)) { state_handle_k_event(); continue; @@ -2237,9 +2240,7 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth) } } else { // No match; may have to check for termcode at next character. - if (max_mlen < mlen) { - max_mlen = mlen; - } + max_mlen = MAX(max_mlen, mlen); } } } @@ -2340,8 +2341,8 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth) const int save_m_noremap = mp->m_noremap; const bool save_m_silent = mp->m_silent; char *save_m_keys = NULL; // only saved when needed - char *save_m_str = NULL; // only saved when needed - const LuaRef save_m_luaref = mp->m_luaref; + char *save_alt_m_keys = NULL; // only saved when needed + const int save_alt_m_keylen = mp->m_alt != NULL ? mp->m_alt->m_keylen : 0; // Handle ":map <expr>": evaluate the {rhs} as an // expression. Also save and restore the command line @@ -2354,10 +2355,10 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth) vgetc_busy = 0; may_garbage_collect = false; - save_m_keys = xstrdup(mp->m_keys); - if (save_m_luaref == LUA_NOREF) { - save_m_str = xstrdup(mp->m_str); - } + save_m_keys = xmemdupz(mp->m_keys, (size_t)mp->m_keylen); + save_alt_m_keys = mp->m_alt != NULL + ? xmemdupz(mp->m_alt->m_keys, (size_t)save_alt_m_keylen) + : NULL; map_str = eval_map_expr(mp, NUL); if ((map_str == NULL || *map_str == NUL)) { @@ -2374,9 +2375,7 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth) if (State & MODE_CMDLINE) { // redraw the command below the error msg_didout = true; - if (msg_row < cmdline_row) { - msg_row = cmdline_row; - } + msg_row = MAX(msg_row, cmdline_row); redrawcmd(); } } else if (State & (MODE_NORMAL | MODE_INSERT)) { @@ -2408,11 +2407,18 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth) if (save_m_noremap != REMAP_YES) { noremap = save_m_noremap; - } else if (strncmp(map_str, save_m_keys != NULL ? save_m_keys : mp->m_keys, - (size_t)keylen) != 0) { - noremap = REMAP_YES; - } else { + } else if (save_m_expr + ? strncmp(map_str, save_m_keys, (size_t)keylen) == 0 + || (save_alt_m_keys != NULL + && strncmp(map_str, save_alt_m_keys, + (size_t)save_alt_m_keylen) == 0) + : strncmp(map_str, mp->m_keys, (size_t)keylen) == 0 + || (mp->m_alt != NULL + && strncmp(map_str, mp->m_alt->m_keys, + (size_t)mp->m_alt->m_keylen) == 0)) { noremap = REMAP_SKIP; + } else { + noremap = REMAP_YES; } i = ins_typebuf(map_str, noremap, 0, true, cmd_silent || save_m_silent); if (save_m_expr) { @@ -2420,7 +2426,7 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth) } } xfree(save_m_keys); - xfree(save_m_str); + xfree(save_alt_m_keys); *keylenp = keylen; if (i == FAIL) { return map_result_fail; @@ -2728,16 +2734,12 @@ static int vgetorpeek(bool advance) timedout = true; continue; } - // In Ex-mode \n is compatible with original Vim behaviour. + // For the command line only CTRL-C always breaks it. // For the cmdline window: Alternate between ESC and // CTRL-C: ESC for most situations and CTRL-C to close the // cmdline window. - if ((State & MODE_CMDLINE) || (cmdwin_type > 0 && tc == ESC)) { - c = Ctrl_C; - } else { - c = ESC; - } + c = ((State & MODE_CMDLINE) || (cmdwin_type > 0 && tc == ESC)) ? Ctrl_C : ESC; tc = c; // set a flag to indicate this wasn't a normal char @@ -2986,7 +2988,7 @@ int inchar(uint8_t *buf, int maxlen, long wait_time) uint8_t dum[DUM_LEN + 1]; while (true) { - len = os_inchar(dum, DUM_LEN, 0, 0, NULL); + len = input_get(dum, DUM_LEN, 0, 0, NULL); if (len == 0 || (len == 1 && dum[0] == Ctrl_C)) { break; } @@ -3002,7 +3004,7 @@ int inchar(uint8_t *buf, int maxlen, long wait_time) // 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, NULL); + len = input_get(buf, maxlen / 3, (int)wait_time, tb_change_cnt, NULL); } // If the typebuf was changed further down, it is like nothing was added by @@ -3179,7 +3181,7 @@ bool map_execute_lua(bool may_repeat) Error err = ERROR_INIT; Array args = ARRAY_DICT_INIT; nlua_call_ref(ref, NULL, args, kRetNilBool, NULL, &err); - if (err.type != kErrorTypeNone) { + if (ERROR_SET(&err)) { semsg_multiline("E5108: %s", err.msg); api_clear_error(&err); } @@ -3187,3 +3189,122 @@ bool map_execute_lua(bool may_repeat) ga_clear(&line_ga); return true; } + +/// Wraps pasted text stream with K_PASTE_START and K_PASTE_END, and +/// appends to redo buffer and/or record buffer if needed. +/// Escapes all K_SPECIAL and NUL bytes in the content. +/// +/// @param state kFalse for the start of a paste +/// kTrue for the end of a paste +/// kNone for the content of a paste +/// @param str the content of the paste (only used when state is kNone) +void paste_store(const uint64_t channel_id, const TriState state, const String str, const bool crlf) +{ + if (State & MODE_CMDLINE) { + return; + } + + const bool need_redo = !block_redo; + const bool need_record = reg_recording != 0 && !is_internal_call(channel_id); + + if (!need_redo && !need_record) { + return; + } + + if (state != kNone) { + const int c = state == kFalse ? K_PASTE_START : K_PASTE_END; + if (need_redo) { + if (state == kFalse && !(State & MODE_INSERT)) { + ResetRedobuff(); + } + add_char_buff(&redobuff, c); + } + if (need_record) { + add_char_buff(&recordbuff, c); + } + return; + } + + const char *s = str.data; + const char *const str_end = str.data + str.size; + + while (s < str_end) { + const char *start = s; + while (s < str_end && (uint8_t)(*s) != K_SPECIAL && *s != NUL + && *s != NL && !(crlf && *s == CAR)) { + s++; + } + + if (s > start) { + if (need_redo) { + add_buff(&redobuff, start, s - start); + } + if (need_record) { + add_buff(&recordbuff, start, s - start); + } + } + + if (s < str_end) { + int c = (uint8_t)(*s++); + if (crlf && c == CAR) { + if (s < str_end && *s == NL) { + s++; + } + c = NL; + } + if (need_redo) { + add_byte_buff(&redobuff, c); + } + if (need_record) { + add_byte_buff(&recordbuff, c); + } + } + } +} + +/// Gets a paste stored by paste_store() from typeahead and repeats it. +void paste_repeat(int count) +{ + garray_T ga = GA_INIT(1, 32); + bool aborted = false; + + no_mapping++; + + got_int = false; + while (!aborted) { + ga_grow(&ga, 32); + uint8_t c1 = (uint8_t)vgetorpeek(true); + if (c1 == K_SPECIAL) { + c1 = (uint8_t)vgetorpeek(true); + uint8_t c2 = (uint8_t)vgetorpeek(true); + int c = TO_SPECIAL(c1, c2); + if (c == K_PASTE_END) { + break; + } else if (c == K_ZERO) { + ga_append(&ga, NUL); + } else if (c == K_SPECIAL) { + ga_append(&ga, K_SPECIAL); + } else { + ga_append(&ga, K_SPECIAL); + ga_append(&ga, c1); + ga_append(&ga, c2); + } + } else { + ga_append(&ga, c1); + } + aborted = got_int; + } + + no_mapping--; + + String str = cbuf_as_string(ga.ga_data, (size_t)ga.ga_len); + Arena arena = ARENA_EMPTY; + Error err = ERROR_INIT; + for (int i = 0; !aborted && i < count; i++) { + nvim_paste(LUA_INTERNAL_CALL, str, false, -1, &arena, &err); + aborted = ERROR_SET(&err); + } + api_clear_error(&err); + arena_mem_free(arena_finish(&arena)); + ga_clear(&ga); +} |