diff options
-rw-r--r-- | runtime/doc/helphelp.txt | 2 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter.lua | 1 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/language.lua | 2 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 3 | ||||
-rw-r--r-- | src/nvim/ex_cmds2.c | 8 | ||||
-rw-r--r-- | src/nvim/getchar.c | 60 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 10 | ||||
-rw-r--r-- | src/nvim/marktree.h | 1 | ||||
-rw-r--r-- | src/nvim/normal.c | 11 | ||||
-rw-r--r-- | src/nvim/option.c | 27 | ||||
-rw-r--r-- | src/nvim/regexp.c | 2 | ||||
-rw-r--r-- | src/nvim/regexp_nfa.c | 7 | ||||
-rw-r--r-- | src/nvim/screen.c | 27 | ||||
-rw-r--r-- | src/nvim/testdir/test_help.vim | 8 | ||||
-rw-r--r-- | src/nvim/testdir/test_normal.vim | 8 | ||||
-rw-r--r-- | src/nvim/testdir/test_regexp_latin.vim | 8 | ||||
-rw-r--r-- | src/nvim/testdir/test_registers.vim | 11 | ||||
-rw-r--r-- | src/nvim/testdir/test_retab.vim | 3 | ||||
-rw-r--r-- | src/nvim/testdir/test_scriptnames.vim | 6 | ||||
-rw-r--r-- | src/nvim/testdir/test_utf8.vim | 52 | ||||
-rw-r--r-- | src/nvim/testdir/test_visual.vim | 3 | ||||
-rw-r--r-- | test/functional/legacy/visual_mode_spec.lua | 44 | ||||
-rw-r--r-- | test/functional/treesitter/parser_spec.lua | 15 |
23 files changed, 275 insertions, 44 deletions
diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt index 03ec8966d4..569995d319 100644 --- a/runtime/doc/helphelp.txt +++ b/runtime/doc/helphelp.txt @@ -358,7 +358,7 @@ When referring to a Vim option in the help file, place the option name between two single quotes, eg. 'statusline' When referring to any other technical term, such as a filename or function -parameter, surround it in backticks (`), eg. `~/.path/to/init.vim`. +parameter, surround it in backticks, eg. `~/.path/to/init.vim`. HIGHLIGHTING diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 07f6418c0c..f9d539f028 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -11,6 +11,7 @@ local parsers = {} local M = vim.tbl_extend("error", query, language) M.language_version = vim._ts_get_language_version() +M.minimum_language_version = vim._ts_get_minimum_language_version() setmetatable(M, { __index = function (t, k) diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index 6f347ff25f..8b106108df 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -14,7 +14,7 @@ function M.require_language(lang, path, silent) return true end if path == nil then - local fname = 'parser/' .. lang .. '.*' + local fname = 'parser/' .. vim.fn.fnameescape(lang) .. '.*' local paths = a.nvim_get_runtime_file(fname, false) if #paths == 0 then if silent then diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 9f30f98c07..e6d63d08a7 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -5093,8 +5093,7 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool && ((arg[1] != NUL && arg[2] == NUL) || (vim_strchr((char_u *)"%_z@", arg[1]) != NULL && arg[2] != NUL))) { - STRCPY(d, "/\\\\"); - STRCPY(d + 3, arg + 1); + vim_snprintf((char *)d, IOSIZE, "/\\\\%s", arg + 1); // Check for "/\\_$", should be "/\\_\$" if (d[3] == '_' && d[4] == '$') { STRCPY(d + 4, "\\$"); 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/getchar.c b/src/nvim/getchar.c index ef590adb3a..95720c498a 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -284,9 +284,19 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle } } -/* - * Add number "n" to buffer "buf". - */ +/// Delete "slen" bytes from the end of "buf". +/// Only works when it was just added. +static void delete_buff_tail(buffheader_T *buf, int slen) +{ + int len = (int)STRLEN(buf->bh_curr->b_str); + + if (len >= slen) { + buf->bh_curr->b_str[len - slen] = NUL; + buf->bh_space += (size_t)slen; + } +} + +/// Add number "n" to buffer "buf". static void add_num_buff(buffheader_T *buf, long n) { char number[32]; @@ -967,31 +977,31 @@ int ins_typebuf(char_u *str, int noremap, int offset, bool nottyped, bool silent return OK; } -/* - * Put character "c" back into the typeahead buffer. - * Can be used for a character obtained by vgetc() that needs to be put back. - * Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to - * the char. - */ -void ins_char_typebuf(int c, int modifier) +/// Put character "c" back into the typeahead buffer. +/// Can be used for a character obtained by vgetc() that needs to be put back. +/// Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to +/// the char. +/// @return the length of what was inserted +int ins_char_typebuf(int c, int modifier) { - char_u buf[MB_MAXBYTES + 4]; - int idx = 0; + char_u buf[MB_MAXBYTES * 3 + 4]; + int len = 0; if (modifier != 0) { buf[0] = K_SPECIAL; buf[1] = KS_MODIFIER; buf[2] = (char_u)modifier; buf[3] = NUL; - idx = 3; + len = 3; } if (IS_SPECIAL(c)) { - buf[idx] = K_SPECIAL; - buf[idx + 1] = (char_u)K_SECOND(c); - buf[idx + 2] = (char_u)K_THIRD(c); - buf[idx + 3] = NUL; + buf[len] = K_SPECIAL; + buf[len + 1] = (char_u)K_SECOND(c); + buf[len + 2] = (char_u)K_THIRD(c); + buf[len + 3] = NUL; } else { - char_u *p = buf + idx; + char_u *p = buf + len; int char_len = utf_char2bytes(c, p); + len += char_len; // If the character contains K_SPECIAL bytes they need escaping. for (int i = char_len; --i >= 0; p++) { if ((uint8_t)(*p) == K_SPECIAL) { @@ -999,11 +1009,13 @@ void ins_char_typebuf(int c, int modifier) *p++ = K_SPECIAL; *p++ = KS_SPECIAL; *p = KE_FILLER; + len += 2; } } *p = NUL; } (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); + return len; } /// Return TRUE if the typeahead buffer was changed (while waiting for a @@ -1163,6 +1175,18 @@ static void gotchars(const char_u *chars, size_t len) maptick++; } +/// Undo the last gotchars() for "len" bytes. To be used when putting a typed +/// character back into the typeahead buffer, thus gotchars() will be called +/// again. +/// Only affects recorded characters. +void ungetchars(int len) +{ + if (reg_recording != 0) { + delete_buff_tail(&recordbuff, len); + last_recorded_len -= (size_t)len; + } +} + /* * Sync undo. Called when typed characters are obtained from the typeahead * buffer, or when a menu is used. 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/normal.c b/src/nvim/normal.c index 0ac4039d11..76ee9f1f40 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1011,7 +1011,12 @@ static int normal_execute(VimState *state, int key) // restart automatically. // Insert the typed character in the typeahead buffer, so that it can // be mapped in Insert mode. Required for ":lmap" to work. - ins_char_typebuf(s->c, mod_mask); + int len = ins_char_typebuf(s->c, mod_mask); + + // When recording the character will be recorded again, remove the + // previously recording. + ungetchars(len); + if (restart_edit != 0) { s->c = 'd'; } else { @@ -6324,11 +6329,9 @@ static void nv_g_cmd(cmdarg_T *cap) break; case 'M': { - const char_u *const ptr = get_cursor_line_ptr(); - oap->motion_type = kMTCharWise; oap->inclusive = false; - i = (int)mb_string2cells_len(ptr, STRLEN(ptr)); + i = linetabsize(get_cursor_line_ptr()); if (cap->count0 > 0 && cap->count0 <= 100) { coladvance((colnr_T)(i * cap->count0 / 100)); } else { diff --git a/src/nvim/option.c b/src/nvim/option.c index fd6483c555..a4a6423ac7 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2008,9 +2008,9 @@ static void didset_options2(void) // Parse default for 'wildmode'. check_opt_wim(); xfree(curbuf->b_p_vsts_array); - tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array); + (void)tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array); xfree(curbuf->b_p_vts_array); - tabstop_set(curbuf->b_p_vts, &curbuf->b_p_vts_array); + (void)tabstop_set(curbuf->b_p_vts, &curbuf->b_p_vts_array); } /// Check for string options that are NULL (normally only termcap options). @@ -6412,7 +6412,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_sts_nopaste = p_sts_nopaste; buf->b_p_vsts = vim_strsave(p_vsts); if (p_vsts && p_vsts != empty_option) { - tabstop_set(p_vsts, &buf->b_p_vsts_array); + (void)tabstop_set(p_vsts, &buf->b_p_vsts_array); } else { buf->b_p_vsts_array = 0; } @@ -6492,7 +6492,7 @@ void buf_copy_options(buf_T *buf, int flags) if (dont_do_help) { buf->b_p_isk = save_p_isk; if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) { - tabstop_set(p_vts, &buf->b_p_vts_array); + (void)tabstop_set(p_vts, &buf->b_p_vts_array); } else { buf->b_p_vts_array = NULL; } @@ -6502,7 +6502,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_ts = p_ts; buf->b_p_vts = vim_strsave(p_vts); if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) { - tabstop_set(p_vts, &buf->b_p_vts_array); + (void)tabstop_set(p_vts, &buf->b_p_vts_array); } else { buf->b_p_vts_array = NULL; } @@ -7195,7 +7195,7 @@ static void paste_option_changed(void) xfree(buf->b_p_vsts_array); } if (buf->b_p_vsts && buf->b_p_vsts != empty_option) { - tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array); + (void)tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array); } else { buf->b_p_vsts_array = 0; } @@ -7513,6 +7513,7 @@ int check_ff_value(char_u *p) // Set the integer values corresponding to the string setting of 'vartabstop'. // "array" will be set, caller must free it if needed. +// Return false for an error. bool tabstop_set(char_u *var, long **array) { long valcount = 1; @@ -7532,7 +7533,7 @@ bool tabstop_set(char_u *var, long **array) if (cp != end) { emsg(_(e_positive)); } else { - emsg(_(e_invarg)); + semsg(_(e_invarg2), cp); } return false; } @@ -7545,7 +7546,7 @@ bool tabstop_set(char_u *var, long **array) valcount++; continue; } - emsg(_(e_invarg)); + semsg(_(e_invarg2), var); return false; } @@ -7554,7 +7555,15 @@ bool tabstop_set(char_u *var, long **array) t = 1; for (cp = var; *cp != NUL;) { - (*array)[t++] = atoi((char *)cp); + int n = atoi((char *)cp); + + // Catch negative values, overflow and ridiculous big values. + if (n < 0 || n > 9999) { + semsg(_(e_invarg2), cp); + XFREE_CLEAR(*array); + return false; + } + (*array)[t++] = n; while (*cp != NUL && *cp != ',') { cp++; } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 45e580dbee..c8508179a1 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -3232,7 +3232,7 @@ typedef struct { // The current match-position is remembered with these variables: linenr_T lnum; ///< line number, relative to first line char_u *line; ///< start of current line - char_u *input; ///< current input, points into "regline" + char_u *input; ///< current input, points into "line" int need_clear_subexpr; ///< subexpressions still need to be cleared int need_clear_zsubexpr; ///< extmatch subexpressions still need to be diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index cafffc0319..133858f113 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -6071,8 +6071,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, case NFA_MARK_GT: case NFA_MARK_LT: { + size_t col = rex.input - rex.line; pos_T *pos = getmark_buf(rex.reg_buf, t->state->val, false); + // Line may have been freed, get it again. + if (REG_MULTI) { + rex.line = reg_getline(rex.lnum); + rex.input = rex.line + col; + } + // Compare the mark position to the match position, if the mark // exists and mark is set in reg_buf. if (pos != NULL && pos->lnum > 0) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 7e7b34fb14..af023d6785 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1222,12 +1222,33 @@ static void win_update(win_T *wp, Providers *providers) } getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc); - curwin->w_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 && !(get_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 diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim index 8e59efd22d..977dad6a45 100644 --- a/src/nvim/testdir/test_help.vim +++ b/src/nvim/testdir/test_help.vim @@ -101,4 +101,12 @@ func Test_helptag_cmd() call delete('Xdir', 'rf') endfunc +func Test_help_long_argument() + try + exe 'help \%' .. repeat('0', 1021) + catch + call assert_match("E149:", v:exception) + endtry +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index cb1d66cb98..5b7cf6fee5 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -1812,7 +1812,15 @@ fun! Test_normal33_g_cmd2() call assert_equal(87, col('.')) call assert_equal('E', getreg(0)) + " Test for gM with Tab characters + call setline('.', "\ta\tb\tc\td\te\tf") + norm! gMyl + call assert_equal(6, col('.')) + call assert_equal("c", getreg(0)) + " Test for g Ctrl-G + call setline('.', lineC) + norm! 60gMyl set ff=unix let a=execute(":norm! g\<c-g>") call assert_match('Col 87 of 144; Line 2 of 2; Word 1 of 1; Byte 88 of 146', a) diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index 712f1e6025..a92f7e1192 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -787,4 +787,12 @@ func Test_regexp_error() set re& endfunc +func Test_using_mark_position() + " this was using freed memory + new + norm O0 + call assert_fails("s/\\%')", 'E486:') + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index d0bafe47b5..5061ebe202 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -121,6 +121,17 @@ func Test_recording_esc_sequence() endif endfunc +func Test_recording_with_select_mode() + new + call feedkeys("qacc12345\<Esc>gH98765\<Esc>q", "tx") + call assert_equal("98765", getline(1)) + call assert_equal("cc12345\<Esc>gH98765\<Esc>", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("98765", getline(1)) + bwipe! +endfunc + " Test for executing the last used register (@) func Test_last_used_exec_reg() " Test for the @: command diff --git a/src/nvim/testdir/test_retab.vim b/src/nvim/testdir/test_retab.vim index f11a32bade..e7b8946ccf 100644 --- a/src/nvim/testdir/test_retab.vim +++ b/src/nvim/testdir/test_retab.vim @@ -74,4 +74,7 @@ endfunc func Test_retab_error() call assert_fails('retab -1', 'E487:') call assert_fails('retab! -1', 'E487:') + call assert_fails('ret -1000', 'E487:') + call assert_fails('ret 10000', 'E475:') + call assert_fails('ret 80000000000000000000', 'E475:') endfunc 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_utf8.vim b/src/nvim/testdir/test_utf8.vim index 0818c2e4b0..36776d5a64 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -1,5 +1,6 @@ " Tests for Unicode manipulations +source check.vim source view_util.vim " Visual block Insert adjusts for multi-byte char @@ -148,4 +149,55 @@ func Test_print_overlong() bwipe! endfunc +func Test_recording_with_select_mode_utf8() + call Run_test_recording_with_select_mode_utf8() +endfunc + +func Run_test_recording_with_select_mode_utf8() + new + + " No escaping + call feedkeys("qacc12345\<Esc>gH哦\<Esc>q", "tx") + call assert_equal("哦", getline(1)) + call assert_equal("cc12345\<Esc>gH哦\<Esc>", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("哦", getline(1)) + + " 固 is 0xE5 0x9B 0xBA where 0x9B is CSI + call feedkeys("qacc12345\<Esc>gH固\<Esc>q", "tx") + call assert_equal("固", getline(1)) + call assert_equal("cc12345\<Esc>gH固\<Esc>", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("固", getline(1)) + + " 四 is 0xE5 0x9B 0x9B where 0x9B is CSI + call feedkeys("qacc12345\<Esc>gH四\<Esc>q", "tx") + call assert_equal("四", getline(1)) + call assert_equal("cc12345\<Esc>gH四\<Esc>", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("四", getline(1)) + + " 倒 is 0xE5 0x80 0x92 where 0x80 is K_SPECIAL + call feedkeys("qacc12345\<Esc>gH倒\<Esc>q", "tx") + call assert_equal("倒", getline(1)) + call assert_equal("cc12345\<Esc>gH倒\<Esc>", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("倒", getline(1)) + + bwipe! +endfunc + +" This must be done as one of the last tests, because it starts the GUI, which +" cannot be undone. +func Test_zz_recording_with_select_mode_utf8_gui() + CheckCanRunGui + + gui -f + call Run_test_recording_with_select_mode_utf8() +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index b087c88c35..76274fb038 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1124,6 +1124,9 @@ 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) diff --git a/test/functional/legacy/visual_mode_spec.lua b/test/functional/legacy/visual_mode_spec.lua index c8e83ed649..8b5dd0c2dc 100644 --- a/test/functional/legacy/visual_mode_spec.lua +++ b/test/functional/legacy/visual_mode_spec.lua @@ -1,5 +1,3 @@ --- Test visual line mode selection redraw after scrolling - local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') @@ -10,6 +8,7 @@ local feed_command = helpers.feed_command local funcs = helpers.funcs local meths = helpers.meths local eq = helpers.eq +local exec = helpers.exec describe('visual line mode', function() local screen @@ -40,3 +39,44 @@ describe('visual line mode', function() ]]) end) end) + +describe('visual block mode', function() + it('shows selection correctly with virtualedit=block', function() + clear() + local screen = Screen.new(30, 7) + screen:set_default_attr_ids({ + [1] = {bold = true}, -- ModeMsg + [2] = {background = Screen.colors.LightGrey}, -- Visual + [3] = {foreground = Screen.colors.Blue, bold = true} -- NonText + }) + screen:attach() + + exec([[ + call setline(1, ['aaaaaa', 'bbbb', 'cc']) + set virtualedit=block + normal G + ]]) + + feed('<C-V>gg$') + screen:expect([[ + {2:aaaaaa}^ | + {2:bbbb } | + {2:cc } | + {3:~ }| + {3:~ }| + {3:~ }| + {1:-- VISUAL BLOCK --} | + ]]) + + feed('<Esc>gg<C-V>G$') + screen:expect([[ + {2:aaaaaa } | + {2:bbbb } | + {2:cc}^ {2: } | + {3:~ }| + {3:~ }| + {3:~ }| + {1:-- VISUAL BLOCK --} | + ]]) + end) +end) diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 44a7f00d8b..396fe5feab 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -666,6 +666,21 @@ int x = INT_MAX; -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) }, get_ranges()) end) + + it("should not inject bad languages", function() + if helpers.pending_win32(pending) then return end + exec_lua([=[ + vim.treesitter.add_directive("inject-bad!", function(match, _, _, pred, metadata) + metadata.language = "{" + metadata.combined = true + metadata.content = pred[2] + end) + + parser = vim.treesitter.get_parser(0, "c", { + injections = { + c = "(preproc_function_def value: ((preproc_arg) @_a (#inject-bad! @_a)))"}}) + ]=]) + end) end) describe("when using the offset directive", function() |