aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/autoload/userreg.vim7
-rw-r--r--runtime/doc/options.txt59
-rw-r--r--runtime/lua/vim/userreg.lua51
-rw-r--r--runtime/plugin/userreg.vim1
-rw-r--r--src/nvim/api/keysets.lua2
-rw-r--r--src/nvim/api/win_config.c103
-rw-r--r--src/nvim/buffer_defs.h19
-rw-r--r--src/nvim/drawscreen.c50
-rw-r--r--src/nvim/edit.c2
-rw-r--r--src/nvim/eval.c10
-rw-r--r--src/nvim/eval/funcs.c3
-rw-r--r--src/nvim/eval/vars.c14
-rw-r--r--src/nvim/generators/gen_options.lua20
-rw-r--r--src/nvim/keycodes.c27
-rw-r--r--src/nvim/keycodes.h27
-rw-r--r--src/nvim/map.c2
-rw-r--r--src/nvim/map.h2
-rw-r--r--src/nvim/ops.c406
-rw-r--r--src/nvim/ops.h13
-rw-r--r--src/nvim/option.c5
-rw-r--r--src/nvim/option_defs.h2
-rw-r--r--src/nvim/options.lua9
-rw-r--r--src/nvim/po/da.po2
-rw-r--r--src/nvim/po/fr.po2
-rw-r--r--src/nvim/po/tr.po2
-rw-r--r--src/nvim/po/uk.po2
-rw-r--r--src/nvim/popupmenu.c167
-rw-r--r--src/nvim/screen.c1
-rw-r--r--src/nvim/shada.c4
-rw-r--r--src/nvim/window.c5
-rw-r--r--src/nvim/yankmap.c42
-rw-r--r--src/nvim/yankmap.h24
-rw-r--r--test/functional/ui/highlight_spec.lua7
-rw-r--r--test/symbolic/klee/nvim/keymap.c27
34 files changed, 980 insertions, 139 deletions
diff --git a/runtime/autoload/userreg.vim b/runtime/autoload/userreg.vim
new file mode 100644
index 0000000000..fd026a12e6
--- /dev/null
+++ b/runtime/autoload/userreg.vim
@@ -0,0 +1,7 @@
+" This is used for the default userreg function.
+
+lua vim.userreg = require('vim.userreg')
+
+function! userreg#func(action, register, content) abort
+ return v:lua.vim.userreg.fn(a:action, a:register, a:content)
+endfunction
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index a1f2eac5ed..cf099af391 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1338,8 +1338,10 @@ A jump table for the options with a short description can be found at |Q_op|.
'colorcolumn' 'cc' string (default "")
local to window
'colorcolumn' is a comma-separated list of screen columns that are
- highlighted with ColorColumn |hl-ColorColumn|. Useful to align
- text. Will make screen redrawing slower.
+ highlighted with ColorColumn |hl-ColorColumn| and drawn using the
+ colocol option from 'fillchars'. Useful to align text. Will make
+ screen redrawing slower.
+
The screen column can be an absolute number, or a number preceded with
'+' or '-', which is added to or subtracted from 'textwidth'. >
@@ -2464,6 +2466,7 @@ A jump table for the options with a short description can be found at |Q_op|.
diff:c '-' deleted lines of the 'diff' option
msgsep:c ' ' message separator 'display'
eob:c '~' empty lines at the end of a buffer
+ colorcol:c ' ' character to display in the colorcolumn
Any one that is omitted will fall back to the default. For "stl" and
"stlnc" the space will be used when there is highlighting, '^' or '='
@@ -2501,6 +2504,7 @@ A jump table for the options with a short description can be found at |Q_op|.
fold:c Folded |hl-Folded|
diff:c DiffDelete |hl-DiffDelete|
eob:c EndOfBuffer |hl-EndOfBuffer|
+ colorcol:c EndOfBuffer |hl-ColorColumn|
*'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'*
'fixendofline' 'fixeol' boolean (default on)
@@ -6707,6 +6711,57 @@ A jump table for the options with a short description can be found at |Q_op|.
written to disk (see |crash-recovery|). Also used for the
|CursorHold| autocommand event.
+ *'userregfun'* *'urf'*
+'userregfun' 'urf' string (default "")
+ global
+ The option specifies a function to be used to handle any registers
+ that Neovim does not natively handle. This option unlocks all
+ characters to be used as registers by the user.
+
+ The 'userregfun' function is called each time a user register is read
+ from or written to.
+
+ The 'userregfun' function must take the following parameters:
+
+ {action} The action being done on this register (either 'yank'
+ or 'put'
+
+ {register} The string holding the name of the register. This
+ is always a single character, though multi-byte
+ characters are allowed.
+
+ {content} If the action is 'yank' this is the content being
+ yanked into the register. The content is a dictionary
+ with the following items:
+
+ {lines} The lines being yanked, as a list.
+
+ {type} The type of yank, either "line", "char", or
+ "block"
+
+ {width} The width in case of "block" mode.
+
+ {additional_data} Additional data. (can be returned in
+ put mode).
+
+ In case the action is 'put', the 'userregfun' function should return
+ the content to place in that location. The content can either be a
+ string, in which case "char" mode is inferred, or it can return a
+ dictionary of the same template that populates 'content'.
+
+ A very simple example of a 'userregfun' function that behaves exactly
+ like traditional registers would look like: >
+
+ let s:contents = {}
+ function! MyUserregFunction(action, register, content) abort
+ if a:action == "put"
+ return get(s:contents, a:register, "")
+ else
+ let s:contents[a:register] = a:content
+ endif
+ endfunction
+ set userregfun=MyUserregFunction
+<
*'varsofttabstop'* *'vsts'*
'varsofttabstop' 'vsts' string (default "")
local to buffer
diff --git a/runtime/lua/vim/userreg.lua b/runtime/lua/vim/userreg.lua
new file mode 100644
index 0000000000..5abcff0407
--- /dev/null
+++ b/runtime/lua/vim/userreg.lua
@@ -0,0 +1,51 @@
+-- Defualt implementation of the userregfunc. This default implementation is
+-- extensible and allows other plugins to register handlers for different
+-- registers.
+--
+-- The default handler behaves just as a normal register would.
+
+local userreg = {}
+
+-- Returns a "default handler" which behaves exactly like the builtin registers
+-- in Vim. Simply stores whatever was yanked and returns the last thing that was
+-- yanked.
+function userreg._default_handler()
+ local d = {}
+
+ function d.do_yank(self, content)
+ self.content = content
+ end
+
+ function d.do_put(self)
+ return self.content or {}
+ end
+
+ return d
+end
+
+-- The store for registers default handler
+userreg._regtable = {}
+
+-- Function for the userreg. This function will defer to the handler registered
+-- to the given register. If no handler is registered to the given register, the
+-- default handler is used.
+function userreg.fn(action, register, content)
+ if not userreg._regtable[register] then
+ userreg._regtable[register] = userreg._default_handler()
+ end
+
+ if action == "yank" then
+ userreg._regtable[register]:do_yank(content)
+ return nil
+ else
+ return userreg._regtable[register]:do_put()
+ end
+end
+
+-- Registers a handler with a register. Future yanks and puts will defer to the
+-- handler when determining the content to put/yank.
+function userreg.register_handler(register, handler)
+ userreg._regtable[register] = handler
+end
+
+return userreg
diff --git a/runtime/plugin/userreg.vim b/runtime/plugin/userreg.vim
new file mode 100644
index 0000000000..c2a0e71fea
--- /dev/null
+++ b/runtime/plugin/userreg.vim
@@ -0,0 +1 @@
+set userregfun=userreg#func
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index 6fad52ba75..32104ef6dc 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -73,6 +73,8 @@ return {
"border";
"style";
"noautocmd";
+ "title";
+ "title_position";
};
runtime = {
"is_lua";
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 6c37df6af8..0c89726d71 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -437,9 +437,73 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
}
}
+static bool parse_title(FloatConfig* out, String s)
+{
+ // The raw title is going to be at most the length of the string.
+ char* out_title_raw = xcalloc(sizeof(char), s.size + 1);
+ size_t out_cursor = 0;
+
+ char* data = s.data;
+
+ size_t out_hlrec_nalloc = 4;
+ stl_hlrec_t* out_hlrec = xcalloc(sizeof(stl_hlrec_t), out_hlrec_nalloc);
+ out_hlrec[0].start = (char*) out_title_raw;
+ out_hlrec[0].userhl = 0;
+ size_t out_hl_cur = 1;
+
+ char hlbuf[128];
+ size_t hlbuf_cur = 0;
+
+ int hl;
+
+ for (size_t i = 0; i < s.size; i ++) {
+ if (data[i] == '\\' && i < s.size - 1) {
+ i ++;
+ out_title_raw[out_cursor++] = data[i];
+ } else if (
+ data[i] == '%' &&
+ i < s.size - 1 && data[i + 1] == '#') {
+ i += 2;
+ while (i < s.size && data[i] != '#') {
+ if (hlbuf_cur < sizeof(hlbuf) - 1) {
+ hlbuf[hlbuf_cur ++] = data[i];
+ }
+ i ++;
+ }
+ hlbuf[hlbuf_cur++] = 0;
+ hl = syn_check_group(hlbuf, strlen(hlbuf));
+ hlbuf_cur = 0;
+
+ if (out_hl_cur >= out_hlrec_nalloc - 1) { // Leave room for last.
+ out_hlrec =
+ xrealloc(out_hlrec, sizeof(stl_hlrec_t) * (out_hlrec_nalloc *= 2));
+ }
+
+ out_hlrec[out_hl_cur].start = (out_title_raw + out_cursor);
+ out_hlrec[out_hl_cur++].userhl = -hl;
+ } else {
+ out_title_raw[out_cursor++] = data[i];
+ }
+ }
+
+ out->n_title = out_cursor;
+ out_title_raw[out_cursor++] = 0;
+ out_hlrec[out_hl_cur] = (stl_hlrec_t) { 0, 0 };
+ out->title_hl = out_hlrec;
+ out->title = out_title_raw;
+
+ return true;
+}
+
static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, bool reconf,
bool new_win, Error *err)
{
+ xfree(fconfig->title);
+ xfree(fconfig->title_hl);
+ fconfig->title_hl = NULL;
+ fconfig->n_title = 0;
+ fconfig->title = NULL;
+
bool has_relative = false, relative_is_win = false;
if (config->relative.type == kObjectTypeString) {
// ignore empty string, to match nvim_win_get_config
@@ -634,5 +698,44 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
}
}
+ if (HAS_KEY(config->title)) {
+ if (!parse_title(fconfig, config->title.data.string)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'title' key.");
+ goto free_and_fail;
+ }
+ } else if (config->title.type == kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation, "'title' must be a string");
+ return false;
+ }
+
+ if (HAS_KEY(config->title_position)) {
+ if (config->title_position.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'title_position' key");
+ goto free_and_fail;
+ }
+
+ if (striequal(config->title_position.data.string.data, "left")) {
+ fconfig->title_pos = kTitleLeft;
+ } else if (striequal(config->title_position.data.string.data, "center")) {
+ fconfig->title_pos = kTitleCenter;
+ } else if (striequal(config->title_position.data.string.data, "right")) {
+ fconfig->title_pos = kTitleRight;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value for 'title_position'");
+ goto free_and_fail;
+ }
+ }
+
return true;
+
+free_and_fail:
+ xfree(fconfig->title);
+ xfree(fconfig->title_hl);
+ fconfig->n_title = 0;
+ fconfig->title_hl = NULL;
+ fconfig->title = NULL;
+ return false;
}
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 15b964f5ed..769788a63e 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -710,6 +710,7 @@ struct file_buffer {
char_u *b_p_cfu; ///< 'completefunc'
char_u *b_p_ofu; ///< 'omnifunc'
char_u *b_p_tfu; ///< 'tagfunc'
+ char_u *b_p_urf; ///< 'userregfunc'
int b_p_eol; ///< 'endofline'
int b_p_fixeol; ///< 'fixendofline'
int b_p_et; ///< 'expandtab'
@@ -1095,6 +1096,12 @@ typedef enum {
kWinStyleMinimal, /// Minimal UI: no number column, eob markers, etc
} WinStyle;
+typedef enum {
+ kTitleLeft = 0,
+ kTitleCenter,
+ kTitleRight
+} TitlePosition;
+
typedef struct {
Window window;
lpos_T bufpos;
@@ -1112,6 +1119,11 @@ typedef struct {
int border_hl_ids[8];
int border_attr[8];
bool noautocmd;
+
+ stl_hlrec_t* title_hl;
+ char* title;
+ size_t n_title;
+ TitlePosition title_pos;
} FloatConfig;
#define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \
@@ -1121,7 +1133,11 @@ typedef struct {
.focusable = true, \
.zindex = kZIndexFloatDefault, \
.style = kWinStyleUnused, \
- .noautocmd = false })
+ .noautocmd = false, \
+ .title_hl = NULL, \
+ .title = NULL, \
+ .n_title = 0, \
+ .title_pos = kTitleLeft})
// Structure to store last cursor position and topline. Used by check_lnums()
// and reset_lnums().
@@ -1221,6 +1237,7 @@ struct window_S {
int diff;
int msgsep;
int eob;
+ int colorcol;
} w_p_fcs_chars;
/*
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 8cd1bdddd8..75fe0565c2 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -692,13 +692,61 @@ static void win_redr_border(win_T *wp)
int *adj = wp->w_border_adj;
int irow = wp->w_height_inner + wp->w_winbar_height, icol = wp->w_width_inner;
+ char* title = wp->w_float_config.title;
+ size_t n_title = wp->w_float_config.n_title;
+ stl_hlrec_t* title_hl = wp->w_float_config.title_hl;
+
+ int m8[MAX_MCO + 1];
+ int cc;
+ int len;
+ int t_attr = title_hl != NULL && title_hl->userhl
+ ? syn_id2attr(title_hl->userhl)
+ : 0;
+ t_attr = hl_combine_attr(attrs[1], t_attr);
+
+ int title_pos = 2;
+ switch (wp->w_float_config.title_pos) {
+ case kTitleLeft:
+ title_pos = 2;
+ break;
+ case kTitleRight:
+ title_pos = icol - 2 - vim_strsize(title);
+ break;
+ case kTitleCenter:
+ title_pos = (icol - vim_strsize(title)) / 2 - 1;
+ break;
+ }
+ title_pos = title_pos < 2 ? 2 : title_pos;
+
if (adj[0]) {
grid_puts_line_start(grid, 0);
if (adj[3]) {
grid_put_schar(grid, 0, 0, chars[0], attrs[0]);
}
for (int i = 0; i < icol; i++) {
- grid_put_schar(grid, 0, i + adj[3], chars[1], attrs[1]);
+ schar_T ch;
+ int attr;
+ // Draw the title if in the correct position.
+ if (i > title_pos && n_title > 0 && i < icol - 2) {
+ cc = utfc_ptr2char((char_u*) title, m8);
+ len = utfc_ptr2len(title);
+ n_title -= len;
+ title += len;
+
+ while (title_hl != NULL &&
+ (title_hl + 1)->start != NULL &&
+ (title_hl + 1)->start < title) {
+ ++ title_hl;
+ t_attr = hl_combine_attr(attrs[1], syn_id2attr(-title_hl->userhl));
+ }
+
+ schar_from_cc(ch, cc, m8);
+ attr = t_attr;
+ } else {
+ memcpy(ch, chars[1], sizeof(schar_T));
+ attr = attrs[1];
+ }
+ grid_put_schar(grid, 0, i + adj[3], ch, attr);
}
if (adj[1]) {
grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]);
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 05abeee550..3a47731715 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -4080,11 +4080,13 @@ static void ins_ctrl_g(void)
// Don't map the second key. This also prevents the mode message to be
// deleted when ESC is hit.
no_mapping++;
+
allow_keys++;
c = plain_vgetc();
no_mapping--;
allow_keys--;
switch (c) {
+
// CTRL-G k and CTRL-G <Up>: cursor up to Insstart.col
case K_UP:
case Ctrl_K:
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 21068e9101..53f1074033 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -3059,12 +3059,10 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// Register contents: @r.
case '@':
(*arg)++;
+ int regname = mb_cptr2char_adv((const char_u**) arg);
if (evaluate) {
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = get_reg_contents(**arg, kGRegExprSrc);
- }
- if (**arg != NUL) {
- (*arg)++;
+ rettv->vval.v_string = get_reg_contents(regname, kGRegExprSrc);
}
break;
@@ -4164,7 +4162,7 @@ bool garbage_collect(bool testing)
// registers (ShaDa additional data)
{
- const void *reg_iter = NULL;
+ iter_register_T reg_iter = ITER_REGISTER_NULL;
do {
yankreg_T reg;
char name = NUL;
@@ -4173,7 +4171,7 @@ bool garbage_collect(bool testing)
if (name != NUL) {
ABORTING(set_ref_dict)(reg.additional_data, copyID);
}
- } while (reg_iter != NULL);
+ } while (reg_iter != ITER_REGISTER_NULL);
}
// global marks (ShaDa additional data)
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 456fd9fdf6..3b9dc92137 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -3748,6 +3748,9 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"winaltkeys",
"writebackup",
"nvim",
+ "colorcolchar",
+ "omnihighlight",
+ "userreg",
};
// XXX: eval_has_provider() may shell out :(
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 1ede7b35d3..51123e1a85 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -388,7 +388,7 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon)
static const char *skip_var_one(const char *arg)
{
if (*arg == '@' && arg[1] != NUL) {
- return arg + 2;
+ return arg + 1 + utfc_ptr2len(arg + 1);
}
return (char *)find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
@@ -700,10 +700,14 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
return NULL;
}
arg++;
+
+ int regname = utf_ptr2char(arg);
+ int mblen = utf_ptr2len(arg);
+
if (op != NULL && vim_strchr("+-*/%", *op) != NULL) {
semsg(_(e_letwrong), op);
} else if (endchars != NULL
- && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) {
+ && vim_strchr(endchars, *skipwhite(arg + mblen)) == NULL) {
emsg(_(e_letunexp));
} else {
char *s;
@@ -711,7 +715,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
char *ptofree = NULL;
const char *p = tv_get_string_chk(tv);
if (p != NULL && op != NULL && *op == '.') {
- s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc);
+ s = get_reg_contents(*arg == '@' ? '"' : regname, kGRegExprSrc);
if (s != NULL) {
ptofree = (char *)concat_str((char_u *)s, (const char_u *)p);
p = (const char *)ptofree;
@@ -719,9 +723,9 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
}
}
if (p != NULL) {
- write_reg_contents(*arg == '@' ? '"' : *arg,
+ write_reg_contents(*arg == '@' ? '"' : regname,
(const char_u *)p, (ssize_t)STRLEN(p), false);
- arg_end = arg + 1;
+ arg_end = arg + mblen;
}
xfree(ptofree);
}
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index 0454c54faf..51ffdae3b0 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -183,6 +183,26 @@ local dump_option = function(i, o)
w(' },')
end
+-- Verify options are correctly ordered. The options with the same first letter
+-- must be together or else some of the options may seem to disappear from
+-- Neovim.
+done_chrs = {}
+last_opt = nil
+for _, o in ipairs(options.options) do
+ if (last_opt == nil) then
+ last_opt = o
+ elseif last_opt.full_name:sub(1, 1) ~= o.full_name:sub(1, 1) then
+ done_chrs[last_opt.full_name:sub(1, 1)] = true
+ last_opt = o
+
+ if done_chrs[o.full_name:sub(1, 1)] then
+ print("Option '" .. o.full_name .. "' must be next to options with the "
+ .. "same starting character.")
+ os.exit(1)
+ end
+ end
+end
+
w('static vimoption_T options[] = {')
for i, o in ipairs(options.options) do
dump_option(i, o)
diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c
index 5a5257efb2..9899f10788 100644
--- a/src/nvim/keycodes.c
+++ b/src/nvim/keycodes.c
@@ -248,6 +248,33 @@ static const struct key_name_entry {
{ K_F62, "F62" },
{ K_F63, "F63" },
+ { K_F38, "F38" },
+ { K_F39, "F39" },
+ { K_F40, "F40" },
+ { K_F41, "F41" },
+ { K_F42, "F42" },
+ { K_F43, "F43" },
+ { K_F44, "F44" },
+ { K_F45, "F45" },
+ { K_F46, "F46" },
+ { K_F47, "F47" },
+ { K_F48, "F48" },
+ { K_F49, "F49" },
+ { K_F50, "F50" },
+ { K_F51, "F51" },
+ { K_F52, "F52" },
+ { K_F53, "F53" },
+ { K_F54, "F54" },
+ { K_F55, "F55" },
+ { K_F56, "F56" },
+ { K_F57, "F57" },
+ { K_F58, "F58" },
+ { K_F59, "F59" },
+ { K_F60, "F60" },
+ { K_F61, "F61" },
+ { K_F62, "F62" },
+ { K_F63, "F63" },
+
{ K_XF1, "xF1" },
{ K_XF2, "xF2" },
{ K_XF3, "xF3" },
diff --git a/src/nvim/keycodes.h b/src/nvim/keycodes.h
index c4d984ee17..943c127325 100644
--- a/src/nvim/keycodes.h
+++ b/src/nvim/keycodes.h
@@ -329,6 +329,33 @@ enum key_extra {
#define K_F62 TERMCAP2KEY('F', 'q')
#define K_F63 TERMCAP2KEY('F', 'r')
+#define K_F38 TERMCAP2KEY('F', 'S')
+#define K_F39 TERMCAP2KEY('F', 'T')
+#define K_F40 TERMCAP2KEY('F', 'U')
+#define K_F41 TERMCAP2KEY('F', 'V')
+#define K_F42 TERMCAP2KEY('F', 'W')
+#define K_F43 TERMCAP2KEY('F', 'X')
+#define K_F44 TERMCAP2KEY('F', 'Y')
+#define K_F45 TERMCAP2KEY('F', 'Z')
+#define K_F46 TERMCAP2KEY('F', 'a')
+#define K_F47 TERMCAP2KEY('F', 'b')
+#define K_F48 TERMCAP2KEY('F', 'c')
+#define K_F49 TERMCAP2KEY('F', 'd')
+#define K_F50 TERMCAP2KEY('F', 'e')
+#define K_F51 TERMCAP2KEY('F', 'f')
+#define K_F52 TERMCAP2KEY('F', 'g')
+#define K_F53 TERMCAP2KEY('F', 'h')
+#define K_F54 TERMCAP2KEY('F', 'i')
+#define K_F55 TERMCAP2KEY('F', 'j')
+#define K_F56 TERMCAP2KEY('F', 'k')
+#define K_F57 TERMCAP2KEY('F', 'l')
+#define K_F58 TERMCAP2KEY('F', 'm')
+#define K_F59 TERMCAP2KEY('F', 'n')
+#define K_F60 TERMCAP2KEY('F', 'o')
+#define K_F61 TERMCAP2KEY('F', 'p')
+#define K_F62 TERMCAP2KEY('F', 'q')
+#define K_F63 TERMCAP2KEY('F', 'r')
+
// extra set of shifted function keys F1-F4, for vt100 compatible xterm
#define K_S_XF1 TERMCAP2KEY(KS_EXTRA, KE_S_XF1)
#define K_S_XF2 TERMCAP2KEY(KS_EXTRA, KE_S_XF2)
diff --git a/src/nvim/map.c b/src/nvim/map.c
index d3058a5d52..86e7317b56 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -163,6 +163,8 @@ static inline bool ColorKey_eq(ColorKey ae1, ColorKey ae2)
return memcmp(&ae1, &ae2, sizeof(ae1)) == 0;
}
+MAP_IMPL(ptr_t, int, DEFAULT_INITIALIZER)
+MAP_IMPL(int, ptr_t, DEFAULT_INITIALIZER)
MAP_IMPL(int, int, DEFAULT_INITIALIZER)
MAP_IMPL(int, cstr_t, DEFAULT_INITIALIZER)
MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER)
diff --git a/src/nvim/map.h b/src/nvim/map.h
index 845daac3f7..acc796bd98 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -35,6 +35,8 @@
// NOTE: Keys AND values must be allocated! khash.h does not make a copy.
//
MAP_DECLS(int, int)
+MAP_DECLS(int, ptr_t)
+MAP_DECLS(ptr_t, int)
MAP_DECLS(int, cstr_t)
MAP_DECLS(cstr_t, ptr_t)
MAP_DECLS(cstr_t, int)
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index c3edc5b315..e121118fe9 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -21,6 +21,7 @@
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
#include "nvim/ex_getln.h"
@@ -55,8 +56,24 @@
#include "nvim/undo.h"
#include "nvim/vim.h"
#include "nvim/window.h"
+#include "nvim/yankmap.h"
-static yankreg_T y_regs[NUM_REGISTERS] = { 0 };
+struct yank_registers {
+ yankmap_T inner;
+};
+
+yank_registers_T y_regs;
+
+static yankreg_T *get_reg(yank_registers_T *regs, int idx)
+{
+ return yankmap_get(&regs->inner, idx);
+
+}
+
+static yankreg_T *get_global_reg(int idx)
+{
+ return get_reg(&y_regs, idx);
+}
static yankreg_T *y_previous = NULL; // ptr to last written yankreg
@@ -770,6 +787,24 @@ char_u *get_expr_line_src(void)
return vim_strsave(expr_line);
}
+
+int get_userreg(int regname)
+{
+ if ((regname >= 'a' && regname <= 'z')
+ || (regname >= 'A' && regname <= 'Z')
+ || (regname >= '0' && regname <= '9')
+ || (regname <= 127 && strchr("\"-:.%#=*+_/", regname))
+ || regname == Ctrl_F
+ || regname == Ctrl_P
+ || regname == Ctrl_W
+ || regname == Ctrl_A
+ || (regname + USER_REGISTERS_START) < regname) {
+ return -1;
+ }
+
+ return regname + USER_REGISTERS_START;
+}
+
/// @return whether `regname` is a valid name of a yank register.
///
/// @note: There is no check for 0 (default register), caller should do this.
@@ -786,12 +821,152 @@ bool valid_yank_reg(int regname, bool writing)
|| regname == '-'
|| regname == '_'
|| regname == '*'
- || regname == '+') {
+ || regname == '+'
+ || get_userreg(regname) != -1) {
return true;
}
return false;
}
+static int call_userreg_put(const char* urf, int regname, typval_T* out)
+{
+ char regname_str[5];
+ int len;
+
+ len = (*utf_char2len)(regname);
+ regname_str[len] = 0;
+ utf_char2bytes(regname, regname_str);
+
+ typval_T args[3];
+ args[0].v_type = VAR_STRING;
+ args[1].v_type = VAR_STRING;
+ args[2].v_type = VAR_NUMBER;
+
+ args[0].vval.v_string = "put";
+ args[1].vval.v_string = regname_str;
+ args[2].vval.v_number = 0;
+
+ funcexe_T funcexe = FUNCEXE_INIT;
+ funcexe.evaluate = true;
+
+ return call_func(
+ urf,
+ -1,
+ out,
+ /* argcount_in = */ 3,
+ args,
+ &funcexe);
+}
+
+// Converts a typval returned from the userregfunction to a register.
+static void typval_to_yankreg(yankreg_T* yankreg, typval_T* val)
+{
+ if (!yankreg || !val) return;
+
+ char* type;
+ dict_T* dict;
+ typval_T tv;
+ size_t i;
+ size_t sz;
+
+ free_register(yankreg);
+ memset(yankreg, 0, sizeof(*yankreg));
+
+ switch (val->v_type) {
+
+ case VAR_DICT:
+ dict = val->vval.v_dict;
+ type = tv_dict_get_string(dict, "type", false);
+
+ if (!strcmp(type, "block")) {
+ yankreg->y_width = (int) tv_dict_get_number(dict, "width");
+ yankreg->y_type = kMTBlockWise;
+ } else if (!strcmp(type, "line")) {
+ yankreg->y_type = kMTLineWise;
+ } else {
+ yankreg->y_type = kMTCharWise;
+ }
+
+ if (tv_dict_get_tv(dict, "lines", &tv) == OK) {
+ if (tv.v_type == VAR_STRING) {
+ yankreg->y_array = xcalloc(sizeof(char*), 1);
+ yankreg->y_array[0] = strdup(tv.vval.v_string);
+ } else if (tv.v_type == VAR_LIST) {
+ yankreg->y_array =
+ xcalloc(sizeof(char*), (size_t) tv_list_len(tv.vval.v_list));
+
+ i = 0;
+ TV_LIST_ITER_CONST(tv.vval.v_list, li, {
+ if (li->li_tv.v_type == VAR_STRING) {
+ yankreg->y_array[i] = strdup(tv_get_string(&li->li_tv));
+ } else {
+ yankreg->y_array[i] = NULL;
+ }
+ ++ i;
+ });
+
+ yankreg->y_size = i;
+ }
+ } else {
+ yankreg->y_array = NULL;
+ }
+
+ if (tv_dict_get_tv(dict, "additional_data", &tv) == OK) {
+ if (tv.v_type == VAR_DICT) {
+ yankreg->additional_data = tv.vval.v_dict;
+ }
+ }
+ break;
+
+ case VAR_LIST:
+ yankreg->y_type = kMTLineWise;
+ sz = (size_t) tv_list_len(val->vval.v_list);
+ yankreg->y_array = xcalloc(sizeof(char*), sz);
+ yankreg->y_size = sz;
+ i = 0;
+ TV_LIST_ITER_CONST(val->vval.v_list, li, {
+ yankreg->y_array[i] = strdup(tv_get_string(&li->li_tv));
+ i ++;
+ });
+ break;
+
+ default:
+ yankreg->y_type = kMTCharWise;
+ yankreg->y_size = 1;
+
+ if (val->vval.v_string) {
+ yankreg->y_array = xcalloc(sizeof(char*), 1);
+ yankreg->y_array[0] = strdup(tv_get_string(val));
+ } else {
+ yankreg->y_array = NULL;
+ }
+
+ break;
+
+ }
+
+ yankreg->timestamp = os_time();
+}
+
+static void copy_userreg(yankreg_T* into, int regname)
+{
+ if (!into) return;
+
+ if (!curbuf->b_p_urf || strlen((char *) curbuf->b_p_urf) == 0)
+ return;
+
+
+ typval_T* ret = xmalloc(sizeof(typval_T));
+
+ if (call_userreg_put((const char*) curbuf->b_p_urf, regname, ret) == FAIL) {
+ return;
+ }
+
+ typval_to_yankreg(into, ret);
+
+ tv_free(ret);
+}
+
/// @return yankreg_T to use, according to the value of `regname`.
/// Cannot handle the '_' (black hole) register.
/// Must only be called with a valid register name!
@@ -829,7 +1004,11 @@ yankreg_T *get_yank_register(int regname, int mode)
if (i == -1) {
i = 0;
}
- reg = &y_regs[i];
+ reg = get_global_reg(i);
+ if (get_userreg(regname) != -1 && mode != YREG_YANK) {
+ // If the mode is not yank, copy the userreg data to the reg.
+ copy_userreg(reg, regname);
+ }
if (mode == YREG_YANK) {
// remember the written register for unnamed paste
@@ -898,8 +1077,7 @@ int do_record(int c)
if (reg_recording == 0) {
// start recording
- // registers 0-9, a-z and " are allowed
- if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) {
+ if (c < 0) {
retval = FAIL;
} else {
reg_recording = c;
@@ -931,9 +1109,10 @@ int do_record(int c)
}
// Name of requested register, or empty string for unnamed operation.
- char buf[NUMBUFLEN + 2];
- buf[0] = (char)regname;
- buf[1] = NUL;
+ char buf[NUMBUFLEN + 5];
+ int len = (*utf_char2len)(regname);
+ utf_char2bytes(regname, buf);
+ buf[len] = NUL;
(void)tv_dict_add_str(dict, S_LEN("regname"), buf);
tv_dict_set_keys_readonly(dict);
@@ -1014,6 +1193,9 @@ static int stuff_yank(int regname, char_u *p)
reg->y_type = kMTCharWise;
}
reg->timestamp = os_time();
+ if (get_userreg(regname) != -1) {
+ return eval_yank_userreg(curbuf->b_p_urf, regname, reg);
+ }
return OK;
}
@@ -1289,6 +1471,7 @@ int insert_reg(int regname, bool literally_arg)
}
} else { // Name or number register.
yankreg_T *reg = get_yank_register(regname, YREG_PASTE);
+
if (reg->y_array == NULL) {
retval = FAIL;
} else {
@@ -1340,6 +1523,79 @@ static void stuffescaped(const char *arg, bool literally)
}
}
+/// Converts a yankreg to a dict which can be used as an argument to the
+// userregfunc.
+static dict_T* yankreg_to_dict(yankreg_T* yankreg) {
+ dict_T *const dict = tv_dict_alloc();
+ dict->dv_refcount = 1;
+ tv_dict_add_nr(dict, S_LEN("width"), yankreg->y_width);
+
+ const char* type;
+
+ switch(yankreg->y_type) {
+ case kMTLineWise:
+ type = "line";
+ break;
+ case kMTCharWise:
+ type = "char";
+ break;
+ case kMTBlockWise:
+ type = "block";
+ break;
+ default:
+ type = "unknown";
+ }
+
+ tv_dict_add_str(dict, S_LEN("type"), type);
+ if (yankreg->additional_data) {
+ tv_dict_add_dict(dict, S_LEN("additional_data"), yankreg->additional_data);
+ }
+
+ list_T *const lines = tv_list_alloc(yankreg->y_size);
+
+ size_t i;
+ for (i = 0; i < yankreg->y_size; ++ i) {
+ tv_list_append_string(
+ lines, yankreg->y_array[i], strlen(yankreg->y_array[i]));
+ }
+
+ tv_dict_add_list(dict, S_LEN("lines"), lines);
+
+ return dict;
+}
+
+/*
+ * Executes the yank() function on a user-defined register to set the contents
+ * of that register.
+ */
+static int eval_yank_userreg(const char_u *ufn, int regname, yankreg_T *reg)
+{
+ if (!reg)
+ return -1;
+
+ int ret, len;
+ char regname_str[5];
+
+ len = (*utf_char2len)(regname);
+ regname_str[len] = 0;
+ utf_char2bytes(regname, regname_str);
+
+ typval_T args[4];
+ args[0].v_type = VAR_STRING;
+ args[1].v_type = VAR_STRING;
+ args[2].v_type = VAR_DICT;
+ args[3].v_type = VAR_UNKNOWN;
+
+ args[0].vval.v_string = "yank";
+ args[1].vval.v_string = regname_str;
+ args[2].vval.v_dict = yankreg_to_dict(reg);
+
+ char *dup_ufn = strdup((char *)ufn);
+ ret = (int)call_func_retnr(dup_ufn, 3, args);
+ xfree(dup_ufn);
+ return ret;
+}
+
/// If "regname" is a special register, return true and store a pointer to its
/// value in "argp".
///
@@ -1426,6 +1682,16 @@ bool get_spec_reg(int regname, char **argp, bool *allocated, bool errmsg)
case '_': // black hole: always empty
*argp = "";
return true;
+
+ default: /* User-defined registers. */
+ break;
+ // if (get_userreg(regname) != -1) {
+ // if (!curbuf->b_p_urf || strlen((char *) curbuf->b_p_urf) == 0)
+ // return false;
+ // eval_urf_put(curbuf->b_p_urf, regname, argp);
+ // *allocated = true;
+ // return true;
+ // }
}
return false;
@@ -1472,14 +1738,14 @@ bool cmdline_paste_reg(int regname, bool literally_arg, bool remcr)
/// Shift the delete registers: "9 is cleared, "8 becomes "9, etc.
static void shift_delete_registers(bool y_append)
{
- free_register(&y_regs[9]); // free register "9
+ free_register(get_global_reg(9)); // free register "9
for (int n = 9; n > 1; n--) {
- y_regs[n] = y_regs[n - 1];
+ *get_global_reg(n) = *get_global_reg(n - 1);
}
if (!y_append) {
- y_previous = &y_regs[1];
+ y_previous = get_global_reg(1);
}
- y_regs[1].y_array = NULL; // set register "1 to empty
+ get_global_reg(1)->y_array = NULL; // set register "1 to empty
}
/// Handle a delete operation.
@@ -1580,7 +1846,7 @@ int op_delete(oparg_T *oap)
// compatible)
if (oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) {
shift_delete_registers(is_append_register(oap->regname));
- reg = &y_regs[1];
+ reg = get_global_reg(1);
op_yank_reg(oap, false, reg, false);
did_yank = true;
}
@@ -2585,13 +2851,22 @@ int op_change(oparg_T *oap)
return retval;
}
+
+/*
+ * set all the yank registers to empty (called from main())
+ */
+void init_yank(void)
+{
+ init_yankmap(&y_regs.inner);
+}
+
#if defined(EXITFREE)
void clear_registers(void)
{
int i;
for (i = 0; i < NUM_REGISTERS; i++) {
- free_register(&y_regs[i]);
+ free_register(get_global_reg(i));
}
}
@@ -2635,6 +2910,14 @@ bool op_yank(oparg_T *oap, bool message)
yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK);
op_yank_reg(oap, message, reg, is_append_register(oap->regname));
+
+ if (get_userreg(oap->regname) != -1) {
+ if (eval_yank_userreg(curbuf->b_p_urf, oap->regname, reg) == -1) {
+ beep_flush();
+ return false;
+ }
+ }
+
set_clipboard(oap->regname, reg);
do_autocmd_textyankpost(oap, reg);
@@ -2816,7 +3099,11 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
if (oap->regname == NUL) {
*namebuf = NUL;
} else {
- vim_snprintf(namebuf, sizeof(namebuf), _(" into \"%c"), oap->regname);
+ char buf[5];
+ int len = (*utf_char2len) (oap->regname);
+ utf_char2bytes(oap->regname, buf);
+ buf[len] = 0;
+ vim_snprintf(namebuf, sizeof(namebuf), _(" into \"%s"), buf);
}
// redisplay now, so message is not deleted
@@ -2883,13 +3170,13 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
FUNC_ATTR_NONNULL_ALL
{
static bool recursive = false;
+ int len;
if (recursive || !has_event(EVENT_TEXTYANKPOST)) {
// No autocommand was defined, or we yanked from this autocommand.
return;
}
- recursive = true;
save_v_event_T save_v_event;
// Set the v:event dictionary with information about the yank.
@@ -2904,13 +3191,15 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
(void)tv_dict_add_list(dict, S_LEN("regcontents"), list);
// Register type.
- char buf[NUMBUFLEN + 2];
+ char buf[NUMBUFLEN + 6];
format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf));
(void)tv_dict_add_str(dict, S_LEN("regtype"), buf);
// Name of requested register, or empty string for unnamed operation.
- buf[0] = (char)oap->regname;
- buf[1] = NUL;
+ len = (*utf_char2len)(oap->regname);
+ buf[len] = 0;
+ utf_char2bytes(oap->regname, buf);
+ recursive = true;
(void)tv_dict_add_str(dict, S_LEN("regname"), buf);
// Motion type: inclusive or exclusive.
@@ -3129,6 +3418,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
reg = get_yank_register(regname, YREG_PASTE);
}
+ if (get_userreg(regname) != -1) {
+ copy_userreg(reg, regname);
+ }
+
y_type = reg->y_type;
y_width = reg->y_width;
y_size = reg->y_size;
@@ -3781,7 +4074,7 @@ int get_register_name(int num)
/// @return the index of the register "" points to.
int get_unname_register(void)
{
- return y_previous == NULL ? -1 : (int)(y_previous - &y_regs[0]);
+ return yankmap_find(&y_regs.inner, y_previous);
}
/// ":dis" and ":registers": Display the contents of the yank registers.
@@ -3820,10 +4113,10 @@ void ex_display(exarg_T *eap)
if (y_previous != NULL) {
yb = y_previous;
} else {
- yb = &(y_regs[0]);
+ yb = get_global_reg(0);
}
} else {
- yb = &(y_regs[i]);
+ yb = get_global_reg(i);
}
get_clipboard(name, &yb, true);
@@ -5537,6 +5830,10 @@ static yankreg_T *init_write_reg(int name, yankreg_T **old_y_previous, bool must
static void finish_write_reg(int name, yankreg_T *reg, yankreg_T *old_y_previous)
{
+ if (get_userreg(name) != -1) {
+ eval_yank_userreg(curbuf->b_p_urf, name, reg);
+ }
+
// Send text of clipboard register to the clipboard.
set_clipboard(name, reg);
@@ -6943,7 +7240,7 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
}
if (explicit_cb_reg) {
- target = &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER];
+ target = get_global_reg(*name == '*' ? STAR_REGISTER : PLUS_REGISTER);
if (writing && (cb_flags & (*name == '*' ? CB_UNNAMED : CB_UNNAMEDPLUS))) {
clipboard_needs_update = false;
}
@@ -6960,10 +7257,10 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
if (cb_flags & CB_UNNAMEDPLUS) {
*name = (cb_flags & CB_UNNAMED && writing) ? '"': '+';
- target = &y_regs[PLUS_REGISTER];
+ target = get_global_reg(PLUS_REGISTER);
} else {
*name = '*';
- target = &y_regs[STAR_REGISTER];
+ target = get_global_reg(STAR_REGISTER);
}
goto end;
}
@@ -7279,11 +7576,11 @@ static inline bool reg_empty(const yankreg_T *const reg)
/// Iterate over global registers.
///
/// @see op_register_iter
-const void *op_global_reg_iter(const void *const iter, char *const name, yankreg_T *const reg,
- bool *is_unnamed)
+iter_register_T op_global_reg_iter(iter_register_T iter, char *const name,
+ yankreg_T *const reg, bool *is_unnamed)
FUNC_ATTR_NONNULL_ARG(2, 3, 4) FUNC_ATTR_WARN_UNUSED_RESULT
{
- return op_reg_iter(iter, y_regs, name, reg, is_unnamed);
+ return op_reg_iter(iter, &y_regs, name, reg, is_unnamed);
}
/// Iterate over registers `regs`.
@@ -7295,30 +7592,31 @@ const void *op_global_reg_iter(const void *const iter, char *const name, yankreg
///
/// @return Pointer that must be passed to next `op_register_iter` call or
/// NULL if iteration is over.
-const void *op_reg_iter(const void *const iter, const yankreg_T *const regs, char *const name,
- yankreg_T *const reg, bool *is_unnamed)
+iter_register_T op_reg_iter(iter_register_T iter, yank_registers_T *regs,
+ char *const name, yankreg_T *const reg,
+ bool *is_unnamed)
FUNC_ATTR_NONNULL_ARG(3, 4, 5) FUNC_ATTR_WARN_UNUSED_RESULT
{
*name = NUL;
- const yankreg_T *iter_reg = (iter == NULL
- ? &(regs[0])
- : (const yankreg_T *const)iter);
- while (iter_reg - &(regs[0]) < NUM_SAVED_REGISTERS && reg_empty(iter_reg)) {
- iter_reg++;
- }
- if (iter_reg - &(regs[0]) == NUM_SAVED_REGISTERS || reg_empty(iter_reg)) {
- return NULL;
- }
- int iter_off = (int)(iter_reg - &(regs[0]));
- *name = (char)get_register_name(iter_off);
- *reg = *iter_reg;
- *is_unnamed = (iter_reg == y_previous);
- while (++iter_reg - &(regs[0]) < NUM_SAVED_REGISTERS) {
- if (!reg_empty(iter_reg)) {
- return (void *)iter_reg;
+ int iter_idx = (int)(iter == ITER_REGISTER_NULL ? 0 : iter - 1);
+
+ while (iter_idx < NUM_SAVED_REGISTERS && reg_empty(get_reg(regs, iter_idx)))
+ ++iter_idx;
+
+ if (iter_idx >= NUM_SAVED_REGISTERS || reg_empty(get_reg(regs, iter_idx)))
+ return ITER_REGISTER_NULL;
+
+ *reg = *get_reg(regs, iter_idx);
+ *name = (char)get_register_name((int)iter_idx);
+ *is_unnamed = (get_reg(regs, iter_idx) == y_previous);
+
+ while (++iter_idx < NUM_SAVED_REGISTERS) {
+ if (!reg_empty(get_reg(regs, iter_idx))) {
+ return (iter_register_T)(iter_idx + 1);
}
}
- return NULL;
+
+ return ITER_REGISTER_NULL;
}
/// Get a number of non-empty registers
@@ -7326,8 +7624,8 @@ size_t op_reg_amount(void)
FUNC_ATTR_WARN_UNUSED_RESULT
{
size_t ret = 0;
- for (size_t i = 0; i < NUM_SAVED_REGISTERS; i++) {
- if (!reg_empty(y_regs + i)) {
+ for (int i = 0; i < NUM_SAVED_REGISTERS; i++) {
+ if (!reg_empty(get_global_reg(i))) {
ret++;
}
}
@@ -7347,11 +7645,11 @@ bool op_reg_set(const char name, const yankreg_T reg, bool is_unnamed)
if (i == -1) {
return false;
}
- free_register(&y_regs[i]);
- y_regs[i] = reg;
+ free_register(get_global_reg(i));
+ *get_global_reg(i) = reg;
if (is_unnamed) {
- y_previous = &y_regs[i];
+ y_previous = get_global_reg(i);
}
return true;
}
@@ -7367,7 +7665,7 @@ const yankreg_T *op_reg_get(const char name)
if (i == -1) {
return NULL;
}
- return &y_regs[i];
+ return get_global_reg(i);
}
/// Set the previous yank register
@@ -7382,7 +7680,7 @@ bool op_reg_set_previous(const char name)
return false;
}
- y_previous = &y_regs[i];
+ y_previous = get_global_reg(i);
return true;
}
diff --git a/src/nvim/ops.h b/src/nvim/ops.h
index 05893c9940..a456d68003 100644
--- a/src/nvim/ops.h
+++ b/src/nvim/ops.h
@@ -22,6 +22,7 @@ typedef int (*Indenter)(void);
#define PUT_LINE_SPLIT 16 // split line for linewise register
#define PUT_LINE_FORWARD 32 // put linewise register below Visual sel.
#define PUT_BLOCK_INNER 64 // in block mode, do not add trailing spaces
+#define ITER_REGISTER_NULL 0
/*
* Registers:
@@ -37,7 +38,8 @@ typedef int (*Indenter)(void);
// The following registers should not be saved in ShaDa file:
#define STAR_REGISTER 37
#define PLUS_REGISTER 38
-#define NUM_REGISTERS 39
+#define USER_REGISTERS_START 39
+#define NUM_REGISTERS USER_REGISTERS_START
// Operator IDs; The order must correspond to opchars[] in ops.c!
#define OP_NOP 0 // no pending operation
@@ -96,6 +98,8 @@ typedef enum {
YREG_YANK,
YREG_PUT,
} yreg_mode_t;
+/// Returns a reference to a user-defined register.
+int get_userreg(const int regname);
/// Convert register name into register index
///
@@ -118,10 +122,15 @@ static inline int op_reg_index(const int regname)
} else if (regname == '+') {
return PLUS_REGISTER;
} else {
- return -1;
+ return get_userreg(regname);
}
}
+struct yank_registers;
+typedef struct yank_registers yank_registers_T;
+
+typedef size_t iter_register_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ops.h.generated.h"
#endif
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 09793cbdcf..9fe007a453 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -2062,6 +2062,7 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_cpt);
check_string_option(&buf->b_p_cfu);
check_string_option(&buf->b_p_ofu);
+ check_string_option(&buf->b_p_urf);
check_string_option(&buf->b_p_keymap);
check_string_option(&buf->b_p_gp);
check_string_option(&buf->b_p_mp);
@@ -5724,6 +5725,8 @@ static char_u *get_varp(vimoption_T *p)
return (char_u *)&(curbuf->b_p_cfu);
case PV_OFU:
return (char_u *)&(curbuf->b_p_ofu);
+ case PV_URF:
+ return (char_u *)&(curbuf->b_p_urf);
case PV_EOL:
return (char_u *)&(curbuf->b_p_eol);
case PV_FIXEOL:
@@ -6121,6 +6124,8 @@ void buf_copy_options(buf_T *buf, int flags)
COPY_OPT_SCTX(buf, BV_CFU);
buf->b_p_ofu = vim_strsave(p_ofu);
COPY_OPT_SCTX(buf, BV_OFU);
+ buf->b_p_urf = vim_strsave(p_urf);
+ COPY_OPT_SCTX(buf, BV_URF);
buf->b_p_tfu = vim_strsave(p_tfu);
COPY_OPT_SCTX(buf, BV_TFU);
buf->b_p_sts = p_sts;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 237288fbad..f3f7cf219e 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -753,6 +753,7 @@ EXTERN int p_wa; // 'writeany'
EXTERN int p_wb; // 'writebackup'
EXTERN long p_wd; // 'writedelay'
EXTERN int p_cdh; // 'cdhome'
+EXTERN char_u *p_urf; // 'userregister'
EXTERN int p_force_on; ///< options that cannot be turned off.
EXTERN int p_force_off; ///< options that cannot be turned on.
@@ -849,6 +850,7 @@ enum {
BV_WM,
BV_VSTS,
BV_VTS,
+ BV_URF,
BV_COUNT, // must be the last one
};
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 2f941f5d0c..fd4661f9f9 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2691,6 +2691,15 @@ return {
defaults={if_true=4000}
},
{
+ full_name='userregfun', abbreviation='urf',
+ type='string', scope={'buffer'},
+ secure=true,
+ vi_def=true,
+ alloced=true,
+ varname='p_urf',
+ defaults={if_true=""}
+ },
+ {
full_name='varsofttabstop', abbreviation='vsts',
short_desc=N_("list of numbers of spaces that <Tab> uses while editing"),
type='string', list='comma', scope={'buffer'},
diff --git a/src/nvim/po/da.po b/src/nvim/po/da.po
index dfcc052288..f2b28ccc90 100644
--- a/src/nvim/po/da.po
+++ b/src/nvim/po/da.po
@@ -4142,7 +4142,7 @@ msgid "freeing %ld lines"
msgstr "frigør %ld linjer"
#, c-format
-msgid " into \"%c"
+msgid " into \"%s"
msgstr " i \"%c"
#, c-format
diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po
index 614ba013e6..239d6da211 100644
--- a/src/nvim/po/fr.po
+++ b/src/nvim/po/fr.po
@@ -4410,7 +4410,7 @@ msgid "freeing %ld lines"
msgstr "libration de %ld lignes"
#, c-format
-msgid " into \"%c"
+msgid " into \"%s"
msgstr " dans \"%c"
#, c-format
diff --git a/src/nvim/po/tr.po b/src/nvim/po/tr.po
index fae2fd4967..d534666bbb 100644
--- a/src/nvim/po/tr.po
+++ b/src/nvim/po/tr.po
@@ -4149,7 +4149,7 @@ msgid "E748: No previously used register"
msgstr "E748: Daha önce kullanılan bir yazmaç yok"
#, c-format
-msgid " into \"%c"
+msgid " into \"%s"
msgstr " \"%c"
#, c-format
diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po
index da87d50683..1b514ad7c3 100644
--- a/src/nvim/po/uk.po
+++ b/src/nvim/po/uk.po
@@ -4132,7 +4132,7 @@ msgid "E748: No previously used register"
msgstr "E748: Регістри перед цим не вживались"
#, c-format
-msgid " into \"%c"
+msgid " into \"%s"
msgstr " у \"%c"
#, c-format
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index a4afe97ac8..92db2db735 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -28,28 +28,29 @@
#include "nvim/popupmenu.h"
#include "nvim/search.h"
#include "nvim/strings.h"
+#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
#include "nvim/vim.h"
#include "nvim/window.h"
static pumitem_T *pum_array = NULL; // items of displayed pum
-static int pum_size; // nr of items in "pum_array"
-static int pum_selected; // index of selected item or -1
-static int pum_first = 0; // index of top item
-
-static int pum_height; // nr of displayed pum items
-static int pum_width; // width of displayed pum items
-static int pum_base_width; // width of pum items base
-static int pum_kind_width; // width of pum items kind column
-static int pum_extra_width; // width of extra stuff
-static int pum_scrollbar; // one when scrollbar present, else zero
-static bool pum_rl; // true when popupmenu is drawn 'rightleft'
-
-static int pum_anchor_grid; // grid where position is defined
-static int pum_row; // top row of pum
-static int pum_col; // left column of pum
-static bool pum_above; // pum is drawn above cursor line
+static int pum_size; // nr of items in "pum_array"
+static int pum_selected; // index of selected item or -1
+static int pum_first = 0; // index of top item
+
+static int pum_height; // nr of displayed pum items
+static int pum_width; // width of displayed pum items
+static int pum_base_width; // width of pum items base
+static int pum_kind_width; // width of pum items kind column
+static int pum_extra_width; // width of extra stuff
+static int pum_scrollbar; // one when scrollbar present, else zero
+static bool pum_rl; // true when popupmenu is drawn 'rightleft'
+
+static int pum_anchor_grid; // grid where position is defined
+static int pum_row; // top row of pum
+static int pum_col; // left column of pum
+static bool pum_above; // pum is drawn above cursor line
static bool pum_is_visible = false;
static bool pum_is_drawn = false;
@@ -60,6 +61,15 @@ static bool pum_invalid = false; // the screen was just cleared
# include "popupmenu.c.generated.h"
#endif
#define PUM_DEF_HEIGHT 10
+#define PUM_DEF_WIDTH 15
+
+static int str_dispnsize(char *s, int len);
+static int str_dispsize(char *s);
+
+// Forward declarations of needed syn functions.
+int syn_id2attr(int hl_id);
+char_u *syn_id2name(int id);
+int syn_name2id(char_u*);
static void pum_compute_size(void)
{
@@ -70,19 +80,19 @@ static void pum_compute_size(void)
for (int i = 0; i < pum_size; i++) {
int w;
if (pum_array[i].pum_text != NULL) {
- w = vim_strsize((char *)pum_array[i].pum_text);
+ w = str_dispsize((char*) pum_array[i].pum_text);
if (pum_base_width < w) {
pum_base_width = w;
}
}
if (pum_array[i].pum_kind != NULL) {
- w = vim_strsize((char *)pum_array[i].pum_kind) + 1;
+ w = str_dispsize((char*) pum_array[i].pum_kind) + 1;
if (pum_kind_width < w) {
pum_kind_width = w;
}
}
if (pum_array[i].pum_extra != NULL) {
- w = vim_strsize((char *)pum_array[i].pum_extra) + 1;
+ w = str_dispsize((char*) pum_array[i].pum_extra) + 1;
if (pum_extra_width < w) {
pum_extra_width = w;
}
@@ -178,7 +188,8 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
int def_width = (int)p_pw;
win_T *pvwin = NULL;
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab)
+ {
if (wp->w_p_pvw) {
pvwin = wp;
break;
@@ -286,8 +297,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
def_width = max_width;
}
- if ((((cursor_col < Columns - p_pw) || (cursor_col < Columns - max_width))
- && !pum_rl)
+ if ((((cursor_col < Columns - p_pw) || (cursor_col < Columns - max_width)) && !pum_rl)
|| (pum_rl && ((cursor_col > p_pw) || (cursor_col > max_width)))) {
// align pum with "cursor_col"
pum_col = cursor_col;
@@ -300,8 +310,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
pum_width = Columns - pum_col - pum_scrollbar;
}
- if ((pum_width > max_width + pum_kind_width + pum_extra_width + 1)
- && (pum_width > p_pw)) {
+ if ((pum_width > max_width + pum_kind_width + pum_extra_width + 1) && (pum_width > p_pw)) {
// the width is more than needed for the items, make it
// narrower
pum_width = max_width + pum_kind_width + pum_extra_width + 1;
@@ -310,8 +319,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
pum_width = (int)p_pw;
}
} else if (((cursor_col > p_pw || cursor_col > max_width) && !pum_rl)
- || (pum_rl && (cursor_col < Columns - p_pw
- || cursor_col < Columns - max_width))) {
+ || (pum_rl && (cursor_col < Columns - p_pw || cursor_col < Columns - max_width))) {
// align pum edge with "cursor_col"
if (pum_rl && W_ENDCOL(curwin) < max_width + pum_scrollbar + 1) {
pum_col = cursor_col + max_width + pum_scrollbar + 1;
@@ -319,8 +327,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
pum_col = Columns - 1;
}
} else if (!pum_rl) {
- if (curwin->w_wincol > Columns - max_width - pum_scrollbar
- && max_width <= p_pw) {
+ if (curwin->w_wincol > Columns - max_width - pum_scrollbar && max_width <= p_pw) {
// use full width to end of the screen
pum_col = Columns - max_width - pum_scrollbar;
if (pum_col < 0) {
@@ -460,8 +467,7 @@ void pum_redraw(void)
if (thumb_height == 0) {
thumb_height = 1;
}
- thumb_pos = (pum_first * (pum_height - thumb_height)
- + (pum_size - pum_height) / 2)
+ thumb_pos = (pum_first * (pum_height - thumb_height) + (pum_size - pum_height) / 2)
/ (pum_size - pum_height);
}
@@ -503,6 +509,7 @@ void pum_redraw(void)
break;
}
+ int cur_attr = attr;
if (p != NULL) {
for (;; MB_PTR_ADV(p)) {
if (s == NULL) {
@@ -510,7 +517,7 @@ void pum_redraw(void)
}
w = ptr2cells((char *)p);
- if ((*p == NUL) || (*p == TAB) || (totwidth + w > pum_width)) {
+ if ((*p == NUL) || (*p == TAB) || (*p == '%' && *(p + 1) == '#') || (totwidth + w > pum_width)) {
// Display the text that fits or comes before a Tab.
// First convert it to printable characters.
char_u *st;
@@ -525,9 +532,9 @@ void pum_redraw(void)
}
if (pum_rl) {
- char *rt = (char *)reverse_text(st);
+ char *rt = (char *) reverse_text(st);
char *rt_start = rt;
- int size = vim_strsize(rt);
+ int size = str_dispsize(rt);
if (size > pum_width) {
do {
@@ -550,28 +557,47 @@ void pum_redraw(void)
grid_col -= width;
} else {
// use grid_puts_len() to truncate the text
- grid_puts(&pum_grid, st, row, grid_col, attr);
+ grid_puts(&pum_grid, st, row, grid_col, cur_attr);
xfree(st);
grid_col += width;
}
- if (*p != TAB) {
- break;
- }
-
// Display two spaces for a Tab.
- if (pum_rl) {
- grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col - 1,
- attr);
- grid_col -= 2;
+ if (*p == TAB) {
+ if (pum_rl) {
+ grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col - 1, attr);
+ grid_col -= 2;
+ } else {
+ grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col, attr);
+ grid_col += 2;
+ }
+ totwidth += 2;
+ // start text at next char
+ s = NULL;
+ width = 0;
+ } else if (*p == '%' && *(p + 1) == '#') {
+ // Parse highlights. Highlights are status-line-like.
+ char_u hl_attr[128];
+ unsigned pi = 0;
+ p += 2;
+ while (*p != '#' && *p != '\0') {
+ if (pi < sizeof(hl_attr) - 1) {
+ hl_attr[pi++] = *p;
+ }
+ p ++;
+ }
+ hl_attr[pi] = 0;
+ if (*p == 0)
+ break;
+ cur_attr =
+ hl_combine_attr(attr, syn_id2attr(syn_name2id(hl_attr)));
+
+ // Start the next char.
+ s = NULL;
+ width = 0;
} else {
- grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col, attr);
- grid_col += 2;
+ break;
}
- totwidth += 2;
- // start text at next char
- s = NULL;
- width = 0;
} else {
width += w;
}
@@ -585,11 +611,8 @@ void pum_redraw(void)
}
// Stop when there is nothing more to display.
- if ((round == 3)
- || ((round == 2)
- && (pum_array[idx].pum_extra == NULL))
- || ((round == 1)
- && (pum_array[idx].pum_kind == NULL)
+ if ((round == 3) || ((round == 2) && (pum_array[idx].pum_extra == NULL))
+ || ((round == 1) && (pum_array[idx].pum_kind == NULL)
&& (pum_array[idx].pum_extra == NULL))
|| (pum_base_width + n >= pum_width)) {
break;
@@ -618,12 +641,10 @@ void pum_redraw(void)
if (pum_scrollbar > 0) {
if (pum_rl) {
grid_putchar(&pum_grid, ' ', row, col_off - pum_width,
- i >= thumb_pos && i < thumb_pos + thumb_height
- ? attr_thumb : attr_scroll);
+ i >= thumb_pos && i < thumb_pos + thumb_height ? attr_thumb : attr_scroll);
} else {
grid_putchar(&pum_grid, ' ', row, col_off + pum_width,
- i >= thumb_pos && i < thumb_pos + thumb_height
- ? attr_thumb : attr_scroll);
+ i >= thumb_pos && i < thumb_pos + thumb_height ? attr_thumb : attr_scroll);
}
}
grid_puts_line_flush(false);
@@ -936,8 +957,36 @@ void pum_set_event_info(dict_T *dict)
(void)tv_dict_add_float(dict, S_LEN("row"), r);
(void)tv_dict_add_float(dict, S_LEN("col"), c);
(void)tv_dict_add_nr(dict, S_LEN("size"), pum_size);
- (void)tv_dict_add_bool(dict, S_LEN("scrollbar"),
- pum_scrollbar ? kBoolVarTrue : kBoolVarFalse);
+ (void)tv_dict_add_bool(dict, S_LEN("scrollbar"), pum_scrollbar ? kBoolVarTrue : kBoolVarFalse);
+}
+
+static int str_dispnsize(char *s, int len)
+{
+ assert(s != NULL);
+ int size = 0;
+ int on = 1;
+
+ while (*s != NUL && --len >= 0) {
+ if (*s == '%' && *(s + 1) == '#') {
+ on = 0;
+ s += 2;
+ len -= 2;
+ } else {
+ int l = (*utfc_ptr2len)(s);
+ if (on)
+ size += ptr2cells(s);
+ s += l;
+ len -= l - 1;
+ if (*s == '#')
+ on = 1;
+ }
+ }
+ return size;
+}
+
+static int str_dispsize(char *s)
+{
+ return str_dispnsize(s, MAXCOL);
}
static void pum_position_at_mouse(int min_width)
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 2419a42a2a..b343b167f8 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -1760,6 +1760,7 @@ char *set_chars_option(win_T *wp, char_u **varp, bool set)
{ &wp->w_p_fcs_chars.diff, "diff", '-' },
{ &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' },
{ &wp->w_p_fcs_chars.eob, "eob", '~' },
+ { &wp->w_p_fcs_chars.colorcol, "colorcol", ' ' },
};
struct chars_tab lcs_tab[] = {
{ &wp->w_p_lcs_chars.eol, "eol", NUL },
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index daa8e99d31..0927473be0 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -2378,7 +2378,7 @@ static inline void add_search_pattern(PossiblyFreedShadaEntry *const ret_pse,
static inline void shada_initialize_registers(WriteMergerState *const wms, int max_reg_lines)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE
{
- const void *reg_iter = NULL;
+ iter_register_T reg_iter = ITER_REGISTER_NULL;
const bool limit_reg_lines = max_reg_lines >= 0;
do {
yankreg_T reg;
@@ -2409,7 +2409,7 @@ static inline void shada_initialize_registers(WriteMergerState *const wms, int m
}
}
};
- } while (reg_iter != NULL);
+ } while (reg_iter != ITER_REGISTER_NULL);
}
/// Replace numbered mark in WriteMergerState
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 564b5c4c51..2d995af00d 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -3071,6 +3071,11 @@ static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp)
wp = firstwin;
}
}
+ xfree(win->w_float_config.title);
+ xfree(win->w_float_config.title_hl);
+ win->w_float_config.title_hl = NULL;
+ win->w_float_config.title = NULL;
+ win->w_float_config.n_title = 0;
win_free(win, tp);
// When deleting the current window of another tab page select a new
diff --git a/src/nvim/yankmap.c b/src/nvim/yankmap.c
new file mode 100644
index 0000000000..d9229e015d
--- /dev/null
+++ b/src/nvim/yankmap.c
@@ -0,0 +1,42 @@
+#include "nvim/yankmap.h"
+
+#include "nvim/memory.h"
+
+void init_yankmap(yankmap_T* map)
+{
+ memset(map, 0, sizeof(yankmap_T));
+
+ map_init(int, ptr_t, &map->reg_to_yankreg);
+ map_init(ptr_t, int, &map->yankreg_to_reg);
+}
+
+yankreg_T* yankmap_get(yankmap_T* yankmap, int reg)
+{
+ yankreg_T** ret =
+ (yankreg_T**) map_ref(int, ptr_t)(&yankmap->reg_to_yankreg, reg, true);
+
+ if (ret) {
+ if (! (*ret)) {
+ *ret = xcalloc(sizeof(yankreg_T), 1);
+ }
+
+ /* Add the back-reference */
+ int* ref = map_ref(ptr_t, int)(&yankmap->yankreg_to_reg, *ret, true);
+ *ref = reg;
+
+ return *ret;
+ }
+
+ return NULL;
+}
+
+int yankmap_find(yankmap_T* yankmap, yankreg_T* yankreg)
+{
+ int* ref = map_ref(ptr_t, int)(&yankmap->yankreg_to_reg, yankreg, false);
+
+ if (ref) {
+ return *ref;
+ }
+
+ return -1;
+}
diff --git a/src/nvim/yankmap.h b/src/nvim/yankmap.h
new file mode 100644
index 0000000000..da7c4dcf13
--- /dev/null
+++ b/src/nvim/yankmap.h
@@ -0,0 +1,24 @@
+#ifndef YANK_TRIE_H_
+#define YANK_TRIE_H_
+
+#include <stdbool.h>
+#include "nvim/ops.h"
+#include "nvim/map.h"
+
+typedef struct {
+ /* Register name to yank register. */
+ Map(int, ptr_t) reg_to_yankreg;
+
+ /* Yank register to register name. */
+ Map(ptr_t, int) yankreg_to_reg;
+} yankmap_T;
+
+void init_yankmap(yankmap_T* yankmap);
+
+yankreg_T* yankmap_get(yankmap_T* yankmap, int index);
+
+yankreg_T* yankmap_put(yankmap_T* yankmap, int index);
+
+int yankmap_find(yankmap_T* yankmap, yankreg_T* yankreg);
+
+#endif
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index 4e3d62509c..c5f882a831 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -856,13 +856,14 @@ describe('CursorLine and CursorLineNr highlights', function()
|
]])
+ command('set fillchars+=colorcol:│')
command('set colorcolumn=3')
feed('i <esc>')
screen:expect([[
- {1:{} {7: } |
+ {1:{} {7:│} |
"{2:a}{7:"} : {3:abc} {3:// 10;} |
- {1:}} {7: } |
- {5: ^ }{7: }{5: }|
+ {1:}} {7:│} |
+ {5: ^ }{7:│}{5: }|
|
]])
end)
diff --git a/test/symbolic/klee/nvim/keymap.c b/test/symbolic/klee/nvim/keymap.c
index 1f7f0e0911..26cdd9db47 100644
--- a/test/symbolic/klee/nvim/keymap.c
+++ b/test/symbolic/klee/nvim/keymap.c
@@ -223,6 +223,33 @@ static const struct key_name_entry {
{ K_F35, "F35" },
{ K_F36, "F36" },
{ K_F37, "F37" },
+ { K_F38, "F38" },
+
+ { K_F39, "F39" },
+ { K_F40, "F40" },
+ { K_F41, "F41" },
+ { K_F42, "F42" },
+ { K_F43, "F43" },
+ { K_F44, "F44" },
+ { K_F45, "F45" },
+ { K_F46, "F46" },
+ { K_F47, "F47" },
+ { K_F48, "F48" },
+ { K_F49, "F49" },
+ { K_F50, "F50" },
+ { K_F51, "F51" },
+ { K_F52, "F52" },
+ { K_F53, "F53" },
+ { K_F54, "F54" },
+ { K_F55, "F55" },
+ { K_F56, "F56" },
+ { K_F57, "F57" },
+ { K_F58, "F58" },
+ { K_F59, "F59" },
+ { K_F60, "F60" },
+ { K_F61, "F61" },
+ { K_F62, "F62" },
+ { K_F63, "F63" },
{ K_XF1, "xF1" },
{ K_XF2, "xF2" },