diff options
-rw-r--r-- | runtime/doc/options.txt | 46 | ||||
-rw-r--r-- | src/nvim/api/keysets.lua | 2 | ||||
-rw-r--r-- | src/nvim/api/win_config.c | 103 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 19 | ||||
-rw-r--r-- | src/nvim/eval.c | 28 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 3 | ||||
-rw-r--r-- | src/nvim/generators/gen_options.lua | 20 | ||||
-rw-r--r-- | src/nvim/keycodes.c | 27 | ||||
-rw-r--r-- | src/nvim/keycodes.h | 27 | ||||
-rw-r--r-- | src/nvim/map.c | 2 | ||||
-rw-r--r-- | src/nvim/map.h | 2 | ||||
-rw-r--r-- | src/nvim/ops.c | 244 | ||||
-rw-r--r-- | src/nvim/ops.h | 13 | ||||
-rw-r--r-- | src/nvim/option.c | 6 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 2 | ||||
-rw-r--r-- | src/nvim/options.lua | 9 | ||||
-rw-r--r-- | src/nvim/popupmnu.c | 172 | ||||
-rw-r--r-- | src/nvim/screen.c | 53 | ||||
-rw-r--r-- | src/nvim/shada.c | 4 | ||||
-rw-r--r-- | src/nvim/window.c | 5 | ||||
-rw-r--r-- | src/nvim/yankmap.c | 42 | ||||
-rw-r--r-- | src/nvim/yankmap.h | 24 | ||||
-rw-r--r-- | test/functional/ui/highlight_spec.lua | 7 | ||||
-rw-r--r-- | test/symbolic/klee/nvim/keymap.c | 27 |
24 files changed, 758 insertions, 129 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 0f1c2051a6..eda596bf71 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1332,8 +1332,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'. > @@ -2457,6 +2459,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 '=' @@ -2494,6 +2497,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) @@ -6695,6 +6699,44 @@ 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. + + In case the action is 'put', the 'userregfun' function should return + the content to place in that location. + + 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/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 21319fb7a6..1f1fa1e63a 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -72,6 +72,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 969643eeef..d36c5bfb95 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 8c70765d30..4e890f7d10 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' @@ -1096,6 +1097,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; @@ -1113,6 +1120,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, \ @@ -1122,7 +1134,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(). @@ -1256,6 +1272,7 @@ struct window_S { int diff; int msgsep; int eob; + int colorcol; } w_p_fcs_chars; /* diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 096fcba981..16c3e72c5b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1607,7 +1607,7 @@ static 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); @@ -1913,10 +1913,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; @@ -1924,7 +1928,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(regname == '@' ? '"' : regname, kGRegExprSrc); if (s != NULL) { ptofree = (char *)concat_str((char_u *)s, (const char_u *)p); p = (const char *)ptofree; @@ -1932,9 +1936,10 @@ 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(regname == '@' ? '"' : regname, (const char_u *)p, (ssize_t)STRLEN(p), false); - arg_end = arg + 1; + arg_end = arg + mblen; } xfree(ptofree); } @@ -4180,13 +4185,14 @@ 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); } + // if (**arg != NUL) { + // ++*arg; + // } break; // nested expression: (expression). @@ -5285,7 +5291,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; @@ -5294,7 +5300,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 022e2497b7..7bed21e99b 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -4276,6 +4276,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/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 dd78ebe722..cd3c7316bf 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 f6c576f6ee..67ec092f60 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 21ab26898e..3602eb2a3e 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -56,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(®s->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 @@ -771,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. @@ -787,7 +821,8 @@ bool valid_yank_reg(int regname, bool writing) || regname == '-' || regname == '_' || regname == '*' - || regname == '+') { + || regname == '+' + || get_userreg(regname) != -1) { return true; } return false; @@ -830,7 +865,7 @@ yankreg_T *get_yank_register(int regname, int mode) if (i == -1) { i = 0; } - reg = &y_regs[i]; + reg = get_global_reg(i); if (mode == YREG_YANK) { // remember the written register for unnamed paste @@ -1332,7 +1367,96 @@ static void stuffescaped(const char *arg, bool literally) /// @param errmsg give error message when failing /// /// @return true if "regname" is a special register, -bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg) +bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg); + +/* + * Executes a call to the put() function on a user-defined register to get the + * contents of a user defined register. + */ +static int eval_urf_put(char_u *ufn, int regname, char_u **argp) +{ + char regname_str[5]; + int len; + + len = (*utf_char2len)(regname); + regname_str[len] = 0; + utf_char2bytes(regname, (char*) regname_str); + + typval_T args[3]; + args[0].v_type = VAR_STRING; + args[1].v_type = VAR_STRING; + args[2].v_type = VAR_UNKNOWN; + + args[0].vval.v_string = "put"; + args[1].vval.v_string = regname_str; + + *argp = (char_u *)call_func_retstr((char *)ufn, 3, args); + return *argp == NULL; +} + +/* + * 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; + + char *totalbuf; + size_t totallen = 0; + size_t i, j, k; + int ret, len; + char regname_str[5]; + + { + // Concat the contents of the register to pass into the yank() + // user-defined function. + for (i = 0; i < reg->y_size; ++i) { + totallen += strlen((char *)reg->y_array[i]) + 1; + } + totalbuf = xmalloc(sizeof(char_u) * totallen); + j = 0; + for (i = 0; i < reg->y_size; ++i) { + for (k = 0; reg->y_array[i][k] != 0; ++k, ++j) { + totalbuf[j] = reg->y_array[i][k]; + } + if (i < reg->y_size - 1) { + totalbuf[j++] = '\n'; + } + } + totalbuf[j++] = 0; + } + + 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_STRING; + args[3].v_type = VAR_UNKNOWN; + + args[0].vval.v_string = "yank"; + args[1].vval.v_string = regname_str; + args[2].vval.v_string = totalbuf; + + char *dup_ufn = strdup((char *)ufn); + ret = (int)call_func_retnr(dup_ufn, 3, args); + xfree(dup_ufn); + xfree(totalbuf); + return ret; +} + +// If "regname" is a special register, return true and store a pointer to its +// value in "argp". +bool get_spec_reg( + int regname, + char_u **argp, + bool *allocated, // return: true when value was allocated + bool errmsg // give error message when failing +) { size_t cnt; @@ -1410,6 +1534,15 @@ bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg) case '_': // black hole: always empty *argp = (char_u *)""; return true; + + default: /* User-defined registers. */ + 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; @@ -1456,14 +1589,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. @@ -1564,7 +1697,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; } @@ -2569,13 +2702,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)); } } @@ -2619,6 +2761,11 @@ 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) { + return eval_yank_userreg(curbuf->b_p_urf, oap->regname, reg) != -1; + } + set_clipboard(oap->regname, reg); do_autocmd_textyankpost(oap, reg); @@ -3071,7 +3218,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) if (insert_string != NULL) { y_type = kMTCharWise; - if (regname == '=') { + if (regname == '=' || get_userreg(regname) != -1) { /* For the = register we need to split the string at NL * characters. * Loop twice: count the number of lines and save them. */ @@ -3768,7 +3915,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. @@ -3807,10 +3954,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); @@ -5524,6 +5671,10 @@ static void finish_write_reg(int name, yankreg_T *reg, yankreg_T *old_y_previous // Send text of clipboard register to the clipboard. set_clipboard(name, reg); + if (get_userreg(name) != -1) { + eval_yank_userreg(curbuf->b_p_urf, name, reg); + } + // ':let @" = "val"' should change the meaning of the "" register if (name != '"') { y_previous = old_y_previous; @@ -6928,7 +7079,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; } @@ -6945,10 +7096,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; } @@ -7264,11 +7415,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`. @@ -7280,30 +7431,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 @@ -7311,8 +7463,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++; } } @@ -7332,11 +7484,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; } @@ -7352,7 +7504,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 @@ -7367,7 +7519,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 821c7208e3..cfd8248eb6 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3638,6 +3638,7 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set) struct chars_tab *tab; struct chars_tab fcs_tab[] = { + { &wp->w_p_fcs_chars.colorcol, "colorcol", NUL }, { &wp->w_p_fcs_chars.stl, "stl", ' ' }, { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' }, { &wp->w_p_fcs_chars.wbr, "wbr", ' ' }, @@ -6162,7 +6163,6 @@ static char_u *get_varp(vimoption_T *p) return (char_u *)&(curwin->w_p_cocu); case PV_COLE: return (char_u *)&(curwin->w_p_cole); - case PV_AI: return (char_u *)&(curbuf->b_p_ai); case PV_BIN: @@ -6203,6 +6203,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: @@ -6599,6 +6601,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 9e4a6a084c..37f3770506 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/popupmnu.c b/src/nvim/popupmnu.c index 841277f8f3..625fd15886 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -5,6 +5,8 @@ /// /// Popup menu (PUM) +#include "nvim/popupmnu.h" + #include <assert.h> #include <inttypes.h> #include <stdbool.h> @@ -16,6 +18,7 @@ #include "nvim/edit.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" +#include "nvim/highlight.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/menu.h" @@ -25,28 +28,29 @@ #include "nvim/screen.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; @@ -54,9 +58,18 @@ static bool pum_external = false; static bool pum_invalid = false; // the screen was just cleared #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "popupmnu.c.generated.h" +#include "popupmnu.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) { @@ -67,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; } @@ -175,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; @@ -283,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; @@ -297,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; @@ -307,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; @@ -316,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) { @@ -457,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); } @@ -500,6 +509,7 @@ void pum_redraw(void) break; } + int cur_attr = attr; if (p != NULL) { for (;; MB_PTR_ADV(p)) { if (s == NULL) { @@ -507,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; @@ -522,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 { @@ -547,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; } @@ -582,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; @@ -615,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); @@ -933,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 de837720c1..e99f9b9153 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -3949,6 +3949,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc col_attr = cuc_attr; } else if (draw_color_col && VCOL_HLC == *color_cols) { col_attr = mc_attr; + // Draw the colorcolumn character. + c = wp->w_p_fcs_chars.colorcol; + schar_from_char(linebuf_char[off], c); } col_attr = hl_combine_attr(col_attr, line_attr); @@ -5495,13 +5498,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/shada.c b/src/nvim/shada.c index 6e80b550d8..cd3b967a9f 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -2377,7 +2377,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; @@ -2408,7 +2408,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 06231150d5..2bffe2055f 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -3067,6 +3067,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 e065a727f3..946129b082 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -868,13 +868,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" }, |