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.c527
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;
+}