aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/eval.c64
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/ex_cmds.c8
-rw-r--r--src/nvim/ex_docmd.c4
-rw-r--r--src/nvim/fileio.c123
-rw-r--r--src/nvim/os/time.c39
-rw-r--r--src/nvim/path.c47
-rw-r--r--src/nvim/testdir/Makefile2
-rw-r--r--src/nvim/testdir/test_autocmd.vim31
-rw-r--r--src/nvim/testdir/test_expr.vim11
-rw-r--r--src/nvim/testdir/test_usercommands.vim64
-rw-r--r--src/nvim/version.c15
12 files changed, 275 insertions, 134 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 45f317fbea..4501c2e0a6 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -5162,12 +5162,18 @@ dict_equal (
dictitem_T *item2;
int todo;
- if (d1 == NULL || d2 == NULL)
- return FALSE;
- if (d1 == d2)
- return TRUE;
- if (dict_len(d1) != dict_len(d2))
- return FALSE;
+ if (d1 == NULL && d2 == NULL) {
+ return true;
+ }
+ if (d1 == NULL || d2 == NULL) {
+ return false;
+ }
+ if (d1 == d2) {
+ return true;
+ }
+ if (dict_len(d1) != dict_len(d2)) {
+ return false;
+ }
todo = (int)d1->dv_hashtab.ht_used;
for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi) {
@@ -6669,9 +6675,12 @@ dictitem_T *dict_find(dict_T *d, char_u *key, int len)
char_u *tofree = NULL;
hashitem_T *hi;
- if (len < 0)
+ if (d == NULL) {
+ return NULL;
+ }
+ if (len < 0) {
akey = key;
- else if (len >= AKEYLEN) {
+ } else if (len >= AKEYLEN) {
tofree = akey = vim_strnsave(key, len);
} else {
/* Avoid a malloc/free by using buf[]. */
@@ -9242,7 +9251,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
dict_T *d = NULL;
typval_T save_val;
typval_T save_key;
- int rem;
+ int rem = false;
int todo;
char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
char_u *arg_errmsg = (char_u *)(map ? N_("map() argument")
@@ -10067,7 +10076,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} else if (STRCMP(varname, "changedtick") == 0) {
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = curbuf->b_changedtick;
- done = TRUE;
+ done = true;
} else {
/* Look up the variable. */
/* Let getbufvar({nr}, "") return the "b:" dictionary. */
@@ -15064,7 +15073,6 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
buf_T *buf;
- aco_save_T aco;
char_u *varname, *bufvarname;
typval_T *varp;
char_u nbuf[NUMBUFLEN];
@@ -15077,29 +15085,34 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
varp = &argvars[2];
if (buf != NULL && varname != NULL && varp != NULL) {
- /* set curbuf to be our buf, temporarily */
- aucmd_prepbuf(&aco, buf);
-
if (*varname == '&') {
long numval;
char_u *strval;
- int error = FALSE;
+ int error = false;
+ aco_save_T aco;
+
+ // set curbuf to be our buf, temporarily
+ aucmd_prepbuf(&aco, buf);
++varname;
numval = get_tv_number_chk(varp, &error);
strval = get_tv_string_buf_chk(varp, nbuf);
if (!error && strval != NULL)
set_option_value(varname, numval, strval, OPT_LOCAL);
+
+ // reset notion of buffer
+ aucmd_restbuf(&aco);
} else {
+ buf_T *save_curbuf = curbuf;
+
bufvarname = xmalloc(STRLEN(varname) + 3);
+ curbuf = buf;
STRCPY(bufvarname, "b:");
STRCPY(bufvarname + 2, varname);
set_var(bufvarname, varp, TRUE);
xfree(bufvarname);
+ curbuf = save_curbuf;
}
-
- /* reset notion of buffer */
- aucmd_restbuf(&aco);
}
}
@@ -15516,7 +15529,10 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (argvars[1].v_type == VAR_LIST) {
- int len = argvars[1].vval.v_list->lv_len;
+ list_T *ll = argvars[1].vval.v_list;
+ // If the list is NULL handle like an empty list.
+ int len = ll == NULL ? 0 : ll->lv_len;
+
// First half: use for pointers to result lines; second half: use for
// pointers to allocated copies.
char_u **lstval = xmalloc(sizeof(char_u *) * ((len + 1) * 2));
@@ -15525,7 +15541,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char_u **curallocval = allocval;
char_u buf[NUMBUFLEN];
- for (listitem_T *li = argvars[1].vval.v_list->lv_first;
+ for (listitem_T *li = ll == NULL ? NULL : ll->lv_first;
li != NULL;
li = li->li_next) {
char_u *strval = get_tv_string_buf_chk(&li->li_tv, buf);
@@ -17249,14 +17265,6 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
-/*
- * "test(list)" function: Just checking the walls...
- */
-static void f_test(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- /* Used for unit testing. Change the code below to your liking. */
-}
-
static bool callback_from_typval(Callback *callback, typval_T *arg)
{
if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) {
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 980a8d2326..5fb99fecc6 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -302,7 +302,6 @@ return {
tanh={args=1, func="float_op_wrapper", data="&tanh"},
tempname={},
termopen={args={1, 2}},
- test={args=1},
timer_start={args={2,3}},
timer_stop={args=1},
tolower={args=1},
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index fcaf17a0fc..b1a17e8c44 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1611,6 +1611,7 @@ int do_write(exarg_T *eap)
int retval = FAIL;
char_u *free_fname = NULL;
buf_T *alt_buf = NULL;
+ int name_was_missing;
if (not_writing()) /* check 'write' option */
return FAIL;
@@ -1734,6 +1735,7 @@ int do_write(exarg_T *eap)
fname = curbuf->b_sfname;
}
+ name_was_missing = curbuf->b_ffname == NULL;
retval = buf_write(curbuf, ffname, fname, eap->line1, eap->line2,
eap, eap->append, eap->forceit, TRUE, FALSE);
@@ -1743,7 +1745,11 @@ int do_write(exarg_T *eap)
curbuf->b_p_ro = FALSE;
redraw_tabline = TRUE;
}
- /* Change directories when the 'acd' option is set. */
+ }
+
+ // Change directories when the 'acd' option is set and the file name
+ // got changed or set.
+ if (eap->cmdidx == CMD_saveas || name_was_missing) {
do_autochdir();
}
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 79a0b5849f..eccece7ac7 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1374,7 +1374,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
}
continue;
}
- if (!checkforcmd(&ea.cmd, "noswapfile", 6)) {
+ if (!checkforcmd(&ea.cmd, "noswapfile", 3)) {
break;
}
cmdmod.noswapfile = true;
@@ -1711,7 +1711,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
xfree(p);
// If the autocommands did something and didn't cause an error, try
// finding the command again.
- p = (ret && !aborting()) ? find_command(&ea, NULL) : NULL;
+ p = (ret && !aborting()) ? find_command(&ea, NULL) : ea.cmd;
}
if (p == NULL) {
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 529b48adbd..bbfa56dfbf 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -5652,13 +5652,14 @@ static event_T event_name2nr(char_u *start, char_u **end)
int i;
int len;
- /* the event name ends with end of line, a blank or a comma */
- for (p = start; *p && !ascii_iswhite(*p) && *p != ','; ++p)
- ;
- for (i = 0; event_names[i].name != NULL; ++i) {
- len = (int) event_names[i].len;
- if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
+ // the event name ends with end of line, '|', a blank or a comma */
+ for (p = start; *p && !ascii_iswhite(*p) && *p != ',' && *p != '|'; p++) {
+ }
+ for (i = 0; event_names[i].name != NULL; i++) {
+ len = (int)event_names[i].len;
+ if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0) {
break;
+ }
}
if (*p == ',')
++p;
@@ -5700,7 +5701,7 @@ find_end_event (
}
pat = arg + 1;
} else {
- for (pat = arg; *pat && !ascii_iswhite(*pat); pat = p) {
+ for (pat = arg; *pat && *pat != '|' && !ascii_iswhite(*pat); pat = p) {
if ((int)event_name2nr(pat, &p) >= (int)NUM_EVENTS) {
if (have_group)
EMSG2(_("E216: No such event: %s"), pat);
@@ -5815,8 +5816,9 @@ void au_event_restore(char_u *old_ei)
*
* Mostly a {group} argument can optionally appear before <event>.
*/
-void do_autocmd(char_u *arg, int forceit)
+void do_autocmd(char_u *arg_in, int forceit)
{
+ char_u *arg = arg_in;
char_u *pat;
char_u *envpat = NULL;
char_u *cmd;
@@ -5825,10 +5827,13 @@ void do_autocmd(char_u *arg, int forceit)
int nested = FALSE;
int group;
- /*
- * Check for a legal group name. If not, use AUGROUP_ALL.
- */
- group = au_get_grouparg(&arg);
+ if (*arg == '|') {
+ arg = (char_u *)"";
+ group = AUGROUP_ALL; // no argument, use all groups
+ } else {
+ // Check for a legal group name. If not, use AUGROUP_ALL.
+ group = au_get_grouparg(&arg);
+ }
/*
* Scan over the events.
@@ -5838,50 +5843,54 @@ void do_autocmd(char_u *arg, int forceit)
if (pat == NULL)
return;
- /*
- * Scan over the pattern. Put a NUL at the end.
- */
pat = skipwhite(pat);
- cmd = pat;
- while (*cmd && (!ascii_iswhite(*cmd) || cmd[-1] == '\\'))
- cmd++;
- if (*cmd)
- *cmd++ = NUL;
-
- /* Expand environment variables in the pattern. Set 'shellslash', we want
- * forward slashes here. */
- if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL) {
+ if (*pat == '|') {
+ pat = (char_u *)"";
+ cmd = (char_u *)"";
+ } else {
+ // Scan over the pattern. Put a NUL at the end.
+ cmd = pat;
+ while (*cmd && (!ascii_iswhite(*cmd) || cmd[-1] == '\\')) {
+ cmd++;
+ }
+ if (*cmd) {
+ *cmd++ = NUL;
+ }
+
+ // Expand environment variables in the pattern. Set 'shellslash', we want
+ // forward slashes here.
+ if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL) {
#ifdef BACKSLASH_IN_FILENAME
- int p_ssl_save = p_ssl;
+ int p_ssl_save = p_ssl;
- p_ssl = TRUE;
+ p_ssl = true;
#endif
- envpat = expand_env_save(pat);
+ envpat = expand_env_save(pat);
#ifdef BACKSLASH_IN_FILENAME
- p_ssl = p_ssl_save;
+ p_ssl = p_ssl_save;
#endif
- if (envpat != NULL)
- pat = envpat;
- }
+ if (envpat != NULL) {
+ pat = envpat;
+ }
+ }
- /*
- * Check for "nested" flag.
- */
- cmd = skipwhite(cmd);
- if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])) {
- nested = TRUE;
- cmd = skipwhite(cmd + 6);
- }
+ // Check for "nested" flag.
+ cmd = skipwhite(cmd);
+ if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0
+ && ascii_iswhite(cmd[6])) {
+ nested = true;
+ cmd = skipwhite(cmd + 6);
+ }
- /*
- * Find the start of the commands.
- * Expand <sfile> in it.
- */
- if (*cmd != NUL) {
- cmd = expand_sfile(cmd);
- if (cmd == NULL) /* some error */
- return;
- need_free = TRUE;
+ // Find the start of the commands.
+ // Expand <sfile> in it.
+ if (*cmd != NUL) {
+ cmd = expand_sfile(cmd);
+ if (cmd == NULL) { // some error
+ return;
+ }
+ need_free = true;
+ }
}
/*
@@ -5895,16 +5904,17 @@ void do_autocmd(char_u *arg, int forceit)
/*
* Loop over the events.
*/
- last_event = (event_T)-1; /* for listing the event name */
- last_group = AUGROUP_ERROR; /* for listing the group name */
- if (*arg == '*' || *arg == NUL) {
+ last_event = (event_T)-1; // for listing the event name
+ last_group = AUGROUP_ERROR; // for listing the group name
+ if (*arg == '*' || *arg == NUL || *arg == '|') {
for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
- event = (event_T)((int)event + 1))
- if (do_autocmd_event(event, pat,
- nested, cmd, forceit, group) == FAIL)
+ event = (event_T)((int)event + 1)) {
+ if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) {
break;
+ }
+ }
} else {
- while (*arg && !ascii_iswhite(*arg)) {
+ while (*arg && *arg != '|' && !ascii_iswhite(*arg)) {
event_T event = event_name2nr(arg, &arg);
assert(event < NUM_EVENTS);
if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) {
@@ -5931,7 +5941,8 @@ static int au_get_grouparg(char_u **argp)
char_u *arg = *argp;
int group = AUGROUP_ALL;
- p = skiptowhite(arg);
+ for (p = arg; *p && !ascii_iswhite(*p) && *p != '|'; p++) {
+ }
if (p > arg) {
group_name = vim_strnsave(arg, (int)(p - arg));
group = au_find_group(group_name);
diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c
index 2205ad0958..8ce2ecf4f4 100644
--- a/src/nvim/os/time.c
+++ b/src/nvim/os/time.c
@@ -7,6 +7,7 @@
#include <uv.h>
#include "nvim/os/time.h"
+#include "nvim/os/input.h"
#include "nvim/event/loop.h"
#include "nvim/vim.h"
#include "nvim/main.h"
@@ -34,10 +35,10 @@ uint64_t os_hrtime(void)
return uv_hrtime();
}
-/// Sleeps for a certain amount of milliseconds
+/// Sleeps for a certain amount of milliseconds.
///
/// @param milliseconds Number of milliseconds to sleep
-/// @param ignoreinput If true, allow a SIGINT to interrupt us
+/// @param ignoreinput If true, only SIGINT (CTRL-C) can interrupt.
void os_delay(uint64_t milliseconds, bool ignoreinput)
{
if (ignoreinput) {
@@ -46,26 +47,42 @@ void os_delay(uint64_t milliseconds, bool ignoreinput)
}
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)milliseconds, got_int);
} else {
- os_microdelay(milliseconds * 1000);
+ os_microdelay(milliseconds * 1000u, ignoreinput);
}
}
-/// Sleeps for a certain amount of microseconds
+/// Sleeps for a certain amount of microseconds.
///
-/// @param microseconds Number of microseconds to sleep
-void os_microdelay(uint64_t microseconds)
+/// @param ms Number of microseconds to sleep.
+/// @param ignoreinput If true, ignore all input (including SIGINT/CTRL-C).
+/// If false, waiting is aborted on any input.
+void os_microdelay(uint64_t ms, bool ignoreinput)
{
- uint64_t elapsed = 0;
- uint64_t ns = microseconds * 1000; // convert to nanoseconds
+ uint64_t elapsed = 0u;
uint64_t base = uv_hrtime();
+ // Convert microseconds to nanoseconds, or UINT64_MAX on overflow.
+ const uint64_t ns = (ms < UINT64_MAX / 1000u) ? ms * 1000u : UINT64_MAX;
uv_mutex_lock(&delay_mutex);
while (elapsed < ns) {
- if (uv_cond_timedwait(&delay_cond, &delay_mutex, ns - elapsed)
- == UV_ETIMEDOUT)
+ // If ignoring input, we simply wait the full delay.
+ // Else we check for input in ~100ms intervals.
+ const uint64_t ns_delta = ignoreinput
+ ? ns - elapsed
+ : MIN(ns - elapsed, 100000000u); // 100ms
+
+ const int rv = uv_cond_timedwait(&delay_cond, &delay_mutex, ns_delta);
+ if (0 != rv && UV_ETIMEDOUT != rv) {
+ assert(false);
+ break;
+ } // Else: Timeout proceeded normally.
+
+ if (!ignoreinput && os_char_avail()) {
break;
- uint64_t now = uv_hrtime();
+ }
+
+ const uint64_t now = uv_hrtime();
elapsed += now - base;
base = now;
}
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 6149e1ab99..3d1def8dd4 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -417,15 +417,11 @@ char *FullName_save(char *fname, bool force)
}
char *buf = xmalloc(MAXPATHL);
- char *new_fname = NULL;
- if (vim_FullName(fname, buf, MAXPATHL, force) != FAIL) {
- new_fname = xstrdup(buf);
- } else {
- new_fname = xstrdup(fname);
+ if (vim_FullName(fname, buf, MAXPATHL, force) == FAIL) {
+ xfree(buf);
+ return xstrdup(fname);
}
- xfree(buf);
-
- return new_fname;
+ return buf;
}
/// Saves the absolute path.
@@ -1649,30 +1645,37 @@ bool vim_isAbsName(char_u *name)
/// Save absolute file name to "buf[len]".
///
-/// @param fname is the filename to evaluate
-/// @param[out] buf is the buffer for returning the absolute path for `fname`
-/// @param len is the length of `buf`
-/// @param force is a flag to force expanding even if the path is absolute
+/// @param fname filename to evaluate
+/// @param[out] buf contains `fname` absolute path, or:
+/// - truncated `fname` if longer than `len`
+/// - unmodified `fname` if absolute path fails or is a URL
+/// @param len length of `buf`
+/// @param force flag to force expanding even if the path is absolute
///
/// @return FAIL for failure, OK otherwise
int vim_FullName(const char *fname, char *buf, size_t len, bool force)
FUNC_ATTR_NONNULL_ARG(2)
{
- int retval = OK;
- int url;
-
*buf = NUL;
- if (fname == NULL)
+ if (fname == NULL) {
return FAIL;
+ }
- url = path_with_url(fname);
- if (!url)
- retval = path_get_absolute_path((char_u *)fname, (char_u *)buf, len, force);
- if (url || retval == FAIL) {
- /* something failed; use the file name (truncate when too long) */
+ if (strlen(fname) > (len - 1)) {
+ xstrlcpy(buf, fname, len); // truncate
+ return FAIL;
+ }
+
+ if (path_with_url(fname)) {
xstrlcpy(buf, fname, len);
+ return OK;
}
- return retval;
+
+ int rv = path_get_absolute_path((char_u *)fname, (char_u *)buf, len, force);
+ if (rv == FAIL) {
+ xstrlcpy(buf, fname, len); // something failed; use the filename
+ }
+ return rv;
}
/// Get the full resolved path for `fname`
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 84a0c0b889..612071e2e2 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -43,8 +43,8 @@ NEW_TESTS = \
test_quickfix.res \
test_signs.res \
test_syntax.res \
- test_usercommands.res \
test_timers.res \
+ test_usercommands.res \
test_viml.res \
test_visual.res \
test_window_id.res \
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 1dceb70cd4..5675697dc4 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -19,6 +19,7 @@ if has('timers')
call timer_start(100, 'ExitInsertMode')
call feedkeys('a', 'x!')
call assert_equal(1, g:triggered)
+ au! CursorHoldI
endfunc
func Test_cursorhold_insert_ctrl_x()
@@ -29,6 +30,7 @@ if has('timers')
" CursorHoldI does not trigger after CTRL-X
call feedkeys("a\<C-X>", 'x!')
call assert_equal(0, g:triggered)
+ au! CursorHoldI
endfunc
endif
@@ -58,5 +60,34 @@ function Test_bufunload()
bwipeout
call assert_equal(["bufunload", "bufdelete", "bufwipeout"], s:li)
+ au! test_bufunload_group
augroup! test_bufunload_group
endfunc
+
+func s:AddAnAutocmd()
+ augroup vimBarTest
+ au BufReadCmd * echo 'hello'
+ augroup END
+ call assert_equal(3, len(split(execute('au vimBarTest'), "\n")))
+endfunc
+
+func Test_early_bar()
+ " test that a bar is recognized before the {event}
+ call s:AddAnAutocmd()
+ augroup vimBarTest | au! | augroup END
+ call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+
+ call s:AddAnAutocmd()
+ augroup vimBarTest| au!| augroup END
+ call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+
+ " test that a bar is recognized after the {event}
+ call s:AddAnAutocmd()
+ augroup vimBarTest| au!BufReadCmd| augroup END
+ call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+
+ " test that a bar is recognized after the {group}
+ call s:AddAnAutocmd()
+ au! vimBarTest|echo 'hello'
+ call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+endfunc
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 571a37c62c..7483973fca 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -81,3 +81,14 @@ func Test_loop_over_null_list()
call assert_true(0, 'should not get here')
endfor
endfunc
+
+func Test_compare_null_dict()
+ call assert_fails('let x = v:_null_dict[10]')
+ call assert_equal({}, {})
+ call assert_equal(v:_null_dict, v:_null_dict)
+ call assert_notequal({}, v:_null_dict)
+endfunc
+
+func Test_set_reg_null_list()
+ call setreg('x', v:_null_list)
+endfunc
diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim
index f593d16dbf..d0864ec64c 100644
--- a/src/nvim/testdir/test_usercommands.vim
+++ b/src/nvim/testdir/test_usercommands.vim
@@ -8,31 +8,57 @@ function Test_cmdmods()
MyCmd
aboveleft MyCmd
+ abo MyCmd
belowright MyCmd
+ bel MyCmd
botright MyCmd
+ bo MyCmd
browse MyCmd
+ bro MyCmd
confirm MyCmd
+ conf MyCmd
hide MyCmd
+ hid MyCmd
keepalt MyCmd
+ keepa MyCmd
keepjumps MyCmd
+ keepj MyCmd
keepmarks MyCmd
+ kee MyCmd
keeppatterns MyCmd
+ keepp MyCmd
+ leftabove MyCmd " results in :aboveleft
+ lefta MyCmd
lockmarks MyCmd
+ loc MyCmd
+ " noautocmd MyCmd
noswapfile MyCmd
+ nos MyCmd
+ rightbelow MyCmd " results in :belowright
+ rightb MyCmd
+ " sandbox MyCmd
silent MyCmd
+ sil MyCmd
tab MyCmd
topleft MyCmd
+ to MyCmd
+ " unsilent MyCmd
verbose MyCmd
+ verb MyCmd
vertical MyCmd
+ vert MyCmd
aboveleft belowright botright browse confirm hide keepalt keepjumps
\ keepmarks keeppatterns lockmarks noswapfile silent tab
\ topleft verbose vertical MyCmd
- call assert_equal(' aboveleft belowright botright browse confirm ' .
- \ 'hide keepalt keepjumps keepmarks keeppatterns lockmarks ' .
- \ 'noswapfile silent tab topleft verbose vertical aboveleft ' .
- \ 'belowright botright browse confirm hide keepalt keepjumps ' .
+ call assert_equal(' aboveleft aboveleft belowright belowright botright ' .
+ \ 'botright browse browse confirm confirm hide hide ' .
+ \ 'keepalt keepalt keepjumps keepjumps keepmarks keepmarks ' .
+ \ 'keeppatterns keeppatterns aboveleft aboveleft lockmarks lockmarks noswapfile ' .
+ \ 'noswapfile belowright belowright silent silent tab topleft topleft verbose verbose ' .
+ \ 'vertical vertical ' .
+ \ 'aboveleft belowright botright browse confirm hide keepalt keepjumps ' .
\ 'keepmarks keeppatterns lockmarks noswapfile silent tab topleft ' .
\ 'verbose vertical ', g:mods)
@@ -46,3 +72,33 @@ function Test_cmdmods()
delcommand MyQCmd
unlet g:mods
endfunction
+
+func Test_Ambiguous()
+ command Doit let g:didit = 'yes'
+ command Dothat let g:didthat = 'also'
+ call assert_fails('Do', 'E464:')
+ Doit
+ call assert_equal('yes', g:didit)
+ Dothat
+ call assert_equal('also', g:didthat)
+ unlet g:didit
+ unlet g:didthat
+
+ delcommand Doit
+ Do
+ call assert_equal('also', g:didthat)
+ delcommand Dothat
+endfunc
+
+func Test_CmdUndefined()
+ call assert_fails('Doit', 'E492:')
+ au CmdUndefined Doit :command Doit let g:didit = 'yes'
+ Doit
+ call assert_equal('yes', g:didit)
+ delcommand Doit
+
+ call assert_fails('Dothat', 'E492:')
+ au CmdUndefined * let g:didnot = 'yes'
+ call assert_fails('Dothat', 'E492:')
+ call assert_equal('yes', g:didnot)
+endfunc
diff --git a/src/nvim/version.c b/src/nvim/version.c
index f6f984bc8b..19062be730 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -313,7 +313,7 @@ static int included_patches[] = {
// 2130 NA
// 2129 NA
2128,
- // 2127,
+ 2127,
2126,
// 2125 NA
2124,
@@ -330,14 +330,14 @@ static int included_patches[] = {
// 2113,
2112,
// 2111,
- // 2110,
+ 2110,
2109,
// 2108 NA
// 2107,
// 2106,
// 2105 NA
// 2104,
- // 2103,
+ 2103,
// 2102 NA
// 2101,
// 2100,
@@ -397,7 +397,7 @@ static int included_patches[] = {
// 2046,
// 2045 NA
// 2044,
- // 2043,
+ 2043,
// 2042 NA
// 2041 NA
// 2040 NA
@@ -425,7 +425,7 @@ static int included_patches[] = {
// 2018,
// 2017,
// 2016 NA
- // 2015,
+ 2015,
2014,
2013,
2012,
@@ -593,7 +593,7 @@ static int included_patches[] = {
// 1850 NA
// 1849 NA
// 1848 NA
- // 1847,
+ 1847,
// 1846 NA
// 1845 NA
// 1844 NA
@@ -602,7 +602,7 @@ static int included_patches[] = {
1841,
1840,
1839,
- // 1838 NA
+ 1838,
1837,
1836,
1835,
@@ -689,7 +689,6 @@ static int included_patches[] = {
1755,
1754,
1753,
- // 1753,
1752,
1751,
// 1750 NA