aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/clint.py4
-rw-r--r--src/nvim/autocmd.c7
-rw-r--r--src/nvim/buffer_defs.h2
-rw-r--r--src/nvim/change.c2
-rw-r--r--src/nvim/eval/funcs.c5
-rw-r--r--src/nvim/ex_getln.c144
-rw-r--r--src/nvim/ex_session.c32
-rw-r--r--src/nvim/getchar.c5
-rw-r--r--src/nvim/main.c2
-rw-r--r--src/nvim/message.c25
-rw-r--r--src/nvim/ops.c2
-rw-r--r--src/nvim/option_defs.h4
-rw-r--r--src/nvim/path.c2
-rw-r--r--src/nvim/quickfix.c1
-rw-r--r--src/nvim/regexp.c6
-rw-r--r--src/nvim/regexp_bt.c38
-rw-r--r--src/nvim/regexp_nfa.c30
-rw-r--r--src/nvim/screen.c11
-rw-r--r--src/nvim/spellfile.c3
-rw-r--r--src/nvim/state.c7
-rw-r--r--src/nvim/testdir/test_filechanged.vim1
-rw-r--r--src/nvim/testdir/test_messages.vim53
-rw-r--r--src/nvim/testdir/test_mksession.vim43
-rw-r--r--src/nvim/testdir/test_regexp_latin.vim97
-rw-r--r--src/nvim/testdir/test_sort.vim1
-rw-r--r--src/nvim/window.c4
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;