aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/private/converter.c1
-rw-r--r--src/nvim/api/vim.c5
-rw-r--r--src/nvim/buffer.c16
-rw-r--r--src/nvim/buffer_defs.h4
-rw-r--r--src/nvim/change.c2
-rw-r--r--src/nvim/channel.h2
-rw-r--r--src/nvim/cursor.c8
-rw-r--r--src/nvim/edit.c21
-rw-r--r--src/nvim/eval.c10
-rw-r--r--src/nvim/eval/funcs.c4
-rw-r--r--src/nvim/eval/typval.h3
-rw-r--r--src/nvim/ex_cmds.c3
-rw-r--r--src/nvim/ex_cmds.lua6
-rw-r--r--src/nvim/ex_cmds2.c8
-rw-r--r--src/nvim/ex_docmd.c12
-rw-r--r--src/nvim/ex_getln.c2
-rw-r--r--src/nvim/file_search.c6
-rw-r--r--src/nvim/globals.h2
-rw-r--r--src/nvim/lua/converter.c1
-rw-r--r--src/nvim/lua/executor.c10
-rw-r--r--src/nvim/marktree.h1
-rw-r--r--src/nvim/message.c11
-rw-r--r--src/nvim/normal.c59
-rw-r--r--src/nvim/ops.c34
-rw-r--r--src/nvim/option.c76
-rw-r--r--src/nvim/option.h20
-rw-r--r--src/nvim/option_defs.h13
-rw-r--r--src/nvim/options.lua2
-rw-r--r--src/nvim/path.c28
-rw-r--r--src/nvim/screen.c90
-rw-r--r--src/nvim/state.c9
-rw-r--r--src/nvim/strings.c28
-rw-r--r--src/nvim/testdir/check.vim6
-rw-r--r--src/nvim/testdir/test_buffer.vim31
-rw-r--r--src/nvim/testdir/test_conceal.vim4
-rw-r--r--src/nvim/testdir/test_edit.vim23
-rw-r--r--src/nvim/testdir/test_ex_mode.vim10
-rw-r--r--src/nvim/testdir/test_expr.vim65
-rw-r--r--src/nvim/testdir/test_filetype.vim21
-rw-r--r--src/nvim/testdir/test_filter_map.vim10
-rw-r--r--src/nvim/testdir/test_ins_complete.vim22
-rw-r--r--src/nvim/testdir/test_listchars.vim32
-rw-r--r--src/nvim/testdir/test_listlbr_utf8.vim10
-rw-r--r--src/nvim/testdir/test_normal.vim12
-rw-r--r--src/nvim/testdir/test_options.vim11
-rw-r--r--src/nvim/testdir/test_scriptnames.vim6
-rw-r--r--src/nvim/testdir/test_selectmode.vim57
-rw-r--r--src/nvim/testdir/test_statusline.vim9
-rw-r--r--src/nvim/testdir/test_timers.vim23
-rw-r--r--src/nvim/testdir/test_virtualedit.vim135
-rw-r--r--src/nvim/testdir/test_visual.vim66
-rw-r--r--src/nvim/undo.c4
52 files changed, 891 insertions, 163 deletions
diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c
index e370c0d4d4..3d4ff202fe 100644
--- a/src/nvim/api/private/converter.c
+++ b/src/nvim/api/private/converter.c
@@ -233,6 +233,7 @@ Object vim_to_object(typval_T *obj)
{
if (obj->v_type == VAR_FUNC) {
ufunc_T *fp = find_func(obj->vval.v_string);
+ assert(fp != NULL);
if (fp->uf_cb == nlua_CFunction_func_call) {
LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref);
return LUAREF_OBJ(ref);
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index cc622a00dc..7c194935ce 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -2283,6 +2283,11 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
fillchar = ' ';
} else {
wp = find_window_by_handle(window, err);
+
+ if (wp == NULL) {
+ api_set_error(err, kErrorTypeException, "unknown winid %d", window);
+ return result;
+ }
ewp = wp;
if (fillchar == 0) {
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index abd22fba26..eee5a0b46c 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1454,7 +1454,10 @@ void set_curbuf(buf_T *buf, int action)
}
if (bufref_valid(&prevbufref) && !aborting()) {
win_T *previouswin = curwin;
- if (prevbuf == curbuf) {
+ // Do not sync when in Insert mode and the buffer is open in
+ // another window, might be a timer doing something in another
+ // window.
+ if (prevbuf == curbuf && ((State & INSERT) == 0 || curbuf->b_nwindows <= 1)) {
u_sync(false);
}
close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL,
@@ -4001,14 +4004,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
case STL_VIRTCOL:
case STL_VIRTCOL_ALT: {
- // In list mode virtcol needs to be recomputed
- colnr_T virtcol = wp->w_virtcol;
- if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) {
- wp->w_p_list = false;
- getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
- wp->w_p_list = true;
- }
- virtcol++;
+ colnr_T virtcol = wp->w_virtcol + 1;
// Don't display %V if it's the same as %c.
if (opt == STL_VIRTCOL_ALT
&& (virtcol == (colnr_T)(!(State & INSERT) && empty_line
@@ -4351,7 +4347,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
// Only free the string buffer if we allocated it.
// Note: This is not needed if `str` is pointing at `tmp`
if (opt == STL_VIM_EXPR) {
- xfree(str);
+ XFREE_CLEAR(str);
}
if (num >= 0 || (!itemisflag && str && *str)) {
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index bba53b415a..7b17c5b506 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -204,6 +204,10 @@ typedef struct {
#define w_p_nu w_onebuf_opt.wo_nu // 'number'
int wo_rnu;
#define w_p_rnu w_onebuf_opt.wo_rnu // 'relativenumber'
+ char_u *wo_ve;
+#define w_p_ve w_onebuf_opt.wo_ve // 'virtualedit'
+ unsigned wo_ve_flags;
+#define w_ve_flags w_onebuf_opt.wo_ve_flags // flags for 'virtualedit'
long wo_nuw;
#define w_p_nuw w_onebuf_opt.wo_nuw // 'numberwidth'
int wo_wfh;
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 1dbbfff024..0c16b204e3 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -789,7 +789,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
// fixpos is true, we don't want to end up positioned at the NUL,
// unless "restart_edit" is set or 'virtualedit' contains "onemore".
if (col > 0 && fixpos && restart_edit == 0
- && (ve_flags & VE_ONEMORE) == 0) {
+ && (get_ve_flags() & VE_ONEMORE) == 0) {
curwin->w_cursor.col--;
curwin->w_cursor.coladd = 0;
curwin->w_cursor.col -= utf_head_off(oldp, oldp + curwin->w_cursor.col);
diff --git a/src/nvim/channel.h b/src/nvim/channel.h
index 81b75e2d31..50d6b3600a 100644
--- a/src/nvim/channel.h
+++ b/src/nvim/channel.h
@@ -96,6 +96,8 @@ struct Channel {
EXTERN PMap(uint64_t) channels INIT(= MAP_INIT);
+EXTERN Callback on_print INIT(= CALLBACK_INIT);
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "channel.h.generated.h"
#endif
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 6e2c6232d7..55f55a46b2 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -15,6 +15,7 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/move.h"
+#include "nvim/option.h"
#include "nvim/plines.h"
#include "nvim/screen.h"
#include "nvim/state.h"
@@ -110,7 +111,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
|| (State & TERM_FOCUS)
|| restart_edit != NUL
|| (VIsual_active && *p_sel != 'o')
- || ((ve_flags & VE_ONEMORE) && wcol < MAXCOL);
+ || ((get_ve_flags() & VE_ONEMORE) && wcol < MAXCOL);
line = ml_get_buf(curbuf, pos->lnum, false);
if (wcol >= MAXCOL) {
@@ -366,6 +367,7 @@ void check_cursor_col_win(win_T *win)
colnr_T len;
colnr_T oldcol = win->w_cursor.col;
colnr_T oldcoladd = win->w_cursor.col + win->w_cursor.coladd;
+ unsigned int cur_ve_flags = get_ve_flags();
len = (colnr_T)STRLEN(ml_get_buf(win->w_buffer, win->w_cursor.lnum, false));
if (len == 0) {
@@ -377,7 +379,7 @@ void check_cursor_col_win(win_T *win)
* - 'virtualedit' is set */
if ((State & INSERT) || restart_edit
|| (VIsual_active && *p_sel != 'o')
- || (ve_flags & VE_ONEMORE)
+ || (cur_ve_flags & VE_ONEMORE)
|| virtual_active()) {
win->w_cursor.col = len;
} else {
@@ -394,7 +396,7 @@ void check_cursor_col_win(win_T *win)
// line.
if (oldcol == MAXCOL) {
win->w_cursor.coladd = 0;
- } else if (ve_flags == VE_ALL) {
+ } else if (cur_ve_flags == VE_ALL) {
if (oldcoladd > win->w_cursor.col) {
win->w_cursor.coladd = oldcoladd - win->w_cursor.col;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index a9c606ec13..b7656230dd 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -663,8 +663,12 @@ static int insert_execute(VimState *state, int key)
InsertState *const s = (InsertState *)state;
if (stop_insert_mode) {
// Insert mode ended, possibly from a callback.
+ if (key != K_IGNORE && key != K_NOP) {
+ vungetc(key);
+ }
s->count = 0;
s->nomove = true;
+ ins_compl_prep(ESC);
return 0;
}
@@ -909,7 +913,7 @@ static int insert_handle_key(InsertState *s)
ins_ctrl_o();
// don't move the cursor left when 'virtualedit' has "onemore".
- if (ve_flags & VE_ONEMORE) {
+ if (get_ve_flags() & VE_ONEMORE) {
ins_at_eol = false;
s->nomove = true;
}
@@ -5631,8 +5635,12 @@ int get_literal(void)
i = 0;
for (;;) {
nc = plain_vgetc();
- if (!(State & CMDLINE)
- && MB_BYTE2LEN_CHECK(nc) == 1) {
+ if ((mod_mask & ~MOD_MASK_SHIFT) != 0) {
+ // A character with non-Shift modifiers should not be a valid
+ // character for i_CTRL-V_digit.
+ break;
+ }
+ if (!(State & CMDLINE) && MB_BYTE2LEN_CHECK(nc) == 1) {
add_to_showcmd(nc);
}
if (nc == 'x' || nc == 'X') {
@@ -5698,6 +5706,8 @@ int get_literal(void)
--no_mapping;
if (nc) {
vungetc(nc);
+ // A character typed with i_CTRL-V_digit cannot have modifiers.
+ mod_mask = 0;
}
got_int = false; // CTRL-C typed after CTRL-V is not an interrupt
return cc;
@@ -6905,8 +6915,7 @@ int oneright(void)
// move "l" bytes right, but don't end up on the NUL, unless 'virtualedit'
// contains "onemore".
- if (ptr[l] == NUL
- && (ve_flags & VE_ONEMORE) == 0) {
+ if (ptr[l] == NUL && (get_ve_flags() & VE_ONEMORE) == 0) {
return FAIL;
}
curwin->w_cursor.col += l;
@@ -8028,7 +8037,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
&& !VIsual_active
))
&& !revins_on) {
- if (curwin->w_cursor.coladd > 0 || ve_flags == VE_ALL) {
+ if (curwin->w_cursor.coladd > 0 || get_ve_flags() == VE_ALL) {
oneleft();
if (restart_edit != NUL) {
curwin->w_cursor.coladd++;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 6dbdc09c3b..d25903c12a 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -6468,6 +6468,10 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
if (argvars[0].v_type == VAR_DICT) {
vimvars[VV_KEY].vv_type = VAR_STRING;
+ const VarLockStatus prev_lock = d->dv_lock;
+ if (map && d->dv_lock == VAR_UNLOCKED) {
+ d->dv_lock = VAR_LOCKED;
+ }
ht = &d->dv_hashtab;
hash_lock(ht);
todo = (int)ht->ht_used;
@@ -6498,6 +6502,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
}
}
hash_unlock(ht);
+ d->dv_lock = prev_lock;
} else if (argvars[0].v_type == VAR_BLOB) {
vimvars[VV_KEY].vv_type = VAR_NUMBER;
@@ -6530,6 +6535,10 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
assert(argvars[0].v_type == VAR_LIST);
vimvars[VV_KEY].vv_type = VAR_NUMBER;
+ const VarLockStatus prev_lock = tv_list_locked(l);
+ if (map && tv_list_locked(l) == VAR_UNLOCKED) {
+ tv_list_set_lock(l, VAR_LOCKED);
+ }
for (listitem_T *li = tv_list_first(l); li != NULL;) {
if (map
&& var_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg,
@@ -6548,6 +6557,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
}
idx++;
}
+ tv_list_set_lock(l, prev_lock);
}
restore_vimvar(VV_KEY, &save_key);
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 7701688b49..4a07f6a850 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -10215,6 +10215,10 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (!tv_dict_get_callback(opts, S_LEN("on_stdin"), &on_stdin.cb)) {
return;
}
+ if (!tv_dict_get_callback(opts, S_LEN("on_print"), &on_print)) {
+ return;
+ }
+
on_stdin.buffered = tv_dict_get_number(opts, "stdin_buffered");
if (on_stdin.buffered && on_stdin.cb.type == kCallbackNone) {
on_stdin.self = opts;
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index d1275d6512..ad01c01499 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -81,7 +81,8 @@ typedef struct {
} data;
CallbackType type;
} Callback;
-#define CALLBACK_NONE ((Callback){ .type = kCallbackNone })
+#define CALLBACK_INIT { .type = kCallbackNone }
+#define CALLBACK_NONE ((Callback)CALLBACK_INIT)
/// Structure holding dictionary watcher
typedef struct dict_watcher {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 95390b1a70..ca5e14ee63 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1099,6 +1099,9 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
}
appended_lines_mark(n, count);
+ if (VIsual_active) {
+ check_pos(curbuf, &VIsual);
+ }
msgmore((long)count);
}
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index c388373ac1..c391cf96aa 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -2413,7 +2413,7 @@ module.cmds = {
},
{
command='set',
- flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK),
+ flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, SBOXOK),
addr_type='ADDR_NONE',
func='ex_set',
},
@@ -2425,13 +2425,13 @@ module.cmds = {
},
{
command='setglobal',
- flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK),
+ flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, SBOXOK),
addr_type='ADDR_NONE',
func='ex_set',
},
{
command='setlocal',
- flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK),
+ flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, SBOXOK),
addr_type='ADDR_NONE',
func='ex_set',
},
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 267f5616f5..846789233f 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -2323,9 +2323,11 @@ void ex_scriptnames(exarg_T *eap)
for (int i = 1; i <= script_items.ga_len && !got_int; i++) {
if (SCRIPT_ITEM(i).sn_name != NULL) {
- home_replace(NULL, SCRIPT_ITEM(i).sn_name,
- NameBuff, MAXPATHL, true);
- smsg("%3d: %s", i, NameBuff);
+ home_replace(NULL, SCRIPT_ITEM(i).sn_name, NameBuff, MAXPATHL, true);
+ vim_snprintf((char *)IObuff, IOSIZE, "%3d: %s", i, NameBuff);
+ msg_putchar('\n');
+ msg_outtrans(IObuff);
+ line_breakcheck();
}
}
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index a4b43e598d..bfcb8c1663 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -9604,18 +9604,6 @@ static void ex_digraphs(exarg_T *eap)
}
}
-static void ex_set(exarg_T *eap)
-{
- int flags = 0;
-
- if (eap->cmdidx == CMD_setlocal) {
- flags = OPT_LOCAL;
- } else if (eap->cmdidx == CMD_setglobal) {
- flags = OPT_GLOBAL;
- }
- (void)do_set(eap->arg, flags);
-}
-
void set_no_hlsearch(bool flag)
{
no_hlsearch = flag;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 78b8e43e65..fd75cfc7f8 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -772,7 +772,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
ccline.cmdindent = (s->firstc > 0 ? s->indent : 0);
// alloc initial ccline.cmdbuff
- alloc_cmdbuff(exmode_active ? 250 : s->indent + 1);
+ alloc_cmdbuff(indent + 50);
ccline.cmdlen = ccline.cmdpos = 0;
ccline.cmdbuff[0] = NUL;
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index b2cd5c510b..b4becb3066 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -1433,7 +1433,11 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
rel_fname = NULL;
}
- if (first == TRUE) {
+ if (first == true) {
+ if (len == 0) {
+ return NULL;
+ }
+
// copy file name into NameBuff, expanding environment variables
save_char = ptr[len];
ptr[len] = NUL;
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 31a09b3969..041b60d838 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -524,6 +524,8 @@ EXTERN pos_T VIsual;
EXTERN int VIsual_active INIT(= false);
/// Whether Select mode is active.
EXTERN int VIsual_select INIT(= false);
+/// Register name for Select mode
+EXTERN int VIsual_select_reg INIT(= 0);
/// Restart Select mode when next cmd finished
EXTERN int restart_VIsual_select INIT(= 0);
/// Whether to restart the selection after a Select-mode mapping or menu.
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index f9a2533d4e..0fbd56ed53 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -619,6 +619,7 @@ bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special)
}
if (tv->v_type == VAR_FUNC) {
ufunc_T *fp = find_func(tv->vval.v_string);
+ assert(fp != NULL);
if (fp->uf_cb == nlua_CFunction_func_call) {
nlua_pushref(lstate, ((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref);
return true;
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index cfdbe7b344..5c4d7e3c91 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -4,6 +4,7 @@
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
+#include <tree_sitter/api.h>
#include "luv/luv.h"
#include "nvim/api/private/defs.h"
@@ -1267,6 +1268,12 @@ int tslua_get_language_version(lua_State *L)
return 1;
}
+int tslua_get_minimum_language_version(lua_State *L)
+{
+ lua_pushnumber(L, TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION);
+ return 1;
+}
+
static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
{
tslua_init(lstate);
@@ -1288,6 +1295,9 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_pushcfunction(lstate, tslua_get_language_version);
lua_setfield(lstate, -2, "_ts_get_language_version");
+
+ lua_pushcfunction(lstate, tslua_get_minimum_language_version);
+ lua_setfield(lstate, -2, "_ts_get_minimum_language_version");
}
int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char_u ***results)
diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h
index 95d63dd14a..30f5aacebc 100644
--- a/src/nvim/marktree.h
+++ b/src/nvim/marktree.h
@@ -2,6 +2,7 @@
#define NVIM_MARKTREE_H
#include <stdint.h>
+#include <assert.h>
#include "nvim/assert.h"
#include "nvim/garray.h"
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 17ccef37f1..e1e253cd2e 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -2647,6 +2647,17 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen)
char buf[7];
char *p;
+ if (on_print.type != kCallbackNone) {
+ typval_T argv[1];
+ argv[0].v_type = VAR_STRING;
+ argv[0].v_lock = VAR_UNLOCKED;
+ argv[0].vval.v_string = (char_u *)str;
+ typval_T rettv = TV_INITIAL_VALUE;
+ callback_call(&on_print, 1, argv, &rettv);
+ tv_clear(&rettv);
+ return;
+ }
+
while ((maxlen < 0 || s - str < maxlen) && *s != NUL) {
int len = utf_ptr2len((const char_u *)s);
if (!(silent_mode && p_verbose == 0)) {
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index f802dce20d..ddebfb9756 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -164,7 +164,7 @@ static const struct nv_cmd {
{ Ctrl_O, nv_ctrlo, 0, 0 },
{ Ctrl_P, nv_up, NV_STS, false },
{ Ctrl_Q, nv_visual, 0, false },
- { Ctrl_R, nv_redo, 0, 0 },
+ { Ctrl_R, nv_redo_or_register, 0, 0 },
{ Ctrl_S, nv_ignore, 0, 0 },
{ Ctrl_T, nv_tagpop, NV_NCW, 0 },
{ Ctrl_U, nv_halfpage, 0, 0 },
@@ -961,6 +961,7 @@ normal_end:
&& s->oa.regname == 0) {
if (restart_VIsual_select == 1) {
VIsual_select = true;
+ VIsual_select_reg = 0;
trigger_modechanged();
showmode();
restart_VIsual_select = 0;
@@ -3286,7 +3287,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
int col_off1; // margin offset for first screen line
int col_off2; // margin offset for wrapped screen line
int width1; // text width for first screen line
- int width2; // test width for wrapped screen line
+ int width2; // text width for wrapped screen line
oap->motion_type = kMTCharWise;
oap->inclusive = (curwin->w_curswant == MAXCOL);
@@ -3410,6 +3411,13 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
virtcol -= vim_strsize(get_showbreak_value(curwin));
}
+ int c = utf_ptr2char(get_cursor_pos_ptr());
+ if (dir == FORWARD && virtcol < curwin->w_curswant
+ && (curwin->w_curswant <= (colnr_T)width1)
+ && !vim_isprintc(c) && c > 255) {
+ oneright();
+ }
+
if (virtcol > curwin->w_curswant
&& (curwin->w_curswant < (colnr_T)width1
? (curwin->w_curswant > (colnr_T)width1 / 2)
@@ -4477,8 +4485,13 @@ bool get_visual_text(cmdarg_T *cap, char_u **pp, size_t *lenp)
*pp = ml_get_pos(&VIsual);
*lenp = (size_t)curwin->w_cursor.col - (size_t)VIsual.col + 1;
}
- // Correct the length to include the whole last character.
- *lenp += (size_t)(utfc_ptr2len(*pp + (*lenp - 1)) - 1);
+ if (**pp == NUL) {
+ *lenp = 0;
+ }
+ if (*lenp > 0) {
+ // Correct the length to include all bytes of the last character.
+ *lenp += (size_t)(utfc_ptr2len(*pp + (*lenp - 1)) - 1);
+ }
}
reset_VIsual_and_resel();
return true;
@@ -5968,11 +5981,8 @@ static void nv_visual(cmdarg_T *cap)
* was only one -- webb
*/
if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1) {
- curwin->w_cursor.lnum +=
- resel_VIsual_line_count * cap->count0 - 1;
- if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
- curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- }
+ curwin->w_cursor.lnum += resel_VIsual_line_count * cap->count0 - 1;
+ check_cursor();
}
VIsual_mode = resel_VIsual_mode;
if (VIsual_mode == 'v') {
@@ -6051,7 +6061,7 @@ static void n_start_visual_mode(int c)
// Corner case: the 0 position in a tab may change when going into
// virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting.
//
- if (c == Ctrl_V && (ve_flags & VE_BLOCK) && gchar_cursor() == TAB) {
+ if (c == Ctrl_V && (get_ve_flags() & VE_BLOCK) && gchar_cursor() == TAB) {
validate_virtcol();
coladvance(curwin->w_virtcol);
}
@@ -6191,6 +6201,7 @@ static void nv_g_cmd(cmdarg_T *cap)
// start Select mode.
if (cap->arg) {
VIsual_select = true;
+ VIsual_select_reg = 0;
} else {
may_start_select('c');
}
@@ -6703,11 +6714,26 @@ static void nv_dot(cmdarg_T *cap)
}
}
-/*
- * CTRL-R: undo undo
- */
-static void nv_redo(cmdarg_T *cap)
+// CTRL-R: undo undo or specify register in select mode
+static void nv_redo_or_register(cmdarg_T *cap)
{
+ if (VIsual_select && VIsual_active) {
+ int reg;
+ // Get register name
+ no_mapping++;
+ reg = plain_vgetc();
+ LANGMAP_ADJUST(reg, true);
+ no_mapping--;
+
+ if (reg == '"') {
+ // the unnamed register is 0
+ reg = 0;
+ }
+
+ VIsual_select_reg = valid_yank_reg(reg, true) ? reg : 0;
+ return;
+ }
+
if (!checkclearopq(cap->oap)) {
u_redo((int)cap->count1);
curwin->w_set_curswant = true;
@@ -6956,7 +6982,7 @@ static void adjust_cursor(oparg_T *oap)
if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL
&& (!VIsual_active || *p_sel == 'o')
&& !virtual_active()
- && (ve_flags & VE_ONEMORE) == 0) {
+ && (get_ve_flags() & VE_ONEMORE) == 0) {
curwin->w_cursor.col--;
// prevent cursor from moving on the trail byte
mb_adjust_cursor();
@@ -7028,6 +7054,7 @@ static void nv_select(cmdarg_T *cap)
{
if (VIsual_active) {
VIsual_select = true;
+ VIsual_select_reg = 0;
} else if (VIsual_reselect) {
cap->nchar = 'v'; // fake "gv" command
cap->arg = true;
@@ -7162,7 +7189,7 @@ static void nv_esc(cmdarg_T *cap)
void set_cursor_for_append_to_line(void)
{
curwin->w_set_curswant = true;
- if (ve_flags == VE_ALL) {
+ if (get_ve_flags() == VE_ALL) {
const int save_State = State;
// Pretend Insert mode here to allow the cursor on the
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 2bc92ce295..e66935db7e 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -1435,6 +1435,11 @@ int op_delete(oparg_T *oap)
return FAIL;
}
+ if (VIsual_select && oap->is_VIsual) {
+ // Use the register given with CTRL_R, defaults to zero
+ oap->regname = VIsual_select_reg;
+ }
+
mb_adjust_opend(oap);
/*
@@ -2196,19 +2201,22 @@ void op_insert(oparg_T *oap, long count1)
// doing block_prep(). When only "block" is used, virtual edit is
// already disabled, but still need it when calling
// coladvance_force().
+ // coladvance_force() uses get_ve_flags() to get the 'virtualedit'
+ // state for the current window. To override that state, we need to
+ // set the window-local value of ve_flags rather than the global value.
if (curwin->w_cursor.coladd > 0) {
- unsigned old_ve_flags = ve_flags;
+ unsigned old_ve_flags = curwin->w_ve_flags;
- ve_flags = VE_ALL;
if (u_save_cursor() == FAIL) {
return;
}
+ curwin->w_ve_flags = VE_ALL;
coladvance_force(oap->op_type == OP_APPEND
? oap->end_vcol + 1 : getviscol());
if (oap->op_type == OP_APPEND) {
--curwin->w_cursor.col;
}
- ve_flags = old_ve_flags;
+ curwin->w_ve_flags = old_ve_flags;
}
// Get the info about the block before entering the text
block_prep(oap, &bd, oap->start.lnum, true);
@@ -2801,7 +2809,7 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx,
if (exclude_trailing_space) {
int s = bd->textlen + bd->endspaces;
- while (ascii_iswhite(*(bd->textstart + s - 1)) && s > 0) {
+ while (s > 0 && ascii_iswhite(*(bd->textstart + s - 1))) {
s = s - utf_head_off(bd->textstart, bd->textstart + s - 1) - 1;
pnew--;
}
@@ -2906,6 +2914,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
char_u *insert_string = NULL;
bool allocated = false;
long cnt;
+ unsigned int cur_ve_flags = get_ve_flags();
if (flags & PUT_FIXINDENT) {
orig_indent = get_indent();
@@ -2976,7 +2985,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
eol = (*(cursor_pos + utfc_ptr2len(cursor_pos)) == NUL);
}
- bool ve_allows = (ve_flags == VE_ALL || ve_flags == VE_ONEMORE);
+ bool ve_allows = (cur_ve_flags == VE_ALL || cur_ve_flags == VE_ONEMORE);
bool eof = curbuf->b_ml.ml_line_count == curwin->w_cursor.lnum
&& one_past_line;
if (ve_allows || !(eol || eof)) {
@@ -3152,7 +3161,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
yanklen = (int)STRLEN(y_array[0]);
- if (ve_flags == VE_ALL && y_type == kMTCharWise) {
+ if (cur_ve_flags == VE_ALL && y_type == kMTCharWise) {
if (gchar_cursor() == TAB) {
int viscol = getviscol();
long ts = curbuf->b_p_ts;
@@ -3181,7 +3190,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
colnr_T endcol2 = 0;
if (dir == FORWARD && c != NUL) {
- if (ve_flags == VE_ALL) {
+ if (cur_ve_flags == VE_ALL) {
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
} else {
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
@@ -3195,9 +3204,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
col += curwin->w_cursor.coladd;
- if (ve_flags == VE_ALL
- && (curwin->w_cursor.coladd > 0
- || endcol2 == curwin->w_cursor.col)) {
+ if (cur_ve_flags == VE_ALL
+ && (curwin->w_cursor.coladd > 0 || endcol2 == curwin->w_cursor.col)) {
if (dir == FORWARD && c == NUL) {
col++;
}
@@ -3629,14 +3637,16 @@ end:
*/
void adjust_cursor_eol(void)
{
+ unsigned int cur_ve_flags = get_ve_flags();
+
if (curwin->w_cursor.col > 0
&& gchar_cursor() == NUL
- && (ve_flags & VE_ONEMORE) == 0
+ && (cur_ve_flags & VE_ONEMORE) == 0
&& !(restart_edit || (State & INSERT))) {
// Put the cursor on the last character in the line.
dec_cursor();
- if (ve_flags == VE_ALL) {
+ if (cur_ve_flags == VE_ALL) {
colnr_T scol, ecol;
// Coladd is set to the width of the last character.
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 659965b64c..fd6483c555 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -281,7 +281,7 @@ typedef struct vimoption {
# include "options.generated.h"
#endif
-#define PARAM_COUNT ARRAY_SIZE(options)
+#define OPTION_COUNT ARRAY_SIZE(options)
static char *(p_ambw_values[]) = { "single", "double", NULL };
static char *(p_bg_values[]) = { "light", "dark", NULL };
@@ -931,6 +931,21 @@ void set_title_defaults(void)
}
}
+void ex_set(exarg_T *eap)
+{
+ int flags = 0;
+
+ if (eap->cmdidx == CMD_setlocal) {
+ flags = OPT_LOCAL;
+ } else if (eap->cmdidx == CMD_setglobal) {
+ flags = OPT_GLOBAL;
+ }
+ if (eap->forceit) {
+ flags |= OPT_ONECOLUMN;
+ }
+ (void)do_set(eap->arg, flags);
+}
+
/// Parse 'arg' for option settings.
///
/// 'arg' may be IObuff, but only when no errors can be present and option
@@ -3084,14 +3099,27 @@ ambw_end:
if (foldmethodIsIndent(curwin)) {
foldUpdateAll(curwin);
}
- } else if (varp == &p_ve) { // 'virtualedit'
- if (opt_strings_flags(p_ve, p_ve_values, &ve_flags, true) != OK) {
- errmsg = e_invarg;
- } else if (STRCMP(p_ve, oldval) != 0) {
- // Recompute cursor position in case the new 've' setting
- // changes something.
- validate_virtcol();
- coladvance(curwin->w_virtcol);
+ } else if (gvarp == &p_ve) { // 'virtualedit'
+ char_u *ve = p_ve;
+ unsigned int *flags = &ve_flags;
+
+ if (opt_flags & OPT_LOCAL) {
+ ve = curwin->w_p_ve;
+ flags = &curwin->w_ve_flags;
+ }
+
+ if ((opt_flags & OPT_LOCAL) && *ve == NUL) {
+ // make the local value empty: use the global value
+ *flags = 0;
+ } else {
+ if (opt_strings_flags(ve, p_ve_values, flags, true) != OK) {
+ errmsg = e_invarg;
+ } else if (STRCMP(p_ve, oldval) != 0) {
+ // Recompute cursor position in case the new 've' setting
+ // changes something.
+ validate_virtcol();
+ coladvance(curwin->w_virtcol);
+ }
}
} else if (varp == &p_csqf) {
if (p_csqf != NULL) {
@@ -5184,7 +5212,7 @@ static void showoptions(int all, int opt_flags)
#define INC 20
#define GAP 3
- vimoption_T **items = xmalloc(sizeof(vimoption_T *) * PARAM_COUNT);
+ vimoption_T **items = xmalloc(sizeof(vimoption_T *) * OPTION_COUNT);
// Highlight title
if (opt_flags & OPT_GLOBAL) {
@@ -5198,6 +5226,7 @@ static void showoptions(int all, int opt_flags)
// Do the loop two times:
// 1. display the short items
// 2. display the long items (only strings and numbers)
+ // When "opt_flags" has OPT_ONECOLUMN do everything in run 2.
for (run = 1; run <= 2 && !got_int; run++) {
// collect the items in items[]
item_count = 0;
@@ -5208,7 +5237,7 @@ static void showoptions(int all, int opt_flags)
}
varp = NULL;
- if (opt_flags != 0) {
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) != 0) {
if (p->indir != PV_NONE) {
varp = get_varp_scope(p, opt_flags);
}
@@ -5217,8 +5246,10 @@ static void showoptions(int all, int opt_flags)
}
if (varp != NULL
&& (all == 1 || (all == 0 && !optval_default(p, varp)))) {
- if (p->flags & P_BOOL) {
- len = 1; // a toggle option fits always
+ if (opt_flags & OPT_ONECOLUMN) {
+ len = Columns;
+ } else if (p->flags & P_BOOL) {
+ len = 1; // a toggle option fits always
} else {
option_value2string(p, opt_flags);
len = (int)STRLEN(p->fullname) + vim_strsize(NameBuff) + 1;
@@ -5748,6 +5779,10 @@ void unset_global_local_option(char *name, void *from)
set_chars_option((win_T *)from, &((win_T *)from)->w_p_fcs, true);
redraw_later((win_T *)from, NOT_VALID);
break;
+ case PV_VE:
+ clear_string_option(&((win_T *)from)->w_p_ve);
+ ((win_T *)from)->w_ve_flags = 0;
+ break;
}
}
@@ -5814,6 +5849,8 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
return (char_u *)&(curwin->w_p_fcs);
case PV_LCS:
return (char_u *)&(curwin->w_p_lcs);
+ case PV_VE:
+ return (char_u *)&(curwin->w_p_ve);
}
return NULL; // "cannot happen"
}
@@ -5908,6 +5945,9 @@ static char_u *get_varp(vimoption_T *p)
case PV_LCS:
return *curwin->w_p_lcs != NUL
? (char_u *)&(curwin->w_p_lcs) : p->var;
+ case PV_VE:
+ return *curwin->w_p_ve != NUL
+ ? (char_u *)&curwin->w_p_ve : p->var;
case PV_ARAB:
return (char_u *)&(curwin->w_p_arab);
@@ -6148,6 +6188,8 @@ void copy_winopt(winopt_T *from, winopt_T *to)
to->wo_list = from->wo_list;
to->wo_nu = from->wo_nu;
to->wo_rnu = from->wo_rnu;
+ to->wo_ve = vim_strsave(from->wo_ve);
+ to->wo_ve_flags = from->wo_ve_flags;
to->wo_nuw = from->wo_nuw;
to->wo_rl = from->wo_rl;
to->wo_rlc = vim_strsave(from->wo_rlc);
@@ -6224,6 +6266,7 @@ static void check_winopt(winopt_T *wop)
check_string_option(&wop->wo_winhl);
check_string_option(&wop->wo_fcs);
check_string_option(&wop->wo_lcs);
+ check_string_option(&wop->wo_ve);
}
/// Free the allocated memory inside a winopt_T.
@@ -6248,6 +6291,7 @@ void clear_winopt(winopt_T *wop)
clear_string_option(&wop->wo_winhl);
clear_string_option(&wop->wo_fcs);
clear_string_option(&wop->wo_lcs);
+ clear_string_option(&wop->wo_ve);
}
void didset_window_options(win_T *wp)
@@ -7815,6 +7859,12 @@ unsigned int get_bkc_value(buf_T *buf)
return buf->b_bkc_flags ? buf->b_bkc_flags : bkc_flags;
}
+/// Get the local or global value of the 'virtualedit' flags.
+unsigned int get_ve_flags(void)
+{
+ return (curwin->w_ve_flags ? curwin->w_ve_flags : ve_flags) & ~(VE_NONE | VE_NONEU);
+}
+
/// Get the local or global value of 'showbreak'.
///
/// @param win If not NULL, the window to get the local option from; global
diff --git a/src/nvim/option.h b/src/nvim/option.h
index f7dbaafeec..9321dd5454 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -13,16 +13,16 @@
/// When OPT_GLOBAL and OPT_LOCAL are both missing, set both local and global
/// values, get local value.
typedef enum {
- OPT_FREE = 1, ///< Free old value if it was allocated.
- OPT_GLOBAL = 2, ///< Use global value.
- OPT_LOCAL = 4, ///< Use local value.
- OPT_MODELINE = 8, ///< Option in modeline.
- OPT_WINONLY = 16, ///< Only set window-local options.
- OPT_NOWIN = 32, ///< Don’t set window-local options.
- OPT_ONECOLUMN = 64, ///< list options one per line
- OPT_NO_REDRAW = 128, ///< ignore redraw flags on option
- OPT_SKIPRTP = 256, ///< "skiprtp" in 'sessionoptions'
- OPT_CLEAR = 512, ///< Clear local value of an option.
+ OPT_FREE = 0x01, ///< Free old value if it was allocated.
+ OPT_GLOBAL = 0x02, ///< Use global value.
+ OPT_LOCAL = 0x04, ///< Use local value.
+ OPT_MODELINE = 0x08, ///< Option in modeline.
+ OPT_WINONLY = 0x10, ///< Only set window-local options.
+ OPT_NOWIN = 0x20, ///< Don’t set window-local options.
+ OPT_ONECOLUMN = 0x40, ///< list options one per line
+ OPT_NO_REDRAW = 0x80, ///< ignore redraw flags on option
+ OPT_SKIPRTP = 0x100, ///< "skiprtp" in 'sessionoptions'
+ OPT_CLEAR = 0x200, ///< Clear local value of an option.
} OptionFlags;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 09c3bf3800..5d6aca9574 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -705,12 +705,14 @@ EXTERN int p_vb; ///< 'visualbell'
EXTERN char_u *p_ve; ///< 'virtualedit'
EXTERN unsigned ve_flags;
#ifdef IN_OPTION_C
-static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", NULL };
+static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", "none", "NONE", NULL };
#endif
-#define VE_BLOCK 5 // includes "all"
-#define VE_INSERT 6 // includes "all"
-#define VE_ALL 4
-#define VE_ONEMORE 8
+#define VE_BLOCK 5U // includes "all"
+#define VE_INSERT 6U // includes "all"
+#define VE_ALL 4U
+#define VE_ONEMORE 8U
+#define VE_NONE 16U // "none"
+#define VE_NONEU 32U // "NONE"
EXTERN long p_verbose; // 'verbose'
#ifdef IN_OPTION_C
char_u *p_vfile = (char_u *)""; // used before options are initialized
@@ -869,6 +871,7 @@ enum {
WV_LBR,
WV_NU,
WV_RNU,
+ WV_VE,
WV_NUW,
WV_PVW,
WV_RL,
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 5133fe7ac8..aea2179a61 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2736,7 +2736,7 @@ return {
{
full_name='virtualedit', abbreviation='ve',
short_desc=N_("when to use virtual editing"),
- type='string', list='onecomma', scope={'global'},
+ type='string', list='onecomma', scope={'global', 'window'},
deny_duplicates=true,
redraw={'curswant'},
varname='p_ve',
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 674d67e21a..39e276e0a5 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -1682,6 +1682,10 @@ char_u *find_file_name_in_path(char_u *ptr, size_t len, int options, long count,
char_u *file_name;
char_u *tofree = NULL;
+ if (len == 0) {
+ return NULL;
+ }
+
if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) {
tofree = (char_u *)eval_includeexpr((char *)ptr, len);
if (tofree != NULL) {
@@ -1743,14 +1747,32 @@ int path_is_url(const char *p)
return 0;
}
-/// Check if "fname" starts with "name://". Return URL_SLASH if it does.
+/// Check if "fname" starts with "name://" or "name:\\".
///
/// @param fname is the filename to test
-/// @return URL_BACKSLASH for "name:\\", zero otherwise.
+/// @return URL_SLASH for "name://", URL_BACKSLASH for "name:\\", zero otherwise.
int path_with_url(const char *fname)
{
const char *p;
- for (p = fname; isalpha(*p); p++) {}
+
+ // We accept alphabetic characters and a dash in scheme part.
+ // RFC 3986 allows for more, but it increases the risk of matching
+ // non-URL text.
+
+ // first character must be alpha
+ if (!isalpha(*fname)) {
+ return 0;
+ }
+
+ // check body: alpha or dash
+ for (p = fname; (isalpha(*p) || (*p == '-')); p++) {}
+
+ // check last char is not a dash
+ if (p[-1] == '-') {
+ return 0;
+ }
+
+ // "://" or ":\\" must follow
return path_is_url(p);
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 538604cf79..af023d6785 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -1215,19 +1215,40 @@ static void win_update(win_T *wp, Providers *providers)
*/
if (VIsual_mode == Ctrl_V) {
colnr_T fromc, toc;
- int save_ve_flags = ve_flags;
+ unsigned int save_ve_flags = curwin->w_ve_flags;
if (curwin->w_p_lbr) {
- ve_flags = VE_ALL;
+ curwin->w_ve_flags = VE_ALL;
}
getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc);
- ve_flags = save_ve_flags;
toc++;
+ curwin->w_ve_flags = save_ve_flags;
// Highlight to the end of the line, unless 'virtualedit' has
// "block".
- if (curwin->w_curswant == MAXCOL && !(ve_flags & VE_BLOCK)) {
- toc = MAXCOL;
+ if (curwin->w_curswant == MAXCOL) {
+ if (get_ve_flags() & VE_BLOCK) {
+ pos_T pos;
+ int cursor_above = curwin->w_cursor.lnum < VIsual.lnum;
+
+ // Need to find the longest line.
+ toc = 0;
+ pos.coladd = 0;
+ for (pos.lnum = curwin->w_cursor.lnum;
+ cursor_above ? pos.lnum <= VIsual.lnum : pos.lnum >= VIsual.lnum;
+ pos.lnum += cursor_above ? 1 : -1) {
+ colnr_T t;
+
+ pos.col = STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false));
+ getvvcol(wp, &pos, NULL, NULL, &t);
+ if (toc < t) {
+ toc = t;
+ }
+ }
+ toc++;
+ } else {
+ toc = MAXCOL;
+ }
}
if (fromc != wp->w_old_cursor_fcol
@@ -3720,41 +3741,46 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
tab_len += n_extra - tab_len;
}
- // if n_extra > 0, it gives the number of chars
+ // If n_extra > 0, it gives the number of chars
// to use for a tab, else we need to calculate the width
- // for a tab
+ // for a tab.
int len = (tab_len * utf_char2len(wp->w_p_lcs_chars.tab2));
+ if (wp->w_p_lcs_chars.tab3) {
+ len += utf_char2len(wp->w_p_lcs_chars.tab3);
+ }
if (n_extra > 0) {
len += n_extra - tab_len;
}
c = wp->w_p_lcs_chars.tab1;
p = xmalloc(len + 1);
- memset(p, ' ', len);
- p[len] = NUL;
- xfree(p_extra_free);
- p_extra_free = p;
- for (i = 0; i < tab_len; i++) {
- if (*p == NUL) {
- tab_len = i;
- break;
- }
- int lcs = wp->w_p_lcs_chars.tab2;
+ if (p == NULL) {
+ n_extra = 0;
+ } else {
+ memset(p, ' ', len);
+ p[len] = NUL;
+ xfree(p_extra_free);
+ p_extra_free = p;
+ for (i = 0; i < tab_len; i++) {
+ if (*p == NUL) {
+ tab_len = i;
+ break;
+ }
+ int lcs = wp->w_p_lcs_chars.tab2;
- // if tab3 is given, need to change the char
- // for tab
- if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) {
- lcs = wp->w_p_lcs_chars.tab3;
+ // if tab3 is given, use it for the last char
+ if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) {
+ lcs = wp->w_p_lcs_chars.tab3;
+ }
+ p += utf_char2bytes(lcs, p);
+ n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0);
}
- utf_char2bytes(lcs, p);
- p += utf_char2len(lcs);
- n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0);
- }
- p_extra = p_extra_free;
+ p_extra = p_extra_free;
- // n_extra will be increased by FIX_FOX_BOGUSCOLS
- // macro below, so need to adjust for that here
- if (vcol_off > 0) {
- n_extra -= vcol_off;
+ // n_extra will be increased by FIX_FOX_BOGUSCOLS
+ // macro below, so need to adjust for that here
+ if (vcol_off > 0) {
+ n_extra -= vcol_off;
+ }
}
}
@@ -4232,6 +4258,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// Show "extends" character from 'listchars' if beyond the line end and
// 'list' is set.
if (wp->w_p_lcs_chars.ext != NUL
+ && draw_state == WL_LINE
&& wp->w_p_list
&& !wp->w_p_wrap
&& filler_todo <= 0
@@ -4427,7 +4454,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
*/
if ((wp->w_p_rl ? (col < 0) : (col >= grid->Columns))
&& foldinfo.fi_lines == 0
- && (*ptr != NUL
+ && (draw_state != WL_LINE
+ || *ptr != NUL
|| filler_todo > 0
|| (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
&& p_extra != at_end_str)
diff --git a/src/nvim/state.c b/src/nvim/state.c
index 1fe8bb671d..9e4c9b2bad 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -12,6 +12,7 @@
#include "nvim/lib/kvec.h"
#include "nvim/log.h"
#include "nvim/main.h"
+#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/os/input.h"
#include "nvim/state.h"
@@ -107,15 +108,17 @@ void state_handle_k_event(void)
/// Return true if in the current mode we need to use virtual.
bool virtual_active(void)
{
+ unsigned int cur_ve_flags = get_ve_flags();
+
// While an operator is being executed we return "virtual_op", because
// VIsual_active has already been reset, thus we can't check for "block"
// being used.
if (virtual_op != kNone) {
return virtual_op;
}
- return ve_flags == VE_ALL
- || ((ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V)
- || ((ve_flags & VE_INSERT) && (State & INSERT));
+ return cur_ve_flags == VE_ALL
+ || ((cur_ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V)
+ || ((cur_ve_flags & VE_INSERT) && (State & INSERT));
}
/// VISUAL, SELECTMODE and OP_PENDING State are never set, they are equal to
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index e2a8108c45..291138ef23 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -1001,22 +1001,20 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
- str_arg);
}
if (fmt_spec == 'S') {
- if (min_field_width != 0) {
- min_field_width += (strlen(str_arg)
- - mb_string2cells((char_u *)str_arg));
- }
- if (precision) {
- char_u *p1;
- size_t i = 0;
-
- for (p1 = (char_u *)str_arg; *p1;
- p1 += utfc_ptr2len(p1)) {
- i += (size_t)utf_ptr2cells(p1);
- if (i > precision) {
- break;
- }
+ char_u *p1;
+ size_t i;
+
+ for (i = 0, p1 = (char_u *)str_arg; *p1; p1 += utfc_ptr2len(p1)) {
+ size_t cell = (size_t)utf_ptr2cells(p1);
+ if (precision_specified && i + cell > precision) {
+ break;
}
- str_arg_l = (size_t)(p1 - (char_u *)str_arg);
+ i += cell;
+ }
+
+ str_arg_l = (size_t)(p1 - (char_u *)str_arg);
+ if (min_field_width != 0) {
+ min_field_width += str_arg_l - i;
}
}
break;
diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim
index 14bab33a2f..ab26dddbe0 100644
--- a/src/nvim/testdir/check.vim
+++ b/src/nvim/testdir/check.vim
@@ -12,9 +12,9 @@ endfunc
" Command to check for the absence of a feature.
command -nargs=1 CheckNotFeature call CheckNotFeature(<f-args>)
func CheckNotFeature(name)
- if !has(a:name, 1)
- throw 'Checking for non-existent feature ' .. a:name
- endif
+ " if !has(a:name, 1)
+ " throw 'Checking for non-existent feature ' .. a:name
+ " endif
if has(a:name)
throw 'Skipped: ' .. a:name .. ' feature present'
endif
diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim
index 40111fdf06..a31cdbb49a 100644
--- a/src/nvim/testdir/test_buffer.vim
+++ b/src/nvim/testdir/test_buffer.vim
@@ -1,5 +1,7 @@
" Tests for Vim buffer
+source check.vim
+
func Test_buffer_error()
new foo1
new foo2
@@ -30,4 +32,33 @@ func Test_balt()
call assert_equal('OtherBuffer', bufname())
endfunc
+" Test for buffer match URL(scheme) check
+" scheme is alpha and inner hyphen only.
+func Test_buffer_scheme()
+ CheckMSWindows
+
+ set noshellslash
+ %bwipe!
+ let bufnames = [
+ \ #{id: 'b0', name: 'test://xyz/foo/b0' , match: 1},
+ \ #{id: 'b1', name: 'test+abc://xyz/foo/b1', match: 0},
+ \ #{id: 'b2', name: 'test_abc://xyz/foo/b2', match: 0},
+ \ #{id: 'b3', name: 'test-abc://xyz/foo/b3', match: 1},
+ \ #{id: 'b4', name: '-test://xyz/foo/b4' , match: 0},
+ \ #{id: 'b5', name: 'test-://xyz/foo/b5' , match: 0},
+ \]
+ for buf in bufnames
+ new `=buf.name`
+ if buf.match
+ call assert_equal(buf.name, getbufinfo(buf.id)[0].name)
+ else
+ " slashes will have become backslashes
+ call assert_notequal(buf.name, getbufinfo(buf.id)[0].name)
+ endif
+ bwipe
+ endfor
+
+ set shellslash&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_conceal.vim b/src/nvim/testdir/test_conceal.vim
index 1306dbe5cf..bffc2f49d3 100644
--- a/src/nvim/testdir/test_conceal.vim
+++ b/src/nvim/testdir/test_conceal.vim
@@ -4,10 +4,10 @@ source check.vim
CheckFeature conceal
source screendump.vim
-" CheckScreendump
func Test_conceal_two_windows()
CheckScreendump
+
let code =<< trim [CODE]
let lines = ["one one one one one", "two |hidden| here", "three |hidden| three"]
call setline(1, lines)
@@ -111,6 +111,7 @@ endfunc
func Test_conceal_with_cursorline()
CheckScreendump
+
" Opens a help window, where 'conceal' is set, switches to the other window
" where 'cursorline' needs to be updated when the cursor moves.
let code =<< trim [CODE]
@@ -139,6 +140,7 @@ endfunc
func Test_conceal_resize_term()
CheckScreendump
+
let code =<< trim [CODE]
call setline(1, '`one` `two` `three` `four` `five`, the backticks should be concealed')
setl cocu=n cole=3
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index fc4e80f0d6..c1f74e7675 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1006,16 +1006,16 @@ func Test_edit_DROP()
endfunc
func Test_edit_CTRL_V()
- if has("ebcdic")
- return
- endif
+ CheckNotFeature ebcdic
+
new
call setline(1, ['abc'])
call cursor(2, 1)
+
" force some redraws
set showmode showcmd
- "call test_override_char_avail(1)
- " call test_override('ALL', 1)
+ " call test_override('char_avail', 1)
+
call feedkeys("A\<c-v>\<c-n>\<c-v>\<c-l>\<c-v>\<c-b>\<esc>", 'tnix')
call assert_equal(["abc\x0e\x0c\x02"], getline(1, '$'))
@@ -1028,8 +1028,19 @@ func Test_edit_CTRL_V()
set norl
endif
- " call test_override('ALL', 0)
set noshowmode showcmd
+ " call test_override('char_avail', 0)
+
+ " No modifiers should be applied to the char typed using i_CTRL-V_digit.
+ call feedkeys(":append\<CR>\<C-V>76c\<C-V>76\<C-F2>\<C-V>u3c0j\<C-V>u3c0\<M-F3>\<CR>.\<CR>", 'tnix')
+ call assert_equal('LcL<C-F2>Ο€jΟ€<M-F3>', getline(2))
+
+ if has('osx')
+ " A char with a modifier should not be a valid char for i_CTRL-V_digit.
+ call feedkeys("o\<C-V>\<D-j>\<C-V>\<D-1>\<C-V>\<D-o>\<C-V>\<D-x>\<C-V>\<D-u>", 'tnix')
+ call assert_equal('<D-j><D-1><D-o><D-x><D-u>', getline(3))
+ endif
+
bw!
endfunc
diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim
index 92e0559618..78663f7deb 100644
--- a/src/nvim/testdir/test_ex_mode.vim
+++ b/src/nvim/testdir/test_ex_mode.vim
@@ -98,4 +98,14 @@ func Test_ex_mode_count_overflow()
call delete('Xexmodescript')
endfunc
+func Test_ex_mode_large_indent()
+ new
+ set ts=500 ai
+ call setline(1, "\t")
+ exe "normal gQi\<CR>."
+ set ts=8 noai
+ bwipe!
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 1d7fd3e385..5b10e691e5 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -283,6 +283,71 @@ function Test_printf_misc()
call assert_equal('🐍', printf('%.2S', '🐍🐍'))
call assert_equal('', printf('%.1S', '🐍🐍'))
+ call assert_equal('[ あいう]', printf('[%10.6S]', 'γ‚γ„γ†γˆγŠ'))
+ call assert_equal('[ γ‚γ„γ†γˆ]', printf('[%10.8S]', 'γ‚γ„γ†γˆγŠ'))
+ call assert_equal('[γ‚γ„γ†γˆγŠ]', printf('[%10.10S]', 'γ‚γ„γ†γˆγŠ'))
+ call assert_equal('[γ‚γ„γ†γˆγŠ]', printf('[%10.12S]', 'γ‚γ„γ†γˆγŠ'))
+
+ call assert_equal('あいう', printf('%S', 'あいう'))
+ call assert_equal('あいう', printf('%#S', 'あいう'))
+
+ call assert_equal('あb', printf('%2S', 'あb'))
+ call assert_equal('あb', printf('%.4S', 'あb'))
+ call assert_equal('あ', printf('%.2S', 'あb'))
+ call assert_equal(' あb', printf('%4S', 'あb'))
+ call assert_equal('0あb', printf('%04S', 'あb'))
+ call assert_equal('あb ', printf('%-4S', 'あb'))
+ call assert_equal('あ ', printf('%-4.2S', 'あb'))
+
+ call assert_equal('aい', printf('%2S', 'aい'))
+ call assert_equal('aい', printf('%.4S', 'aい'))
+ call assert_equal('a', printf('%.2S', 'aい'))
+ call assert_equal(' aい', printf('%4S', 'aい'))
+ call assert_equal('0aい', printf('%04S', 'aい'))
+ call assert_equal('aい ', printf('%-4S', 'aい'))
+ call assert_equal('a ', printf('%-4.2S', 'aい'))
+
+ call assert_equal('[あいう]', printf('[%05S]', 'あいう'))
+ call assert_equal('[あいう]', printf('[%06S]', 'あいう'))
+ call assert_equal('[0あいう]', printf('[%07S]', 'あいう'))
+
+ call assert_equal('[あiう]', printf('[%05S]', 'あiう'))
+ call assert_equal('[0あiう]', printf('[%06S]', 'あiう'))
+ call assert_equal('[00あiう]', printf('[%07S]', 'あiう'))
+
+ call assert_equal('[0あい]', printf('[%05.4S]', 'あいう'))
+ call assert_equal('[00あい]', printf('[%06.4S]', 'あいう'))
+ call assert_equal('[000あい]', printf('[%07.4S]', 'あいう'))
+
+ call assert_equal('[00あi]', printf('[%05.4S]', 'あiう'))
+ call assert_equal('[000あi]', printf('[%06.4S]', 'あiう'))
+ call assert_equal('[0000あi]', printf('[%07.4S]', 'あiう'))
+
+ call assert_equal('[0あい]', printf('[%05.5S]', 'あいう'))
+ call assert_equal('[00あい]', printf('[%06.5S]', 'あいう'))
+ call assert_equal('[000あい]', printf('[%07.5S]', 'あいう'))
+
+ call assert_equal('[あiう]', printf('[%05.5S]', 'あiう'))
+ call assert_equal('[0あiう]', printf('[%06.5S]', 'あiう'))
+ call assert_equal('[00あiう]', printf('[%07.5S]', 'あiう'))
+
+ call assert_equal('[0000000000]', printf('[%010.0S]', 'あいう'))
+ call assert_equal('[0000000000]', printf('[%010.1S]', 'あいう'))
+ call assert_equal('[00000000あ]', printf('[%010.2S]', 'あいう'))
+ call assert_equal('[00000000あ]', printf('[%010.3S]', 'あいう'))
+ call assert_equal('[000000あい]', printf('[%010.4S]', 'あいう'))
+ call assert_equal('[000000あい]', printf('[%010.5S]', 'あいう'))
+ call assert_equal('[0000あいう]', printf('[%010.6S]', 'あいう'))
+ call assert_equal('[0000あいう]', printf('[%010.7S]', 'あいう'))
+
+ call assert_equal('[0000000000]', printf('[%010.1S]', 'あiう'))
+ call assert_equal('[00000000あ]', printf('[%010.2S]', 'あiう'))
+ call assert_equal('[0000000あi]', printf('[%010.3S]', 'あiう'))
+ call assert_equal('[0000000あi]', printf('[%010.4S]', 'あiう'))
+ call assert_equal('[00000あiう]', printf('[%010.5S]', 'あiう'))
+ call assert_equal('[00000あiう]', printf('[%010.6S]', 'あiう'))
+ call assert_equal('[00000あiう]', printf('[%010.7S]', 'あiう'))
+
call assert_equal('1%', printf('%d%%', 1))
endfunc
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 8930d62fd3..eb4824aa32 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -190,8 +190,11 @@ let s:filename_checks = {
\ 'freebasic': ['file.fb'],
\ 'fsharp': ['file.fs', 'file.fsi', 'file.fsx'],
\ 'fstab': ['fstab', 'mtab'],
+ \ 'fusion': ['file.fusion'],
\ 'fvwm': ['/.fvwm/file', 'any/.fvwm/file'],
\ 'gdb': ['.gdbinit', 'gdbinit'],
+ \ 'gdresource': ['file.tscn', 'file.tres'],
+ \ 'gdscript': ['file.gd'],
\ 'gdmo': ['file.mo', 'file.gdmo'],
\ 'gedcom': ['file.ged', 'lltxxxxx.txt', '/tmp/lltmp', '/tmp/lltmp-file', 'any/tmp/lltmp', 'any/tmp/lltmp-file'],
\ 'gemtext': ['file.gmi', 'file.gemini'],
@@ -202,28 +205,36 @@ let s:filename_checks = {
\ 'gitrebase': ['git-rebase-todo'],
\ 'gitsendemail': ['.gitsendemail.msg.xxxxxx'],
\ 'gkrellmrc': ['gkrellmrc', 'gkrellmrc_x'],
+ \ 'glsl': ['file.glsl'],
\ 'gnash': ['gnashrc', '.gnashrc', 'gnashpluginrc', '.gnashpluginrc'],
\ 'gnuplot': ['file.gpi', '.gnuplot'],
\ 'go': ['file.go'],
\ 'gomod': ['go.mod'],
+ \ 'gowork': ['go.work'],
\ 'gp': ['file.gp', '.gprc'],
\ 'gpg': ['/.gnupg/options', '/.gnupg/gpg.conf', '/usr/any/gnupg/options.skel', 'any/.gnupg/gpg.conf', 'any/.gnupg/options', 'any/usr/any/gnupg/options.skel'],
\ 'grads': ['file.gs'],
+ \ 'graphql': ['file.graphql', 'file.graphqls', 'file.gql'],
\ 'gretl': ['file.gretl'],
\ 'groovy': ['file.gradle', 'file.groovy'],
\ 'group': ['any/etc/group', 'any/etc/group-', 'any/etc/group.edit', 'any/etc/gshadow', 'any/etc/gshadow-', 'any/etc/gshadow.edit', 'any/var/backups/group.bak', 'any/var/backups/gshadow.bak', '/etc/group', '/etc/group-', '/etc/group.edit', '/etc/gshadow', '/etc/gshadow-', '/etc/gshadow.edit', '/var/backups/group.bak', '/var/backups/gshadow.bak'],
\ 'grub': ['/boot/grub/menu.lst', '/boot/grub/grub.conf', '/etc/grub.conf', 'any/boot/grub/grub.conf', 'any/boot/grub/menu.lst', 'any/etc/grub.conf'],
\ 'gsp': ['file.gsp'],
\ 'gtkrc': ['.gtkrc', 'gtkrc', '.gtkrc-file', 'gtkrc-file'],
+ \ 'hack': ['file.hack', 'file.hackpartial'],
\ 'haml': ['file.haml'],
\ 'hamster': ['file.hsm'],
+ \ 'handlebars': ['file.hbs'],
\ 'haskell': ['file.hs', 'file.hsc', 'file.hs-boot', 'file.hsig'],
\ 'haste': ['file.ht'],
\ 'hastepreproc': ['file.htpp'],
\ 'hb': ['file.hb'],
+ \ 'hcl': ['file.hcl'],
\ 'hercules': ['file.vc', 'file.ev', 'file.sum', 'file.errsum'],
+ \ 'heex': ['file.heex'],
\ 'hex': ['file.hex', 'file.h32'],
\ 'hgcommit': ['hg-editor-file.txt'],
+ \ 'hjson': ['file.hjson'],
\ 'hog': ['file.hog', 'snort.conf', 'vision.conf'],
\ 'hollywood': ['file.hws'],
\ 'hostconf': ['/etc/host.conf', 'any/etc/host.conf'],
@@ -279,6 +290,7 @@ let s:filename_checks = {
\ 'latte': ['file.latte', 'file.lte'],
\ 'ld': ['file.ld'],
\ 'ldif': ['file.ldif'],
+ \ 'ledger': ['file.ldg', 'file.ledger', 'file.journal'],
\ 'less': ['file.less'],
\ 'lex': ['file.lex', 'file.l', 'file.lxx', 'file.l++'],
\ 'lftp': ['lftp.conf', '.lftprc', 'anylftp/rc', 'lftp/rc', 'some-lftp/rc'],
@@ -352,6 +364,7 @@ let s:filename_checks = {
\ 'netrc': ['.netrc'],
\ 'nginx': ['file.nginx', 'nginxfile.conf', 'filenginx.conf', 'any/etc/nginx/file', 'any/usr/local/nginx/conf/file', 'any/nginx/file.conf'],
\ 'ninja': ['file.ninja'],
+ \ 'nix': ['file.nix'],
\ 'nqc': ['file.nqc'],
\ 'nroff': ['file.tr', 'file.nr', 'file.roff', 'file.tmac', 'file.mom', 'tmac.file'],
\ 'nsis': ['file.nsi', 'file.nsh'],
@@ -394,6 +407,7 @@ let s:filename_checks = {
\ 'ppd': ['file.ppd'],
\ 'ppwiz': ['file.it', 'file.ih'],
\ 'privoxy': ['file.action'],
+ \ 'prisma': ['file.prisma'],
\ 'proc': ['file.pc'],
\ 'procmail': ['.procmail', '.procmailrc'],
\ 'prolog': ['file.pdb'],
@@ -404,10 +418,12 @@ let s:filename_checks = {
\ 'ps1xml': ['file.ps1xml'],
\ 'psf': ['file.psf'],
\ 'psl': ['file.psl'],
+ \ 'pug': ['file.pug'],
\ 'puppet': ['file.pp'],
\ 'pyret': ['file.arr'],
\ 'pyrex': ['file.pyx', 'file.pxd'],
\ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', 'file.ptl', 'file.pyi', 'SConstruct'],
+ \ 'ql': ['file.ql', 'file.qll'],
\ 'quake': ['anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg', 'baseq2/file.cfg', 'id1/file.cfg', 'quake1/file.cfg', 'some-baseq2/file.cfg', 'some-id1/file.cfg', 'some-quake1/file.cfg'],
\ 'radiance': ['file.rad', 'file.mat'],
\ 'raku': ['file.pm6', 'file.p6', 'file.t6', 'file.pod6', 'file.raku', 'file.rakumod', 'file.rakudoc', 'file.rakutest'],
@@ -489,6 +505,7 @@ let s:filename_checks = {
\ 'stata': ['file.ado', 'file.do', 'file.imata', 'file.mata'],
\ 'stp': ['file.stp'],
\ 'sudoers': ['any/etc/sudoers', 'sudoers.tmp', '/etc/sudoers', 'any/etc/sudoers.d/file'],
+ \ 'surface': ['file.sface'],
\ 'svg': ['file.svg'],
\ 'svn': ['svn-commitfile.tmp', 'svn-commit-file.tmp', 'svn-commit.tmp'],
\ 'swift': ['file.swift'],
@@ -502,8 +519,10 @@ let s:filename_checks = {
\ 'taskdata': ['pending.data', 'completed.data', 'undo.data'],
\ 'taskedit': ['file.task'],
\ 'tcl': ['file.tcl', 'file.tm', 'file.tk', 'file.itcl', 'file.itk', 'file.jacl', '.tclshrc', 'tclsh.rc', '.wishrc'],
+ \ 'teal': ['file.tl'],
\ 'teraterm': ['file.ttl'],
\ 'terminfo': ['file.ti'],
+ \ 'terraform': ['file.tfvars'],
\ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'],
\ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'],
\ 'texmf': ['texmf.cnf'],
@@ -511,6 +530,7 @@ let s:filename_checks = {
\ 'tf': ['file.tf', '.tfrc', 'tfrc'],
\ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'],
\ 'tilde': ['file.t.html'],
+ \ 'tla': ['file.tla'],
\ 'tli': ['file.tli'],
\ 'tmux': ['tmuxfile.conf', '.tmuxfile.conf', '.tmux-file.conf', '.tmux.conf', 'tmux-file.conf', 'tmux.conf', 'tmux.conf.local'],
\ 'toml': ['file.toml', 'Gopkg.lock', 'Pipfile', '/home/user/.cargo/config'],
@@ -571,6 +591,7 @@ let s:filename_checks = {
\ 'xslt': ['file.xsl', 'file.xslt'],
\ 'yacc': ['file.yy', 'file.yxx', 'file.y++'],
\ 'yaml': ['file.yaml', 'file.yml'],
+ \ 'yang': ['file.yang'],
\ 'raml': ['file.raml'],
\ 'z8a': ['file.z8a'],
\ 'zig': ['file.zig'],
diff --git a/src/nvim/testdir/test_filter_map.vim b/src/nvim/testdir/test_filter_map.vim
index a52a66ac2f..1cd3a2287b 100644
--- a/src/nvim/testdir/test_filter_map.vim
+++ b/src/nvim/testdir/test_filter_map.vim
@@ -88,4 +88,14 @@ func Test_map_filter_fails()
call assert_fails("let l = filter('abc', '\"> \" . v:val')", 'E896:')
endfunc
+func Test_map_and_modify()
+ let l = ["abc"]
+ " cannot change the list halfway a map()
+ call assert_fails('call map(l, "remove(l, 0)[0]")', 'E741:')
+
+ let d = #{a: 1, b: 2, c: 3}
+ call assert_fails('call map(d, "remove(d, v:key)[0]")', 'E741:')
+ call assert_fails('echo map(d, {k,v -> remove(d, k)})', 'E741:')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index ce75799551..6803271c03 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -445,6 +445,28 @@ func Test_issue_7021()
set completeslash=
endfunc
+func Test_pum_stopped_by_timer()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, ['hello', 'hullo', 'heeee', ''])
+ func StartCompl()
+ call timer_start(100, { -> execute('stopinsert') })
+ call feedkeys("Gah\<C-N>")
+ endfunc
+ END
+
+ call writefile(lines, 'Xpumscript')
+ let buf = RunVimInTerminal('-S Xpumscript', #{rows: 12})
+ call term_sendkeys(buf, ":call StartCompl()\<CR>")
+ call TermWait(buf, 200)
+ call term_sendkeys(buf, "k")
+ call VerifyScreenDump(buf, 'Test_pum_stopped_by_timer', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xpumscript')
+endfunc
+
func Test_pum_with_folds_two_tabs()
CheckScreendump
diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim
index 0bcbd9c4a5..c6e2ebd406 100644
--- a/src/nvim/testdir/test_listchars.vim
+++ b/src/nvim/testdir/test_listchars.vim
@@ -1,6 +1,8 @@
" Tests for 'listchars' display with 'list' and :list
+source check.vim
source view_util.vim
+source screendump.vim
func Test_listchars()
enew!
@@ -517,4 +519,34 @@ func Test_listchars_window_local()
set list& listchars&
endfunc
+func Test_listchars_foldcolumn()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, ['aaa', '', 'a', 'aaaaaa'])
+ vsplit
+ vsplit
+ windo set signcolumn=yes foldcolumn=1 winminwidth=0 nowrap list listchars=extends:>,precedes:<
+ END
+ call writefile(lines, 'XTest_listchars')
+
+ let buf = RunVimInTerminal('-S XTest_listchars', {'rows': 10, 'cols': 60})
+
+ call term_sendkeys(buf, "13\<C-W>>")
+ call VerifyScreenDump(buf, 'Test_listchars_01', {})
+ call term_sendkeys(buf, "\<C-W>>")
+ call VerifyScreenDump(buf, 'Test_listchars_02', {})
+ call term_sendkeys(buf, "\<C-W>>")
+ call VerifyScreenDump(buf, 'Test_listchars_03', {})
+ call term_sendkeys(buf, "\<C-W>>")
+ call VerifyScreenDump(buf, 'Test_listchars_04', {})
+ call term_sendkeys(buf, "\<C-W>>")
+ call VerifyScreenDump(buf, 'Test_listchars_05', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XTest_listchars')
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_listlbr_utf8.vim b/src/nvim/testdir/test_listlbr_utf8.vim
index c38e0c5f3c..1f100d6244 100644
--- a/src/nvim/testdir/test_listlbr_utf8.vim
+++ b/src/nvim/testdir/test_listlbr_utf8.vim
@@ -69,6 +69,16 @@ func Test_nolinebreak_with_list()
call s:close_windows()
endfunc
+" this was causing a crash
+func Test_linebreak_with_list_and_tabs()
+ set linebreak list listchars=tab:⇀\ β‡₯ tabstop=100
+ new
+ call setline(1, "\t\t\ttext")
+ redraw
+ bwipe!
+ set nolinebreak nolist listchars&vim tabstop=8
+endfunc
+
func Test_linebreak_with_nolist()
call s:test_windows('setl nolist')
call setline(1, "\t*mask = nil;")
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index aff22f5d01..cb1d66cb98 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -2759,4 +2759,16 @@ func Test_normal_count_after_operator()
bw!
endfunc
+func Test_normal_gj_on_extra_wide_char()
+ new | 25vsp
+ let text='1 foooooooo ar e ins‍zwe1 foooooooo ins‍zwei' .
+ \ ' i drei vier fΓΌnf sechs sieben acht un zehn elf zwΓΆfl' .
+ \ ' dreizehn v ierzehn fΓΌnfzehn'
+ put =text
+ call cursor(2,1)
+ norm! gj
+ call assert_equal([0,2,25,0], getpos('.'))
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 5946732937..2312df5450 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -51,7 +51,7 @@ func Test_wildoptions()
call assert_equal('tagfile', &wildoptions)
endfunc
-function! Test_options()
+func Test_options_command()
let caught = 'ok'
try
options
@@ -88,7 +88,7 @@ function! Test_options()
" close option-window
close
-endfunction
+endfunc
function! Test_path_keep_commas()
" Test that changing 'path' keeps two commas.
@@ -368,6 +368,13 @@ func Test_set_all()
set tw& iskeyword& splitbelow&
endfunc
+func Test_set_one_column()
+ let out_mult = execute('set all')->split("\n")
+ let out_one = execute('set! all')->split("\n")
+ " one column should be two to four times as many lines
+ call assert_inrange(len(out_mult) * 2, len(out_mult) * 4, len(out_one))
+endfunc
+
func Test_set_values()
" The file is only generated when running "make test" in the src directory.
if filereadable('opt_test.vim')
diff --git a/src/nvim/testdir/test_scriptnames.vim b/src/nvim/testdir/test_scriptnames.vim
index fc6c910bfa..44ec146666 100644
--- a/src/nvim/testdir/test_scriptnames.vim
+++ b/src/nvim/testdir/test_scriptnames.vim
@@ -23,4 +23,10 @@ func Test_scriptnames()
bwipe
call delete('Xscripting')
+
+ let msgs = execute('messages')
+ scriptnames
+ call assert_equal(msgs, execute('messages'))
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_selectmode.vim b/src/nvim/testdir/test_selectmode.vim
new file mode 100644
index 0000000000..b483841060
--- /dev/null
+++ b/src/nvim/testdir/test_selectmode.vim
@@ -0,0 +1,57 @@
+" Test for Select-mode
+
+source shared.vim
+
+" Test for selecting a register with CTRL-R
+func Test_selectmode_register()
+ new
+
+ " Default behavior: use unnamed register
+ call setline(1, 'foo')
+ call setreg('"', 'bar')
+ call setreg('a', 'baz')
+ exe ":norm! v\<c-g>a"
+ call assert_equal(getline('.'), 'aoo')
+ call assert_equal('f', getreg('"'))
+ call assert_equal('baz', getreg('a'))
+
+ " Use the black hole register
+ call setline(1, 'foo')
+ call setreg('"', 'bar')
+ call setreg('a', 'baz')
+ exe ":norm! v\<c-g>\<c-r>_a"
+ call assert_equal(getline('.'), 'aoo')
+ call assert_equal('bar', getreg('"'))
+ call assert_equal('baz', getreg('a'))
+
+ " Invalid register: use unnamed register
+ call setline(1, 'foo')
+ call setreg('"', 'bar')
+ call setreg('a', 'baz')
+ exe ":norm! v\<c-g>\<c-r>?a"
+ call assert_equal(getline('.'), 'aoo')
+ call assert_equal('f', getreg('"'))
+ call assert_equal('baz', getreg('a'))
+
+ " Use unnamed register
+ call setline(1, 'foo')
+ call setreg('"', 'bar')
+ call setreg('a', 'baz')
+ exe ":norm! v\<c-g>\<c-r>\"a"
+ call assert_equal(getline('.'), 'aoo')
+ call assert_equal('f', getreg('"'))
+ call assert_equal('baz', getreg('a'))
+
+ " use specicifed register, unnamed register is also written
+ call setline(1, 'foo')
+ call setreg('"', 'bar')
+ call setreg('a', 'baz')
+ exe ":norm! v\<c-g>\<c-r>aa"
+ call assert_equal(getline('.'), 'aoo')
+ call assert_equal('f', getreg('"'))
+ call assert_equal('f', getreg('a'))
+
+ bw!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim
index a3e4dcdd25..f40c9ae097 100644
--- a/src/nvim/testdir/test_statusline.vim
+++ b/src/nvim/testdir/test_statusline.vim
@@ -186,7 +186,16 @@ func Test_statusline()
set virtualedit=all
norm 10|
call assert_match('^10,-10\s*$', s:get_statusline())
+ set list
+ call assert_match('^10,-10\s*$', s:get_statusline())
set virtualedit&
+ exe "norm A\<Tab>\<Tab>a\<Esc>"
+ " In list mode a <Tab> is shown as "^I", which is 2-wide.
+ call assert_match('^9,-9\s*$', s:get_statusline())
+ set list&
+ " Now the second <Tab> ends at the 16th screen column.
+ call assert_match('^17,-17\s*$', s:get_statusline())
+ undo
" %w: Preview window flag, text is "[Preview]".
" %W: Preview window flag, text is ",PRV".
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index aae315b2c5..09eed4e10d 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -379,4 +379,27 @@ func Test_timer_invalid_callback()
call assert_fails('call timer_start(0, "0")', 'E921')
endfunc
+func Test_timer_using_win_execute_undo_sync()
+ let bufnr1 = bufnr()
+ new
+ let g:bufnr2 = bufnr()
+ let g:winid = win_getid()
+ exe "buffer " .. bufnr1
+ wincmd w
+ call setline(1, ['test'])
+ autocmd InsertEnter * call timer_start(100, { -> win_execute(g:winid, 'buffer ' .. g:bufnr2) })
+ call timer_start(200, { -> feedkeys("\<CR>bbbb\<Esc>") })
+ call feedkeys("Oaaaa", 'x!t')
+ " will hang here until the second timer fires
+ call assert_equal(['aaaa', 'bbbb', 'test'], getline(1, '$'))
+ undo
+ call assert_equal(['test'], getline(1, '$'))
+
+ bwipe!
+ bwipe!
+ unlet g:winid
+ unlet g:bufnr2
+ au! InsertEnter
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim
index 0a218898ed..d2a5258bd3 100644
--- a/src/nvim/testdir/test_virtualedit.vim
+++ b/src/nvim/testdir/test_virtualedit.vim
@@ -258,4 +258,139 @@ func Test_yank_paste_small_del_reg()
set virtualedit=
endfunc
+" After calling s:TryVirtualeditReplace(), line 1 will contain one of these
+" two strings, depending on whether virtual editing is on or off.
+let s:result_ve_on = 'a x'
+let s:result_ve_off = 'x'
+
+" Utility function for Test_global_local_virtualedit()
+func s:TryVirtualeditReplace()
+ call setline(1, 'a')
+ normal gg7l
+ normal rx
+endfunc
+
+" Test for :set and :setlocal
+func Test_global_local_virtualedit()
+ new
+
+ " Verify that 'virtualedit' is initialized to empty, can be set globally to
+ " all and to empty, and can be set locally to all and to empty.
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ set ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ set ve=
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ setlocal ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ setlocal ve=
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+
+ " Verify that :set affects multiple windows.
+ split
+ set ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ wincmd p
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ set ve=
+ wincmd p
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ bwipe!
+
+ " Verify that :setlocal affects only the current window.
+ new
+ split
+ setlocal ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ wincmd p
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ bwipe!
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+
+ " Verify that the buffer 'virtualedit' state follows the global value only
+ " when empty and that "none" works as expected.
+ "
+ " 'virtualedit' State
+ " +--------+--------------------------+
+ " | Local | Global |
+ " | | |
+ " +--------+--------+--------+--------+
+ " | | "" | "all" | "none" |
+ " +--------+--------+--------+--------+
+ " | "" | off | on | off |
+ " | "all" | on | on | on |
+ " | "none" | off | off | off |
+ " +--------+--------+--------+--------+
+ new
+
+ setglobal ve=
+ setlocal ve=
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ setlocal ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ setlocal ve=none
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+
+ setglobal ve=all
+ setlocal ve=
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ setlocal ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ setlocal ve=none
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ setlocal ve=NONE
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+
+ setglobal ve=none
+ setlocal ve=
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ setlocal ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ setlocal ve=none
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+
+ bwipe!
+
+ " Verify that the 'virtualedit' state is copied to new windows.
+ new
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ split
+ setlocal ve=all
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ split
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_on, getline(1))
+ setlocal ve=
+ split
+ call s:TryVirtualeditReplace()
+ call assert_equal(s:result_ve_off, getline(1))
+ bwipe!
+
+ setlocal virtualedit&
+ set virtualedit&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index d58ca92a2f..76274fb038 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -1103,6 +1103,13 @@ func Test_visual_put_blockedit_zy_and_zp()
bw!
endfunc
+func Test_visual_block_yank_zy()
+ new
+ " this was reading before the start of the line
+ exe "norm o\<C-T>\<Esc>\<C-V>zy"
+ bwipe!
+endfunc
+
func Test_visual_block_with_virtualedit()
CheckScreendump
@@ -1117,10 +1124,67 @@ func Test_visual_block_with_virtualedit()
call term_sendkeys(buf, "\<C-V>gg$")
call VerifyScreenDump(buf, 'Test_visual_block_with_virtualedit', {})
+ call term_sendkeys(buf, "\<Esc>gg\<C-V>G$")
+ call VerifyScreenDump(buf, 'Test_visual_block_with_virtualedit2', {})
+
" clean up
call term_sendkeys(buf, "\<Esc>")
call StopVimInTerminal(buf)
- call delete('XTest_beval')
+ call delete('XTest_block')
+endfunc
+
+func Test_visual_block_ctrl_w_f()
+ " Emtpy block selected in new buffer should not result in an error.
+ au! BufNew foo sil norm f
+ edit foo
+
+ au! BufNew
+endfunc
+
+func Test_visual_reselect_with_count()
+ " this was causing an illegal memory access
+ let lines =<< trim END
+
+
+
+ :
+ r<sfile>
+ exe "%norm e3\<c-v>kr\t"
+ :
+
+ :
+ END
+ call writefile(lines, 'XvisualReselect')
+ source XvisualReselect
+
+ bwipe!
+ call delete('XvisualReselect')
+endfunc
+
+" this was leaving the end of the Visual area beyond the end of a line
+func Test_visual_ex_copy_line()
+ new
+ call setline(1, ["aaa", "bbbbbbbbbxbb"])
+ /x
+ exe "normal ggvjfxO"
+ t0
+ normal gNU
+ bwipe!
+endfunc
+
+" This was leaving the end of the Visual area beyond the end of a line.
+" Set 'undolevels' to start a new undo block.
+func Test_visual_undo_deletes_last_line()
+ new
+ call setline(1, ["aaa", "ccc", "dyd"])
+ set undolevels=100
+ exe "normal obbbbbbbbbxbb\<Esc>"
+ set undolevels=100
+ /y
+ exe "normal ggvjfxO"
+ undo
+ normal gNU
+ bwipe!
endfunc
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index d18f35a43a..2d8df4cad8 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -2633,6 +2633,10 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
}
}
+ if (VIsual_active) {
+ check_pos(curbuf, &VIsual);
+ }
+
smsg_attr_keep(0,
_("%" PRId64 " %s; %s #%" PRId64 " %s"),
u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount,