diff options
Diffstat (limited to 'src/nvim/getchar.c')
-rw-r--r-- | src/nvim/getchar.c | 527 |
1 files changed, 332 insertions, 195 deletions
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 6b1150cefa..85a5c176bb 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -15,6 +15,7 @@ #include <stdbool.h> #include <string.h> +#include "nvim/api/private/helpers.h" #include "nvim/ascii.h" #include "nvim/assert.h" #include "nvim/buffer_defs.h" @@ -58,25 +59,18 @@ static int curscript = 0; FileDescriptor *scriptin[NSCRIPT] = { NULL }; -/* - * These buffers are used for storing: - * - stuffed characters: A command that is translated into another command. - * - redo characters: will redo the last change. - * - recorded characters: for the "q" command. - * - * The bytes are stored like in the typeahead buffer: - * - K_SPECIAL introduces a special key (two more bytes follow). A literal - * K_SPECIAL is stored as K_SPECIAL KS_SPECIAL KE_FILLER. - * - CSI introduces a GUI termcap code (also when gui.in_use is FALSE, - * otherwise switching the GUI on would make mappings invalid). - * A literal CSI is stored as CSI KS_EXTRA KE_CSI. - * These translations are also done on multi-byte characters! - * - * Escaping CSI bytes is done by the system-specific input functions, called - * by ui_inchar(). - * Escaping K_SPECIAL is done by inchar(). - * Un-escaping is done by vgetc(). - */ +// These buffers are used for storing: +// - stuffed characters: A command that is translated into another command. +// - redo characters: will redo the last change. +// - recorded characters: for the "q" command. +// +// The bytes are stored like in the typeahead buffer: +// - K_SPECIAL introduces a special key (two more bytes follow). A literal +// K_SPECIAL is stored as K_SPECIAL KS_SPECIAL KE_FILLER. +// These translations are also done on multi-byte characters! +// +// Escaping K_SPECIAL is done by inchar(). +// Un-escaping is done by vgetc(). #define MINIMAL_SIZE 20 // minimal size for b_str @@ -146,7 +140,7 @@ static int KeyNoremap = 0; // remapping flags // typebuf.tb_buf has three parts: room in front (for result of mappings), the // middle for typeahead and room for new characters (which needs to be 3 * -// MAXMAPLEN) for the Amiga). +// MAXMAPLEN for the Amiga). #define TYPELEN_INIT (5 * (MAXMAPLEN + 3)) static char_u typebuf_init[TYPELEN_INIT]; // initial typebuf.tb_buf static char_u noremapbuf_init[TYPELEN_INIT]; // initial typebuf.tb_noremap @@ -169,10 +163,11 @@ void free_buff(buffheader_T *buf) xfree(p); } buf->bh_first.b_next = NULL; + buf->bh_curr = NULL; } /// Return the contents of a buffer as a single string. -/// K_SPECIAL and CSI in the returned string are escaped. +/// K_SPECIAL in the returned string is escaped. /// /// @param dozero count == zero is not an error static char_u *get_buffcont(buffheader_T *buffer, int dozero) @@ -201,11 +196,9 @@ static char_u *get_buffcont(buffheader_T *buffer, int dozero) return p; } -/* - * Return the contents of the record buffer as a single string - * and clear the record buffer. - * K_SPECIAL and CSI in the returned string are escaped. - */ +/// Return the contents of the record buffer as a single string +/// and clear the record buffer. +/// K_SPECIAL in the returned string is escaped. char_u *get_recorded(void) { char_u *p; @@ -235,10 +228,8 @@ char_u *get_recorded(void) return p; } -/* - * Return the contents of the redo buffer as a single string. - * K_SPECIAL and CSI in the returned string are escaped. - */ +/// Return the contents of the redo buffer as a single string. +/// K_SPECIAL in the returned string is escaped. char_u *get_inserted(void) { return get_buffcont(&redobuff, FALSE); @@ -246,7 +237,7 @@ char_u *get_inserted(void) /// Add string after the current block of the given buffer /// -/// K_SPECIAL and CSI should have been escaped already. +/// K_SPECIAL should have been escaped already. /// /// @param[out] buf Buffer to add to. /// @param[in] s String to add. @@ -294,9 +285,23 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle } } -/* - * Add number "n" to buffer "buf". - */ +/// Delete "slen" bytes from the end of "buf". +/// Only works when it was just added. +static void delete_buff_tail(buffheader_T *buf, int slen) +{ + int len; + + if (buf->bh_curr == NULL) { + return; // nothing to delete + } + len = (int)STRLEN(buf->bh_curr->b_str); + if (len >= slen) { + buf->bh_curr->b_str[len - slen] = NUL; + buf->bh_space += (size_t)slen; + } +} + +/// Add number "n" to buffer "buf". static void add_num_buff(buffheader_T *buf, long n) { char number[32]; @@ -304,10 +309,8 @@ static void add_num_buff(buffheader_T *buf, long n) add_buff(buf, number, -1L); } -/* - * Add character 'c' to buffer "buf". - * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. - */ +/// 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) { uint8_t bytes[MB_MAXBYTES + 1]; @@ -339,12 +342,10 @@ static void add_char_buff(buffheader_T *buf, int c) } } -/* - * Get one byte from the read buffers. Use readbuf1 one first, use readbuf2 - * if that one is empty. - * If advance == TRUE go to the next char. - * No translation is done K_SPECIAL and CSI are escaped. - */ +/// Get one byte from the read buffers. Use readbuf1 one first, use readbuf2 +/// if that one is empty. +/// If advance == TRUE go to the next char. +/// No translation is done K_SPECIAL is escaped. static int read_readbuffers(int advance) { int c; @@ -523,10 +524,8 @@ void restoreRedobuff(save_redo_T *save_redo) old_redobuff = save_redo->sr_old_redobuff; } -/* - * Append "s" to the redo buffer. - * K_SPECIAL and CSI should already have been escaped. - */ +/// Append "s" to the redo buffer. +/// K_SPECIAL should already have been escaped. void AppendToRedobuff(const char *s) { if (!block_redo) { @@ -535,7 +534,7 @@ void AppendToRedobuff(const char *s) } /// Append to Redo buffer literally, escaping special characters with CTRL-V. -/// K_SPECIAL and CSI are escaped as well. +/// K_SPECIAL is escaped as well. /// /// @param str String to append /// @param len Length of `str` or -1 for up to the NUL. @@ -583,10 +582,8 @@ void AppendToRedobuffLit(const char_u *str, int len) } } -/* - * Append a character to the redo buffer. - * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. - */ +/// Append a character to the redo buffer. +/// Translates special keys, NUL, K_SPECIAL and multibyte characters. void AppendCharToRedobuff(int c) { if (!block_redo) { @@ -604,17 +601,15 @@ void AppendNumberToRedobuff(long n) } } -/* - * Append string "s" to the stuff buffer. - * CSI and K_SPECIAL must already have been escaped. - */ +/// Append string "s" to the stuff buffer. +/// K_SPECIAL must already have been escaped. void stuffReadbuff(const char *s) { add_buff(&readbuf1, s, -1L); } /// Append string "s" to the redo stuff buffer. -/// @remark CSI and K_SPECIAL must already have been escaped. +/// @remark K_SPECIAL must already have been escaped. void stuffRedoReadbuff(const char *s) { add_buff(&readbuf2, s, -1L); @@ -625,11 +620,9 @@ void stuffReadbuffLen(const char *s, long len) add_buff(&readbuf1, s, len); } -/* - * Stuff "s" into the stuff buffer, leaving special key codes unmodified and - * escaping other K_SPECIAL and CSI bytes. - * Change CR, LF and ESC into a space. - */ +/// Stuff "s" into the stuff buffer, leaving special key codes unmodified and +/// escaping other K_SPECIAL bytes. +/// Change CR, LF and ESC into a space. void stuffReadbuffSpec(const char *s) { while (*s != NUL) { @@ -647,10 +640,8 @@ void stuffReadbuffSpec(const char *s) } } -/* - * Append a character to the stuff buffer. - * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. - */ +/// Append a character to the stuff buffer. +/// Translates special keys, NUL, K_SPECIAL and multibyte characters. void stuffcharReadbuff(int c) { add_char_buff(&readbuf1, c); @@ -664,12 +655,12 @@ void stuffnumReadbuff(long n) add_num_buff(&readbuf1, n); } -// Read a character from the redo buffer. Translates K_SPECIAL, CSI and -// multibyte characters. -// The redo buffer is left as it is. -// If init is true, prepare for redo, return FAIL if nothing to redo, OK -// otherwise. -// If old_redo is true, use old_redobuff instead of redobuff. +/// Read a character from the redo buffer. Translates K_SPECIAL and +/// multibyte characters. +/// The redo buffer is left as it is. +/// If init is true, prepare for redo, return FAIL if nothing to redo, OK +/// otherwise. +/// If old_redo is true, use old_redobuff instead of redobuff. static int read_redo(bool init, bool old_redo) { static buffblock_T *bp; @@ -723,9 +714,9 @@ static int read_redo(bool init, bool old_redo) return c; } -// Copy the rest of the redo buffer into the stuff buffer (in a slow way). -// If old_redo is true, use old_redobuff instead of redobuff. -// The escaped K_SPECIAL and CSI are copied without translation. +/// Copy the rest of the redo buffer into the stuff buffer (in a slow way). +/// If old_redo is true, use old_redobuff instead of redobuff. +/// The escaped K_SPECIAL is copied without translation. static void copy_redo(bool old_redo) { int c; @@ -861,7 +852,7 @@ void init_default_mappings(void) // // If noremap is REMAP_YES, new string can be mapped again. // If noremap is REMAP_NONE, new string cannot be mapped again. -// If noremap is REMAP_SKIP, fist char of new string cannot be mapped again, +// If noremap is REMAP_SKIP, first char of new string cannot be mapped again, // but abbreviations are allowed. // If noremap is REMAP_SCRIPT, new string cannot be mapped again, except for // script-local mappings. @@ -991,36 +982,34 @@ int ins_typebuf(char_u *str, int noremap, int offset, bool nottyped, bool silent return OK; } -/* - * Put character "c" back into the typeahead buffer. - * Can be used for a character obtained by vgetc() that needs to be put back. - * Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to - * the char. - */ -void ins_char_typebuf(int c) +/// Put character "c" back into the typeahead buffer. +/// Can be used for a character obtained by vgetc() that needs to be put back. +/// Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to +/// the char. +/// @return the length of what was inserted +int ins_char_typebuf(int c, int modifier) { - char_u buf[MB_MAXBYTES + 1]; - if (IS_SPECIAL(c)) { + char_u buf[MB_MAXBYTES * 3 + 4]; + int len = 0; + if (modifier != 0) { buf[0] = K_SPECIAL; - buf[1] = (char_u)K_SECOND(c); - buf[2] = (char_u)K_THIRD(c); + buf[1] = KS_MODIFIER; + buf[2] = (char_u)modifier; buf[3] = NUL; + len = 3; + } + if (IS_SPECIAL(c)) { + buf[len] = K_SPECIAL; + buf[len + 1] = (char_u)K_SECOND(c); + buf[len + 2] = (char_u)K_THIRD(c); + buf[len + 3] = NUL; } else { - buf[utf_char2bytes(c, buf)] = NUL; - char_u *p = buf; - while (*p) { - if ((uint8_t)(*p) == CSI || (uint8_t)(*p) == K_SPECIAL) { - bool is_csi = (uint8_t)(*p) == CSI; - memmove(p + 3, p + 1, STRLEN(p + 1) + 1); - *p++ = K_SPECIAL; - *p++ = is_csi ? KS_EXTRA : KS_SPECIAL; - *p++ = is_csi ? KE_CSI : KE_FILLER; - } else { - p++; - } - } + char_u *end = add_char2buf(c, buf + len); + *end = NUL; + len = (int)(end - buf); } (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); + return len; } /// Return TRUE if the typeahead buffer was changed (while waiting for a @@ -1180,6 +1169,18 @@ static void gotchars(const char_u *chars, size_t len) maptick++; } +/// Undo the last gotchars() for "len" bytes. To be used when putting a typed +/// character back into the typeahead buffer, thus gotchars() will be called +/// again. +/// Only affects recorded characters. +void ungetchars(int len) +{ + if (reg_recording != 0) { + delete_buff_tail(&recordbuff, len); + last_recorded_len -= (size_t)len; + } +} + /* * Sync undo. Called when typed characters are obtained from the typeahead * buffer, or when a menu is used. @@ -1426,15 +1427,13 @@ static void updatescript(int c) } } -/* - * Get the next input character. - * Can return a special key or a multi-byte character. - * Can return NUL when called recursively, use safe_vgetc() if that's not - * wanted. - * This translates escaped K_SPECIAL and CSI bytes to a K_SPECIAL or CSI byte. - * Collects the bytes of a multibyte character into the whole character. - * Returns the modifiers in the global "mod_mask". - */ +/// Get the next input character. +/// Can return a special key or a multi-byte character. +/// Can return NUL when called recursively, use safe_vgetc() if that's not +/// wanted. +/// This translates escaped K_SPECIAL bytes to a K_SPECIAL byte. +/// Collects the bytes of a multibyte character into the whole character. +/// Returns the modifiers in the global "mod_mask". int vgetc(void) { int c, c2; @@ -1460,8 +1459,9 @@ int vgetc(void) mouse_row = old_mouse_row; mouse_col = old_mouse_col; } else { - mod_mask = 0x0; + mod_mask = 0; last_recorded_len = 0; + for (;;) { // this is done twice if there are modifiers bool did_inc = false; if (mod_mask) { // no mapping after modifier has been read @@ -1571,14 +1571,9 @@ int vgetc(void) buf[i] = (char_u)vgetorpeek(true); if (buf[i] == K_SPECIAL) { // Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence, - // which represents a K_SPECIAL (0x80), - // or a CSI - KS_EXTRA - KE_CSI sequence, which represents - // a CSI (0x9B), - // of a K_SPECIAL - KS_EXTRA - KE_CSI, which is CSI too. - c = vgetorpeek(true); - if (vgetorpeek(true) == KE_CSI && c == KS_EXTRA) { - buf[i] = CSI; - } + // which represents a K_SPECIAL (0x80). + (void)vgetorpeek(true); // skip KS_SPECIAL + (void)vgetorpeek(true); // skip KE_FILLER } } no_mapping--; @@ -1592,8 +1587,9 @@ int vgetc(void) if (!no_mapping && KeyTyped && !(State & TERM_FOCUS) && (mod_mask == MOD_MASK_ALT || mod_mask == MOD_MASK_META)) { mod_mask = 0; - ins_char_typebuf(c); - ins_char_typebuf(ESC); + int len = ins_char_typebuf(c, 0); + (void)ins_char_typebuf(ESC, 0); + ungetchars(len + 3); // The ALT/META modifier takes three more bytes continue; } @@ -1693,7 +1689,7 @@ typedef enum { map_result_fail, // failed, break loop map_result_get, // get a character from typeahead map_result_retry, // try to map again - map_result_nomatch // no matching mapping, get char + map_result_nomatch, // no matching mapping, get char } map_result_T; /// Handle mappings in the typeahead buffer. @@ -1714,6 +1710,15 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) int keylen = *keylenp; int i; int local_State = get_real_state(); + bool is_plug_map = false; + + // Check if typehead starts with a <Plug> mapping. + // In that case we will ignore nore flag on it. + if (typebuf.tb_buf[typebuf.tb_off] == K_SPECIAL + && typebuf.tb_buf[typebuf.tb_off+1] == KS_EXTRA + && typebuf.tb_buf[typebuf.tb_off+2] == KE_PLUG) { + is_plug_map = true; + } // Check for a mappable key sequence. // Walk through one maphash[] list until we find an entry that matches. @@ -1729,7 +1734,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) tb_c1 = typebuf.tb_buf[typebuf.tb_off]; if (no_mapping == 0 && maphash_valid && (no_zero_mapping == 0 || tb_c1 != '0') - && (typebuf.tb_maplen == 0 + && (typebuf.tb_maplen == 0 || is_plug_map || (p_remap && !(typebuf.tb_noremap[typebuf.tb_off] & (RM_NONE|RM_ABBR)))) && !(p_paste && (State & (INSERT + CMDLINE))) @@ -1817,7 +1822,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) break; } } - if (n >= 0) { + if (!is_plug_map && n >= 0) { continue; } @@ -1902,7 +1907,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) // complete match if (keylen >= 0 && keylen <= typebuf.tb_len) { - char_u *map_str; + char_u *map_str = NULL; int save_m_expr; int save_m_noremap; int save_m_silent; @@ -1947,20 +1952,52 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) save_m_silent = mp->m_silent; char_u *save_m_keys = NULL; // only saved when needed char_u *save_m_str = NULL; // only saved when needed + LuaRef save_m_luaref = mp->m_luaref; // Handle ":map <expr>": evaluate the {rhs} as an // expression. Also save and restore the command line // for "normal :". if (mp->m_expr) { - int save_vgetc_busy = vgetc_busy; + const int save_vgetc_busy = vgetc_busy; const bool save_may_garbage_collect = may_garbage_collect; + const int save_cursor_row = ui_current_row(); + const int save_cursor_col = ui_current_col(); + const int prev_did_emsg = did_emsg; vgetc_busy = 0; may_garbage_collect = false; save_m_keys = vim_strsave(mp->m_keys); - save_m_str = vim_strsave(mp->m_str); - map_str = eval_map_expr(save_m_str, NUL); + if (save_m_luaref == LUA_NOREF) { + save_m_str = vim_strsave(mp->m_str); + } + map_str = eval_map_expr(mp, NUL); + + // The mapping may do anything, but we expect it to take care of + // redrawing. Do put the cursor back where it was. + ui_cursor_goto(save_cursor_row, save_cursor_col); + ui_flush(); + + // If an error was displayed and the expression returns an empty + // string, generate a <Nop> to allow for a redraw. + if (prev_did_emsg != did_emsg && (map_str == NULL || *map_str == NUL)) { + char_u buf[4]; + xfree(map_str); + buf[0] = K_SPECIAL; + buf[1] = KS_EXTRA; + buf[2] = KE_IGNORE; + buf[3] = NUL; + map_str = vim_strsave(buf); + if (State & CMDLINE) { + // redraw the command below the error + msg_didout = true; + if (msg_row < cmdline_row) { + msg_row = cmdline_row; + } + redrawcmd(); + } + } + vgetc_busy = save_vgetc_busy; may_garbage_collect = save_may_garbage_collect; } else { @@ -2039,7 +2076,7 @@ void vungetc(int c) /// /// When `no_mapping` (global) is zero, checks for mappings in the current mode. /// Only returns one byte (of a multi-byte character). -/// K_SPECIAL and CSI may be escaped, need to get two more bytes then. +/// K_SPECIAL may be escaped, need to get two more bytes then. static int vgetorpeek(bool advance) { int c, c1; @@ -2294,6 +2331,10 @@ static int vgetorpeek(bool advance) c = ESC; } tc = c; + + // no chars to block abbreviations for + typebuf.tb_no_abbr_cnt = 0; + break; } @@ -2452,7 +2493,7 @@ static int vgetorpeek(bool advance) /// 1. a scriptfile /// 2. the keyboard /// -/// As much characters as we can get (up to 'maxlen') are put in "buf" and +/// As many characters as we can get (up to 'maxlen') are put in "buf" and /// NUL terminated (buffer length must be 'maxlen' + 1). /// Minimum for "maxlen" is 3!!!! /// @@ -2470,7 +2511,7 @@ static int vgetorpeek(bool advance) /// Return the number of obtained characters. /// Return -1 when end of input script reached. /// -/// @param wait_time milli seconds +/// @param wait_time milliseconds int inchar(char_u *buf, int maxlen, long wait_time) { int len = 0; // Init for GCC. @@ -2569,7 +2610,7 @@ int fix_input_buffer(char_u *buf, int len) FUNC_ATTR_NONNULL_ALL { if (!using_script()) { - // Should not escape K_SPECIAL/CSI reading input from the user because vim + // Should not escape K_SPECIAL reading input from the user because vim // key codes keys are processed in input.c/input_enqueue. buf[len] = NUL; return len; @@ -2580,9 +2621,8 @@ int fix_input_buffer(char_u *buf, int len) char_u *p = buf; // Two characters are special: NUL and K_SPECIAL. - // Replace NUL by K_SPECIAL KS_ZERO KE_FILLER + // Replace NUL by K_SPECIAL KS_ZERO KE_FILLER // Replace K_SPECIAL by K_SPECIAL KS_SPECIAL KE_FILLER - // Replace CSI by K_SPECIAL KS_EXTRA KE_CSI for (i = len; --i >= 0; ++p) { if (p[0] == NUL || (p[0] == K_SPECIAL @@ -2618,11 +2658,13 @@ int fix_input_buffer(char_u *buf, int len) /// @param[in] orig_lhs Original mapping LHS, with characters to replace. /// @param[in] orig_lhs_len `strlen` of orig_lhs. /// @param[in] orig_rhs Original mapping RHS, with characters to replace. +/// @param[in] rhs_lua Lua reference for Lua maps. /// @param[in] orig_rhs_len `strlen` of orig_rhs. /// @param[in] cpo_flags See param docs for @ref replace_termcodes. /// @param[out] mapargs MapArguments struct holding the replaced strings. -void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, const char_u *orig_rhs, - const size_t orig_rhs_len, int cpo_flags, MapArguments *mapargs) +void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, + const char_u *orig_rhs, const size_t orig_rhs_len, + LuaRef rhs_lua, int cpo_flags, MapArguments *mapargs) { char_u *lhs_buf = NULL; char_u *rhs_buf = NULL; @@ -2638,22 +2680,34 @@ void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, const true, true, true, cpo_flags); mapargs->lhs_len = STRLEN(replaced); STRLCPY(mapargs->lhs, replaced, sizeof(mapargs->lhs)); + mapargs->rhs_lua = rhs_lua; - mapargs->orig_rhs_len = orig_rhs_len; - mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char_u)); - STRLCPY(mapargs->orig_rhs, orig_rhs, mapargs->orig_rhs_len + 1); + if (rhs_lua == LUA_NOREF) { + mapargs->orig_rhs_len = orig_rhs_len; + mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char_u)); + STRLCPY(mapargs->orig_rhs, orig_rhs, mapargs->orig_rhs_len + 1); - if (STRICMP(orig_rhs, "<nop>") == 0) { // "<Nop>" means nothing - mapargs->rhs = xcalloc(1, sizeof(char_u)); // single null-char - mapargs->rhs_len = 0; - mapargs->rhs_is_noop = true; + if (STRICMP(orig_rhs, "<nop>") == 0) { // "<Nop>" means nothing + mapargs->rhs = xcalloc(1, sizeof(char_u)); // single null-char + mapargs->rhs_len = 0; + mapargs->rhs_is_noop = true; + } else { + replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, + false, true, true, cpo_flags); + mapargs->rhs_len = STRLEN(replaced); + mapargs->rhs_is_noop = false; + mapargs->rhs = xcalloc(mapargs->rhs_len + 1, sizeof(char_u)); + STRLCPY(mapargs->rhs, replaced, mapargs->rhs_len + 1); + } } else { - replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, - false, true, true, cpo_flags); - mapargs->rhs_len = STRLEN(replaced); - mapargs->rhs_is_noop = false; - mapargs->rhs = xcalloc(mapargs->rhs_len + 1, sizeof(char_u)); - STRLCPY(mapargs->rhs, replaced, mapargs->rhs_len + 1); + char tmp_buf[64]; + // orig_rhs is not used for Lua mappings, but still needs to be a string. + mapargs->orig_rhs = xcalloc(1, sizeof(char_u)); + mapargs->orig_rhs_len = 0; + // stores <lua>ref_no<cr> in map_str + mapargs->rhs_len = (size_t)vim_snprintf(S_LEN(tmp_buf), "%c%c%c%d\r", K_SPECIAL, + (char_u)KS_EXTRA, KE_LUA, rhs_lua); + mapargs->rhs = vim_strsave((char_u *)tmp_buf); } xfree(lhs_buf); @@ -2765,7 +2819,7 @@ int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *mapargs) size_t orig_rhs_len = STRLEN(rhs_start); set_maparg_lhs_rhs(lhs_to_replace, orig_lhs_len, - rhs_start, orig_rhs_len, + rhs_start, orig_rhs_len, LUA_NOREF, CPO_TO_CPO_FLAGS, &parsed_args); xfree(lhs_to_replace); @@ -2827,7 +2881,7 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T validate_maphash(); bool has_lhs = (args->lhs[0] != NUL); - bool has_rhs = (args->rhs[0] != NUL) || args->rhs_is_noop; + bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop; // check for :unmap without argument if (maptype == 1 && !has_lhs) { @@ -3017,10 +3071,14 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T } else { // new rhs for existing entry mp->m_mode &= ~mode; // remove mode bits if (mp->m_mode == 0 && !did_it) { // reuse entry - xfree(mp->m_str); + XFREE_CLEAR(mp->m_str); + XFREE_CLEAR(mp->m_orig_str); + XFREE_CLEAR(mp->m_desc); + NLUA_CLEAR_REF(mp->m_luaref); + mp->m_str = vim_strsave(rhs); - xfree(mp->m_orig_str); mp->m_orig_str = vim_strsave(orig_rhs); + mp->m_luaref = args->rhs_lua; mp->m_noremap = noremap; mp->m_nowait = args->nowait; mp->m_silent = args->silent; @@ -3028,6 +3086,10 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T mp->m_expr = args->expr; mp->m_script_ctx = current_sctx; mp->m_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&mp->m_script_ctx); + if (args->desc != NULL) { + mp->m_desc = xstrdup(args->desc); + } did_it = true; } } @@ -3096,6 +3158,7 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T mp->m_keys = vim_strsave(lhs); mp->m_str = vim_strsave(rhs); mp->m_orig_str = vim_strsave(orig_rhs); + mp->m_luaref = args->rhs_lua; mp->m_keylen = (int)STRLEN(mp->m_keys); mp->m_noremap = noremap; mp->m_nowait = args->nowait; @@ -3104,6 +3167,11 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T mp->m_expr = args->expr; mp->m_script_ctx = current_sctx; mp->m_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&mp->m_script_ctx); + mp->m_desc = NULL; + if (args->desc != NULL) { + mp->m_desc = xstrdup(args->desc); + } // add the new entry in front of the abbrlist or maphash[] list if (is_abbrev) { @@ -3200,8 +3268,10 @@ static void mapblock_free(mapblock_T **mpp) mp = *mpp; xfree(mp->m_keys); - xfree(mp->m_str); - xfree(mp->m_orig_str); + NLUA_CLEAR_REF(mp->m_luaref); + XFREE_CLEAR(mp->m_str); + XFREE_CLEAR(mp->m_orig_str); + XFREE_CLEAR(mp->m_desc); *mpp = mp->m_next; xfree(mp); } @@ -3392,7 +3462,8 @@ static void showmap(mapblock_T *mp, bool local) { size_t len = 1; - if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)) { + if (message_filtered(mp->m_keys) && message_filtered(mp->m_str) + && (mp->m_desc == NULL || message_filtered((char_u *)mp->m_desc))) { return; } @@ -3437,19 +3508,29 @@ static void showmap(mapblock_T *mp, bool local) /* Use FALSE below if we only want things like <Up> to show up as such on * the rhs, and not M-x etc, TRUE gets both -- webb */ - if (*mp->m_str == NUL) { + if (mp->m_luaref != LUA_NOREF) { + char msg[100]; + snprintf(msg, sizeof(msg), "<Lua function %d>", mp->m_luaref); + msg_puts_attr(msg, HL_ATTR(HLF_8)); + } else if (mp->m_str[0] == NUL) { msg_puts_attr("<Nop>", HL_ATTR(HLF_8)); } else { - // Remove escaping of CSI, because "m_str" is in a format to be used + // Remove escaping of K_SPECIAL, because "m_str" is in a format to be used // as typeahead. char_u *s = vim_strsave(mp->m_str); - vim_unescape_csi(s); + vim_unescape_ks(s); msg_outtrans_special(s, false, 0); xfree(s); } + + if (mp->m_desc != NULL) { + msg_puts("\n "); // Shift line to same level as rhs. + msg_puts(mp->m_desc); + } if (p_verbose > 0) { last_set_msg(mp->m_script_ctx); } + msg_clr_eos(); ui_flush(); // show one line at a time } @@ -3532,8 +3613,7 @@ int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr) mp = maphash[hash]; } for (; mp; mp = mp->m_next) { - if ((mp->m_mode & mode) - && strstr((char *)mp->m_str, rhs) != NULL) { + if ((mp->m_mode & mode) && strstr((char *)mp->m_str, rhs) != NULL) { return true; } } @@ -3818,9 +3898,9 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) int match; if (strchr((const char *)mp->m_keys, K_SPECIAL) != NULL) { - // Might have CSI escaped mp->m_keys. + // Might have K_SPECIAL escaped mp->m_keys. q = vim_strsave(mp->m_keys); - vim_unescape_csi(q); + vim_unescape_ks(q); qlen = (int)STRLEN(q); } // find entries with right mode and keys @@ -3866,7 +3946,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) int newlen = utf_char2bytes(c, tb + j); tb[j + newlen] = NUL; // Need to escape K_SPECIAL. - char_u *escaped = vim_strsave_escape_csi(tb + j); + char_u *escaped = vim_strsave_escape_ks(tb + j); if (escaped != NULL) { newlen = (int)STRLEN(escaped); memmove(tb + j, escaped, (size_t)newlen); @@ -3879,7 +3959,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) (void)ins_typebuf(tb, 1, 0, true, mp->m_silent); } if (mp->m_expr) { - s = eval_map_expr(mp->m_str, c); + s = eval_map_expr(mp, c); } else { s = mp->m_str; } @@ -3909,20 +3989,22 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) /// special characters. /// /// @param c NUL or typed character for abbreviation -static char_u *eval_map_expr(char_u *str, int c) +static char_u *eval_map_expr(mapblock_T *mp, int c) { char_u *res; - char_u *p; - char_u *expr; + char_u *p = NULL; + char_u *expr = NULL; char_u *save_cmd; pos_T save_cursor; int save_msg_col; int save_msg_row; - /* Remove escaping of CSI, because "str" is in a format to be used as - * typeahead. */ - expr = vim_strsave(str); - vim_unescape_csi(expr); + // Remove escaping of K_SPECIAL, because "str" is in a format to be used as + // typeahead. + if (mp->m_luaref == LUA_NOREF) { + expr = vim_strsave(mp->m_str); + vim_unescape_ks(expr); + } save_cmd = save_cmdline_alloc(); @@ -3934,7 +4016,22 @@ static char_u *eval_map_expr(char_u *str, int c) save_cursor = curwin->w_cursor; save_msg_col = msg_col; save_msg_row = msg_row; - p = eval_to_string(expr, NULL, false); + if (mp->m_luaref != LUA_NOREF) { + Error err = ERROR_INIT; + Array args = ARRAY_DICT_INIT; + Object ret = nlua_call_ref(mp->m_luaref, NULL, args, true, &err); + if (ret.type == kObjectTypeString) { + p = (char_u *)xstrndup(ret.data.string.data, ret.data.string.size); + } + api_free_object(ret); + if (err.type != kErrorTypeNone) { + semsg_multiline("E5108: %s", err.msg); + api_clear_error(&err); + } + } else { + p = eval_to_string(expr, NULL, false); + xfree(expr); + } textlock--; ex_normal_lock--; curwin->w_cursor = save_cursor; @@ -3942,23 +4039,20 @@ static char_u *eval_map_expr(char_u *str, int c) msg_row = save_msg_row; restore_cmdline_alloc(save_cmd); - xfree(expr); if (p == NULL) { return NULL; } - // Escape CSI in the result to be able to use the string as typeahead. - res = vim_strsave_escape_csi(p); + // Escape K_SPECIAL in the result to be able to use the string as typeahead. + res = vim_strsave_escape_ks(p); xfree(p); return res; } -/* - * Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result - * can be put in the typeahead buffer. - */ -char_u *vim_strsave_escape_csi(char_u *p) +/// Copy "p" to allocated memory, escaping K_SPECIAL so that the result +/// can be put in the typeahead buffer. +char_u *vim_strsave_escape_ks(char_u *p) { // Need a buffer to hold up to three times as much. Four in case of an // illegal utf-8 byte: @@ -3973,7 +4067,7 @@ char_u *vim_strsave_escape_csi(char_u *p) *d++ = *s++; } else { // Add character, possibly multi-byte to destination, escaping - // CSI and K_SPECIAL. Be careful, it can be an illegal byte! + // K_SPECIAL. Be careful, it can be an illegal byte! d = add_char2buf(utf_ptr2char(s), d); s += utf_ptr2len(s); } @@ -3983,11 +4077,9 @@ char_u *vim_strsave_escape_csi(char_u *p) return res; } -/* - * Remove escaping from CSI and K_SPECIAL characters. Reverse of - * vim_strsave_escape_csi(). Works in-place. - */ -void vim_unescape_csi(char_u *p) +/// Remove escaping from K_SPECIAL characters. Reverse of +/// vim_strsave_escape_ks(). Works in-place. +void vim_unescape_ks(char_u *p) { char_u *s = p, *d = p; @@ -3995,10 +4087,6 @@ void vim_unescape_csi(char_u *p) if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER) { *d++ = K_SPECIAL; s += 3; - } else if ((s[0] == K_SPECIAL || s[0] == CSI) - && s[1] == KS_EXTRA && s[2] == (int)KE_CSI) { - *d++ = CSI; - s += 3; } else { *d++ = *s++; } @@ -4049,8 +4137,11 @@ int makemap(FILE *fd, buf_T *buf) continue; } - // skip mappings that contain a <SNR> (script-local thing), + // skip lua mappings and mappings that contain a <SNR> (script-local thing), // they probably don't work when loaded again + if (mp->m_luaref != LUA_NOREF) { + continue; + } for (p = mp->m_str; *p != NUL; p++) { if (p[0] == K_SPECIAL && p[1] == KS_EXTRA && p[2] == (int)KE_SNR) { @@ -4238,7 +4329,7 @@ int put_escstr(FILE *fd, char_u *strstart, int what) for (; *str != NUL; str++) { // Check for a multi-byte character, which may contain escaped - // K_SPECIAL and CSI bytes. + // K_SPECIAL bytes. const char *p = mb_unescape((const char **)&str); if (p != NULL) { while (*p != NUL) { @@ -4331,10 +4422,11 @@ int put_escstr(FILE *fd, char_u *strstart, int what) /// @param mp_ptr return: pointer to mapblock or NULL /// @param local_ptr return: buffer-local mapping or NULL char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapblock_T **mp_ptr, - int *local_ptr) + int *local_ptr, int *rhs_lua) { int len, minlen; mapblock_T *mp; + *rhs_lua = LUA_NOREF; validate_maphash(); @@ -4375,7 +4467,8 @@ char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapb if (local_ptr != NULL) { *local_ptr = local; } - return mp->m_str; + *rhs_lua = mp->m_luaref; + return mp->m_luaref == LUA_NOREF ? mp->m_str : NULL; } } } @@ -4560,3 +4653,47 @@ char_u *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat) return (char_u *)line_ga.ga_data; } + +bool map_execute_lua(void) +{ + garray_T line_ga; + int c1 = -1; + bool aborted = false; + + ga_init(&line_ga, 1, 32); + + no_mapping++; + + got_int = false; + while (c1 != NUL && !aborted) { + ga_grow(&line_ga, 32); + // Get one character at a time. + c1 = vgetorpeek(true); + if (got_int) { + aborted = true; + } else if (c1 == '\r' || c1 == '\n') { + c1 = NUL; // end the line + } else { + ga_append(&line_ga, (char)c1); + } + } + + no_mapping--; + + if (aborted) { + ga_clear(&line_ga); + return false; + } + + LuaRef ref = (LuaRef)atoi(line_ga.ga_data); + Error err = ERROR_INIT; + Array args = ARRAY_DICT_INIT; + nlua_call_ref(ref, NULL, args, false, &err); + if (err.type != kErrorTypeNone) { + semsg_multiline("E5108: %s", err.msg); + api_clear_error(&err); + } + + ga_clear(&line_ga); + return true; +} |