diff options
-rw-r--r-- | src/nvim/api/private/helpers.c | 5 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 33 | ||||
-rw-r--r-- | src/nvim/ops.c | 42 | ||||
-rw-r--r-- | src/nvim/testdir/test_visual.vim | 15 | ||||
-rw-r--r-- | src/nvim/window.c | 6 | ||||
-rw-r--r-- | test/functional/api/command_spec.lua | 22 |
6 files changed, 89 insertions, 34 deletions
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index ddcfff0097..2b107a3f27 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1384,6 +1384,11 @@ void add_user_command(String name, Object command, Dict(user_command) *opts, int LuaRef luaref = LUA_NOREF; LuaRef compl_luaref = LUA_NOREF; + if (!uc_validate_name(name.data)) { + api_set_error(err, kErrorTypeValidation, "Invalid command name"); + goto err; + } + if (mb_islower(name.data[0])) { api_set_error(err, kErrorTypeValidation, "'name' must begin with an uppercase letter"); goto err; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4dba0b97ed..87f8865133 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5164,6 +5164,24 @@ char_u *get_command_name(expand_T *xp, int idx) return cmdnames[idx].cmd_name; } +/// Check for a valid user command name +/// +/// If the given {name} is valid, then a pointer to the end of the valid name is returned. +/// Otherwise, returns NULL. +char *uc_validate_name(char *name) +{ + if (ASCII_ISALPHA(*name)) { + while (ASCII_ISALNUM(*name)) { + name++; + } + } + if (!ends_excmd(*name) && !ascii_iswhite(*name)) { + return NULL; + } + + return name; +} + int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, int flags, int compl, char_u *compl_arg, LuaRef compl_luaref, cmd_addr_T addr_type, LuaRef luaref, bool force) @@ -5679,23 +5697,18 @@ static void ex_command(exarg_T *eap) // Get the name (if any) and skip to the following argument. name = p; - if (ASCII_ISALPHA(*p)) { - while (ASCII_ISALNUM(*p)) { - p++; - } - } - if (!ends_excmd(*p) && !ascii_iswhite(*p)) { + end = (char_u *)uc_validate_name((char *)name); + if (!end) { emsg(_("E182: Invalid command name")); return; } - end = p; - name_len = (int)(end - name); + name_len = (size_t)(end - name); // If there is nothing after the name, and no attributes were specified, // we are listing commands p = skipwhite(end); if (!has_attr && ends_excmd(*p)) { - uc_list(name, end - name); + uc_list(name, name_len); } else if (!ASCII_ISUPPER(*name)) { emsg(_("E183: User defined commands must start with an uppercase letter")); } else if (name_len <= 4 && STRNCMP(name, "Next", name_len) == 0) { @@ -5703,7 +5716,7 @@ static void ex_command(exarg_T *eap) } else if (compl > 0 && (argt & EX_EXTRA) == 0) { emsg(_(e_complete_used_without_nargs)); } else { - uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, LUA_NOREF, + uc_add_command(name, name_len, p, argt, def, flags, compl, compl_arg, LUA_NOREF, addr_type_arg, LUA_NOREF, eap->forceit); } } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index c9a99fef84..18facef13c 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1961,11 +1961,14 @@ static int op_replace(oparg_T *oap, int c) while (ltoreq(curwin->w_cursor, oap->end)) { n = gchar_cursor(); if (n != NUL) { - if (utf_char2len(c) > 1 || utf_char2len(n) > 1) { + int new_byte_len = utf_char2len(c); + int old_byte_len = utfc_ptr2len(get_cursor_pos_ptr()); + + if (new_byte_len > 1 || old_byte_len > 1) { // This is slow, but it handles replacing a single-byte // with a multi-byte and the other way around. if (curwin->w_cursor.lnum == oap->end.lnum) { - oap->end.col += utf_char2len(c) - utf_char2len(n); + oap->end.col += new_byte_len - old_byte_len; } replace_character(c); } else { @@ -2315,33 +2318,24 @@ void op_insert(oparg_T *oap, long count1) // The user may have moved the cursor before inserting something, try // to adjust the block for that. But only do it, if the difference // does not come from indent kicking in. - if (oap->start.lnum == curbuf->b_op_start_orig.lnum - && !bd.is_MAX - && !did_indent) { + if (oap->start.lnum == curbuf->b_op_start_orig.lnum && !bd.is_MAX && !did_indent) { const int t = getviscol2(curbuf->b_op_start_orig.col, curbuf->b_op_start_orig.coladd); - if (!bd.is_MAX) { - if (oap->op_type == OP_INSERT - && oap->start.col + oap->start.coladd - != curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { - oap->start.col = curbuf->b_op_start_orig.col; - pre_textlen -= t - oap->start_vcol; - oap->start_vcol = t; - } else if (oap->op_type == OP_APPEND - && oap->start.col + oap->start.coladd - >= curbuf->b_op_start_orig.col - + curbuf->b_op_start_orig.coladd) { - oap->start.col = curbuf->b_op_start_orig.col; - // reset pre_textlen to the value of OP_INSERT - pre_textlen += bd.textlen; - pre_textlen -= t - oap->start_vcol; - oap->start_vcol = t; - oap->op_type = OP_INSERT; - } - } else if (bd.is_MAX && oap->op_type == OP_APPEND) { + if (oap->op_type == OP_INSERT + && oap->start.col + oap->start.coladd + != curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { + oap->start.col = curbuf->b_op_start_orig.col; + pre_textlen -= t - oap->start_vcol; + oap->start_vcol = t; + } else if (oap->op_type == OP_APPEND + && oap->start.col + oap->start.coladd + >= curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { + oap->start.col = curbuf->b_op_start_orig.col; // reset pre_textlen to the value of OP_INSERT pre_textlen += bd.textlen; pre_textlen -= t - oap->start_vcol; + oap->start_vcol = t; + oap->op_type = OP_INSERT; } } diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 04c7db38ef..ae8f9ba70b 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -636,6 +636,11 @@ func Test_characterwise_visual_mode() normal v$rx call assert_equal(['x'], getline(1, '$')) + " replace a character with composing characters + call setline(1, "xã̳x") + normal gg0lvrb + call assert_equal("xbx", getline(1)) + bwipe! endfunc @@ -1280,6 +1285,16 @@ func Test_visual_block_insert_round_off() bwipe! endfunc +" this was causing an ml_get error +func Test_visual_exchange_windows() + enew! + new + call setline(1, ['foo', 'bar']) + exe "normal G\<C-V>gg\<C-W>\<C-X>OO\<Esc>" + bwipe! + bwipe! +endfunc + " this was leaving the end of the Visual area beyond the end of a line func Test_visual_ex_copy_line() new diff --git a/src/nvim/window.c b/src/nvim/window.c index e09af7a7bb..1e737d2083 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1727,6 +1727,12 @@ static void win_exchange(long Prenum) (void)win_comp_pos(); // recompute window positions + if (wp->w_buffer != curbuf) { + reset_VIsual_and_resel(); + } else if (VIsual_active) { + wp->w_cursor = curwin->w_cursor; + } + win_enter(wp, true); redraw_later(curwin, NOT_VALID); redraw_later(wp, NOT_VALID); diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index d64d324a88..de22c9078c 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -180,6 +180,28 @@ describe('nvim_add_user_command', function() feed('<C-U>Test b<Tab>') eq('Test bbb', funcs.getcmdline()) end) + + it('does not allow invalid command names', function() + matches("'name' must begin with an uppercase letter", pcall_err(exec_lua, [[ + vim.api.nvim_add_user_command('test', 'echo "hi"', {}) + ]])) + + matches('Invalid command name', pcall_err(exec_lua, [[ + vim.api.nvim_add_user_command('t@', 'echo "hi"', {}) + ]])) + + matches('Invalid command name', pcall_err(exec_lua, [[ + vim.api.nvim_add_user_command('T@st', 'echo "hi"', {}) + ]])) + + matches('Invalid command name', pcall_err(exec_lua, [[ + vim.api.nvim_add_user_command('Test!', 'echo "hi"', {}) + ]])) + + matches('Invalid command name', pcall_err(exec_lua, [[ + vim.api.nvim_add_user_command('💩', 'echo "hi"', {}) + ]])) + end) end) describe('nvim_del_user_command', function() |