aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/getchar.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/getchar.c')
-rw-r--r--src/nvim/getchar.c341
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);
+}