diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/clint.py | 4 | ||||
-rw-r--r-- | src/nvim/autocmd.c | 7 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 2 | ||||
-rw-r--r-- | src/nvim/change.c | 2 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 5 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 144 | ||||
-rw-r--r-- | src/nvim/ex_session.c | 32 | ||||
-rw-r--r-- | src/nvim/getchar.c | 5 | ||||
-rw-r--r-- | src/nvim/main.c | 2 | ||||
-rw-r--r-- | src/nvim/message.c | 25 | ||||
-rw-r--r-- | src/nvim/ops.c | 2 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 4 | ||||
-rw-r--r-- | src/nvim/path.c | 2 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 1 | ||||
-rw-r--r-- | src/nvim/regexp.c | 6 | ||||
-rw-r--r-- | src/nvim/regexp_bt.c | 38 | ||||
-rw-r--r-- | src/nvim/regexp_nfa.c | 30 | ||||
-rw-r--r-- | src/nvim/screen.c | 11 | ||||
-rw-r--r-- | src/nvim/spellfile.c | 3 | ||||
-rw-r--r-- | src/nvim/state.c | 7 | ||||
-rw-r--r-- | src/nvim/testdir/test_filechanged.vim | 1 | ||||
-rw-r--r-- | src/nvim/testdir/test_messages.vim | 53 | ||||
-rw-r--r-- | src/nvim/testdir/test_mksession.vim | 43 | ||||
-rw-r--r-- | src/nvim/testdir/test_regexp_latin.vim | 97 | ||||
-rw-r--r-- | src/nvim/testdir/test_sort.vim | 1 | ||||
-rw-r--r-- | src/nvim/window.c | 4 |
26 files changed, 402 insertions, 129 deletions
diff --git a/src/clint.py b/src/clint.py index 10f33d22e2..75aaba176d 100755 --- a/src/clint.py +++ b/src/clint.py @@ -3187,8 +3187,8 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, r'|li_(?:next|prev|tv))\b', line) if match: error(filename, linenum, 'runtime/deprecated', 4, - 'Accessing list_T internals directly is prohibited ' - '(hint: see commit d46e37cb4c71)') + 'Accessing list_T internals directly is prohibited; ' + 'see https://github.com/neovim/neovim/wiki/List-management-in-Neovim') # Check for suspicious usage of "if" like # } if (a == b) { diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index a36f2c97b5..d4af05b961 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2022,6 +2022,11 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) verbose_leave_scroll(); } + // Make sure to set autocmd_nested before executing + // lua code, so that it works properly + autocmd_nested = ac->nested; + current_sctx = ac->script_ctx; + if (ac->exec.type == CALLABLE_CB) { typval_T argsin = TV_INITIAL_VALUE; typval_T rettv = TV_INITIAL_VALUE; @@ -2052,8 +2057,6 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) if (oneshot) { aucmd_del(ac); } - autocmd_nested = ac->nested; - current_sctx = ac->script_ctx; if (ac->last) { acp->nextcmd = NULL; } else { diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 29413281ad..08ca1a6247 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1211,6 +1211,8 @@ struct window_S { colnr_T w_old_visual_col; ///< last known start of visual part colnr_T w_old_curswant; ///< last known value of Curswant + linenr_T w_last_cursor_lnum_rnu; ///< cursor lnum when 'rnu' was last redrawn + // 'listchars' characters. Defaults set in set_chars_option(). struct { int eol; diff --git a/src/nvim/change.c b/src/nvim/change.c index 6c3dbf72e4..0644b1d601 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -287,7 +287,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra } // Relative numbering may require updating more. - if (wp->w_p_rnu) { + if (wp->w_p_rnu && xtra != 0) { redraw_later(wp, SOME_VALID); } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 41b419c150..ae9cb3b1e8 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -9466,6 +9466,11 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) res = ITEM_COMPARE_FAIL; } else { res = tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); + if (res > 0) { + res = 1; + } else if (res < 0) { + res = -1; + } } if (sortinfo->item_compare_func_err) { res = ITEM_COMPARE_FAIL; // return value has wrong type diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 84fca137d2..1d02c74c41 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -192,13 +192,12 @@ typedef struct command_line_state { typedef struct cmdline_info CmdlineInfo; -/* The current cmdline_info. It is initialized in getcmdline() and after that - * used by other functions. When invoking getcmdline() recursively it needs - * to be saved with save_cmdline() and restored with restore_cmdline(). - * TODO: make it local to getcmdline() and pass it around. */ +/// The current cmdline_info. It is initialized in getcmdline() and after that +/// used by other functions. When invoking getcmdline() recursively it needs +/// to be saved with save_cmdline() and restored with restore_cmdline(). static struct cmdline_info ccline; -static int cmd_showtail; // Only show path tail in lists ? +static int cmd_showtail; // Only show path tail in lists ? static int new_cmdpos; // position set by set_cmdline_pos() @@ -732,9 +731,10 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool /// Internal entry point for cmdline mode. /// -/// caller must use save_cmdline and restore_cmdline. Best is to use -/// getcmdline or getcmdline_prompt, instead of calling this directly. -static uint8_t *command_line_enter(int firstc, long count, int indent) +/// @param count only used for incremental search +/// @param indent indent for inside conditionals +/// @param init_ccline clear ccline first +static uint8_t *command_line_enter(int firstc, long count, int indent, bool init_ccline) { // can be invoked recursively, identify each level static int cmdline_level = 0; @@ -751,6 +751,20 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) CommandLineState *s = &state; s->save_p_icm = vim_strsave(p_icm); init_incsearch_state(&s->is_state); + CmdlineInfo save_ccline; + bool did_save_ccline = false; + + if (ccline.cmdbuff != NULL) { + // Currently ccline can never be in use if init_ccline is false. + // Some changes will be needed if this is no longer the case. + assert(init_ccline); + // Being called recursively. Since ccline is global, we need to save + // the current buffer and restore it when returning. + save_cmdline(&save_ccline); + did_save_ccline = true; + } else if (init_ccline) { + memset(&ccline, 0, sizeof(struct cmdline_info)); + } if (s->firstc == -1) { s->firstc = NUL; @@ -997,6 +1011,13 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) } cmdline_level--; + + if (did_save_ccline) { + restore_cmdline(&save_ccline); + } else { + ccline.cmdbuff = NULL; + } + return p; } @@ -1340,15 +1361,9 @@ static int command_line_execute(VimState *state, int key) s->c = get_expr_register(); if (s->c == '=') { - // Need to save and restore ccline. And set "textlock" - // to avoid nasty things like going to another buffer when - // evaluating an expression. - CmdlineInfo save_ccline; - save_cmdline(&save_ccline); textlock++; p = get_expr_line(); textlock--; - restore_cmdline(&save_ccline); if (p != NULL) { len = (int)STRLEN(p); @@ -1889,10 +1904,7 @@ static int command_line_handle_key(CommandLineState *s) beep_flush(); s->c = ESC; } else { - CmdlineInfo save_ccline; - save_cmdline(&save_ccline); s->c = get_expr_register(); - restore_cmdline(&save_ccline); } } @@ -2120,7 +2132,7 @@ static int command_line_handle_key(CommandLineState *s) int len = 0; int old_firstc; - xfree(ccline.cmdbuff); + XFREE_CLEAR(ccline.cmdbuff); s->xpc.xp_context = EXPAND_NOTHING; if (s->hiscnt == hislen) { p = s->lookfor; // back to the old one @@ -2403,14 +2415,7 @@ static void abandon_cmdline(void) /// @param indent indent for inside conditionals char_u *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_UNUSED) { - // Be prepared for situations where cmdline can be invoked recursively. - // That includes cmd mappings, event handlers, as well as update_screen() - // (custom status line eval), which all may invoke ":normal :". - CmdlineInfo save_ccline; - save_cmdline(&save_ccline); - char_u *retval = command_line_enter(firstc, count, indent); - restore_cmdline(&save_ccline); - return retval; + return command_line_enter(firstc, count, indent, true); } /// Get a command line with a prompt @@ -2434,8 +2439,14 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, const int a const int msg_col_save = msg_col; CmdlineInfo save_ccline; - save_cmdline(&save_ccline); - + bool did_save_ccline = false; + if (ccline.cmdbuff != NULL) { + // Save the values of the current cmdline and restore them below. + save_cmdline(&save_ccline); + did_save_ccline = true; + } else { + memset(&ccline, 0, sizeof(struct cmdline_info)); + } ccline.prompt_id = last_prompt_id++; ccline.cmdprompt = (char_u *)prompt; ccline.cmdattr = attr; @@ -2447,9 +2458,11 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, const int a int msg_silent_saved = msg_silent; msg_silent = 0; - char *const ret = (char *)command_line_enter(firstc, 1L, 0); + char *const ret = (char *)command_line_enter(firstc, 1L, 0, false); - restore_cmdline(&save_ccline); + if (did_save_ccline) { + restore_cmdline(&save_ccline); + } msg_silent = msg_silent_saved; // Restore msg_col, the prompt from input() may have changed it. // But only if called recursively and the commandline is therefore being @@ -2601,7 +2614,6 @@ bool cmdline_at_end(void) /* * Allocate a new command line buffer. * Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen. - * Returns the new value of ccline.cmdbuff and ccline.cmdbufflen. */ static void alloc_cmdbuff(int len) { @@ -3367,53 +3379,23 @@ void put_on_cmdline(char_u *str, int len, int redraw) } } -/* - * Save ccline, because obtaining the "=" register may execute "normal :cmd" - * and overwrite it. But get_cmdline_str() may need it, thus make it - * available globally in prev_ccline. - */ +/// Save ccline, because obtaining the "=" register may execute "normal :cmd" +/// and overwrite it. static void save_cmdline(struct cmdline_info *ccp) { *ccp = ccline; + memset(&ccline, 0, sizeof(struct cmdline_info)); ccline.prev_ccline = ccp; - ccline.cmdbuff = NULL; - ccline.cmdprompt = NULL; - ccline.xpc = NULL; - ccline.special_char = NUL; - ccline.level = 0; + ccline.cmdbuff = NULL; // signal that ccline is not in use } -/* - * Restore ccline after it has been saved with save_cmdline(). - */ +/// Restore ccline after it has been saved with save_cmdline(). static void restore_cmdline(struct cmdline_info *ccp) FUNC_ATTR_NONNULL_ALL { ccline = *ccp; } -/* - * Save the command line into allocated memory. Returns a pointer to be - * passed to restore_cmdline_alloc() later. - */ -char_u *save_cmdline_alloc(void) - FUNC_ATTR_NONNULL_RET -{ - struct cmdline_info *p = xmalloc(sizeof(struct cmdline_info)); - save_cmdline(p); - return (char_u *)p; -} - -/* - * Restore the command line from the return value of save_cmdline_alloc(). - */ -void restore_cmdline_alloc(char_u *p) - FUNC_ATTR_NONNULL_ALL -{ - restore_cmdline((struct cmdline_info *)p); - xfree(p); -} - /// Paste a yank register into the command line. /// Used by CTRL-R command in command-line mode. /// insert_reg() can't be used here, because special characters from the @@ -3429,7 +3411,6 @@ static bool cmdline_paste(int regname, bool literally, bool remcr) char_u *arg; char_u *p; bool allocated; - struct cmdline_info save_ccline; // check for valid regname; also accept special characters for CTRL-R in // the command line @@ -3447,13 +3428,11 @@ static bool cmdline_paste(int regname, bool literally, bool remcr) } - // Need to save and restore ccline. And set "textlock" to avoid nasty - // things like going to another buffer when evaluating an expression. - save_cmdline(&save_ccline); + // Need to set "textlock" to avoid nasty things like going to another + // buffer when evaluating an expression. textlock++; const bool i = get_spec_reg(regname, &arg, &allocated, true); textlock--; - restore_cmdline(&save_ccline); if (i) { // Got the value of a special register in "arg". @@ -5307,7 +5286,6 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T typval_T args[4]; char_u *pat = NULL; const sctx_T save_current_sctx = current_sctx; - struct cmdline_info save_ccline; if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) { return NULL; @@ -5329,15 +5307,10 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T args[1].vval.v_string = xp->xp_line; args[2].vval.v_number = xp->xp_col; - // Save the cmdline, we don't know what the function may do. - save_ccline = ccline; - ccline.cmdbuff = NULL; - ccline.cmdprompt = NULL; current_sctx = xp->xp_script_ctx; void *const ret = user_expand_func(xp->xp_arg, 3, args); - ccline = save_ccline; current_sctx = save_current_sctx; if (ccline.cmdbuff != NULL) { ccline.cmdbuff[ccline.cmdlen] = keep; @@ -5947,10 +5920,8 @@ int get_history_idx(int histype) } -/* - * Get pointer to the command line info to use. cmdline_paste() may clear - * ccline and put the previous value in prev_ccline. - */ +/// Get pointer to the command line info to use. save_cmdline() may clear +/// ccline and put the previous value in ccline.prev_ccline. static struct cmdline_info *get_ccline_ptr(void) { if ((State & CMDLINE) == 0) { @@ -6350,6 +6321,11 @@ int hist_type2char(int type) return NUL; } +void cmdline_init(void) +{ + memset(&ccline, 0, sizeof(struct cmdline_info)); +} + /// Open a window on the current command line and history. Allow editing in /// the window. Returns when the window is closed. /// Returns: @@ -6358,7 +6334,6 @@ int hist_type2char(int type) /// K_IGNORE if editing continues static int open_cmdwin(void) { - struct cmdline_info save_ccline; bufref_T old_curbuf; bufref_T bufref; win_T *old_curwin = curwin; @@ -6459,9 +6434,6 @@ static int open_cmdwin(void) } redraw_later(curwin, SOME_VALID); - // Save the command line info, can be used recursively. - save_cmdline(&save_ccline); - // No Ex mode here! exmode_active = false; @@ -6499,8 +6471,6 @@ static int open_cmdwin(void) // Restore KeyTyped in case it is modified by autocommands KeyTyped = save_KeyTyped; - // Restore the command line info. - restore_cmdline(&save_ccline); cmdwin_type = 0; cmdwin_level = 0; diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 39ff75d8c2..1235087500 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -589,12 +589,18 @@ static int makeopens(FILE *fd, char_u *dirnow) "if expand('%') == '' && !&modified && line('$') <= 1" " && getline(1) == ''\n" " let s:wipebuf = bufnr('%')\n" - "endif\n" - // Now save the current files, current buffer first. - "set shortmess=aoO\n") < 0) { + "endif\n") < 0) { return FAIL; } + // save 'shortmess' if not storing options + if ((ssop_flags & SSOP_OPTIONS) == 0) { + PUTLINE_FAIL("let s:shortmess_save = &shortmess"); + } + + // Now save the current files, current buffer first. + PUTLINE_FAIL("set shortmess=aoO"); + // Put all buffers into the buffer list. // Do it very early to preserve buffer order after loading session (which // can be disrupted by prior `edit` or `tabedit` calls). @@ -842,15 +848,21 @@ static int makeopens(FILE *fd, char_u *dirnow) return FAIL; } - // Re-apply 'winheight', 'winwidth' and 'shortmess'. - if (fprintf(fd, - "set winheight=%" PRId64 " winwidth=%" PRId64 - " shortmess=%s\n", - (int64_t)p_wh, - (int64_t)p_wiw, - p_shm) < 0) { + // Re-apply 'winheight' and 'winwidth'. + if (fprintf(fd, "set winheight=%" PRId64 " winwidth=%" PRId64 "\n", + (int64_t)p_wh, (int64_t)p_wiw) < 0) { return FAIL; } + + // Restore 'shortmess'. + if (ssop_flags & SSOP_OPTIONS) { + if (fprintf(fd, "set shortmess=%s\n", p_shm) < 0) { + return FAIL; + } + } else { + PUTLINE_FAIL("let &shortmess = s:shortmess_save"); + } + if (tab_firstwin != NULL && tab_firstwin->w_next != NULL) { // Restore 'winminheight' and 'winminwidth'. PUTLINE_FAIL("let &winminheight = s:save_winminheight"); diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index c10172cc52..b12b99b7ee 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -4001,7 +4001,6 @@ static char_u *eval_map_expr(mapblock_T *mp, int c) char_u *res; char_u *p = NULL; char_u *expr = NULL; - char_u *save_cmd; pos_T save_cursor; int save_msg_col; int save_msg_row; @@ -4013,8 +4012,6 @@ static char_u *eval_map_expr(mapblock_T *mp, int c) vim_unescape_ks(expr); } - save_cmd = save_cmdline_alloc(); - // Forbid changing text or using ":normal" to avoid most of the bad side // effects. Also restore the cursor position. textlock++; @@ -4045,8 +4042,6 @@ static char_u *eval_map_expr(mapblock_T *mp, int c) msg_col = save_msg_col; msg_row = save_msg_row; - restore_cmdline_alloc(save_cmd); - if (p == NULL) { return NULL; } diff --git a/src/nvim/main.c b/src/nvim/main.c index afb9313cba..6ea1cb0875 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -20,6 +20,7 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" +#include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" @@ -156,6 +157,7 @@ bool event_teardown(void) void early_init(mparm_T *paramp) { env_init(); + cmdline_init(); eval_init(); // init global variables init_path(argv0 ? argv0 : "nvim"); init_normal_cmds(); // Init the table of Normal mode commands. diff --git a/src/nvim/message.c b/src/nvim/message.c index b3fefbc0f4..6708001495 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -327,11 +327,12 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) } retval = msg_end(); - if (keep && retval && vim_strsize((char_u *)s) < (Rows - cmdline_row - 1) - * Columns + sc_col) { + if (keep && retval && vim_strsize((char_u *)s) < (Rows - cmdline_row - 1) * Columns + sc_col) { set_keep_msg((char *)s, 0); } + need_fileinfo = false; + xfree(buf); --entered; return retval; @@ -382,6 +383,13 @@ void trunc_string(char_u *s, char_u *buf, int room_in, int buflen) int i; int n; + if (*s == NUL) { + if (buflen > 0) { + *buf = NUL; + } + return; + } + if (room_in < 3) { room = 0; } @@ -1348,6 +1356,7 @@ void msg_start(void) if (!msg_silent) { XFREE_CLEAR(keep_msg); // don't display old message now + need_fileinfo = false; } if (need_clr_eos) { @@ -1486,6 +1495,10 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr) char_u *s; int mb_l; int c; + int save_got_int = got_int; + + // Only quit when got_int was set in here. + got_int = false; // if MSG_HIST flag set, add message to history if (attr & MSG_HIST) { @@ -1503,7 +1516,7 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr) * Go over the string. Special characters are translated and printed. * Normal characters are printed several at a time. */ - while (--len >= 0) { + while (--len >= 0 && !got_int) { // Don't include composing chars after the end. mb_l = utfc_ptr2len_len((char_u *)str, len + 1); if (mb_l > 1) { @@ -1542,11 +1555,13 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr) } } - if (str > plain_start) { + if (str > plain_start && !got_int) { // Print the printable chars at the end. msg_puts_attr_len(plain_start, str - plain_start, attr); } + got_int |= save_got_int; + return retval; } @@ -2013,6 +2028,8 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) if (!msg_use_printf() || (headless_mode && default_grid.chars)) { msg_puts_display((const char_u *)str, len, attr, false); } + + need_fileinfo = false; } /// Print a formatted message diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 4404ee7b80..dab6d237bf 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3893,7 +3893,7 @@ void ex_display(exarg_T *eap) msg_puts_attr("^J", attr); n -= 2; } - for (p = yb->y_array[j]; *p && (n -= ptr2cells(p)) >= 0; p++) { // -V1019 + for (p = yb->y_array[j]; *p != NUL && (n -= ptr2cells(p)) >= 0; p++) { // -V1019 clen = utfc_ptr2len(p); msg_outtrans_len(p, clen); p += clen - 1; diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index d88cd6b9b9..98c2b84770 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -523,11 +523,11 @@ EXTERN long p_mmd; // 'maxmapdepth' EXTERN long p_mmp; // 'maxmempattern' EXTERN long p_mis; // 'menuitems' EXTERN char_u *p_msm; // 'mkspellmem' -EXTERN long p_mle; // 'modelineexpr' +EXTERN int p_mle; // 'modelineexpr' EXTERN long p_mls; // 'modelines' EXTERN char_u *p_mouse; // 'mouse' EXTERN char_u *p_mousem; // 'mousemodel' -EXTERN long p_mousef; // 'mousefocus' +EXTERN int p_mousef; // 'mousefocus' EXTERN long p_mouset; // 'mousetime' EXTERN int p_more; // 'more' EXTERN char_u *p_opfunc; // 'operatorfunc' diff --git a/src/nvim/path.c b/src/nvim/path.c index d3aa5e5bf2..75624778e3 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1777,7 +1777,7 @@ int path_with_url(const char *fname) } // check body: alpha or dash - for (p = fname; (isalpha(*p) || (*p == '-')); p++) {} + for (p = fname + 1; (isalpha(*p) || (*p == '-')); p++) {} // check last char is not a dash if (p[-1] == '-') { diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 9ee7b1cc8a..c8d9e91fb5 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -2780,6 +2780,7 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int // present. if (qfl_type == QFLT_LOCATION) { win_T *wp = win_id2wp(prev_winid); + if (wp == NULL && curwin->w_llist != qi) { emsg(_("E924: Current window was closed")); *opened_window = false; diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 009a26d4e0..d3c43e530a 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -107,8 +107,10 @@ static char_u e_unmatchedpar[] = N_("E55: Unmatched %s)"); static char_u e_z_not_allowed[] = N_("E66: \\z( not allowed here"); static char_u e_z1_not_allowed[] = N_("E67: \\z1 - \\z9 not allowed here"); static char_u e_missing_sb[] = N_("E69: Missing ] after %s%%["); -static char_u e_empty_sb[] = N_("E70: Empty %s%%[]"); -static char_u e_recursive[] = N_("E956: Cannot use pattern recursively"); +static char_u e_empty_sb[] = N_("E70: Empty %s%%[]"); +static char_u e_recursive[] = N_("E956: Cannot use pattern recursively"); +static char_u e_regexp_number_after_dot_pos_search[] + = N_("E1204: No Number allowed after .: '\\%%%c'"); #define NOT_MULTI 0 #define MULTI_ONE 1 diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c index 7340957903..7d0b9a7a77 100644 --- a/src/nvim/regexp_bt.c +++ b/src/nvim/regexp_bt.c @@ -1578,14 +1578,19 @@ static char_u *regatom(int *flagp) } default: - if (ascii_isdigit(c) || c == '<' || c == '>' - || c == '\'') { + if (ascii_isdigit(c) || c == '<' || c == '>' || c == '\'' || c == '.') { uint32_t n = 0; int cmp; + bool cur = false; cmp = c; - if (cmp == '<' || cmp == '>') + if (cmp == '<' || cmp == '>') { c = getchr(); + } + if (no_Magic(c) == '.') { + cur = true; + c = getchr(); + } while (ascii_isdigit(c)) { n = n * 10 + (uint32_t)(c - '0'); c = getchr(); @@ -1602,14 +1607,31 @@ static char_u *regatom(int *flagp) } break; } else if (c == 'l' || c == 'c' || c == 'v') { + if (cur && n) { + semsg(_(e_regexp_number_after_dot_pos_search), no_Magic(c)); + rc_did_emsg = true; + return NULL; + } if (c == 'l') { + if (cur) { + n = curwin->w_cursor.lnum; + } ret = regnode(RE_LNUM); if (save_prev_at_start) { at_start = true; } } else if (c == 'c') { + if (cur) { + n = curwin->w_cursor.col; + n++; + } ret = regnode(RE_COL); } else { + if (cur) { + colnr_T vcol = 0; + getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol); + n = ++vcol; + } ret = regnode(RE_VCOL); } if (ret == JUST_CALC_SIZE) { @@ -3130,9 +3152,17 @@ static bool regmatch( { int mark = OPERAND(scan)[0]; int cmp = OPERAND(scan)[1]; - pos_T *pos; + pos_T *pos; + size_t col = REG_MULTI ? rex.input - rex.line : 0; pos = getmark_buf(rex.reg_buf, mark, false); + + // Line may have been freed, get it again. + if (REG_MULTI) { + rex.line = reg_getline(rex.lnum); + rex.input = rex.line + col; + } + if (pos == NULL // mark doesn't exist || pos->lnum <= 0) { // mark isn't set in reg_buf status = RA_NOMATCH; diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 7e316624f8..a8d6e9c40b 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -1639,10 +1639,20 @@ static int nfa_regatom(void) { int64_t n = 0; const int cmp = c; + bool cur = false; - if (c == '<' || c == '>') + if (c == '<' || c == '>') { c = getchr(); + } + if (no_Magic(c) == '.') { + cur = true; + c = getchr(); + } while (ascii_isdigit(c)) { + if (cur) { + semsg(_(e_regexp_number_after_dot_pos_search), no_Magic(c)); + return FAIL; + } if (n > (INT32_MAX - (c - '0')) / 10) { // overflow. emsg(_(e_value_too_large)); @@ -1655,6 +1665,9 @@ static int nfa_regatom(void) int32_t limit = INT32_MAX; if (c == 'l') { + if (cur) { + n = curwin->w_cursor.lnum; + } // \%{n}l \%{n}<l \%{n}>l EMIT(cmp == '<' ? NFA_LNUM_LT : cmp == '>' ? NFA_LNUM_GT : NFA_LNUM); @@ -1662,10 +1675,19 @@ static int nfa_regatom(void) at_start = true; } } else if (c == 'c') { + if (cur) { + n = curwin->w_cursor.col; + n++; + } // \%{n}c \%{n}<c \%{n}>c EMIT(cmp == '<' ? NFA_COL_LT : cmp == '>' ? NFA_COL_GT : NFA_COL); } else { + if (cur) { + colnr_T vcol = 0; + getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol); + n = ++vcol; + } // \%{n}v \%{n}<v \%{n}>v EMIT(cmp == '<' ? NFA_VCOL_LT : cmp == '>' ? NFA_VCOL_GT : NFA_VCOL); @@ -6227,8 +6249,10 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, case NFA_MARK_GT: case NFA_MARK_LT: { - size_t col = rex.input - rex.line; - pos_T *pos = getmark_buf(rex.reg_buf, t->state->val, false); + pos_T *pos; + size_t col = REG_MULTI ? rex.input - rex.line : 0; + + pos = getmark_buf(rex.reg_buf, t->state->val, false); // Line may have been freed, get it again. if (REG_MULTI) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 38ea861cbd..f922a50260 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1573,9 +1573,9 @@ static void win_update(win_T *wp, DecorProviders *providers) idx++; lnum += foldinfo.fi_lines + 1; } else { - if (wp->w_p_rnu) { - // 'relativenumber' set: The text doesn't need to be drawn, but - // the number column nearly always does. + if (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum) { + // 'relativenumber' set and cursor moved vertically: The + // text doesn't need to be drawn, but the number column does. foldinfo_T info = fold_info(wp, lnum); (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true, info, &line_providers); @@ -1603,6 +1603,8 @@ static void win_update(win_T *wp, DecorProviders *providers) // update w_last_cursorline. wp->w_last_cursorline = cursorline_standout ? wp->w_cursor.lnum : 0; + wp->w_last_cursor_lnum_rnu = wp->w_p_rnu ? wp->w_cursor.lnum : 0; + if (idx > wp->w_lines_valid) { wp->w_lines_valid = idx; } @@ -4329,6 +4331,9 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, break; } } + if (!*s.p) { + continue; + } int attr; bool through = false; if (hl_mode == kHlModeCombine) { diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index d7b220b3f6..07058b34fd 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -5580,6 +5580,9 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo while (!vim_fgets(line, MAXWLEN * 2, fd)) { fpos = fpos_next; fpos_next = ftell(fd); + if (fpos_next < 0) { + break; // should never happen + } if (STRNCMP(word, line, len) == 0 && (line[len] == '/' || line[len] < ' ')) { // Found duplicate word. Remove it by writing a '#' at diff --git a/src/nvim/state.c b/src/nvim/state.c index f9a3aaab7f..3a7636085b 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -15,6 +15,7 @@ #include "nvim/option.h" #include "nvim/option_defs.h" #include "nvim/os/input.h" +#include "nvim/screen.h" #include "nvim/state.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -54,6 +55,12 @@ getkey: // Event was made available after the last multiqueue_process_events call key = K_EVENT; } else { + // Duplicate display updating logic in vgetorpeek() + if (((State & INSERT) != 0 || p_lz) && (State & CMDLINE) == 0 + && must_redraw != 0 && !need_wait_return) { + update_screen(0); + setcursor(); // put cursor back where it belongs + } // Flush screen updates before blocking ui_flush(); // Call `os_inchar` directly to block for events or user input without diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim index 06ccd6e85f..8e1cb2c3ee 100644 --- a/src/nvim/testdir/test_filechanged.vim +++ b/src/nvim/testdir/test_filechanged.vim @@ -142,6 +142,7 @@ endfunc func Test_FileChangedShell_edit_dialog() throw 'Skipped: requires a UI to be active' CheckNotGui + CheckUnix " Using low level feedkeys() does not work on MS-Windows. new Xchanged_r call setline(1, 'reload this') diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 9c84d77dd2..82c4cc128b 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -2,6 +2,9 @@ source check.vim source shared.vim +source term_util.vim +source view_util.vim +source screendump.vim func Test_messages() let oldmore = &more @@ -109,6 +112,22 @@ func Test_echospace() set ruler& showcmd& endfunc +func Test_quit_long_message() + CheckScreendump + + let content =<< trim END + echom range(9999)->join("\x01") + END + call writefile(content, 'Xtest_quit_message') + let buf = RunVimInTerminal('-S Xtest_quit_message', #{rows: 6}) + call term_sendkeys(buf, "q") + call VerifyScreenDump(buf, 'Test_quit_long_message', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_quit_message') +endfunc + " this was missing a terminating NUL func Test_echo_string_partial() function CountSpaces() @@ -116,3 +135,37 @@ func Test_echo_string_partial() call assert_equal("function('CountSpaces', [{'ccccccccccc': ['ab', 'cd'], 'aaaaaaaaaaa': v:false, 'bbbbbbbbbbbb': ''}])", string(function('CountSpaces', [#{aaaaaaaaaaa: v:false, bbbbbbbbbbbb: '', ccccccccccc: ['ab', 'cd']}]))) endfunc +" Message output was previously overwritten by the fileinfo display, shown +" when switching buffers. If a buffer is switched to, then a message if +" echoed, we should show the message, rather than overwriting it with +" fileinfo. +func Test_fileinfo_after_echo() + CheckScreendump + + let content =<< trim END + file a.txt + + hide edit b.txt + call setline(1, "hi") + setlocal modified + + hide buffer a.txt + + autocmd CursorHold * buf b.txt | w | echo "'b' written" + END + + call writefile(content, 'Xtest_fileinfo_after_echo') + let buf = RunVimInTerminal('-S Xtest_fileinfo_after_echo', #{rows: 6}) + call term_sendkeys(buf, ":set updatetime=50\<CR>") + call term_sendkeys(buf, "0$") + call VerifyScreenDump(buf, 'Test_fileinfo_after_echo', {}) + + call term_sendkeys(buf, ":q\<CR>") + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_fileinfo_after_echo') + call delete('b.txt') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index 798cb9e54f..5dbe2cd366 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -795,6 +795,49 @@ func Test_mksession_winminheight() set sessionoptions& endfunc +" Test for mksession with and without options restores shortmess +func Test_mksession_shortmess() + " Without options + set sessionoptions-=options + split + mksession! Xtest_mks.out + let found_save = 0 + let found_restore = 0 + let lines = readfile('Xtest_mks.out') + for line in lines + let line = trim(line) + + if line ==# 'let s:shortmess_save = &shortmess' + let found_save += 1 + endif + + if found_save !=# 0 && line ==# 'let &shortmess = s:shortmess_save' + let found_restore += 1 + endif + endfor + call assert_equal(1, found_save) + call assert_equal(1, found_restore) + call delete('Xtest_mks.out') + close + set sessionoptions& + + " With options + set sessionoptions+=options + split + mksession! Xtest_mks.out + let found_restore = 0 + let lines = readfile('Xtest_mks.out') + for line in lines + if line =~# 's:shortmess_save' + let found_restore += 1 + endif + endfor + call assert_equal(0, found_restore) + call delete('Xtest_mks.out') + close + set sessionoptions& +endfunc + " Test for mksession with 'compatible' option func Test_mksession_compatible() throw 'skipped: Nvim does not support "compatible" option' diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index a0f5ebfb9f..b733e334de 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -787,12 +787,109 @@ func Test_regexp_error() set re& endfunc +" Check patterns matching cursor position. +func s:curpos_test2() + new + call setline(1, ['1', '2 foobar eins zwei drei vier fünf sechse', + \ '3 foobar eins zwei drei vier fünf sechse', + \ '4 foobar eins zwei drei vier fünf sechse', + \ '5 foobar eins zwei drei vier fünf sechse', + \ '6 foobar eins zwei drei vier fünf sechse', + \ '7 foobar eins zwei drei vier fünf sechse']) + call setpos('.', [0, 2, 10, 0]) + s/\%.c.*//g + call setpos('.', [0, 3, 15, 0]) + s/\%.l.*//g + call setpos('.', [0, 5, 3, 0]) + s/\%.v.*/_/g + call assert_equal(['1', + \ '2 foobar ', + \ '', + \ '4 foobar eins zwei drei vier fünf sechse', + \ '5 _', + \ '6 foobar eins zwei drei vier fünf sechse', + \ '7 foobar eins zwei drei vier fünf sechse'], + \ getline(1, '$')) + call assert_fails('call search("\\%.1l")', 'E1204:') + call assert_fails('call search("\\%.1c")', 'E1204:') + call assert_fails('call search("\\%.1v")', 'E1204:') + bwipe! +endfunc + +" Check patterns matching before or after cursor position. +func s:curpos_test3() + new + call setline(1, ['1', '2 foobar eins zwei drei vier fünf sechse', + \ '3 foobar eins zwei drei vier fünf sechse', + \ '4 foobar eins zwei drei vier fünf sechse', + \ '5 foobar eins zwei drei vier fünf sechse', + \ '6 foobar eins zwei drei vier fünf sechse', + \ '7 foobar eins zwei drei vier fünf sechse']) + call setpos('.', [0, 2, 10, 0]) + " Note: This removes all columns, except for the column directly in front of + " the cursor. Bug???? + :s/^.*\%<.c// + call setpos('.', [0, 3, 10, 0]) + :s/\%>.c.*$// + call setpos('.', [0, 5, 4, 0]) + " Note: This removes all columns, except for the column directly in front of + " the cursor. Bug???? + :s/^.*\%<.v/_/ + call setpos('.', [0, 6, 4, 0]) + :s/\%>.v.*$/_/ + call assert_equal(['1', + \ ' eins zwei drei vier fünf sechse', + \ '3 foobar e', + \ '4 foobar eins zwei drei vier fünf sechse', + \ '_foobar eins zwei drei vier fünf sechse', + \ '6 fo_', + \ '7 foobar eins zwei drei vier fünf sechse'], + \ getline(1, '$')) + sil %d + call setline(1, ['1', '2 foobar eins zwei drei vier fünf sechse', + \ '3 foobar eins zwei drei vier fünf sechse', + \ '4 foobar eins zwei drei vier fünf sechse', + \ '5 foobar eins zwei drei vier fünf sechse', + \ '6 foobar eins zwei drei vier fünf sechse', + \ '7 foobar eins zwei drei vier fünf sechse']) + call setpos('.', [0, 4, 4, 0]) + %s/\%<.l.*// + call setpos('.', [0, 5, 4, 0]) + %s/\%>.l.*// + call assert_equal(['', '', '', + \ '4 foobar eins zwei drei vier fünf sechse', + \ '5 foobar eins zwei drei vier fünf sechse', + \ '', ''], + \ getline(1, '$')) + bwipe! +endfunc + +" Test that matching below, at or after the +" cursor position work +func Test_matching_pos() + for val in range(3) + exe "set re=" .. val + " Match at cursor position + call s:curpos_test2() + " Match before or after cursor position + call s:curpos_test3() + endfor + set re& +endfunc + func Test_using_mark_position() " this was using freed memory + " new engine new norm O0 call assert_fails("s/\\%')", 'E486:') bwipe! + + " old engine + new + norm O0 + call assert_fails("s/\\%#=1\\%')", 'E486:') + bwipe! endfunc func Test_using_visual_position() diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim index f72368fc59..5d7dd7bfff 100644 --- a/src/nvim/testdir/test_sort.vim +++ b/src/nvim/testdir/test_sort.vim @@ -59,6 +59,7 @@ endfunc func Test_sort_numbers() call assert_equal([3, 13, 28], sort([13, 28, 3], 'N')) call assert_equal(['3', '13', '28'], sort(['13', '28', '3'], 'N')) + call assert_equal([3997, 4996], sort([4996, 3997], 'Compare1')) endfunc func Test_sort_float() diff --git a/src/nvim/window.c b/src/nvim/window.c index 6bd2ef3ef6..632e9b3f44 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5857,11 +5857,11 @@ void win_drag_status_line(win_T *dragwin, int offset) } else { // drag down up = false; // Only dragging the last status line can reduce p_ch. - room = Rows - cmdline_row - global_stl_height(); + room = Rows - cmdline_row; if (curfr->fr_next == NULL) { room -= 1; } else { - room -= p_ch; + room -= p_ch + global_stl_height(); } if (room < 0) { room = 0; |