diff options
42 files changed, 685 insertions, 144 deletions
diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index 8a70d864c4..6b88c92cf0 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -52,6 +52,7 @@ check_function_exists(setsid HAVE_SETSID) check_function_exists(sigaction HAVE_SIGACTION) check_function_exists(strcasecmp HAVE_STRCASECMP) check_function_exists(strncasecmp HAVE_STRNCASECMP) +check_function_exists(strptime HAVE_STRPTIME) # Symbols check_symbol_exists(FD_CLOEXEC "fcntl.h" HAVE_FD_CLOEXEC) diff --git a/config/config.h.in b/config/config.h.in index 275bff79a0..502f84bbcf 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -33,6 +33,7 @@ #cmakedefine HAVE_STRCASECMP #cmakedefine HAVE_STRINGS_H #cmakedefine HAVE_STRNCASECMP +#cmakedefine HAVE_STRPTIME #cmakedefine HAVE_SYS_SDT_H #cmakedefine HAVE_SYS_UTSNAME_H #cmakedefine HAVE_SYS_WAIT_H diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 1832e2443f..112958f78b 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -444,7 +444,7 @@ as a key. To avoid having to put quotes around every key the #{} form can be used. This does require the key to consist only of ASCII letters, digits, '-' and '_'. Example: > - let mydict = #{zero: 0, one_key: 1, two-key: 2, 333: 3} + :let mydict = #{zero: 0, one_key: 1, two-key: 2, 333: 3} Note that 333 here is the string "333". Empty keys are not possible with #{}. A value can be any expression. Using a Dictionary for a value creates a @@ -2430,7 +2430,7 @@ strcharpart({str}, {start} [, {len}]) String {len} characters of {str} at character {start} strdisplaywidth({expr} [, {col}]) Number display length of the String {expr} -strftime({format} [, {time}]) String time in specified format +strftime({format} [, {time}]) String format time with a specified format strgetchar({str}, {index}) Number get char {index} from {str} stridx({haystack}, {needle} [, {start}]) Number index of {needle} in {haystack} @@ -2439,6 +2439,8 @@ strlen({expr}) Number length of the String {expr} strpart({str}, {start} [, {len} [, {chars}]]) String {len} bytes/chars of {str} at byte {start} +strptime({format}, {timestring}) + Number Convert {timestring} to unix timestamp strridx({haystack}, {needle} [, {start}]) Number last index of {needle} in {haystack} strtrans({expr}) String translate string to make it printable @@ -4983,7 +4985,7 @@ getwininfo([{winid}]) *getwininfo()* getwinpos([{timeout}]) *getwinpos()* The result is a list with two numbers, the result of - getwinposx() and getwinposy() combined: + |getwinposx()| and |getwinposy()| combined: [x-pos, y-pos] {timeout} can be used to specify how long to wait in msec for a response from the terminal. When omitted 100 msec is used. @@ -5853,7 +5855,7 @@ list2str({list} [, {utf8}]) *list2str()* < localtime() *localtime()* Return the current time, measured as seconds since 1st Jan - 1970. See also |strftime()| and |getftime()|. + 1970. See also |strftime()|, |strptime()| and |getftime()|. log({expr}) *log()* @@ -8488,7 +8490,7 @@ strftime({format} [, {time}]) *strftime()* {format} depends on your system, thus this is not portable! See the manual page of the C function strftime() for the format. The maximum length of the result is 80 characters. - See also |localtime()| and |getftime()|. + See also |localtime()|, |getftime()| and |strptime()|. The language can be changed with the |:language| command. Examples: > :echo strftime("%c") Sun Apr 27 11:49:23 1997 @@ -8578,6 +8580,31 @@ strpart({src}, {start} [, {len} [, {chars}]]) *strpart()* example, to get the character under the cursor: > strpart(getline("."), col(".") - 1, 1, v:true) < +strptime({format}, {timestring}) *strptime()* + The result is a Number, which is a unix timestamp representing + the date and time in {timestring}, which is expected to match + the format specified in {format}. + + The accepted {format} depends on your system, thus this is not + portable! See the manual page of the C function strptime() + for the format. Especially avoid "%c". The value of $TZ also + matters. + + If the {timestring} cannot be parsed with {format} zero is + returned. If you do not know the format of {timestring} you + can try different {format} values until you get a non-zero + result. + + See also |strftime()|. + Examples: > + :echo strptime("%Y %b %d %X", "1997 Apr 27 11:49:23") +< 862156163 > + :echo strftime("%c", strptime("%y%m%d %T", "970427 11:53:55")) +< Sun Apr 27 11:53:55 1997 > + :echo strftime("%c", strptime("%Y%m%d%H%M%S", "19970427115355") + 3600) +< Sun Apr 27 12:53:55 1997 + + strridx({haystack}, {needle} [, {start}]) *strridx()* The result is a Number, which gives the byte index in {haystack} of the last occurrence of the String {needle}. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 63c899da0c..97aacc1403 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -788,6 +788,7 @@ Date and Time: *date-functions* *time-functions* getftime() get last modification time of a file localtime() get current time in seconds strftime() convert time to a string + strptime() convert a date/time string to time reltime() get the current or elapsed time accurately reltimestr() convert reltime() result to a string reltimefloat() convert reltime() result to a Float diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 0c1e216164..eadc1c04a0 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -70,6 +70,8 @@ the differences. - |matchit| plugin is enabled. To disable it in your config: > :let loaded_matchit = 1 +- |g:vimsyn_embed| defaults to "l" to enable Lua highlighting + ============================================================================== 3. New Features *nvim-features* diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 0cf80e1443..eacbd90077 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -304,7 +304,7 @@ M['textDocument/typeDefinition'] = location_handler M['textDocument/implementation'] = location_handler --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp -M['textDocument/signatureHelp'] = function(_, method, result) +M['textDocument/signatureHelp'] = function(_, method, result, _, bufnr) -- When use `autocmd CompleteDone <silent><buffer> lua vim.lsp.buf.signature_help()` to call signatureHelp handler -- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `<silent>` to ignore if not (result and result.signatures and result.signatures[1]) then @@ -317,9 +317,11 @@ M['textDocument/signatureHelp'] = function(_, method, result) print('No signature help available') return end - util.focusable_preview(method, function() + local syntax = api.nvim_buf_get_option(bufnr, 'syntax') + local p_bufnr, _ = util.focusable_preview(method, function() return lines, util.try_trim_markdown_code_blocks(lines) end) + api.nvim_buf_set_option(p_bufnr, 'syntax', syntax) end --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 8b94348994..1b29618997 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -8,10 +8,23 @@ Query.__index = Query local M = {} +local function dedupe_files(files) + local result = {} + local seen = {} + + for _, path in ipairs(files) do + if not seen[path] then + table.insert(result, path) + seen[path] = true + end + end + + return result +end function M.get_query_files(lang, query_name, is_included) local query_path = string.format('queries/%s/%s.scm', lang, query_name) - local lang_files = a.nvim_get_runtime_file(query_path, true) + local lang_files = dedupe_files(a.nvim_get_runtime_file(query_path, true)) if #lang_files == 0 then return {} end diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 1a37af1c8a..92348d57ec 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -616,7 +616,7 @@ syn region vimGlobal matchgroup=Statement start='\<v\%[global]!\=/' skip='\\.' e " g:vimsyn_embed =~# 'r' : embed ruby " g:vimsyn_embed =~# 't' : embed tcl if !exists("g:vimsyn_embed") - let g:vimsyn_embed= 0 + let g:vimsyn_embed = 'l' endif " [-- lua --] {{{3 diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 9a20b06660..e16598e7d2 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -145,8 +145,7 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state) for (size_t i = 0; i < kv_size(state->active); i++) { HlRange item = kv_A(state->active, i); if (item.virt_text_owned) { - clear_virttext(item.virt_text); - xfree(item.virt_text); + clear_virttext(&item.virt_text); } } kv_size(state->active) = 0; @@ -229,7 +228,7 @@ static void decor_add(DecorState *state, int start_row, int start_col, HlRange range = { start_row, start_col, end_row, end_col, attr_id, MAX(priority, decor->priority), - kv_size(decor->virt_text) ? &decor->virt_text : NULL, + decor->virt_text, decor->virt_text_pos, decor->virt_text_hide, decor->hl_mode, kv_size(decor->virt_text) && owned, -1 }; @@ -304,7 +303,7 @@ next_mark: bool active = false, keep = true; if (item.end_row < state->row || (item.end_row == state->row && item.end_col <= col)) { - if (!(item.start_row >= state->row && item.virt_text)) { + if (!(item.start_row >= state->row && kv_size(item.virt_text))) { keep = false; } } else { @@ -324,14 +323,13 @@ next_mark: attr = hl_combine_attr(attr, item.attr_id); } if ((item.start_row == state->row && item.start_col <= col) - && item.virt_text && item.virt_col == -1) { + && kv_size(item.virt_text) && item.virt_col == -1) { item.virt_col = (item.virt_text_hide && hidden) ? -2 : virt_col; } if (keep) { kv_A(state->active, j++) = item; } else if (item.virt_text_owned) { - clear_virttext(item.virt_text); - xfree(item.virt_text); + clear_virttext(&item.virt_text); } } kv_size(state->active) = j; @@ -344,22 +342,26 @@ void decor_redraw_end(DecorState *state) state->buf = NULL; } -VirtText *decor_redraw_virt_text(buf_T *buf, DecorState *state) +VirtText decor_redraw_virt_text(buf_T *buf, DecorState *state) { decor_redraw_col(buf, MAXCOL, MAXCOL, false, state); for (size_t i = 0; i < kv_size(state->active); i++) { HlRange item = kv_A(state->active, i); - if (item.start_row == state->row && item.virt_text + if (item.start_row == state->row && kv_size(item.virt_text) && item.virt_text_pos == kVTEndOfLine) { return item.virt_text; } } - return NULL; + return VIRTTEXT_EMPTY; } void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, Decoration *decor, DecorPriority priority) { + if (end_row == -1) { + end_row = start_row; + end_col = start_col; + } decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true, priority); } diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 264e8a4a82..c5424a1642 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -53,7 +53,7 @@ typedef struct { // TODO(bfredl): embed decoration instead, perhaps using an arena // for ephemerals? DecorPriority priority; - VirtText *virt_text; + VirtText virt_text; VirtTextPos virt_text_pos; bool virt_text_hide; HlMode hl_mode; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index cffa46fa77..b5d5d67e90 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -8764,6 +8764,10 @@ static bool ins_tab(void) getvcol(curwin, &fpos, &vcol, NULL, NULL); getvcol(curwin, cursor, &want_vcol, NULL, NULL); + // save start of changed region for extmark_splice + int start_row = fpos.lnum; + colnr_T start_col = fpos.col; + // Use as many TABs as possible. Beware of 'breakindent', 'showbreak' // and 'linebreak' adding extra virtual columns. while (ascii_iswhite(*ptr)) { @@ -8813,6 +8817,11 @@ static bool ins_tab(void) replace_join(repl_off); } } + if (!(State & VREPLACE_FLAG)) { + extmark_splice_cols(curbuf, start_row - 1, start_col, + cursor->col - start_col, fpos.col - start_col, + kExtmarkUndo); + } } cursor->col -= i; diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index e94d7831b0..d1a3ae3ff8 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -341,6 +341,7 @@ return { string={args=1}, strlen={args=1}, strpart={args={2, 4}}, + strptime={args=2}, strridx={args={2, 3}}, strtrans={args=1}, strwidth={args=1}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 01d23654de..650b4e3882 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -10189,6 +10189,38 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = (char_u *)xmemdupz(p + n, (size_t)len); } +// "strptime({format}, {timestring})" function +static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + char fmt_buf[NUMBUFLEN]; + char str_buf[NUMBUFLEN]; + + struct tm tmval = { + .tm_isdst = -1, + }; + char *fmt = (char *)tv_get_string_buf(&argvars[0], fmt_buf); + char *str = (char *)tv_get_string_buf(&argvars[1], str_buf); + + vimconv_T conv = { + .vc_type = CONV_NONE, + }; + char_u *enc = enc_locale(); + convert_setup(&conv, p_enc, enc); + if (conv.vc_type != CONV_NONE) { + fmt = (char *)string_convert(&conv, (char_u *)fmt, NULL); + } + if (fmt == NULL + || os_strptime(str, fmt, &tmval) == NULL + || (rettv->vval.v_number = mktime(&tmval)) == -1) { + rettv->vval.v_number = 0; + } + if (conv.vc_type != CONV_NONE) { + xfree(fmt); + } + convert_setup(&conv, NULL, NULL); + xfree(enc); +} + /* * "strridx()" function */ diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 3038fad894..0ff427c261 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3177,7 +3177,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // insert the new text totlen = (size_t)(count * (yanklen + spaces) + bd.startspaces + bd.endspaces); - newp = (char_u *) xmalloc(totlen + oldlen + 1); + int addcount = (int)totlen + lines_appended; + newp = (char_u *)xmalloc(totlen + oldlen + 1); // copy part up to cursor to new line ptr = newp; memmove(ptr, oldp, (size_t)bd.textcol); @@ -3194,6 +3195,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) if ((j < count - 1 || !shortline) && spaces) { memset(ptr, ' ', (size_t)spaces); ptr += spaces; + } else { + addcount -= spaces; } } // may insert some spaces after the new text @@ -3205,7 +3208,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) memmove(ptr, oldp + bd.textcol + delcount, (size_t)columns); ml_replace(curwin->w_cursor.lnum, newp, false); extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum-1, bd.textcol, - delcount, (int)totlen + lines_appended, kExtmarkUndo); + delcount, addcount, kExtmarkUndo); ++curwin->w_cursor.lnum; if (i == 0) diff --git a/src/nvim/option.c b/src/nvim/option.c index d04329e104..949558894f 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -379,8 +379,8 @@ void set_init_1(bool clean_arg) # else static char *(names[3]) = {"TMPDIR", "TEMP", "TMP"}; # endif - int len; garray_T ga; + opt_idx = findoption("backupskip"); ga_init(&ga, 1, 100); for (size_t n = 0; n < ARRAY_SIZE(names); n++) { @@ -401,15 +401,23 @@ void set_init_1(bool clean_arg) } if (p != NULL && *p != NUL) { // First time count the NUL, otherwise count the ','. - len = (int)strlen(p) + 3; - ga_grow(&ga, len); - if (!GA_EMPTY(&ga)) { - STRCAT(ga.ga_data, ","); + const size_t len = strlen(p) + 3; + char *item = xmalloc(len); + xstrlcpy(item, p, len); + add_pathsep(item); + xstrlcat(item, "*", len); + if (find_dup_item(ga.ga_data, (char_u *)item, options[opt_idx].flags) + == NULL) { + ga_grow(&ga, (int)len); + if (!GA_EMPTY(&ga)) { + STRCAT(ga.ga_data, ","); + } + STRCAT(ga.ga_data, p); + add_pathsep(ga.ga_data); + STRCAT(ga.ga_data, "*"); + ga.ga_len += (int)len; } - STRCAT(ga.ga_data, p); - add_pathsep(ga.ga_data); - STRCAT(ga.ga_data, "*"); - ga.ga_len += len; + xfree(item); } if(mustfree) { xfree(p); @@ -713,6 +721,38 @@ static void set_string_default(const char *name, char *val, bool allocated) } } +// For an option value that contains comma separated items, find "newval" in +// "origval". Return NULL if not found. +static char_u *find_dup_item(char_u *origval, const char_u *newval, + uint32_t flags) + FUNC_ATTR_NONNULL_ARG(2) +{ + int bs = 0; + + if (origval == NULL) { + return NULL; + } + + const size_t newlen = STRLEN(newval); + for (char_u *s = origval; *s != NUL; s++) { + if ((!(flags & P_COMMA) || s == origval || (s[-1] == ',' && !(bs & 1))) + && STRNCMP(s, newval, newlen) == 0 + && (!(flags & P_COMMA) || s[newlen] == ',' || s[newlen] == NUL)) { + return s; + } + // Count backslashes. Only a comma with an even number of backslashes + // or a single backslash preceded by a comma before it is recognized as + // a separator. + if ((s > origval + 1 && s[-1] == '\\' && s[-2] != ',') + || (s == origval + 1 && s[-1] == '\\')) { + bs++; + } else { + bs = 0; + } + } + return NULL; +} + /// Set the Vi-default value of a number option. /// Used for 'lines' and 'columns'. void set_number_default(char *name, long val) @@ -1285,9 +1325,7 @@ int do_set( char *saved_newval = NULL; unsigned newlen; int comma; - int bs; - int new_value_alloced; /* new string option - was allocated */ + bool new_value_alloced = false; // new string option was allocated /* When using ":set opt=val" for a global option * with a local value the local value will be @@ -1486,34 +1524,20 @@ int do_set( i = 0; // init for GCC if (removing || (flags & P_NODUP)) { i = (int)STRLEN(newval); - bs = 0; - for (s = origval; *s; s++) { - if ((!(flags & P_COMMA) - || s == origval - || (s[-1] == ',' && !(bs & 1))) - && STRNCMP(s, newval, i) == 0 - && (!(flags & P_COMMA) - || s[i] == ',' - || s[i] == NUL)) { - break; - } - // Count backslashes. Only a comma with an even number of - // backslashes or a single backslash preceded by a comma - // before it is recognized as a separator - if ((s > origval + 1 && s[-1] == '\\' && s[-2] != ',') - || (s == origval + 1 && s[-1] == '\\')) { - bs++; - } else { - bs = 0; - } - } + s = find_dup_item(origval, newval, flags); // do not add if already there - if ((adding || prepending) && *s) { + if ((adding || prepending) && s != NULL) { prepending = false; adding = false; STRCPY(newval, origval); } + + // if no duplicate, move pointer to end of + // original value + if (s == NULL) { + s = origval + (int)STRLEN(origval); + } } /* concatenate the two strings; add a ',' if @@ -2310,7 +2334,7 @@ static char_u * did_set_string_option( int opt_idx, // index in options[] table char_u **varp, // pointer to the option variable - int new_value_alloced, // new value was allocated + bool new_value_alloced, // new value was allocated char_u *oldval, // previous value of the option char_u *errbuf, // buffer for errors, or NULL size_t errbuflen, // length of errors buffer diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index 5cf628935f..e7e0dc4013 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -196,6 +196,22 @@ char *os_ctime(char *result, size_t result_len) return os_ctime_r(&rawtime, result, result_len); } +/// Portable version of POSIX strptime() +/// +/// @param str[in] string to convert +/// @param format[in] format to parse "str" +/// @param tm[out] time representation of "str" +/// @return Pointer to first unprocessed character or NULL +char *os_strptime(const char *str, const char *format, struct tm *tm) + FUNC_ATTR_NONNULL_ALL +{ +#ifdef HAVE_STRPTIME + return strptime(str, format, tm); +#else + return NULL; +#endif +} + /// Obtains the current Unix timestamp. /// /// @return Seconds since epoch. diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 69c614fff9..68abf57413 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -140,7 +140,9 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, } pum_anchor_grid = (int)curwin->w_grid.target->handle; - if (!ui_has(kUIMultigrid)) { + pum_win_row += curwin->w_grid.row_offset; + cursor_col += curwin->w_grid.col_offset; + if (!ui_has(kUIMultigrid) && curwin->w_grid.target != &default_grid) { pum_anchor_grid = (int)default_grid.handle; pum_win_row += curwin->w_winrow; cursor_col += curwin->w_wincol; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index d50520f49e..749627de80 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2322,7 +2322,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, getvcol(curwin, &pos, (colnr_T *)&tocol, NULL, NULL); } // do at least one character; happens when past end of line - if (fromcol == tocol) { + if (fromcol == tocol && search_match_endcol) { tocol = fromcol + 1; } area_highlighting = true; @@ -2758,7 +2758,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // :sign defined with "numhl" highlight. char_attr = sign_get_attr(num_sign, SIGN_NUMHL); } else if ((wp->w_p_cul || wp->w_p_rnu) - && lnum == wp->w_cursor.lnum) { + && lnum == wp->w_cursor.lnum + && filler_todo == 0) { // When 'cursorline' is set highlight the line number of // the current line differently. // TODO(vim): Can we use CursorLine instead of CursorLineNr @@ -3921,9 +3922,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, .hl_id = hl_err })); do_virttext = true; } else if (has_decor) { - VirtText *vp = decor_redraw_virt_text(wp->w_buffer, &decor_state); - if (vp) { - virt_text = *vp; + virt_text = decor_redraw_virt_text(wp->w_buffer, &decor_state); + if (kv_size(virt_text)) { do_virttext = true; } } @@ -4353,10 +4353,10 @@ void draw_virt_text(buf_T *buf, int *end_col, int max_col) DecorState *state = &decor_state; for (size_t i = 0; i < kv_size(state->active); i++) { HlRange *item = &kv_A(state->active, i); - if (item->start_row == state->row && item->virt_text + if (item->start_row == state->row && kv_size(item->virt_text) && item->virt_text_pos == kVTOverlay && item->virt_col >= 0) { - VirtText vt = *item->virt_text; + VirtText vt = item->virt_text; LineState s = LINE_STATE(""); int virt_attr = 0; int col = item->virt_col; diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index e52fd888bd..b760828458 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -36,15 +36,9 @@ NEW_TESTS_IN_ALOT := $(shell sed -n '/^source/ s/^source //; s/\.vim$$//p' $(add NEW_TESTS_IN_ALOT_LATIN := $(shell sed -n '/^source/ s/^source //; s/\.vim$$//p' test_alot_latin.vim) # Ignored tests. # test_alot_latin: Nvim does not allow setting encoding. -# test_autochdir: ported to Lua, but kept for easier merging. -# test_eval_func: used as include in old-style test (test_eval.in). -# test_listlbr: Nvim does not allow setting encoding. # test_largefile: uses too much resources to run on CI. NEW_TESTS_IGNORE := \ test_alot_latin $(NEW_TESTS_IN_ALOT_LATIN) \ - test_autochdir \ - test_eval_func \ - test_listlbr \ test_largefile \ NEW_TESTS := $(sort $(basename $(notdir $(wildcard test_*.vim)))) diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 275edece1e..2d94b637e0 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -373,9 +373,6 @@ let s:flaky_tests = [ \ 'Test_with_partial_callback()', \ ] -" Pattern indicating a common flaky test failure. -let s:flaky_errors_re = 'StopVimInTerminal\|VerifyScreenDump' - " Locate Test_ functions and execute them. redir @q silent function /^Test_ @@ -410,6 +407,9 @@ for s:test in sort(s:tests) let total_errors = [] let run_nr = 1 + " A test can set g:test_is_flaky to retry running the test. + let g:test_is_flaky = 0 + call RunTheTest(s:test) " Repeat a flaky test. Give up when: @@ -417,7 +417,7 @@ for s:test in sort(s:tests) " - it fails five times (with a different message) if len(v:errors) > 0 \ && (index(s:flaky_tests, s:test) >= 0 - \ || v:errors[0] =~ s:flaky_errors_re) + \ || g:test_is_flaky) while 1 call add(s:messages, 'Found errors in ' . s:test . ':') call extend(s:messages, v:errors) diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim index 67c537b407..d071f4b325 100644 --- a/src/nvim/testdir/test_autochdir.vim +++ b/src/nvim/testdir/test_autochdir.vim @@ -1,10 +1,10 @@ " Test 'autochdir' behavior -if !exists("+autochdir") - throw 'Skipped: autochdir feature missing' -endif +source check.vim +CheckOption autochdir func Test_set_filename() + CheckFunction test_autochdir let cwd = getcwd() call test_autochdir() set acd @@ -17,3 +17,5 @@ func Test_set_filename() exe 'cd ' . cwd call delete('samples/Xtest') endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 7788e09d43..5e99edf233 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -76,7 +76,7 @@ if has('timers') endfunc func Test_OptionSet_modeline() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override call test_override('starting', 1) au! OptionSet augroup set_tabstop @@ -507,7 +507,7 @@ func s:AutoCommandOptionSet(match) endfunc func Test_OptionSet() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override if !has("eval") || !exists("+autochdir") return endif @@ -648,7 +648,7 @@ func Test_OptionSet() endfunc func Test_OptionSet_diffmode() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override call test_override('starting', 1) " 18: Changing an option when entering diff mode new @@ -682,7 +682,7 @@ func Test_OptionSet_diffmode() endfunc func Test_OptionSet_diffmode_close() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override call test_override('starting', 1) " 19: Try to close the current window when entering diff mode " should not segfault @@ -1285,9 +1285,9 @@ func Test_autocommand_all_events() endfunc " Test TextChangedI and TextChangedP +" See test/functional/viml/completion_spec.lua' func Test_ChangedP() - " Nvim does not support test_override(). - throw 'skipped: see test/functional/viml/completion_spec.lua' + CheckFunction test_override new call setline(1, ['foo', 'bar', 'foobar']) call test_override("char_avail", 1) @@ -1350,7 +1350,7 @@ func SetLineOne() endfunc func Test_TextChangedI_with_setline() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override new call test_override('char_avail', 1) autocmd TextChangedI <buffer> call SetLineOne() @@ -1366,9 +1366,11 @@ func Test_TextChangedI_with_setline() endfunc func Test_Changed_FirstTime() - if !has('terminal') || has('gui_running') - return - endif + CheckFeature terminal + CheckNotGui + " Starting a terminal to run Vim is always considered flaky. + let g:test_is_flaky = 1 + " Prepare file for TextChanged event. call writefile([''], 'Xchanged.txt') let buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile'], {'term_rows': 3}) diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim index ff50d53d86..73b57f302e 100644 --- a/src/nvim/testdir/test_eval_stuff.vim +++ b/src/nvim/testdir/test_eval_stuff.vim @@ -24,7 +24,7 @@ endfunc func Test_for_invalid() call assert_fails("for x in 99", 'E714:') - call assert_fails("for x in 'asdf'", 'E714:') + call assert_fails("for x in function('winnr')", 'E714:') call assert_fails("for x in {'a': 9}", 'E714:') if 0 diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 5dae8d681a..555f549743 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1,5 +1,7 @@ " Tests for various functions. + source shared.vim +source check.vim " Must be done first, since the alternate buffer must be unset. func Test_00_bufexists() @@ -171,9 +173,8 @@ func Test_str2nr() endfunc func Test_strftime() - if !exists('*strftime') - return - endif + CheckFunction strftime + " Format of strftime() depends on system. We assume " that basic formats tested here are available and " identical on all systems which support strftime(). @@ -214,6 +215,33 @@ func Test_strftime() endif endfunc +func Test_strptime() + CheckFunction strptime + CheckNotMSWindows + + if exists('$TZ') + let tz = $TZ + endif + let $TZ = 'UTC' + + call assert_equal(1484653763, strptime('%Y-%m-%d %T', '2017-01-17 11:49:23')) + + " Force DST and check that it's considered + let $TZ = 'WINTER0SUMMER,J1,J365' + call assert_equal(1484653763 - 3600, strptime('%Y-%m-%d %T', '2017-01-17 11:49:23')) + + call assert_fails('call strptime()', 'E119:') + call assert_fails('call strptime("xxx")', 'E119:') + call assert_equal(0, strptime("%Y", '')) + call assert_equal(0, strptime("%Y", "xxx")) + + if exists('tz') + let $TZ = tz + else + unlet $TZ + endif +endfunc + func Test_resolve_unix() if !has('unix') return diff --git a/src/nvim/testdir/test_listlbr.vim b/src/nvim/testdir/test_listlbr.vim index d619ac0eb5..e0518de3c2 100644 --- a/src/nvim/testdir/test_listlbr.vim +++ b/src/nvim/testdir/test_listlbr.vim @@ -1,9 +1,5 @@ " Test for linebreak and list option (non-utf8) -" Nvim does not allow setting 'encoding', so skip this test. -finish - -set encoding=latin1 scriptencoding latin1 if !exists("+linebreak") || !has("conceal") @@ -46,6 +42,7 @@ func Test_set_linebreak() endfunc func Test_linebreak_with_list() + throw 'skipped: Nvim does not support enc=latin1' call s:test_windows('setl ts=4 sbr=+ list listchars=') call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ") let lines = s:screen_lines([1, 4], winwidth(0)) @@ -217,6 +214,7 @@ func Test_norm_after_block_visual() endfunc func Test_block_replace_after_wrapping() + throw 'skipped: Nvim does not support enc=latin1' call s:test_windows() call setline(1, repeat("a", 150)) exe "norm! 0yypk147|\<C-V>jr0" diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 3ebd048f46..08586dffe1 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -1,5 +1,6 @@ " Tests for :messages, :echomsg, :echoerr +source check.vim source shared.vim func Test_messages() @@ -77,7 +78,7 @@ func Test_echomsg() endfunc func Test_echoerr() - throw 'skipped: Nvim does not support test_ignore_error()' + CheckFunction test_ignore_error call test_ignore_error('IgNoRe') call assert_equal("\nIgNoRe hello", execute(':echoerr "IgNoRe hello"')) call assert_equal("\n12345 IgNoRe", execute(':echoerr 12345 "IgNoRe"')) diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 07e2481f95..5aef33cb09 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -448,6 +448,36 @@ func Test_backupskip() endif endfor + " Duplicates from environment variables should be filtered out (option has + " P_NODUP). Run this in a separate instance and write v:errors in a file, + " so that we see what happens on startup. + let after =<< trim [CODE] + let bsklist = split(&backupskip, ',') + call assert_equal(uniq(copy(bsklist)), bsklist) + call writefile(['errors:'] + v:errors, 'Xtestout') + qall + [CODE] + call writefile(after, 'Xafter') + " let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "set enc=utf8"' + let cmd = GetVimProg() . ' -S Xafter --cmd "set enc=utf8"' + + let saveenv = {} + for var in ['TMPDIR', 'TMP', 'TEMP'] + let saveenv[var] = getenv(var) + call setenv(var, '/duplicate/path') + endfor + + exe 'silent !' . cmd + call assert_equal(['errors:'], readfile('Xtestout')) + + " restore environment variables + for var in ['TMPDIR', 'TMP', 'TEMP'] + call setenv(var, saveenv[var]) + endfor + + call delete('Xtestout') + call delete('Xafter') + " Duplicates should be filtered out (option has P_NODUP) let backupskip = &backupskip set backupskip= diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 4ee16558a0..9443958984 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -871,7 +871,7 @@ func Test_popup_complete_backwards_ctrl_p() endfunc fun! Test_complete_o_tab() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override let s:o_char_pressed = 0 fun! s:act_on_text_changed() diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 704fdacdcd..da949f5940 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -2660,7 +2660,7 @@ endfunc " Test for incsearch highlighting of the :vimgrep pattern " This test used to cause "E315: ml_get: invalid lnum" errors. func Test_vimgrep_incsearch() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override enew set incsearch call test_override("char_avail", 1) diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 0703a6b141..d4d529e4b9 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -2,10 +2,11 @@ source shared.vim source screendump.vim +source check.vim +" See test/functional/legacy/search_spec.lua func Test_search_cmdline() - " See test/functional/legacy/search_spec.lua - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override if !exists('+incsearch') return endif @@ -202,9 +203,9 @@ func Test_search_cmdline() bw! endfunc +" See test/functional/legacy/search_spec.lua func Test_search_cmdline2() - " See test/functional/legacy/search_spec.lua - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override if !exists('+incsearch') return endif @@ -351,7 +352,7 @@ func Test_searchc() endfunc func Cmdline3_prep() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override " need to disable char_avail, " so that expansion of commandline works call test_override("char_avail", 1) @@ -361,14 +362,13 @@ func Cmdline3_prep() endfunc func Incsearch_cleanup() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override set noincsearch call test_override("char_avail", 0) bw! endfunc func Test_search_cmdline3() - throw 'skipped: Nvim does not support test_override()' if !exists('+incsearch') return endif @@ -382,7 +382,6 @@ func Test_search_cmdline3() endfunc func Test_search_cmdline3s() - throw 'skipped: Nvim does not support test_override()' if !exists('+incsearch') return endif @@ -409,7 +408,6 @@ func Test_search_cmdline3s() endfunc func Test_search_cmdline3g() - throw 'skipped: Nvim does not support test_override()' if !exists('+incsearch') return endif @@ -433,7 +431,6 @@ func Test_search_cmdline3g() endfunc func Test_search_cmdline3v() - throw 'skipped: Nvim does not support test_override()' if !exists('+incsearch') return endif @@ -450,9 +447,9 @@ func Test_search_cmdline3v() call Incsearch_cleanup() endfunc +" See test/functional/legacy/search_spec.lua func Test_search_cmdline4() - " See test/functional/legacy/search_spec.lua - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override if !exists('+incsearch') return endif @@ -507,7 +504,7 @@ func Test_search_cmdline5() endfunc func Test_search_cmdline7() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override " Test that pressing <c-g> in an empty command line " does not move the cursor if !exists('+incsearch') @@ -798,7 +795,7 @@ func Test_incsearch_vimgrep_dump() endfunc func Test_keep_last_search_pattern() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override if !exists('+incsearch') return endif @@ -820,7 +817,7 @@ func Test_keep_last_search_pattern() endfunc func Test_word_under_cursor_after_match() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override if !exists('+incsearch') return endif @@ -840,7 +837,7 @@ func Test_word_under_cursor_after_match() endfunc func Test_subst_word_under_cursor() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override if !exists('+incsearch') return endif @@ -882,7 +879,7 @@ func Test_incsearch_with_change() endfunc func Test_incsearch_cmdline_modifier() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override if !exists('+incsearch') return endif @@ -960,7 +957,7 @@ func Test_incsearch_search_dump() endfunc func Test_incsearch_substitute() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override if !exists('+incsearch') return endif @@ -982,7 +979,7 @@ func Test_incsearch_substitute() endfunc func Test_incsearch_substitute_long_line() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override new call test_override("char_avail", 1) set incsearch @@ -1104,7 +1101,7 @@ func Test_one_error_msg() endfunc func Test_incsearch_add_char_under_cursor() - throw 'skipped: Nvim does not support test_override()' + CheckFunction test_override if !exists('+incsearch') return endif @@ -1192,4 +1189,40 @@ func Test_search_smartcase_utf8() close! endfunc +func Test_zzzz_incsearch_highlighting_newline() + CheckRunVimInTerminal + CheckOption incsearch + CheckScreendump + new + call test_override("char_avail", 1) + + let commands =<< trim [CODE] + set incsearch nohls + call setline(1, ['test', 'xxx']) + [CODE] + call writefile(commands, 'Xincsearch_nl') + let buf = RunVimInTerminal('-S Xincsearch_nl', {'rows': 5, 'cols': 10}) + " Need to send one key at a time to force a redraw + call term_sendkeys(buf, '/test') + sleep 100m + call VerifyScreenDump(buf, 'Test_incsearch_newline1', {}) + call term_sendkeys(buf, '\n') + sleep 100m + call VerifyScreenDump(buf, 'Test_incsearch_newline2', {}) + call term_sendkeys(buf, 'x') + sleep 100m + call VerifyScreenDump(buf, 'Test_incsearch_newline3', {}) + call term_sendkeys(buf, 'x') + call VerifyScreenDump(buf, 'Test_incsearch_newline4', {}) + call term_sendkeys(buf, "\<CR>") + sleep 100m + call VerifyScreenDump(buf, 'Test_incsearch_newline5', {}) + call StopVimInTerminal(buf) + + " clean up + call delete('Xincsearch_nl') + call test_override("char_avail", 0) + bw +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index 4bbd722fdb..9c3a5636ce 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -1,8 +1,7 @@ " Test for signs -if !has('signs') - finish -endif +source check.vim +CheckFeature signs source screendump.vim @@ -1541,7 +1540,7 @@ endfunc " Tests for memory allocation failures in sign functions func Test_sign_memfailures() - throw 'skipped: Nvim does not support test_alloc_fail()' + CheckFunction test_alloc_fail call writefile(repeat(["Sun is shining"], 30), "Xsign") edit Xsign diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index 4d1ad10c23..eb9378194f 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -862,6 +862,34 @@ func Test_x_arg() call delete('Xtest_x_arg') endfunc +" Test for --not-a-term avoiding escape codes. +func Test_not_a_term() + CheckUnix + CheckNotGui + + if &shellredir =~ '%s' + let redir = printf(&shellredir, 'Xvimout') + else + let redir = &shellredir .. ' Xvimout' + endif + + " Without --not-a-term there are a few escape sequences. + " This will take 2 seconds because of the missing --not-a-term + let cmd = GetVimProg() .. ' --cmd quit ' .. redir + exe "silent !" . cmd + " call assert_match("\<Esc>", readfile('Xvimout')->join()) + call assert_match("\<Esc>", join(readfile('Xvimout'))) + call delete('Xvimout') + + " With --not-a-term there are no escape sequences. + let cmd = GetVimProg() .. ' --not-a-term --cmd quit ' .. redir + exe "silent !" . cmd + " call assert_notmatch("\<Esc>", readfile('Xvimout')->join()) + call assert_notmatch("\<Esc>", join(readfile('Xvimout'))) + call delete('Xvimout') +endfunc + + " Test starting vim with various names: vim, ex, view, evim, etc. func Test_progname() CheckUnix diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim index 9cf8690d57..6bbe714d19 100644 --- a/src/nvim/testdir/test_system.vim +++ b/src/nvim/testdir/test_system.vim @@ -93,7 +93,6 @@ function! Test_system_exmode() endfunc func Test_system_with_shell_quote() - throw 'skipped: enable after porting method patches' CheckMSWindows call mkdir('Xdir with spaces', 'p') @@ -122,7 +121,8 @@ func Test_system_with_shell_quote() let msg = printf('shell=%s shellxquote=%s', &shell, &shellxquote) try - let out = 'echo 123'->system() + " let out = 'echo 123'->system() + let out = system('echo 123') catch call assert_report(printf('%s: %s', msg, v:exception)) continue diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 13971a918d..ceaa5de92b 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -317,8 +317,8 @@ endfunc " Test that the garbage collector isn't triggered if a timer callback invokes " vgetc(). func Test_nocatch_garbage_collect() - " skipped: Nvim does not support test_garbagecollect_soon(), test_override() - return + CheckFunction test_garbagecollect_soon + CheckFunction test_override " 'uptimetime. must be bigger than the timer timeout set ut=200 call test_garbagecollect_soon() diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim index 3b66071d6d..54caed3983 100644 --- a/src/nvim/testdir/test_undo.vim +++ b/src/nvim/testdir/test_undo.vim @@ -3,6 +3,8 @@ " undo-able pieces. Do that by setting 'undolevels'. " Also tests :earlier and :later. +source check.vim + func Test_undotree() new @@ -135,7 +137,7 @@ func BackOne(expected) endfunc func Test_undo_del_chars() - throw 'skipped: Nvim does not support test_settime()' + CheckFunction test_settime " Setup a buffer without creating undo entries new @@ -330,7 +332,7 @@ func Test_insert_expr() endfunc func Test_undofile_earlier() - throw 'skipped: Nvim does not support test_settime()' + CheckFunction test_settime let t0 = localtime() - 43200 call test_settime(t0) diff --git a/src/nvim/ui.c b/src/nvim/ui.c index c6c09c80d7..94b6e9e39d 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -110,6 +110,7 @@ static char uilog_last_event[1024] = { 0 }; void ui_init(void) { default_grid.handle = 1; + msg_grid_adj.target = &default_grid; ui_comp_init(); } diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 946215d957..a2e9266fbb 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -184,14 +184,12 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width, insert_at--; } // not found: new grid - kv_push(layers, grid); - if (insert_at < kv_size(layers)-1) { - for (size_t i = kv_size(layers)-1; i > insert_at; i--) { - kv_A(layers, i) = kv_A(layers, i-1); - kv_A(layers, i)->comp_index = i; - } - kv_A(layers, insert_at) = grid; + kv_pushp(layers); + for (size_t i = kv_size(layers)-1; i > insert_at; i--) { + kv_A(layers, i) = kv_A(layers, i-1); + kv_A(layers, i)->comp_index = i; } + kv_A(layers, insert_at) = grid; grid->comp_row = row; grid->comp_col = col; @@ -280,6 +278,9 @@ static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, // should configure all grids before entering win_update() if (curgrid != &default_grid) { size_t new_index = kv_size(layers)-1; + if (kv_A(layers, new_index) == &msg_grid) { + new_index--; + } if (kv_A(layers, new_index) == &pum_grid) { new_index--; } diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 7a6b5be8bc..6d7d9b4d8b 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -666,6 +666,70 @@ describe('lua: nvim_buf_attach on_bytes', function() } end) + it("tab with noexpandtab and softtabstop", function() + command("set noet") + command("set ts=4") + command("set sw=2") + command("set sts=2") + + local check_events = setup_eventcheck(verify, {'asdfasdf'}) + + feed("gg0i<tab>") + + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 0, 0, 0, 0, 1, 1 }, + { "test1", "bytes", 1, 4, 0, 1, 1, 0, 0, 0, 0, 1, 1 }, + } + feed("<tab>") + + -- when spaces are merged into a tabstop + check_events { + { "test1", "bytes", 1, 5, 0, 2, 2, 0, 0, 0, 0, 1, 1 }, + { "test1", "bytes", 1, 6, 0, 3, 3, 0, 0, 0, 0, 1, 1 }, + { "test1", "bytes", 1, 7, 0, 0, 0, 0, 4, 4, 0, 1, 1 }, + } + + feed("<esc>u") + check_events { + { "test1", "bytes", 1, 8, 0, 0, 0, 0, 1, 1, 0, 4, 4 }, + { "test1", "bytes", 1, 8, 0, 0, 0, 0, 4, 4, 0, 0, 0 } + } + + -- in REPLACE mode + feed("R<tab><tab>") + check_events { + { "test1", "bytes", 1, 9, 0, 0, 0, 0, 1, 1, 0, 1, 1 }, + { "test1", "bytes", 1, 10, 0, 1, 1, 0, 0, 0, 0, 1, 1 }, + { "test1", "bytes", 1, 11, 0, 2, 2, 0, 1, 1, 0, 1, 1 }, + { "test1", "bytes", 1, 12, 0, 3, 3, 0, 0, 0, 0, 1, 1 }, + { "test1", "bytes", 1, 13, 0, 0, 0, 0, 4, 4, 0, 1, 1 }, + } + feed("<esc>u") + check_events { + { "test1", "bytes", 1, 14, 0, 0, 0, 0, 1, 1, 0, 4, 4 }, + { "test1", "bytes", 1, 14, 0, 2, 2, 0, 2, 2, 0, 1, 1 }, + { "test1", "bytes", 1, 14, 0, 0, 0, 0, 2, 2, 0, 1, 1 } + } + + -- in VISUALREPLACE mode + feed("gR<tab><tab>") + check_events { + { "test1", "bytes", 1, 15, 0, 0, 0, 0, 1, 1, 0, 1, 1 }; + { "test1", "bytes", 1, 16, 0, 1, 1, 0, 1, 1, 0, 1, 1 }; + { "test1", "bytes", 1, 17, 0, 2, 2, 0, 1, 1, 0, 1, 1 }; + { "test1", "bytes", 1, 18, 0, 3, 3, 0, 1, 1, 0, 1, 1 }; + { "test1", "bytes", 1, 19, 0, 3, 3, 0, 1, 1, 0, 0, 0 }; + { "test1", "bytes", 1, 20, 0, 3, 3, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 22, 0, 2, 2, 0, 1, 1, 0, 0, 0 }; + { "test1", "bytes", 1, 23, 0, 2, 2, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 25, 0, 1, 1, 0, 1, 1, 0, 0, 0 }; + { "test1", "bytes", 1, 26, 0, 1, 1, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 28, 0, 0, 0, 0, 1, 1, 0, 0, 0 }; + { "test1", "bytes", 1, 29, 0, 0, 0, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 31, 0, 0, 0, 0, 4, 4, 0, 1, 1 }; + } + end) + it("sends events when undoing with undofile", function() write_file("Xtest-undofile", dedent([[ 12345 @@ -717,6 +781,26 @@ describe('lua: nvim_buf_attach on_bytes', function() command("bw!") end) + it("blockwise paste with uneven line lengths", function() + local check_events = setup_eventcheck(verify, {'aaaa', 'aaa', 'aaa'}) + + -- eq({}, meths.buf_get_lines(0, 0, -1, true)) + feed("gg0<c-v>jj$d") + + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 0, 4, 4, 0, 0, 0 }, + { "test1", "bytes", 1, 3, 1, 0, 1, 0, 3, 3, 0, 0, 0 }, + { "test1", "bytes", 1, 3, 2, 0, 2, 0, 3, 3, 0, 0, 0 }, + } + + feed("p") + check_events { + { "test1", "bytes", 1, 4, 0, 0, 0, 0, 0, 0, 0, 4, 4 }, + { "test1", "bytes", 1, 4, 1, 0, 5, 0, 0, 0, 0, 3, 3 }, + { "test1", "bytes", 1, 4, 2, 0, 9, 0, 0, 0, 0, 3, 3 }, + } + + end) teardown(function() os.remove "Xtest-reload" diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 7f4ab3ee5d..295a54aec8 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -302,6 +302,35 @@ describe('decorations providers', function() | ]]} end) + + it('can have virtual text', function() + insert(mulholland) + setup_provider [[ + local hl = a.nvim_get_hl_id_by_name "ErrorMsg" + local test_ns = a.nvim_create_namespace "mulholland" + function on_do(event, ...) + if event == "line" then + local win, buf, line = ... + a.nvim_buf_set_extmark(buf, test_ns, line, 0, { + virt_text = {{'+', 'ErrorMsg'}}; + virt_text_pos='overlay'; + ephemeral = true; + }) + end + end + ]] + + screen:expect{grid=[[ + {2:+}/ just to see if there was an accident | + {2:+}/ on Mulholland Drive | + {2:+}ry_start(); | + {2:+}ufref_T save_buf; | + {2:+}witch_buffer(&save_buf, buf); | + {2:+}osp = getmark(mark, false); | + {2:+}estore_buffer(&save_buf);^ | + | + ]]} + end) end) describe('extmark decorations', function() diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua index 69b6ab8cf0..a8d9fb02fc 100644 --- a/test/functional/ui/diff_spec.lua +++ b/test/functional/ui/diff_spec.lua @@ -1049,6 +1049,8 @@ it('diff updates line numbers below filler lines', function() [9] = {background = Screen.colors.LightMagenta}, [10] = {bold = true, foreground = Screen.colors.Brown}, [11] = {foreground = Screen.colors.Brown}, + [12] = {foreground = Screen.colors.Brown, bold = true, background = Screen.colors.Red}; + [13] = {background = Screen.colors.Gray90}; }) source([[ call setline(1, ['a', 'a', 'a', 'y', 'b', 'b', 'b', 'b', 'b']) @@ -1107,4 +1109,22 @@ it('diff updates line numbers below filler lines', function() {3:[No Name] [+] }{7:[No Name] [+] }| | ]]) + command("set signcolumn number tgc cursorline") + command("hi CursorLineNr guibg=red") + screen:expect{grid=[[ + {1: }a {3:│}{11: 2 }a | + {1: }a {3:│}{11: 1 }a | + {1: }a {3:│}{12:3 }{13:^a }| + {1: }{8:x}{9: }{3:│}{11: 1 }{8:y}{9: }| + {1: }{4:x }{3:│}{11: }{2:----------------}| + {1: }{4:x }{3:│}{11: }{2:----------------}| + {1: }b {3:│}{11: 2 }b | + {1: }b {3:│}{11: 3 }b | + {1: }b {3:│}{11: 4 }b | + {1: }b {3:│}{11: 5 }b | + {1: }b {3:│}{11: 6 }b | + {6:~ }{3:│}{6:~ }| + {3:[No Name] [+] }{7:[No Name] [+] }| + signcolumn=auto | + ]]} end) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index e4824521b0..664b8e7ab7 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -774,6 +774,131 @@ describe('float window', function() end end) + it('with border show popupmenu', function() + screen:try_resize(40,10) + local buf = meths.create_buf(false, false) + meths.buf_set_lines(buf, 0, -1, true, {'aaa aab ', + 'abb acc ', ''}) + meths.open_win(buf, true, {relative='editor', width=9, height=3, row=0, col=5, border="double"}) + feed 'G' + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 5 + {5:╔═════════╗}| + {5:║}{1:aaa aab }{5:║}| + {5:║}{1:abb acc }{5:║}| + {5:║}{1:^ }{5:║}| + {5:╚═════════╝}| + ]], float_pos={ + [5] = { { id = 1002 }, "NW", 1, 0, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 0}; + }} + else + screen:expect{grid=[[ + {5:╔═════════╗} | + {0:~ }{5:║}{1:aaa aab }{5:║}{0: }| + {0:~ }{5:║}{1:abb acc }{5:║}{0: }| + {0:~ }{5:║}{1:^ }{5:║}{0: }| + {0:~ }{5:╚═════════╝}{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + end + + feed 'i<c-x><c-p>' + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {3:-- }{8:match 1 of 4} | + ## grid 5 + {5:╔═════════╗}| + {5:║}{1:aaa aab }{5:║}| + {5:║}{1:abb acc }{5:║}| + {5:║}{1:acc^ }{5:║}| + {5:╚═════════╝}| + ## grid 6 + {1: aaa }| + {1: aab }| + {1: abb }| + {13: acc }| + ]], float_pos={ + [5] = { { + id = 1002 + }, "NW", 1, 0, 5, true }, + [6] = { { + id = -1 + }, "NW", 5, 4, 0, false } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 3}; + }} + else + screen:expect{grid=[[ + {5:╔═════════╗} | + {0:~ }{5:║}{1:aaa aab }{5:║}{0: }| + {0:~ }{5:║}{1:abb acc }{5:║}{0: }| + {0:~ }{5:║}{1:acc^ }{5:║}{0: }| + {0:~ }{1: aaa }{0: }| + {0:~ }{1: aab }{0: }| + {0:~ }{1: abb }{0: }| + {0:~ }{13: acc }{0: }| + {0:~ }| + {3:-- }{8:match 1 of 4} | + ]]} + end + end) + it('can have minimum size', function() insert("the background text") local buf = meths.create_buf(false, true) diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index ff9f30d0a1..958e137f65 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -24,10 +24,6 @@ describe('screen', function() } ) end) - after_each(function() - screen:detach() - end) - it('default initial screen', function() screen:expect([[ ^ | @@ -67,10 +63,6 @@ local function screen_tests(linegrid) } ) end) - after_each(function() - screen:detach() - end) - describe(':suspend', function() it('is forwarded to the UI', function() local function check() @@ -1004,3 +996,39 @@ describe('Screen default colors', function() end} end) end) + + +describe('screen with msgsep deactivated on startup', function() + local screen + + before_each(function() + clear('--cmd', 'set display-=msgsep') + screen = Screen.new() + screen:attach() + screen:set_default_attr_ids { + [0] = {bold=true, foreground=255}; + [7] = {bold = true, foreground = Screen.colors.SeaGreen}; + } + end) + + it('execute command with multi-line output', function() + feed ':ls<cr>' + screen:expect([[ + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + :ls | + 1 %a "[No Name]" line 1 | + {7:Press ENTER or type command to continue}^ | + ]]) + feed '<cr>' -- skip the "Press ENTER..." state or tests will hang + end) +end) |