diff options
83 files changed, 2441 insertions, 1091 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index c9d526d9aa..7172091ceb 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -232,7 +232,44 @@ An example of calling the api from vimscript: > " later call nvim_buf_clear_namespace(0, src, 0, -1) + + +============================================================================== +Floating windows *api-floatwin* + +Nvim supports floating windows, windows that are displayed on top of ordinary +windows. This is useful to implement simple widgets, such as tooltips +displaying information next to cursor text. Floating windows are fully +functional buffer windows and support user editing. They support the standard +|api-window| calls and almost all window options (with some exceptions such as +'statusline' is not supported currently). + +Floating windows are created either by |nvim_open_win()| to open a new window, +or |nvim_win_config()| to reconfigure a normal window into a float. Currently +the position can either be grid coordinates relative the top-left of some +window, or a position relative to the current window cursor. The parameters +for positioning are described in detail at |nvim_open_win()| help. + +|nvim_open_win()| assumes an existing buffer to display in the window. To create +a scratch buffer for the float, |nvim_create_buffer()| can be used. The text in +the buffer can be highlighted using standard functionality, such as syntax +highlighting, or |api-highlights|. + +By default, floats will use |hl-NormalFloat| as normal highlight, which +links to |hl-Pmenu| in the builtin color scheme. The 'winhighlight' option can +be used to override it. Currently, floating windows don't support any visual +decorations like a border or additional widgets like scrollbar. + +Here is an example for creating a float with scratch buffer: > + + let buf = nvim_create_buf(v:false, v:true) + call nvim_buf_set_lines(buf, 0, -1, v:true, ["test", "text"]) + let opts = {'relative': 'cursor', 'col':0, 'row':1, 'anchor': 'NW'} + let win = nvim_open_win(buf, 0, 10, 2, opts) + " optional: change highlight, otherwise Pmenu is used + call nvim_win_set_option(win, 'winhl', 'Normal:MyHighlight') > +To close the float, |nvim_win_close()| can be used. ============================================================================== Global Functions *api-global* @@ -576,6 +613,94 @@ nvim_set_current_win({window}) *nvim_set_current_win()* Parameters: ~ {window} Window handle +nvim_create_buf({listed}, {scratch}) *nvim_create_buf()* + Creates a new, empty, unnamed buffer. + + Parameters: ~ + {listed} Controls 'buflisted' + {scratch} Creates a "throwaway" |scratch-buffer| for + temporary work (always 'nomodified') + + Return: ~ + Buffer handle, or 0 on error + + *nvim_open_win()* +nvim_open_win({buffer}, {enter}, {width}, {height}, {options}) + Open a new window. + + Currently this is used to open floating and external windows. + Floats are windows that are drawn above the split layout, at + some anchor position in some other window. Floats can be draw + internally or by external GUI with the |ui-multigrid| + extension. External windows are only supported with multigrid + GUIs, and are displayed as separate top-level windows. + + For a general overview of floats, see |api-floatwin|. + + Exactly one of `external` and `relative` must be specified. + + Parameters: ~ + {buffer} handle of buffer to be displayed in the window + {enter} whether the window should be entered (made the + current window) + {width} width of window (in character cells) + {height} height of window (in character cells) + {options} dict of options for configuring window + positioning accepts the following keys: + + `relative`: If set, the window becomes a + floating window. The window will be placed with + row,col coordinates relative one of the + following: + "editor" the global editor grid + "win" a window. Use 'win' option below to + specify window id, or current window will + be used by default. + "cursor" the cursor position in current window. + + `anchor`: the corner of the float that the row,col + position defines + "NW" north-west (default) + "NE" north-east + "SW" south-west + "SE" south-east + + `focusable`: Whether window can be focused by wincmds and + mouse events. Defaults to true. Even if set to false, + the window can still be entered using + |nvim_set_current_win()| API call. + + `row`: row position. Screen cell height are used as unit. + Can be floating point. + + `col`: column position. Screen cell width is used as + unit. Can be floating point. + + `win`: when using relative='win', window id of the window + where the position is defined. + + `external`: GUI should display the window as an external + top-level window. Currently accepts no other + positioning options together with this. + + With editor positioning row=0, col=0 refers to the top-left + corner of the screen-grid and row=Lines-1, Columns-1 refers to + the bottom-right corner. Floating point values are allowed, + but the builtin implementation (used by TUI and GUIs without + multigrid support) will always round down to nearest integer. + + Out-of-bounds values, and configurations that make the float + not fit inside the main editor, are allowed. The builtin + implementation will truncate values so floats are completely + within the main screen grid. External GUIs could let floats + hover outside of the main window like a tooltip, but this + should not be used to specify arbitrary WM screen positions. + + Parameters: ~ + + Return: ~ + the window handle or 0 when error + nvim_list_tabpages() *nvim_list_tabpages()* Gets the current list of tabpage handles. @@ -1436,6 +1561,37 @@ nvim_win_is_valid({window}) *nvim_win_is_valid()* Return: ~ true if the window is valid, false otherwise + *nvim_win_config()* +nvim_win_config({window}, {width}, {height}, {options}) + Configure window position. Currently this is only used to + configure floating and external windows (including changing a + split window to these types). + + See documentation at |nvim_open_win()|, for the meaning of + parameters. Pass in -1 for 'witdh' and 'height' to keep + exiting size. + + When reconfiguring a floating window, absent option keys will + not be changed. The following restriction apply: `row`, `col` + and `relative` must be reconfigured together. Only changing a + subset of these is an error. + +nvim_win_close({window}, {force}) *nvim_win_close()* + Close a window. + + This is equivalent to |:close| with count except that it takes + a window id. + + Parameters: ~ + {window} Window handle + {force} Behave like `:close!` The last window of a + buffer with unwritten changes can be closed. The + buffer will become hidden, even if 'hidden' is + not set. + + Return: ~ + Window number + ============================================================================== Tabpage Functions *api-tabpage* diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 89d9e45e0d..ca10d44467 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -40,15 +40,18 @@ effects. Be careful not to destroy your text. 2. Defining autocommands *autocmd-define* *:au* *:autocmd* -:au[tocmd] [group] {event} {pat} [nested] {cmd} +:au[tocmd] [group] {event} {pat} [++once] [++nested] {cmd} Add {cmd} to the list of commands that Vim will execute automatically on {event} for a file matching {pat} |autocmd-patterns|. Note: A quote character is seen as argument to the :autocmd and won't start a comment. - Vim always adds the {cmd} after existing autocommands, - so that the autocommands execute in the order in which - they were given. See |autocmd-nested| for [nested]. + Nvim always adds {cmd} after existing autocommands so + they execute in the order in which they were defined. + See |autocmd-nested| for [++nested]. + *autocmd-once* + If [++once] is supplied the command is executed once, + then removed ("one shot"). The special pattern <buffer> or <buffer=N> defines a buffer-local autocommand. See |autocmd-buflocal|. @@ -116,10 +119,11 @@ prompt. When one command outputs two messages this can happen anyway. ============================================================================== 3. Removing autocommands *autocmd-remove* -:au[tocmd]! [group] {event} {pat} [nested] {cmd} +:au[tocmd]! [group] {event} {pat} [++once] [++nested] {cmd} Remove all autocommands associated with {event} and - {pat}, and add the command {cmd}. See - |autocmd-nested| for [nested]. + {pat}, and add the command {cmd}. + See |autocmd-once| for [++once]. + See |autocmd-nested| for [++nested]. :au[tocmd]! [group] {event} {pat} Remove all autocommands associated with {event} and @@ -806,20 +810,25 @@ InsertCharPre When a character is typed in Insert mode, a different character. When |v:char| is set to more than one character this text is inserted literally. - It is not allowed to change the text |textlock|. - The event is not triggered when 'paste' is - set. + + Cannot change the text. |textlock| + Not triggered when 'paste' is set. *TextYankPost* TextYankPost Just after a |yank| or |deleting| command, but not if the black hole register |quote_| is used nor for |setreg()|. Pattern must be *. Sets these |v:event| keys: + inclusive operator regcontents regname regtype + The `inclusive` flag combined with the |'[| + and |']| marks can be used to calculate the + precise region of the operation. + Recursion is ignored. - It is not allowed to change the text |textlock|. + Cannot change the text. |textlock| *InsertEnter* InsertEnter Just before starting Insert mode. Also for Replace mode and Virtual Replace mode. The @@ -985,7 +994,6 @@ SwapExists Detected an existing swap file when starting It is not allowed to change to another buffer, change a buffer name or change directory here. - {only available with the +eval feature} *Syntax* Syntax When the 'syntax' option has been set. The pattern is matched against the syntax name. @@ -1340,8 +1348,7 @@ option will not cause any commands to be executed. another extension. Example: > :au BufEnter *.cpp so ~/.config/nvim/init_cpp.vim :au BufEnter *.cpp doau BufEnter x.c -< Be careful to avoid endless loops. See - |autocmd-nested|. +< Be careful to avoid endless loops. |autocmd-nested| When the [group] argument is not given, Vim executes the autocommands for all groups. When the [group] @@ -1456,9 +1463,9 @@ instead of ":q!". *autocmd-nested* *E218* By default, autocommands do not nest. If you use ":e" or ":w" in an autocommand, Vim does not execute the BufRead and BufWrite autocommands for -those commands. If you do want this, use the "nested" flag for those commands -in which you want nesting. For example: > - :autocmd FileChangedShell *.c nested e! +those commands. If you do want this, use the "++nested" flag for those +commands in which you want nesting. For example: > + :autocmd FileChangedShell *.c ++nested e! The nesting is limited to 10 levels to get out of recursive loops. It's possible to use the ":au" command in an autocommand. This can be a diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index c73b460767..1761616651 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1635,6 +1635,17 @@ j Where it makes sense, remove a comment leader when joining lines. For // in the list ~ Becomes: int i; // the index in the list ~ +p Don't break lines at single spaces that follow periods. This is + intended to complement 'joinspaces' and |cpo-J|, for prose with + sentences separated by two spaces. For example, with 'textwidth' set + to 28: > + Surely you're joking, Mr. Feynman! +< Becomes: > + Surely you're joking, + Mr. Feynman! +< Instead of: > + Surely you're joking, Mr. + Feynman! With 't' and 'c' you can specify when Vim performs auto-wrapping: diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 2610b2683e..020427c66f 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1539,6 +1539,7 @@ v:event Dictionary of event data for the current |autocommand|. Valid cmdlevel Level of cmdline. cmdtype Type of cmdline, |cmdline-char|. cwd Current working directory. + inclusive Motion is |inclusive|, else exclusive. scope Event-specific scope name. operator Current |operator|. Also set for Ex commands (unlike |v:operator|). For @@ -4517,11 +4518,13 @@ getwininfo([{winid}]) *getwininfo()* tab pages is returned. Each List item is a Dictionary with the following entries: + botline last displayed buffer line bufnr number of buffer in the window height window height (excluding winbar) loclist 1 if showing a location list quickfix 1 if quickfix or location list window tabnr tab page number + topline first displayed buffer line variables a reference to the dictionary with window-local variables width window width diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index 84867318a4..d5a123e3ea 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -66,10 +66,11 @@ and end position. Generally, motions that move between lines affect lines characterwise). However, there are some exceptions. *exclusive* *inclusive* -A character motion is either inclusive or exclusive. When inclusive, the +Character motion is either inclusive or exclusive. When inclusive, the start and end position of the motion are included in the operation. When exclusive, the last character towards the end of the buffer is not included. -Linewise motions always include the start and end position. +Linewise motions always include the start and end position. Plugins can +check the v:event.inclusive flag of the |TextYankPost| event. Which motions are linewise, inclusive or exclusive is mentioned with the command. There are however, two general exceptions: diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index cdcb61404f..af94c60629 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -35,7 +35,7 @@ There are 3 ways to create a terminal buffer: < Note: The "term://" pattern is handled by a BufReadCmd handler, so the |autocmd-nested| modifier is required to use it in an autocmd. > - autocmd VimEnter * nested split term://sh + autocmd VimEnter * ++nested split term://sh < This is only mentioned for reference; use |:terminal| instead. When the terminal starts, the buffer contents are updated and the buffer is diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 684830b78d..b60a952def 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -4953,6 +4953,8 @@ NonText '@' at the end of the window, characters from 'showbreak' fit at the end of the line). See also |hl-EndOfBuffer|. *hl-Normal* Normal normal text + *hl-NormalFloat* +NormalFloat Normal text in floating windows. *hl-NormalNC* NormalNC normal text in non-current windows *hl-Pmenu* diff --git a/runtime/doc/usr_40.txt b/runtime/doc/usr_40.txt index e5d55fb857..9a1fe50f31 100644 --- a/runtime/doc/usr_40.txt +++ b/runtime/doc/usr_40.txt @@ -453,15 +453,15 @@ matching BufWritePre autocommands and executes them, and then it performs the ":write". The general form of the :autocmd command is as follows: > - :autocmd [group] {events} {file_pattern} [nested] {command} + :autocmd [group] {events} {file_pattern} [++nested] {command} The [group] name is optional. It is used in managing and calling the commands (more on this later). The {events} parameter is a list of events (comma separated) that trigger the command. {file_pattern} is a filename, usually with wildcards. For example, using "*.txt" makes the autocommand be used for all files whose name end in ".txt". -The optional [nested] flag allows for nesting of autocommands (see below), and -finally, {command} is the command to be executed. +The optional [++nested] flag allows for nesting of autocommands (see below), +and finally, {command} is the command to be executed. EVENTS @@ -576,9 +576,9 @@ NESTING Generally, commands executed as the result of an autocommand event will not trigger any new events. If you read a file in response to a FileChangedShell event, it will not trigger the autocommands that would set the syntax, for -example. To make the events triggered, add the "nested" argument: > +example. To make the events triggered, add the "++nested" flag: > - :autocmd FileChangedShell * nested edit + :autocmd FileChangedShell * ++nested edit EXECUTING AUTOCOMMANDS diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 25517e506b..55c3be9a57 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -133,6 +133,7 @@ Command-line highlighting: removed in the future). Commands: + |:autocmd| accepts the `++once` flag |:checkhealth| |:cquit| can use [count] to set the exit code |:drop| is always available @@ -160,6 +161,7 @@ Functions: Highlight groups: |expr-highlight| highlight groups (prefixed with "Nvim") + |hl-NormalFloat| highlights floating window |hl-NormalNC| highlights non-current windows |hl-MsgSeparator| highlights separator for scrolled messages |hl-QuickFixLine| diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 6e1ad0f1f4..63ffd91bfc 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -880,15 +880,15 @@ CTRL-W g } *CTRL-W_g}* cursor. This is less clever than using |:ptag|, but you don't need a tags file and it will also find matches in system include files. Example: > - :au! CursorHold *.[ch] nested exe "silent! psearch " . expand("<cword>") + :au! CursorHold *.[ch] ++nested exe "silent! psearch " . expand("<cword>") < Warning: This can be slow. Example *CursorHold-example* > - :au! CursorHold *.[ch] nested exe "silent! ptag " . expand("<cword>") + :au! CursorHold *.[ch] ++nested exe "silent! ptag " . expand("<cword>") This will cause a ":ptag" to be executed for the keyword under the cursor, -when the cursor hasn't moved for the time set with 'updatetime'. The "nested" +when the cursor hasn't moved for the time set with 'updatetime'. "++nested" makes other autocommands be executed, so that syntax highlighting works in the preview window. The "silent!" avoids an error message when the tag could not be found. Also see |CursorHold|. To disable this again: > @@ -898,7 +898,7 @@ be found. Also see |CursorHold|. To disable this again: > A nice addition is to highlight the found tag, avoid the ":ptag" when there is no word under the cursor, and a few other things: > - :au! CursorHold *.[ch] nested call PreviewWord() + :au! CursorHold *.[ch] ++nested call PreviewWord() :func PreviewWord() : if &previewwindow " don't do this in the preview window : return diff --git a/runtime/nvim.png b/runtime/nvim.png Binary files differindex dbc65ef363..5d6cee3b49 100644 --- a/runtime/nvim.png +++ b/runtime/nvim.png diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 9cd178eaeb..3613a8f8bc 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -380,8 +380,6 @@ void nvim_buf_set_lines(uint64_t channel_id, } } - win_T *save_curwin = NULL; - tabpage_T *save_curtab = NULL; size_t new_len = replacement.size; size_t old_len = (size_t)(end - start); ptrdiff_t extra = 0; // lines added to text, can be negative @@ -397,8 +395,8 @@ void nvim_buf_set_lines(uint64_t channel_id, } try_start(); - bufref_T save_curbuf = { NULL, 0, 0 }; - switch_to_win_for_buf(buf, &save_curwin, &save_curtab, &save_curbuf); + aco_save_T aco; + aucmd_prepbuf(&aco, (buf_T *)buf); if (u_save((linenr_T)(start - 1), (linenr_T)end) == FAIL) { api_set_error(err, kErrorTypeException, "Failed to save undo information"); @@ -465,27 +463,21 @@ void nvim_buf_set_lines(uint64_t channel_id, // changed range, and move any in the remainder of the buffer. // Only adjust marks if we managed to switch to a window that holds // the buffer, otherwise line numbers will be invalid. - if (save_curbuf.br_buf == NULL) { - mark_adjust((linenr_T)start, - (linenr_T)(end - 1), - MAXLNUM, - (long)extra, - false); - } + mark_adjust((linenr_T)start, + (linenr_T)(end - 1), + MAXLNUM, + (long)extra, + false); changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra, true); - if (save_curbuf.br_buf == NULL) { - fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra); - } - end: for (size_t i = 0; i < new_len; i++) { xfree(lines[i]); } xfree(lines); - restore_win_for_buf(save_curwin, save_curtab, &save_curbuf); + aucmd_restbuf(&aco); try_end(err); } @@ -1109,28 +1101,6 @@ free_exit: return 0; } -// Check if deleting lines made the cursor position invalid. -// Changed the lines from "lo" to "hi" and added "extra" lines (negative if -// deleted). -static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra) -{ - if (curwin->w_cursor.lnum >= lo) { - // Adjust the cursor position if it's in/after the changed - // lines. - if (curwin->w_cursor.lnum >= hi) { - curwin->w_cursor.lnum += extra; - check_cursor_col(); - } else if (extra < 0) { - curwin->w_cursor.lnum = lo; - check_cursor(); - } else { - check_cursor_col(); - } - changed_cline_bef_curs(); - } - invalidate_botline(); -} - // Normalizes 0-based indexes to buffer line numbers static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob) { diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 94d1697083..a773234ea0 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -15,6 +15,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/dispatch.h" #include "nvim/api/buffer.h" +#include "nvim/api/window.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/lua/executor.h" @@ -31,6 +32,7 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/fileio.h" #include "nvim/option.h" #include "nvim/state.h" #include "nvim/syntax.h" @@ -978,11 +980,12 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err) return 0; } if (scratch) { - WITH_BUFFER(buf, { - set_option_value("bh", 0L, "hide", OPT_LOCAL); - set_option_value("bt", 0L, "nofile", OPT_LOCAL); - set_option_value("swf", 0L, NULL, OPT_LOCAL); - }); + aco_save_T aco; + aucmd_prepbuf(&aco, buf); + set_option_value("bh", 0L, "hide", OPT_LOCAL); + set_option_value("bt", 0L, "nofile", OPT_LOCAL); + set_option_value("swf", 0L, NULL, OPT_LOCAL); + aucmd_restbuf(&aco); } return buf->b_fnum; } @@ -995,6 +998,8 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err) /// GUI with the |ui-multigrid| extension. External windows are only supported /// with multigrid GUIs, and are displayed as separate top-level windows. /// +/// For a general overview of floats, see |api-floatwin|. +/// /// Exactly one of `external` and `relative` must be specified. /// /// @param buffer handle of buffer to be displayed in the window @@ -1047,7 +1052,6 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary options, Error *err) FUNC_API_SINCE(6) { - win_T *old = curwin; FloatConfig config = FLOAT_CONFIG_INIT; if (!parse_float_config(options, &config, false, err)) { return 0; @@ -1056,11 +1060,11 @@ Window nvim_open_win(Buffer buffer, Boolean enter, if (!wp) { return 0; } - if (buffer > 0) { - nvim_set_current_buf(buffer, err); + if (enter) { + win_enter(wp, false); } - if (!enter) { - win_enter(old, false); + if (buffer > 0) { + nvim_win_set_buf(wp->handle, buffer, err); } return wp->handle; } diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 6e4e7afeb2..8cb4e32815 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2446,7 +2446,7 @@ void get_winopts(buf_T *buf) */ pos_T *buflist_findfpos(buf_T *buf) { - static pos_T no_position = INIT_POS_T(1, 0, 0); + static pos_T no_position = { 1, 0, 0 }; wininfo_T *wip = find_wininfo(buf, FALSE); return (wip == NULL) ? &no_position : &(wip->wi_fpos); diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index 79bed049ea..ee3fda5f6d 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -63,35 +63,6 @@ enum bfa_values { # include "buffer.h.generated.h" #endif -// Find a window that contains "buf" and switch to it. -// If there is no such window, use the current window and change "curbuf". -// Caller must initialize save_curbuf to NULL. -// restore_win_for_buf() MUST be called later! -static inline void switch_to_win_for_buf(buf_T *buf, - win_T **save_curwinp, - tabpage_T **save_curtabp, - bufref_T *save_curbuf) -{ - win_T *wp; - tabpage_T *tp; - - if (!find_win_for_buf(buf, &wp, &tp) - || switch_win(save_curwinp, save_curtabp, wp, tp, true) == FAIL) { - switch_buffer(save_curbuf, buf); - } -} - -static inline void restore_win_for_buf(win_T *save_curwin, - tabpage_T *save_curtab, - bufref_T *save_curbuf) -{ - if (save_curbuf->br_buf == NULL) { - restore_win(save_curwin, save_curtab, true); - } else { - restore_buffer(save_curbuf); - } -} - static inline void buf_set_changedtick(buf_T *const buf, const varnumber_T changedtick) REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE; @@ -103,6 +74,8 @@ static inline void buf_set_changedtick(buf_T *const buf, static inline void buf_set_changedtick(buf_T *const buf, const varnumber_T changedtick) { + typval_T old_val = buf->changedtick_di.di_tv; + #ifndef NDEBUG dictitem_T *const changedtick_di = tv_dict_find( buf->b_vars, S_LEN("changedtick")); @@ -116,6 +89,13 @@ static inline void buf_set_changedtick(buf_T *const buf, assert(changedtick_di == (dictitem_T *)&buf->changedtick_di); #endif buf->changedtick_di.di_tv.vval.v_number = changedtick; + + if (tv_dict_is_watched(buf->b_vars)) { + tv_dict_watcher_notify(buf->b_vars, + (char *)buf->changedtick_di.di_key, + &buf->changedtick_di.di_tv, + &old_val); + } } static inline varnumber_T buf_get_changedtick(const buf_T *const buf) @@ -145,15 +125,4 @@ static inline void buf_inc_changedtick(buf_T *const buf) buf_set_changedtick(buf, buf_get_changedtick(buf) + 1); } -#define WITH_BUFFER(b, code) \ - do { \ - win_T *save_curwin = NULL; \ - tabpage_T *save_curtab = NULL; \ - bufref_T save_curbuf = { NULL, 0, 0 }; \ - switch_to_win_for_buf(b, &save_curwin, &save_curtab, &save_curbuf); \ - code; \ - restore_win_for_buf(save_curwin, save_curtab, &save_curbuf); \ - } while (0) - - #endif // NVIM_BUFFER_H diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 6f0468dbea..f6b5a01915 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -3924,10 +3924,11 @@ static int ins_compl_get_exp(pos_T *ini) compl_direction, compl_pattern); } else found_new_match = searchit(NULL, ins_buf, pos, - compl_direction, - compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG, - RE_LAST, (linenr_T)0, NULL); - --msg_silent; + compl_direction, + compl_pattern, 1L, + SEARCH_KEEP + SEARCH_NFMSG, + RE_LAST, (linenr_T)0, NULL, NULL); + msg_silent--; if (!compl_started || set_match_pos) { /* set "compl_started" even on fail */ compl_started = TRUE; @@ -5508,16 +5509,33 @@ internal_format ( /* remember position of blank just before text */ end_col = curwin->w_cursor.col; - /* find start of sequence of blanks */ + // find start of sequence of blanks + int wcc = 0; // counter for whitespace chars while (curwin->w_cursor.col > 0 && WHITECHAR(cc)) { dec_cursor(); cc = gchar_cursor(); + + // Increment count of how many whitespace chars in this + // group; we only need to know if it's more than one. + if (wcc < 2) { + wcc++; + } } - if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) - break; /* only spaces in front of text */ - /* Don't break until after the comment leader */ - if (curwin->w_cursor.col < leader_len) + if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) { + break; // only spaces in front of text + } + + // Don't break after a period when 'formatoptions' has 'p' and + // there are less than two spaces. + if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2) { + continue; + } + + // Don't break until after the comment leader + if (curwin->w_cursor.col < leader_len) { break; + } + if (has_format_option(FO_ONE_LETTER)) { /* do not break after one-letter words */ if (curwin->w_cursor.col == 0) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d18884ff07..5ef2a8772e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10086,10 +10086,23 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) tv_list_append_number( l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); if (getcurpos) { + const int save_set_curswant = curwin->w_set_curswant; + const colnr_T save_curswant = curwin->w_curswant; + const colnr_T save_virtcol = curwin->w_virtcol; + update_curswant(); tv_list_append_number(l, (curwin->w_curswant == MAXCOL ? (varnumber_T)MAXCOL : (varnumber_T)curwin->w_curswant + 1)); + + // Do not change "curswant", as it is unexpected that a get + // function has a side effect. + if (save_set_curswant) { + curwin->w_set_curswant = save_set_curswant; + curwin->w_curswant = save_curswant; + curwin->w_virtcol = save_virtcol; + curwin->w_valid &= ~VALID_VIRTCOL; + } } } @@ -10303,6 +10316,8 @@ static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) tv_dict_add_nr(dict, S_LEN("winid"), wp->handle); tv_dict_add_nr(dict, S_LEN("height"), wp->w_height); tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow); + tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline); + tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1); tv_dict_add_nr(dict, S_LEN("width"), wp->w_width); tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum); tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol); @@ -13793,7 +13808,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) pos = save_cursor = curwin->w_cursor; subpatnum = searchit(curwin, curbuf, &pos, dir, (char_u *)pat, 1, - options, RE_SEARCH, (linenr_T)lnum_stop, &tm); + options, RE_SEARCH, (linenr_T)lnum_stop, &tm, NULL); if (subpatnum != FAIL) { if (flags & SP_SUBPAT) retval = subpatnum; @@ -14295,10 +14310,11 @@ do_searchpair( pat = pat3; for (;; ) { n = searchit(curwin, curbuf, &pos, dir, pat, 1L, - options, RE_SEARCH, lnum_stop, &tm); - if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) - /* didn't find it or found the first match again: FAIL */ + options, RE_SEARCH, lnum_stop, &tm, NULL); + if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) { + // didn't find it or found the first match again: FAIL break; + } if (firstpos.lnum == 0) firstpos = pos; @@ -20144,7 +20160,7 @@ void ex_function(exarg_T *eap) skip_until = vim_strsave((char_u *)"."); } - // Check for ":python <<EOF", ":lua <<EOF", etc. + // heredoc: Check for ":python <<EOF", ":lua <<EOF", etc. arg = skipwhite(skiptowhite(p)); if (arg[0] == '<' && arg[1] =='<' && ((p[0] == 'p' && p[1] == 'y' diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 2a5793f0d4..4356767cc9 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3432,7 +3432,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, || lnum <= curwin->w_botline); lnum++) { long nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL); + (colnr_T)0, NULL, NULL); if (nmatch) { colnr_T copycol; colnr_T matchcol; @@ -3951,8 +3951,8 @@ skip: if (lastone || nmatch_tl > 0 || (nmatch = vim_regexec_multi(®match, curwin, - curbuf, sub_firstlnum, - matchcol, NULL)) == 0 + curbuf, sub_firstlnum, + matchcol, NULL, NULL)) == 0 || regmatch.startpos[0].lnum > 0) { if (new_start != NULL) { /* @@ -4016,7 +4016,7 @@ skip: } if (nmatch == -1 && !lastone) nmatch = vim_regexec_multi(®match, curwin, curbuf, - sub_firstlnum, matchcol, NULL); + sub_firstlnum, matchcol, NULL, NULL); /* * 5. break if there isn't another match in this line @@ -4314,7 +4314,7 @@ void ex_global(exarg_T *eap) if (global_busy) { lnum = curwin->w_cursor.lnum; match = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL); + (colnr_T)0, NULL, NULL); if ((type == 'g' && match) || (type == 'v' && !match)) { global_exe_one(cmd, lnum); } @@ -4323,7 +4323,7 @@ void ex_global(exarg_T *eap) for (lnum = eap->line1; lnum <= eap->line2 && !got_int; lnum++) { // a match on this line? match = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL); + (colnr_T)0, NULL, NULL); if ((type == 'g' && match) || (type == 'v' && !match)) { ml_setmarked(lnum); ndone++; diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 0ecc389699..4684a1b31d 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1152,6 +1152,21 @@ static void script_dump_profile(FILE *fd) if (vim_fgets(IObuff, IOSIZE, sfd)) { break; } + // When a line has been truncated, append NL, taking care + // of multi-byte characters . + if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL) { + int n = IOSIZE - 2; + + // Move to the first byte of this char. + // utf_head_off() doesn't work, because it checks + // for a truncated character. + while (n > 0 && (IObuff[n] & 0xc0) == 0x80) { + n--; + } + + IObuff[n] = NL; + IObuff[n + 1] = NUL; + } if (i < si->sn_prl_ga.ga_len && (pp = &PRL_ITEM(si, i))->snp_count > 0) { fprintf(fd, "%5d ", pp->snp_count); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 6b39ad8e87..5b9b4fed12 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -3699,7 +3699,7 @@ static linenr_T get_address(exarg_T *eap, } searchcmdlen = 0; if (!do_search(NULL, c, cmd, 1L, - SEARCH_HIS | SEARCH_MSG, NULL)) { + SEARCH_HIS | SEARCH_MSG, NULL, NULL)) { curwin->w_cursor = pos; cmd = NULL; goto error; @@ -3737,7 +3737,7 @@ static linenr_T get_address(exarg_T *eap, if (searchit(curwin, curbuf, &pos, *cmd == '?' ? BACKWARD : FORWARD, (char_u *)"", 1L, SEARCH_MSG, - i, (linenr_T)0, NULL) != FAIL) + i, (linenr_T)0, NULL, NULL) != FAIL) lnum = pos.lnum; else { cmd = NULL; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a50e18efce..8e6fc5ad4f 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1061,7 +1061,7 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match) s->i = searchit(curwin, curbuf, &t, next_match ? FORWARD : BACKWARD, pat, s->count, search_flags, - RE_SEARCH, 0, NULL); + RE_SEARCH, 0, NULL, NULL); emsg_off--; ui_busy_stop(); if (s->i) { @@ -1847,7 +1847,7 @@ static int command_line_changed(CommandLineState *s) } s->i = do_search(NULL, s->firstc, ccline.cmdbuff, s->count, search_flags, - &tm); + &tm, NULL); emsg_off--; // if interrupted while searching, behave like it failed if (got_int) { diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index b9de46efc8..7be4107c94 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -99,8 +99,9 @@ // defined and will have to be executed. // typedef struct AutoCmd { - char_u *cmd; // The command to be executed (NULL - // when command has been removed) + char_u *cmd; // Command to be executed (NULL when + // command has been removed) + bool once; // "One shot": removed after execution char nested; // If autocommands nest here char last; // last command in list scid_T scriptID; // script ID where defined @@ -121,20 +122,20 @@ typedef struct AutoPat { char last; // last pattern for apply_autocmds() } AutoPat; -/* - * struct used to keep status while executing autocommands for an event. - */ +/// +/// Struct used to keep status while executing autocommands for an event. +/// typedef struct AutoPatCmd { - AutoPat *curpat; /* next AutoPat to examine */ - AutoCmd *nextcmd; /* next AutoCmd to execute */ - int group; /* group being used */ - char_u *fname; /* fname to match with */ - char_u *sfname; /* sfname to match with */ - char_u *tail; /* tail of fname */ - event_T event; /* current event */ - int arg_bufnr; /* initially equal to <abuf>, set to zero when - buf is deleted */ - struct AutoPatCmd *next; /* chain of active apc-s for auto-invalidation*/ + AutoPat *curpat; // next AutoPat to examine + AutoCmd *nextcmd; // next AutoCmd to execute + int group; // group being used + char_u *fname; // fname to match with + char_u *sfname; // sfname to match with + char_u *tail; // tail of fname + event_T event; // current event + int arg_bufnr; // initially equal to <abuf>, set to zero when + // buf is deleted + struct AutoPatCmd *next; // chain of active apc-s for auto-invalidation } AutoPatCmd; #define AUGROUP_DEFAULT -1 /* default autocmd group */ @@ -5563,64 +5564,75 @@ static void show_autocmd(AutoPat *ap, event_T event) } } -/* - * Mark an autocommand pattern for deletion. - */ +// Mark an autocommand handler for deletion. static void au_remove_pat(AutoPat *ap) { xfree(ap->pat); ap->pat = NULL; ap->buflocal_nr = -1; - au_need_clean = TRUE; + au_need_clean = true; } -/* - * Mark all commands for a pattern for deletion. - */ +// Mark all commands for a pattern for deletion. static void au_remove_cmds(AutoPat *ap) { - AutoCmd *ac; - - for (ac = ap->cmds; ac != NULL; ac = ac->next) { + for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) { xfree(ac->cmd); ac->cmd = NULL; } - au_need_clean = TRUE; + au_need_clean = true; } -/* - * Cleanup autocommands and patterns that have been deleted. - * This is only done when not executing autocommands. - */ +// Delete one command from an autocmd pattern. +static void au_del_cmd(AutoCmd *ac) +{ + xfree(ac->cmd); + ac->cmd = NULL; + au_need_clean = true; +} + +/// Cleanup autocommands and patterns that have been deleted. +/// This is only done when not executing autocommands. static void au_cleanup(void) { AutoPat *ap, **prev_ap; AutoCmd *ac, **prev_ac; event_T event; - if (autocmd_busy || !au_need_clean) + if (autocmd_busy || !au_need_clean) { return; + } - /* loop over all events */ + // Loop over all events. for (event = (event_T)0; (int)event < (int)NUM_EVENTS; event = (event_T)((int)event + 1)) { - /* loop over all autocommand patterns */ + // Loop over all autocommand patterns. prev_ap = &(first_autopat[(int)event]); for (ap = *prev_ap; ap != NULL; ap = *prev_ap) { - /* loop over all commands for this pattern */ + // Loop over all commands for this pattern. prev_ac = &(ap->cmds); + bool has_cmd = false; + for (ac = *prev_ac; ac != NULL; ac = *prev_ac) { - /* remove the command if the pattern is to be deleted or when - * the command has been marked for deletion */ + // Remove the command if the pattern is to be deleted or when + // the command has been marked for deletion. if (ap->pat == NULL || ac->cmd == NULL) { *prev_ac = ac->next; xfree(ac->cmd); xfree(ac); - } else + } else { + has_cmd = true; prev_ac = &(ac->next); + } + } + + if (ap->pat != NULL && !has_cmd) { + // Pattern was not marked for deletion, but all of its commands were. + // So mark the pattern for deletion. + au_remove_pat(ap); } - /* remove the pattern if it has been marked for deletion */ + // Remove the pattern if it has been marked for deletion. if (ap->pat == NULL) { if (ap->next == NULL) { if (prev_ap == &(first_autopat[(int)event])) { @@ -5634,12 +5646,13 @@ static void au_cleanup(void) *prev_ap = ap->next; vim_regfree(ap->reg_prog); xfree(ap); - } else + } else { prev_ap = &(ap->next); + } } } - au_need_clean = FALSE; + au_need_clean = false; } /* @@ -5674,18 +5687,18 @@ void aubuflocal_remove(buf_T *buf) au_cleanup(); } -/* - * Add an autocmd group name. - * Return it's ID. Returns AUGROUP_ERROR (< 0) for error. - */ +// Add an autocmd group name. +// Return its ID. Returns AUGROUP_ERROR (< 0) for error. static int au_new_group(char_u *name) { int i = au_find_group(name); - if (i == AUGROUP_ERROR) { /* the group doesn't exist yet, add it */ - /* First try using a free entry. */ - for (i = 0; i < augroups.ga_len; ++i) - if (AUGROUP_NAME(i) == NULL) + if (i == AUGROUP_ERROR) { // the group doesn't exist yet, add it. + // First try using a free entry. + for (i = 0; i < augroups.ga_len; i++) { + if (AUGROUP_NAME(i) == NULL) { break; + } + } if (i == augroups.ga_len) { ga_grow(&augroups, 1); } @@ -5701,9 +5714,7 @@ static int au_new_group(char_u *name) static void au_del_group(char_u *name) { - int i; - - i = au_find_group(name); + int i = au_find_group(name); if (i == AUGROUP_ERROR) { // the group doesn't exist EMSG2(_("E367: No such group: \"%s\""), name); } else if (i == current_augroup) { @@ -5760,23 +5771,22 @@ bool au_has_group(const char_u *name) return au_find_group(name) != AUGROUP_ERROR; } -/* - * ":augroup {name}". - */ +/// ":augroup {name}". void do_augroup(char_u *arg, int del_group) { if (del_group) { - if (*arg == NUL) + if (*arg == NUL) { EMSG(_(e_argreq)); - else + } else { au_del_group(arg); - } else if (STRICMP(arg, "end") == 0) /* ":aug end": back to group 0 */ + } + } else if (STRICMP(arg, "end") == 0) { // ":aug end": back to group 0 current_augroup = AUGROUP_DEFAULT; - else if (*arg) { /* ":aug xxx": switch to group xxx */ + } else if (*arg) { // ":aug xxx": switch to group xxx int i = au_new_group(arg); if (i != AUGROUP_ERROR) current_augroup = i; - } else { /* ":aug": list the group names */ + } else { // ":aug": list the group names msg_start(); for (int i = 0; i < augroups.ga_len; ++i) { if (AUGROUP_NAME(i) != NULL) { @@ -5957,38 +5967,38 @@ void au_event_restore(char_u *old_ei) } } -/* - * do_autocmd() -- implements the :autocmd command. Can be used in the - * following ways: - * - * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that - * will be automatically executed for <event> - * when editing a file matching <pat>, in - * the current group. - * :autocmd <event> <pat> Show the autocommands associated with - * <event> and <pat>. - * :autocmd <event> Show the autocommands associated with - * <event>. - * :autocmd Show all autocommands. - * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with - * <event> and <pat>, and add the command - * <cmd>, for the current group. - * :autocmd! <event> <pat> Remove all autocommands associated with - * <event> and <pat> for the current group. - * :autocmd! <event> Remove all autocommands associated with - * <event> for the current group. - * :autocmd! Remove ALL autocommands for the current - * group. - * - * Multiple events and patterns may be given separated by commas. Here are - * some examples: - * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic - * :autocmd bufleave * set tw=79 nosmartindent ic infercase - * - * :autocmd * *.c show all autocommands for *.c files. - * - * Mostly a {group} argument can optionally appear before <event>. - */ +// Implements :autocmd. +// Defines an autocmd (does not execute; cf. apply_autocmds_group). +// +// Can be used in the following ways: +// +// :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that +// will be automatically executed for <event> +// when editing a file matching <pat>, in +// the current group. +// :autocmd <event> <pat> Show the autocommands associated with +// <event> and <pat>. +// :autocmd <event> Show the autocommands associated with +// <event>. +// :autocmd Show all autocommands. +// :autocmd! <event> <pat> <cmd> Remove all autocommands associated with +// <event> and <pat>, and add the command +// <cmd>, for the current group. +// :autocmd! <event> <pat> Remove all autocommands associated with +// <event> and <pat> for the current group. +// :autocmd! <event> Remove all autocommands associated with +// <event> for the current group. +// :autocmd! Remove ALL autocommands for the current +// group. +// +// Multiple events and patterns may be given separated by commas. Here are +// some examples: +// :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic +// :autocmd bufleave * set tw=79 nosmartindent ic infercase +// +// :autocmd * *.c show all autocommands for *.c files. +// +// Mostly a {group} argument can optionally appear before <event>. void do_autocmd(char_u *arg_in, int forceit) { char_u *arg = arg_in; @@ -5997,6 +6007,7 @@ void do_autocmd(char_u *arg_in, int forceit) char_u *cmd; int need_free = false; int nested = false; + bool once = false; int group; if (*arg == '|') { @@ -6046,12 +6057,23 @@ void do_autocmd(char_u *arg_in, int forceit) } } - // 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); + for (size_t i = 0; i < 2; i++) { + if (*cmd != NUL) { + // Check for "++once" flag. + if (!once && STRNCMP(cmd, "++once", 6) == 0 && ascii_iswhite(cmd[6])) { + once = true; + cmd = skipwhite(cmd + 6); + } + // Check for "++nested" flag. + if (!nested + && ((STRNCMP(cmd, "++nested", 8) == 0 && ascii_iswhite(cmd[8])) + // Deprecated form (without "++"). + || (STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])))) { + nested = true; + cmd = skipwhite(cmd + ('+' == cmd[0] ? 8 : 6)); + } + } } // Find the start of the commands. @@ -6081,7 +6103,8 @@ void do_autocmd(char_u *arg_in, int forceit) if (*arg == '*' || *arg == NUL || *arg == '|') { for (event_T 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) { + if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group) + == FAIL) { break; } } @@ -6089,7 +6112,8 @@ void do_autocmd(char_u *arg_in, int forceit) 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) { + if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group) + == FAIL) { break; } } @@ -6127,14 +6151,15 @@ static int au_get_grouparg(char_u **argp) return group; } -/* - * do_autocmd() for one event. - * If *pat == NUL do for all patterns. - * If *cmd == NUL show entries. - * If forceit == TRUE delete entries. - * If group is not AUGROUP_ALL, only use this group. - */ -static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd, int forceit, int group) +// do_autocmd() for one event. +// Defines an autocmd (does not execute; cf. apply_autocmds_group). +// +// If *pat == NUL: do for all patterns. +// If *cmd == NUL: show entries. +// If forceit == TRUE: delete entries. +// If group is not AUGROUP_ALL: only use this group. +static int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, + char_u *cmd, int forceit, int group) { AutoPat *ap; AutoPat **prev_ap; @@ -6333,6 +6358,7 @@ static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd, ac->scriptID = current_SID; ac->next = NULL; *prev_ac = ac; + ac->once = once; ac->nested = nested; } } @@ -6996,7 +7022,7 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, patcmd.event = event; patcmd.arg_bufnr = autocmd_bufnr; patcmd.next = NULL; - auto_next_pat(&patcmd, FALSE); + auto_next_pat(&patcmd, false); /* found one, start executing the autocommands */ if (patcmd.curpat != NULL) { @@ -7020,8 +7046,11 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, } ap->last = true; check_lnums(true); // make sure cursor and topline are valid + + // Execute the autocmd. The `getnextac` callback handles iteration. do_cmdline(NULL, getnextac, (void *)&patcmd, DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); + if (eap != NULL) { (void)set_cmdarg(NULL, save_cmdarg); set_vim_var_nr(VV_CMDBANG, save_cmdbang); @@ -7233,12 +7262,18 @@ char_u *getnextac(int c, void *cookie, int indent) verbose_leave_scroll(); } retval = vim_strsave(ac->cmd); + // Remove one-shot ("once") autocmd in anticipation of its execution. + if (ac->once) { + au_del_cmd(ac); + } autocmd_nested = ac->nested; current_SID = ac->scriptID; - if (ac->last) + if (ac->last) { acp->nextcmd = NULL; - else + } else { acp->nextcmd = ac->next; + } + return retval; } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 52c5d65512..004b3252da 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -602,7 +602,7 @@ EXTERN bool can_si INIT(= false); EXTERN bool can_si_back INIT(= false); // w_cursor before formatting text. -EXTERN pos_T saved_cursor INIT(= INIT_POS_T(0, 0, 0)); +EXTERN pos_T saved_cursor INIT(= { 0, 0, 0 }); /* * Stuff for insert mode. @@ -789,7 +789,7 @@ EXTERN int autocmd_bufnr INIT(= 0); // fnum for <abuf> on cmdline EXTERN char_u *autocmd_match INIT(= NULL); // name for <amatch> on cmdline EXTERN int did_cursorhold INIT(= false); // set when CursorHold t'gerd // for CursorMoved event -EXTERN pos_T last_cursormoved INIT(= INIT_POS_T(0, 0, 0)); +EXTERN pos_T last_cursormoved INIT(= { 0, 0, 0 }); EXTERN int postponed_split INIT(= 0); /* for CTRL-W CTRL-] command */ EXTERN int postponed_split_flags INIT(= 0); /* args for win_split() */ diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 4c5fca6d39..3ba02be32d 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -160,14 +160,19 @@ void update_window_hl(win_T *wp, bool invalid) wp->w_hl_needs_update = false; // determine window specific background set in 'winhighlight' + bool float_win = wp->w_floating && !wp->w_float_config.external; if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] > 0) { wp->w_hl_attr_normal = hl_get_ui_attr(HLF_INACTIVE, wp->w_hl_ids[HLF_INACTIVE], true); + } else if (float_win && wp->w_hl_ids[HLF_NFLOAT] > 0) { + wp->w_hl_attr_normal = hl_get_ui_attr(HLF_NFLOAT, + wp->w_hl_ids[HLF_NFLOAT], true); } else if (wp->w_hl_id_normal > 0) { wp->w_hl_attr_normal = hl_get_ui_attr(-1, wp->w_hl_id_normal, true); } else { - wp->w_hl_attr_normal = 0; + wp->w_hl_attr_normal = float_win ? HL_ATTR(HLF_NFLOAT) : 0; } + if (wp != curwin) { wp->w_hl_attr_normal = hl_combine_attr(HL_ATTR(HLF_INACTIVE), wp->w_hl_attr_normal); diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 1da33bfea5..746d2c2dfc 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -90,6 +90,7 @@ typedef enum { , HLF_0 // Whitespace , HLF_INACTIVE // NormalNC: Normal text in non-current windows , HLF_MSGSEP // message separator line + , HLF_NFLOAT // Floating window , HLF_COUNT // MUST be the last one } hlf_T; @@ -142,6 +143,7 @@ EXTERN const char *hlf_names[] INIT(= { [HLF_0] = "Whitespace", [HLF_INACTIVE] = "NormalNC", [HLF_MSGSEP] = "MsgSeparator", + [HLF_NFLOAT] = "NormalFloat", }); diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 05f78c76bc..af404c354b 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -924,7 +924,7 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2, int i; int fnum = curbuf->b_fnum; linenr_T *lp; - static pos_T initpos = INIT_POS_T(1, 0, 0); + static pos_T initpos = { 1, 0, 0 }; if (line2 < line1 && amount_after == 0L) /* nothing to do */ return; @@ -1069,19 +1069,24 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2, { \ posp->lnum += lnum_amount; \ assert(col_amount > INT_MIN && col_amount <= INT_MAX); \ - if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) \ + if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) { \ posp->col = 0; \ - else \ + } else if (posp->col < spaces_removed) { \ + posp->col = (int)col_amount + spaces_removed; \ + } else { \ posp->col += (colnr_T)col_amount; \ + } \ } \ } -/* - * Adjust marks in line "lnum" at column "mincol" and further: add - * "lnum_amount" to the line number and add "col_amount" to the column - * position. - */ -void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount) +// Adjust marks in line "lnum" at column "mincol" and further: add +// "lnum_amount" to the line number and add "col_amount" to the column +// position. +// "spaces_removed" is the number of spaces that were removed, matters when the +// cursor is inside them. +void mark_col_adjust( + linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount, + int spaces_removed) { int i; int fnum = curbuf->b_fnum; diff --git a/src/nvim/menu.c b/src/nvim/menu.c index aea297fce2..523bb20738 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -171,7 +171,7 @@ ex_menu(exarg_T *eap) if (enable != kNone) { // Change sensitivity of the menu. // For the PopUp menu, remove a menu for each mode separately. - // Careful: menu_nable_recurse() changes menu_path. + // Careful: menu_enable_recurse() changes menu_path. if (STRCMP(menu_path, "*") == 0) { // meaning: do all menus menu_path = (char_u *)""; } @@ -180,11 +180,11 @@ ex_menu(exarg_T *eap) for (i = 0; i < MENU_INDEX_TIP; ++i) if (modes & (1 << i)) { p = popup_mode_name(menu_path, i); - menu_nable_recurse(root_menu, p, MENU_ALL_MODES, enable); + menu_enable_recurse(root_menu, p, MENU_ALL_MODES, enable); xfree(p); } } - menu_nable_recurse(root_menu, menu_path, modes, enable); + menu_enable_recurse(root_menu, menu_path, modes, enable); } else if (unmenu) { /* * Delete menu(s). @@ -485,7 +485,10 @@ erret: * Set the (sub)menu with the given name to enabled or disabled. * Called recursively. */ -static int menu_nable_recurse(vimmenu_T *menu, char_u *name, int modes, int enable) +static int menu_enable_recurse(vimmenu_T *menu, + char_u *name, + int modes, + int enable) { char_u *p; @@ -503,13 +506,14 @@ static int menu_nable_recurse(vimmenu_T *menu, char_u *name, int modes, int enab EMSG(_(e_notsubmenu)); return FAIL; } - if (menu_nable_recurse(menu->children, p, modes, enable) - == FAIL) + if (menu_enable_recurse(menu->children, p, modes, enable) == FAIL) { return FAIL; - } else if (enable) + } + } else if (enable) { menu->enabled |= modes; - else + } else { menu->enabled &= ~modes; + } /* * When name is empty, we are doing all menu items for the given diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index a8cfc2d700..64a4b8b0b4 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -848,10 +848,11 @@ open_line ( /* Move marks after the line break to the new line. */ if (flags & OPENLINE_MARKFIX) mark_col_adjust(curwin->w_cursor.lnum, - curwin->w_cursor.col + less_cols_off, - 1L, (long)-less_cols); - } else + curwin->w_cursor.col + less_cols_off, + 1L, (long)-less_cols, 0); + } else { changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col); + } } /* diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 49eef72a05..f12abd362f 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3782,9 +3782,10 @@ find_decl ( valid = false; (void)valid; // Avoid "dead assignment" warning. t = searchit(curwin, curbuf, &curwin->w_cursor, FORWARD, - pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL); - if (curwin->w_cursor.lnum >= old_pos.lnum) - t = false; /* match after start is failure too */ + pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL, NULL); + if (curwin->w_cursor.lnum >= old_pos.lnum) { + t = false; // match after start is failure too + } if (thisblock && t != false) { const int64_t maxtravel = old_pos.lnum - curwin->w_cursor.lnum + 1; @@ -5384,7 +5385,7 @@ static int normal_search( curwin->w_set_curswant = true; i = do_search(cap->oap, dir, pat, cap->count1, - opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL); + opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL, NULL); if (i == 0) { clearop(cap->oap); } else { @@ -5443,7 +5444,7 @@ static void nv_csearch(cmdarg_T *cap) */ static void nv_brackets(cmdarg_T *cap) { - pos_T new_pos = INIT_POS_T(0, 0, 0); + pos_T new_pos = { 0, 0, 0 }; pos_T prev_pos; pos_T *pos = NULL; /* init for GCC */ pos_T old_pos; /* cursor position before command */ diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 99dee939fc..02ec3aad31 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2605,17 +2605,16 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) static bool recursive = false; if (recursive || !has_event(EVENT_TEXTYANKPOST)) { - // No autocommand was defined - // or we yanked from this autocommand. + // No autocommand was defined, or we yanked from this autocommand. return; } recursive = true; - // set v:event to a dictionary with information about the yank + // Set the v:event dictionary with information about the yank. dict_T *dict = get_vim_var_dict(VV_EVENT); - // the yanked text + // The yanked text contents. list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size); for (size_t i = 0; i < reg->y_size; i++) { tv_list_append_string(list, (const char *)reg->y_array[i], -1); @@ -2623,17 +2622,21 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) tv_list_set_lock(list, VAR_FIXED); tv_dict_add_list(dict, S_LEN("regcontents"), list); - // the register type + // Register type. char buf[NUMBUFLEN+2]; format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); tv_dict_add_str(dict, S_LEN("regtype"), buf); - // name of requested register or the empty string for an unnamed operation. + // Name of requested register, or empty string for unnamed operation. buf[0] = (char)oap->regname; buf[1] = NUL; tv_dict_add_str(dict, S_LEN("regname"), buf); - // kind of operation (yank/delete/change) + // Motion type: inclusive or exclusive. + tv_dict_add_special(dict, S_LEN("inclusive"), + oap->inclusive ? kSpecialVarTrue : kSpecialVarFalse); + + // Kind of operation: yank, delete, change). buf[0] = (char)get_op_char(oap->op_type); buf[1] = NUL; tv_dict_add_str(dict, S_LEN("operator"), buf); @@ -3704,10 +3707,16 @@ int do_join(size_t count, cend -= spaces[t]; memset(cend, ' ', (size_t)(spaces[t])); } + + // If deleting more spaces than adding, the cursor moves no more than + // what is added if it is inside these spaces. + const int spaces_removed = (int)((curr - curr_start) - spaces[t]); + mark_col_adjust(curwin->w_cursor.lnum + t, (colnr_T)0, (linenr_T)-t, - (long)(cend - newp + spaces[t] - (curr - curr_start))); - if (t == 0) + (long)(cend - newp - spaces_removed), spaces_removed); + if (t == 0) { break; + } curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1)); if (remove_comments) curr += comments[t - 1]; @@ -4135,14 +4144,14 @@ format_lines ( if (next_leader_len > 0) { (void)del_bytes(next_leader_len, false, false); mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L, - (long)-next_leader_len); + (long)-next_leader_len, 0); } else if (second_indent > 0) { // the "leader" for FO_Q_SECOND int indent = (int)getwhitecols_curline(); if (indent > 0) { (void)del_bytes(indent, FALSE, FALSE); mark_col_adjust(curwin->w_cursor.lnum, - (colnr_T)0, 0L, (long)-indent); + (colnr_T)0, 0L, (long)-indent, 0); } } curwin->w_cursor.lnum--; diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 6dc5e418fc..74047e7cef 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -74,13 +74,14 @@ #define FO_MBYTE_JOIN 'M' /* no space before/after multi-byte char */ #define FO_MBYTE_JOIN2 'B' /* no space between multi-byte chars */ #define FO_ONE_LETTER '1' -#define FO_WHITE_PAR 'w' /* trailing white space continues paragr. */ -#define FO_AUTO 'a' /* automatic formatting */ -#define FO_REMOVE_COMS 'j' /* remove comment leaders when joining lines */ +#define FO_WHITE_PAR 'w' // trailing white space continues paragr. +#define FO_AUTO 'a' // automatic formatting +#define FO_REMOVE_COMS 'j' // remove comment leaders when joining lines +#define FO_PERIOD_ABBR 'p' // don't break a single space after a period #define DFLT_FO_VI "vt" #define DFLT_FO_VIM "tcqj" -#define FO_ALL "tcroq2vlb1mMBn,awj" /* for do_set() */ +#define FO_ALL "tcroq2vlb1mMBn,awjp" // for do_set() // characters for the p_cpo option: #define CPO_ALTREAD 'a' // ":read" sets alternate file name diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 7f2ebeec2f..27db675c52 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -288,7 +288,11 @@ static bool is_executable(const char *name) // a directory. return (S_ISREG(mode)); #else - return (S_ISREG(mode) && (S_IXUSR & mode)); + int r = -1; + if (S_ISREG(mode)) { + RUN_UV_FS_FUNC(r, uv_fs_access, name, X_OK, NULL); + } + return (r == 0); #endif } diff --git a/src/nvim/pos.h b/src/nvim/pos.h index 0a2afd5847..47d253e083 100644 --- a/src/nvim/pos.h +++ b/src/nvim/pos.h @@ -24,7 +24,6 @@ typedef struct { colnr_T coladd; } pos_T; -# define INIT_POS_T(l, c, ca) {l, c, ca} /* * Same, but without coladd. diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index f0c37c0e38..ee1cf2182f 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -2128,8 +2128,9 @@ win_found: save_cursor = curwin->w_cursor; curwin->w_cursor.lnum = 0; if (!do_search(NULL, '/', qf_ptr->qf_pattern, (long)1, - SEARCH_KEEP, NULL)) + SEARCH_KEEP, NULL, NULL)) { curwin->w_cursor = save_cursor; + } } if ((fdo_flags & FDO_QUICKFIX) && old_KeyTyped) @@ -3758,7 +3759,7 @@ void ex_vimgrep(exarg_T *eap) ++lnum) { col = 0; while (vim_regexec_multi(®match, curwin, buf, lnum, - col, NULL) > 0) { + col, NULL, NULL) > 0) { // Pass the buffer number so that it gets used even for a // dummy buffer, unless duplicate_name is set, then the // buffer will be wiped out below. diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 3b3ca29dad..ab1a7c7b3e 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -1210,6 +1210,31 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp) return p; } +/// Return TRUE if the back reference is legal. We must have seen the close +/// brace. +/// TODO(vim): Should also check that we don't refer to something repeated +/// (+*=): what instance of the repetition should we match? +static int seen_endbrace(int refnum) +{ + if (!had_endbrace[refnum]) { + char_u *p; + + // Trick: check if "@<=" or "@<!" follows, in which case + // the \1 can appear before the referenced match. + for (p = regparse; *p != NUL; p++) { + if (p[0] == '@' && p[1] == '<' && (p[2] == '!' || p[2] == '=')) { + break; + } + } + + if (*p == NUL) { + EMSG(_("E65: Illegal back reference")); + rc_did_emsg = true; + return false; + } + } + return TRUE; +} /* * bt_regcomp() - compile a regular expression into internal code for the @@ -1928,22 +1953,8 @@ static char_u *regatom(int *flagp) int refnum; refnum = c - Magic('0'); - /* - * Check if the back reference is legal. We must have seen the - * close brace. - * TODO: Should also check that we don't refer to something - * that is repeated (+*=): what instance of the repetition - * should we match? - */ - if (!had_endbrace[refnum]) { - /* Trick: check if "@<=" or "@<!" follows, in which case - * the \1 can appear before the referenced match. */ - for (p = regparse; *p != NUL; ++p) - if (p[0] == '@' && p[1] == '<' - && (p[2] == '!' || p[2] == '=')) - break; - if (*p == NUL) - EMSG_RET_NULL(_("E65: Illegal back reference")); + if (!seen_endbrace(refnum)) { + return NULL; } ret = regnode(BACKREF + refnum); } @@ -3297,7 +3308,7 @@ bt_regexec_nl ( rex.reg_icombine = false; rex.reg_maxcol = 0; - long r = bt_regexec_both(line, col, NULL); + long r = bt_regexec_both(line, col, NULL, NULL); assert(r <= INT_MAX); return (int)r; } @@ -3357,7 +3368,8 @@ static inline char_u *cstrchr(const char_u *const s, const int c) /// @return zero if there is no match and number of lines contained in the match /// otherwise. static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, - linenr_T lnum, colnr_T col, proftime_T *tm) + linenr_T lnum, colnr_T col, + proftime_T *tm, int *timed_out) { rex.reg_match = NULL; rex.reg_mmatch = rmp; @@ -3370,18 +3382,16 @@ static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, rex.reg_icombine = false; rex.reg_maxcol = rmp->rmm_maxcol; - return bt_regexec_both(NULL, col, tm); + return bt_regexec_both(NULL, col, tm, timed_out); } -/* - * Match a regexp against a string ("line" points to the string) or multiple - * lines ("line" is NULL, use reg_getline()). - * Returns 0 for failure, number of lines contained in the match otherwise. - */ +/// Match a regexp against a string ("line" points to the string) or multiple +/// lines ("line" is NULL, use reg_getline()). +/// @return 0 for failure, or number of lines contained in the match. static long bt_regexec_both(char_u *line, - colnr_T col, /* column to start looking for match */ - proftime_T *tm /* timeout limit or NULL */ - ) + colnr_T col, // column to start search + proftime_T *tm, // timeout limit or NULL + int *timed_out) // flag set on timeout or NULL { bt_regprog_T *prog; char_u *s; @@ -3483,7 +3493,7 @@ static long bt_regexec_both(char_u *line, && (utf_fold(prog->regstart) == utf_fold(c) || (c < 255 && prog->regstart < 255 && mb_tolower(prog->regstart) == mb_tolower(c))))) { - retval = regtry(prog, col); + retval = regtry(prog, col, tm, timed_out); } else { retval = 0; } @@ -3507,9 +3517,10 @@ static long bt_regexec_both(char_u *line, break; } - retval = regtry(prog, col); - if (retval > 0) + retval = regtry(prog, col, tm, timed_out); + if (retval > 0) { break; + } /* if not currently on the first line, get it again */ if (reglnum != 0) { @@ -3525,8 +3536,12 @@ static long bt_regexec_both(char_u *line, /* Check for timeout once in a twenty times to avoid overhead. */ if (tm != NULL && ++tm_count == 20) { tm_count = 0; - if (profile_passed_limit(*tm)) + if (profile_passed_limit(*tm)) { + if (timed_out != NULL) { + *timed_out = true; + } break; + } } } } @@ -3582,11 +3597,12 @@ void unref_extmatch(reg_extmatch_T *em) } } -/* - * regtry - try match of "prog" with at regline["col"]. - * Returns 0 for failure, number of lines contained in the match otherwise. - */ -static long regtry(bt_regprog_T *prog, colnr_T col) +/// Try match of "prog" with at regline["col"]. +/// @returns 0 for failure, or number of lines contained in the match. +static long regtry(bt_regprog_T *prog, + colnr_T col, + proftime_T *tm, // timeout limit or NULL + int *timed_out) // flag set on timeout or NULL { reginput = regline + col; need_clear_subexpr = TRUE; @@ -3594,8 +3610,9 @@ static long regtry(bt_regprog_T *prog, colnr_T col) if (prog->reghasz == REX_SET) need_clear_zsubexpr = TRUE; - if (regmatch(prog->program + 1) == 0) + if (regmatch(prog->program + 1, tm, timed_out) == 0) { return 0; + } cleanup_subexpr(); if (REG_MULTI) { @@ -3736,24 +3753,23 @@ static int reg_match_visual(void) static long bl_minval; static long bl_maxval; -/* - * regmatch - main matching routine - * - * Conceptually the strategy is simple: Check to see whether the current node - * matches, push an item onto the regstack and loop to see whether the rest - * matches, and then act accordingly. In practice we make some effort to - * avoid using the regstack, in particular by going through "ordinary" nodes - * (that don't need to know whether the rest of the match failed) by a nested - * loop. - * - * Returns TRUE when there is a match. Leaves reginput and reglnum just after - * the last matched character. - * Returns FALSE when there is no match. Leaves reginput and reglnum in an - * undefined state! - */ -static int -regmatch ( - char_u *scan /* Current node. */ +/// Main matching routine +/// +/// Conceptually the strategy is simple: Check to see whether the current node +/// matches, push an item onto the regstack and loop to see whether the rest +/// matches, and then act accordingly. In practice we make some effort to +/// avoid using the regstack, in particular by going through "ordinary" nodes +/// (that don't need to know whether the rest of the match failed) by a nested +/// loop. +/// +/// Returns TRUE when there is a match. Leaves reginput and reglnum just after +/// the last matched character. +/// Returns FALSE when there is no match. Leaves reginput and reglnum in an +/// undefined state! +static int regmatch( + char_u *scan, // Current node. + proftime_T *tm, // timeout limit or NULL + int *timed_out // flag set on timeout or NULL ) { char_u *next; /* Next node. */ @@ -3761,15 +3777,16 @@ regmatch ( int c; regitem_T *rp; int no; - int status; /* one of the RA_ values: */ -#define RA_FAIL 1 /* something failed, abort */ -#define RA_CONT 2 /* continue in inner loop */ -#define RA_BREAK 3 /* break inner loop */ -#define RA_MATCH 4 /* successful match */ -#define RA_NOMATCH 5 /* didn't match */ - - /* Make "regstack" and "backpos" empty. They are allocated and freed in - * bt_regexec_both() to reduce malloc()/free() calls. */ + int status; // one of the RA_ values: + int tm_count = 0; +#define RA_FAIL 1 // something failed, abort +#define RA_CONT 2 // continue in inner loop +#define RA_BREAK 3 // break inner loop +#define RA_MATCH 4 // successful match +#define RA_NOMATCH 5 // didn't match + + // Make "regstack" and "backpos" empty. They are allocated and freed in + // bt_regexec_both() to reduce malloc()/free() calls. regstack.ga_len = 0; backpos.ga_len = 0; @@ -3797,6 +3814,17 @@ regmatch ( status = RA_FAIL; break; } + // Check for timeout once in a 100 times to avoid overhead. + if (tm != NULL && ++tm_count == 100) { + tm_count = 0; + if (profile_passed_limit(*tm)) { + if (timed_out != NULL) { + *timed_out = true; + } + status = RA_FAIL; + break; + } + } status = RA_CONT; #ifdef REGEXP_DEBUG @@ -7116,6 +7144,8 @@ regprog_T *vim_regcomp(char_u *expr_arg, int re_flags) } bt_regengine.expr = expr; nfa_regengine.expr = expr; + // reg_iswordc() uses rex.reg_buf + rex.reg_buf = curbuf; // // First try the NFA engine, unless backtracking was requested. @@ -7273,12 +7303,13 @@ int vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col) /// Return zero if there is no match. Return number of lines contained in the /// match otherwise. long vim_regexec_multi( - regmmatch_T *rmp, - win_T *win, /* window in which to search or NULL */ - buf_T *buf, /* buffer in which to search */ - linenr_T lnum, /* nr of line to start looking for match */ - colnr_T col, /* column to start looking for match */ - proftime_T *tm /* timeout limit or NULL */ + regmmatch_T *rmp, + win_T *win, // window in which to search or NULL + buf_T *buf, // buffer in which to search + linenr_T lnum, // nr of line to start looking for match + colnr_T col, // column to start looking for match + proftime_T *tm, // timeout limit or NULL + int *timed_out // flag is set when timeout limit reached ) { regexec_T rex_save; @@ -7291,7 +7322,7 @@ long vim_regexec_multi( rex_in_use = true; int result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, - tm); + tm, timed_out); // NFA engine aborted because it's very slow, use backtracking engine instead. if (rmp->regprog->re_engine == AUTOMATIC_ENGINE @@ -7311,7 +7342,7 @@ long vim_regexec_multi( if (rmp->regprog != NULL) { result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, - tm); + tm, timed_out); } xfree(pat); diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h index b5d56e07fc..298d82fc6b 100644 --- a/src/nvim/regexp_defs.h +++ b/src/nvim/regexp_defs.h @@ -156,11 +156,11 @@ struct reg_extmatch { }; struct regengine { - regprog_T *(*regcomp)(char_u*, int); + regprog_T *(*regcomp)(char_u *, int); void (*regfree)(regprog_T *); - int (*regexec_nl)(regmatch_T*, char_u*, colnr_T, bool); - long (*regexec_multi)(regmmatch_T*, win_T*, buf_T*, linenr_T, colnr_T, - proftime_T*); + int (*regexec_nl)(regmatch_T *, char_u *, colnr_T, bool); + long (*regexec_multi)(regmmatch_T *, win_T *, buf_T *, linenr_T, colnr_T, + proftime_T *, int *); char_u *expr; }; diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 95030974d8..b935b44291 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -1338,8 +1338,15 @@ static int nfa_regatom(void) case Magic('7'): case Magic('8'): case Magic('9'): - EMIT(NFA_BACKREF1 + (no_Magic(c) - '1')); - nfa_has_backref = TRUE; + { + int refnum = no_Magic(c) - '1'; + + if (!seen_endbrace(refnum + 1)) { + return FAIL; + } + EMIT(NFA_BACKREF1 + refnum); + nfa_has_backref = true; + } break; case Magic('z'): @@ -3568,6 +3575,7 @@ static char *pim_info(nfa_pim_T *pim) // Used during execution: whether a match has been found. static int nfa_match; static proftime_T *nfa_time_limit; +static int *nfa_timed_out; static int nfa_time_count; // Copy postponed invisible match info from "from" to "to". @@ -4574,7 +4582,9 @@ static bool nfa_re_num_cmp(uintmax_t val, int op, uintmax_t pos) * "pim" is NULL or contains info about a Postponed Invisible Match (start * position). */ -static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T *prog, regsubs_T *submatch, regsubs_T *m, int **listids) +static int recursive_regmatch( + nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T *prog, + regsubs_T *submatch, regsubs_T *m, int **listids, int *listids_len) { int save_reginput_col = (int)(reginput - regline); int save_reglnum = reglnum; @@ -4657,8 +4667,10 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T if (nfa_ll_index == 1) { /* Already calling nfa_regmatch() recursively. Save the lastlist[1] * values and clear them. */ - if (*listids == NULL) { + if (*listids == NULL || *listids_len < nstate) { + xfree(*listids); *listids = xmalloc(sizeof(**listids) * nstate); + *listids_len = nstate; } nfa_save_listids(prog, *listids); need_restore = TRUE; @@ -4939,6 +4951,17 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text) #undef PTR2LEN } +static int nfa_did_time_out(void) +{ + if (nfa_time_limit != NULL && profile_passed_limit(*nfa_time_limit)) { + if (nfa_timed_out != NULL) { + *nfa_timed_out = true; + } + return true; + } + return false; +} + /// Main matching routine. /// /// Run NFA to determine whether it matches reginput. @@ -4960,6 +4983,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, nfa_list_T *thislist; nfa_list_T *nextlist; int *listids = NULL; + int listids_len = 0; nfa_state_T *add_state; bool add_here; int add_count; @@ -4982,7 +5006,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, #endif return false; } - if (nfa_time_limit != NULL && profile_passed_limit(*nfa_time_limit)) { + if (nfa_did_time_out()) { #ifdef NFA_REGEXP_DEBUG_LOG fclose(debug); #endif @@ -5095,8 +5119,20 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, if (thislist->n == 0) break; - /* compute nextlist */ - for (listidx = 0; listidx < thislist->n; ++listidx) { + // compute nextlist + for (listidx = 0; listidx < thislist->n; listidx++) { + // If the list gets very long there probably is something wrong. + // At least allow interrupting with CTRL-C. + fast_breakcheck(); + if (got_int) { + break; + } + if (nfa_time_limit != NULL && ++nfa_time_count == 20) { + nfa_time_count = 0; + if (nfa_did_time_out()) { + break; + } + } t = &thislist->t[listidx]; #ifdef NFA_REGEXP_DEBUG_LOG @@ -5240,7 +5276,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, // First try matching the invisible match, then what // follows. result = recursive_regmatch(t->state, NULL, prog, submatch, m, - &listids); + &listids, &listids_len); if (result == NFA_TOO_EXPENSIVE) { nfa_match = result; goto theend; @@ -5341,7 +5377,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, // First try matching the pattern. result = recursive_regmatch(t->state, NULL, prog, submatch, m, - &listids); + &listids, &listids_len); if (result == NFA_TOO_EXPENSIVE) { nfa_match = result; goto theend; @@ -6048,8 +6084,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, fprintf(log_fd, "Postponed recursive nfa_regmatch()\n"); fprintf(log_fd, "\n"); #endif - result = recursive_regmatch(pim->state, pim, - prog, submatch, m, &listids); + result = recursive_regmatch(pim->state, pim, prog, submatch, m, + &listids, &listids_len); pim->result = result ? NFA_PIM_MATCH : NFA_PIM_NOMATCH; // for \@! and \@<! it is a match when the result is // FALSE @@ -6218,7 +6254,7 @@ nextchar: // Check for timeout once every twenty times to avoid overhead. if (nfa_time_limit != NULL && ++nfa_time_count == 20) { nfa_time_count = 0; - if (profile_passed_limit(*nfa_time_limit)) { + if (nfa_did_time_out()) { break; } } @@ -6245,7 +6281,10 @@ theend: // Try match of "prog" with at regline["col"]. // Returns <= 0 for failure, number of lines contained in the match otherwise. -static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm) +static long nfa_regtry(nfa_regprog_T *prog, + colnr_T col, + proftime_T *tm, // timeout limit or NULL + int *timed_out) // flag set on timeout or NULL { int i; regsubs_T subs, m; @@ -6256,6 +6295,7 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm) reginput = regline + col; nfa_time_limit = tm; + nfa_timed_out = timed_out; nfa_time_count = 0; #ifdef REGEXP_DEBUG @@ -6364,10 +6404,12 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm) /// @param line String in which to search or NULL /// @param startcol Column to start looking for match /// @param tm Timeout limit or NULL +/// @param timed_out Flag set on timeout or NULL /// /// @return <= 0 if there is no match and number of lines contained in the /// match otherwise. -static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm) +static long nfa_regexec_both(char_u *line, colnr_T startcol, + proftime_T *tm, int *timed_out) { nfa_regprog_T *prog; long retval = 0L; @@ -6449,7 +6491,7 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm) prog->state[i].lastlist[1] = 0; } - retval = nfa_regtry(prog, col, tm); + retval = nfa_regtry(prog, col, tm, timed_out); nfa_regengine.expr = NULL; @@ -6596,7 +6638,7 @@ nfa_regexec_nl ( rex.reg_ic = rmp->rm_ic; rex.reg_icombine = false; rex.reg_maxcol = 0; - return nfa_regexec_both(line, col, NULL); + return nfa_regexec_both(line, col, NULL, NULL); } /// Matches a regexp against multiple lines. @@ -6608,6 +6650,7 @@ nfa_regexec_nl ( /// @param lnum Number of line to start looking for match /// @param col Column to start looking for match /// @param tm Timeout limit or NULL +/// @param timed_out Flag set on timeout or NULL /// /// @return <= 0 if there is no match and number of lines contained in the match /// otherwise. @@ -6634,7 +6677,8 @@ nfa_regexec_nl ( /// @par /// FIXME if this behavior is not compatible. static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, - linenr_T lnum, colnr_T col, proftime_T *tm) + linenr_T lnum, colnr_T col, + proftime_T *tm, int *timed_out) { rex.reg_match = NULL; rex.reg_mmatch = rmp; @@ -6647,5 +6691,5 @@ static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, rex.reg_icombine = false; rex.reg_maxcol = rmp->rmm_maxcol; - return nfa_regexec_both(NULL, col, tm); + return nfa_regexec_both(NULL, col, tm, timed_out); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 08bb4e4a52..5255fd2a51 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -5651,13 +5651,15 @@ next_search_hl ( && cur != NULL && shl == &cur->hl && cur->match.regprog == cur->hl.rm.regprog); + int timed_out = false; - nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, &(shl->tm)); - /* Copy the regprog, in case it got freed and recompiled. */ + nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, + &(shl->tm), &timed_out); + // Copy the regprog, in case it got freed and recompiled. if (regprog_is_copy) { cur->match.regprog = cur->hl.rm.regprog; } - if (called_emsg || got_int) { + if (called_emsg || got_int || timed_out) { // Error while handling regexp: stop using this regexp. if (shl == &search_hl) { // don't free regprog in the match list, it's a copy diff --git a/src/nvim/search.c b/src/nvim/search.c index f6b80d1b79..777ea07a21 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -523,9 +523,10 @@ int searchit( char_u *pat, long count, int options, - int pat_use, /* which pattern to use when "pat" is empty */ - linenr_T stop_lnum, /* stop after this line number when != 0 */ - proftime_T *tm /* timeout limit or NULL */ + int pat_use, // which pattern to use when "pat" is empty + linenr_T stop_lnum, // stop after this line number when != 0 + proftime_T *tm, // timeout limit or NULL + int *timed_out // set when timed out or NULL ) { int found; @@ -620,9 +621,9 @@ int searchit( // Look for a match somewhere in line "lnum". colnr_T col = at_first_line && (options & SEARCH_COL) ? pos->col : 0; nmatched = vim_regexec_multi(®match, win, buf, - lnum, col, tm); + lnum, col, tm, timed_out); // Abort searching on an error (e.g., out of stack). - if (called_emsg) { + if (called_emsg || (timed_out != NULL && *timed_out)) { break; } if (nmatched > 0) { @@ -686,8 +687,9 @@ int searchit( } if (ptr[matchcol] == NUL - || (nmatched = vim_regexec_multi(®match, win, buf, lnum, - matchcol, tm)) == 0) { + || (nmatched = vim_regexec_multi(®match, win, buf, + lnum, matchcol, tm, + timed_out)) == 0) { match_ok = false; break; } @@ -771,7 +773,7 @@ int searchit( if (ptr[matchcol] == NUL || (nmatched = vim_regexec_multi( ®match, win, buf, lnum + matchpos.lnum, matchcol, - tm)) == 0) { + tm, timed_out)) == 0) { // If the search timed out, we did find a match // but it might be the wrong one, so that's not // OK. @@ -855,30 +857,35 @@ int searchit( * twice. */ if (!p_ws || stop_lnum != 0 || got_int || called_emsg + || (timed_out != NULL && timed_out) || break_loop - || found || loop) + || found || loop) { break; - - /* - * If 'wrapscan' is set we continue at the other end of the file. - * If 'shortmess' does not contain 's', we give a message. - * This message is also remembered in keep_msg for when the screen - * is redrawn. The keep_msg is cleared whenever another message is - * written. - */ - if (dir == BACKWARD) /* start second loop at the other end */ + } + // + // If 'wrapscan' is set we continue at the other end of the file. + // If 'shortmess' does not contain 's', we give a message. + // This message is also remembered in keep_msg for when the screen + // is redrawn. The keep_msg is cleared whenever another message is + // written. + // + if (dir == BACKWARD) { // start second loop at the other end lnum = buf->b_ml.ml_line_count; - else + } else { lnum = 1; - if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG)) + } + if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG)) { give_warning((char_u *)_(dir == BACKWARD - ? top_bot_msg : bot_top_msg), true); + ? top_bot_msg : bot_top_msg), true); + } } if (got_int || called_emsg + || (timed_out != NULL && *timed_out) || break_loop - ) + ) { break; - } while (--count > 0 && found); /* stop after count matches or no match */ + } + } while (--count > 0 && found); // stop after count matches or no match vim_regfree(regmatch.regprog); @@ -965,7 +972,8 @@ int do_search( char_u *pat, long count, int options, - proftime_T *tm /* timeout limit or NULL */ + proftime_T *tm, // timeout limit or NULL + int *timed_out // flag set on timeout or NULL ) { pos_T pos; /* position of the last match */ @@ -1195,7 +1203,7 @@ int do_search( & (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS + SEARCH_MSG + SEARCH_START + ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF)))), - RE_LAST, (linenr_T)0, tm); + RE_LAST, (linenr_T)0, tm, timed_out); if (dircp != NULL) *dircp = dirc; /* restore second '/' or '?' for normal_cmd() */ @@ -3978,7 +3986,7 @@ current_search( result = searchit(curwin, curbuf, &pos, (dir ? FORWARD : BACKWARD), spats[last_idx].pat, i ? count : 1, - SEARCH_KEEP | flags, RE_SEARCH, 0, NULL); + SEARCH_KEEP | flags, RE_SEARCH, 0, NULL, NULL); /* First search may fail, but then start searching from the * beginning of the file (cursor might be on the search match) @@ -4025,7 +4033,7 @@ current_search( for (int i = 0; i < 2; i++) { result = searchit(curwin, curbuf, &pos, direction, spats[last_idx].pat, 0L, flags | SEARCH_KEEP, RE_SEARCH, - 0, NULL); + 0, NULL, NULL); // Search successfull, break out from the loop if (result) { break; @@ -4104,14 +4112,15 @@ static int is_one_char(char_u *pattern, bool move, pos_T *cur, flag = SEARCH_START; } if (searchit(curwin, curbuf, &pos, direction, pattern, 1, - SEARCH_KEEP + flag, RE_SEARCH, 0, NULL) != FAIL) { + SEARCH_KEEP + flag, RE_SEARCH, 0, NULL, NULL) != FAIL) { // Zero-width pattern should match somewhere, then we can check if // start and end are in the same position. called_emsg = false; do { regmatch.startpos[0].col++; nmatched = vim_regexec_multi(®match, curwin, curbuf, - pos.lnum, regmatch.startpos[0].col, NULL); + pos.lnum, regmatch.startpos[0].col, + NULL, NULL); if (!nmatched) { break; } diff --git a/src/nvim/spell.c b/src/nvim/spell.c index ff61c2e5de..ec4da88ea1 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3048,9 +3048,10 @@ void ex_spellrepall(exarg_T *eap) sub_nlines = 0; curwin->w_cursor.lnum = 0; while (!got_int) { - if (do_search(NULL, '/', frompat, 1L, SEARCH_KEEP, NULL) == 0 - || u_save_cursor() == FAIL) + if (do_search(NULL, '/', frompat, 1L, SEARCH_KEEP, NULL, NULL) == 0 + || u_save_cursor() == FAIL) { break; + } // Only replace when the right word isn't there yet. This happens // when changing "etc" to "etc.". diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index b6b7dfff11..8c3ce823d3 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -2902,7 +2902,7 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T } rmp->rmm_maxcol = syn_buf->b_p_smc; - r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL); + r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL, NULL); if (l_syn_time_on) { pt = profile_end(pt); @@ -5956,6 +5956,7 @@ static const char *highlight_init_both[] = { "default link Substitute Search", "default link Whitespace NonText", "default link MsgSeparator StatusLine", + "default link NormalFloat Pmenu", NULL }; diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 50397d40e6..410b9dfcbd 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -109,7 +109,7 @@ static char_u *tagmatchname = NULL; /* name of last used tag */ * Tag for preview window is remembered separately, to avoid messing up the * normal tagstack. */ -static taggy_T ptag_entry = {NULL, {INIT_POS_T(0, 0, 0), 0, 0, NULL}, 0, 0}; +static taggy_T ptag_entry = { NULL, { { 0, 0, 0 }, 0, 0, NULL }, 0, 0 }; /* * Jump to tag; handling of tag commands and tag stack @@ -2508,9 +2508,9 @@ static int jumpto_tag( save_lnum = curwin->w_cursor.lnum; curwin->w_cursor.lnum = 0; /* start search before first line */ if (do_search(NULL, pbuf[0], pbuf + 1, (long)1, - search_options, NULL)) + search_options, NULL, NULL)) { retval = OK; - else { + } else { int found = 1; int cc; @@ -2519,23 +2519,22 @@ static int jumpto_tag( */ p_ic = TRUE; if (!do_search(NULL, pbuf[0], pbuf + 1, (long)1, - search_options, NULL)) { - /* - * Failed to find pattern, take a guess: "^func (" - */ + search_options, NULL, NULL)) { + // Failed to find pattern, take a guess: "^func (" found = 2; (void)test_for_static(&tagp); cc = *tagp.tagname_end; *tagp.tagname_end = NUL; - sprintf((char *)pbuf, "^%s\\s\\*(", tagp.tagname); + snprintf((char *)pbuf, LSIZE, "^%s\\s\\*(", tagp.tagname); if (!do_search(NULL, '/', pbuf, (long)1, - search_options, NULL)) { - /* Guess again: "^char * \<func (" */ - sprintf((char *)pbuf, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(", - tagp.tagname); + search_options, NULL, NULL)) { + // Guess again: "^char * \<func (" + snprintf((char *)pbuf, LSIZE, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(", + tagp.tagname); if (!do_search(NULL, '/', pbuf, (long)1, - search_options, NULL)) + search_options, NULL, NULL)) { found = 0; + } } *tagp.tagname_end = cc; } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 8b4ad4d3af..ffe650f416 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -1112,11 +1112,15 @@ static void refresh_terminal(Terminal *term) return; } long ml_before = buf->b_ml.ml_line_count; - WITH_BUFFER(buf, { - refresh_size(term, buf); - refresh_scrollback(term, buf); - refresh_screen(term, buf); - }); + + // refresh_ functions assume the terminal buffer is current + aco_save_T aco; + aucmd_prepbuf(&aco, buf); + refresh_size(term, buf); + refresh_scrollback(term, buf); + refresh_screen(term, buf); + aucmd_restbuf(&aco); + long ml_added = buf->b_ml.ml_line_count - ml_before; adjust_topline(term, buf, ml_added); } diff --git a/src/nvim/testdir/test50.in b/src/nvim/testdir/test50.in deleted file mode 100644 index 392177b808..0000000000 --- a/src/nvim/testdir/test50.in +++ /dev/null @@ -1,89 +0,0 @@ -Test for shortpathname ':8' extension. -Only for use on Win32 systems! - -STARTTEST -:fun! TestIt(file, bits, expected) - let res=fnamemodify(a:file,a:bits) - if a:expected == '' - echo "'".a:file."'->(".a:bits.")->'".res."'" - else - if substitute(res,'/','\\', 'g') != substitute( a:expected, '/','\\', 'g') - echo "FAILED: '".a:file."'->(".a:bits.")->'".res."'" - echo "Expected: '".a:expected."'" - else - echo "OK" - endif - endif -endfun -:fun! MakeDir( dirname ) - "exe '!mkdir '.substitute(a:dirname,'/','\\','g') - call system('mkdir '.substitute(a:dirname,'/','\\','g')) -endfun -:fun! RMDir( dirname) - "exe '!rmdir '.substitute(a:dirname,'/','\\','g') - call system('rmdir '.substitute(a:dirname,'/','\\','g')) -endfun -:fun! MakeFile( filename) - "exe '!copy nul '.substitute(a:filename,'/','\\','g') - call system('copy nul '.substitute(a:filename,'/','\\','g')) -endfun -:fun! TestColonEight() - redir! >test.out - " This could change for CygWin to //cygdrive/c - let dir1='c:/x.x.y' - if filereadable(dir1) || isdirectory(dir1) - echo "FATAL: '".dir1."' exists, cannot run test" - return - endif - let file1=dir1.'/zz.y.txt' - let nofile1=dir1.'/z.y.txt' - let dir2=dir1.'/VimIsTheGreatestSinceSlicedBread' - let file2=dir2.'/z.txt' - let nofile2=dir2.'/zz.txt' - call MakeDir( dir1 ) - let resdir1 = substitute(fnamemodify(dir1, ':p:8'), '\\$', '', '') - if resdir1 !~ '\V\^c:/XX\x\x\x\x~1.Y\$' - echo "FATAL: unexpected short name: " . resdir1 - echo "INFO: please report your OS to vim-dev" - return - endif - let resfile1=resdir1.'/ZZY~1.TXT' - let resnofile1=resdir1.'/z.y.txt' - let resdir2=resdir1.'/VIMIST~1' - let resfile2=resdir2.'/z.txt' - let resnofile2=resdir2.'/zz.txt' - call MakeDir( dir2 ) - call MakeFile( file1 ) - call MakeFile( file2 ) - call TestIt(file1, ':p:8', resfile1) - call TestIt(nofile1, ':p:8', resnofile1) - call TestIt(file2, ':p:8', resfile2) - call TestIt(nofile2, ':p:8', resnofile2) - call TestIt(nofile2, ':p:8:h', fnamemodify(resnofile2,':h')) - exe 'cd '.dir1 - call TestIt(file1, ':.:8', strpart(resfile1,strlen(resdir1)+1)) - call TestIt(nofile1, ':.:8', strpart(resnofile1,strlen(resdir1)+1)) - call TestIt(file2, ':.:8', strpart(resfile2,strlen(resdir1)+1)) - call TestIt(nofile2, ':.:8', strpart(resnofile2,strlen(resdir1)+1)) - let $HOME=dir1 - call TestIt(file1, ':~:8', '~'.strpart(resfile1,strlen(resdir1))) - call TestIt(nofile1, ':~:8', '~'.strpart(resnofile1,strlen(resdir1))) - call TestIt(file2, ':~:8', '~'.strpart(resfile2,strlen(resdir1))) - call TestIt(nofile2, ':~:8', '~'.strpart(resnofile2,strlen(resdir1))) - cd c:/ - call delete( file2 ) - call delete( file1 ) - call RMDir( dir2 ) - call RMDir( dir1 ) - echo - redir END -endfun -:let dir = getcwd() -:call TestColonEight() -:exe "cd " . dir -:edit! test.out -:set ff=dos -:w -:qa! -ENDTEST - diff --git a/src/nvim/testdir/test50.ok b/src/nvim/testdir/test50.ok deleted file mode 100644 index 91ef1d6604..0000000000 --- a/src/nvim/testdir/test50.ok +++ /dev/null @@ -1,14 +0,0 @@ - -OK -OK -OK -OK -OK -OK -OK -OK -OK -OK -OK -OK -OK diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index f1fb8e67b9..7a6c9478ca 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1161,23 +1161,23 @@ func Test_TextYankPost() norm "ayiw call assert_equal( - \{'regcontents': ['foo'], 'regname': 'a', 'operator': 'y', 'regtype': 'v'}, + \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'a', 'operator': 'y', 'regtype': 'v'}, \g:event) norm y_ call assert_equal( - \{'regcontents': ['foo'], 'regname': '', 'operator': 'y', 'regtype': 'V'}, + \{'regcontents': ['foo'], 'inclusive': v:false, 'regname': '', 'operator': 'y', 'regtype': 'V'}, \g:event) call feedkeys("\<C-V>y", 'x') call assert_equal( - \{'regcontents': ['f'], 'regname': '', 'operator': 'y', 'regtype': "\x161"}, + \{'regcontents': ['f'], 'inclusive': v:true, 'regname': '', 'operator': 'y', 'regtype': "\x161"}, \g:event) norm "xciwbar call assert_equal( - \{'regcontents': ['foo'], 'regname': 'x', 'operator': 'c', 'regtype': 'v'}, + \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'x', 'operator': 'c', 'regtype': 'v'}, \g:event) norm "bdiw call assert_equal( - \{'regcontents': ['bar'], 'regname': 'b', 'operator': 'd', 'regtype': 'v'}, + \{'regcontents': ['bar'], 'inclusive': v:true, 'regname': 'b', 'operator': 'd', 'regtype': 'v'}, \g:event) call assert_equal({}, v:event) diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim index 78e51ed836..0bae161a8b 100644 --- a/src/nvim/testdir/test_findfile.vim +++ b/src/nvim/testdir/test_findfile.vim @@ -119,6 +119,14 @@ func Test_findfile() let &shellslash = save_shellslash endfunc +func Test_findfile_error() + call assert_fails('call findfile([])', 'E730:') + call assert_fails('call findfile("x", [])', 'E730:') + call assert_fails('call findfile("x", "", [])', 'E745:') + call assert_fails('call findfile("x", "**x")', 'E343:') + call assert_fails('call findfile("x", repeat("x", 5000))', 'E854:') +endfunc + " Test finddir({name} [, {path} [, {count}]]) func Test_finddir() let save_path = &path @@ -167,3 +175,11 @@ func Test_finddir() let &path = save_path let &shellslash = save_shellslash endfunc + +func Test_finddir_error() + call assert_fails('call finddir([])', 'E730:') + call assert_fails('call finddir("x", [])', 'E730:') + call assert_fails('call finddir("x", "", [])', 'E745:') + call assert_fails('call finddir("x", "**x")', 'E343:') + call assert_fails('call finddir("x", repeat("x", 5000))', 'E854:') +endfunc diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index bfe13d6b2d..824baffbc9 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -956,8 +956,8 @@ func Test_balloon_show() endfunc func Test_shellescape() - let save_shell = &shell - set shell=bash + let [save_shell, save_shellslash] = [&shell, &shellslash] + set shell=bash shellslash call assert_equal("'text'", shellescape('text')) call assert_equal("'te\"xt'", shellescape('te"xt')) call assert_equal("'te'\\''xt'", shellescape("te'xt")) @@ -971,13 +971,13 @@ func Test_shellescape() call assert_equal("'te\nxt'", shellescape("te\nxt")) call assert_equal("'te\\\nxt'", shellescape("te\nxt", 1)) - set shell=tcsh + set shell=tcsh shellslash call assert_equal("'te\\!xt'", shellescape("te!xt")) call assert_equal("'te\\\\!xt'", shellescape("te!xt", 1)) call assert_equal("'te\\\nxt'", shellescape("te\nxt")) call assert_equal("'te\\\\\nxt'", shellescape("te\nxt", 1)) - let &shell = save_shell + let [&shell, &shellslash] = [save_shell, save_shellslash] endfunc func Test_redo_in_nested_functions() diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim index c352379697..d233046d2b 100644 --- a/src/nvim/testdir/test_gf.vim +++ b/src/nvim/testdir/test_gf.vim @@ -9,6 +9,7 @@ func Test_gf_url() \ "third test for URL:\\\\machine.name\\vimtest2c and other text", \ "fourth test for URL:\\\\machine.name\\tmp\\vimtest2d, and other text", \ "fifth test for URL://machine.name/tmp?q=vim&opt=yes and other text", + \ "sixth test for URL://machine.name:1234?q=vim and other text", \ ]) call cursor(1,1) call search("^first") @@ -20,7 +21,7 @@ func Test_gf_url() if has("ebcdic") set isf=@,240-249,/,.,-,_,+,,,$,:,~,\ else - set isf=@,48-57,/,.,-,_,+,,,$,:,~,\ + set isf=@,48-57,/,.,-,_,+,,,$,~,\ endif call search("^third") call search("name") @@ -33,6 +34,10 @@ func Test_gf_url() call search("URL") call assert_equal("URL://machine.name/tmp?q=vim&opt=yes", expand("<cfile>")) + call search("^sixth") + call search("URL") + call assert_equal("URL://machine.name:1234?q=vim", expand("<cfile>")) + set isf&vim enew! endfunc diff --git a/src/nvim/testdir/test_hlsearch.vim b/src/nvim/testdir/test_hlsearch.vim index 1fc7b04f88..97f6ae7b51 100644 --- a/src/nvim/testdir/test_hlsearch.vim +++ b/src/nvim/testdir/test_hlsearch.vim @@ -32,6 +32,25 @@ function! Test_hlsearch() enew! endfunction +func Test_hlsearch_hangs() + if !has('reltime') || !has('float') + return + endif + + " This pattern takes a long time to match, it should timeout. + new + call setline(1, ['aaa', repeat('abc ', 100000), 'ccc']) + let start = reltime() + set hlsearch nolazyredraw redrawtime=101 + let @/ = '\%#=1a*.*X\@<=b*' + redraw + let elapsed = reltimefloat(reltime(start)) + call assert_true(elapsed > 0.1) + call assert_true(elapsed < 1.0) + set nohlsearch redrawtime& + bwipe! +endfunc + func Test_hlsearch_eol_highlight() new call append(1, repeat([''], 9)) diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index 8858cd22b8..7fd115fd68 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -136,3 +136,44 @@ func Test_marks_cmd_multibyte() bwipe! endfunc + +func Test_delmarks() + new + norm mx + norm `x + delmarks x + call assert_fails('norm `x', 'E20:') + + " Deleting an already deleted mark should not fail. + delmarks x + + " Test deleting a range of marks. + norm ma + norm mb + norm mc + norm mz + delmarks b-z + norm `a + call assert_fails('norm `b', 'E20:') + call assert_fails('norm `c', 'E20:') + call assert_fails('norm `z', 'E20:') + call assert_fails('delmarks z-b', 'E475:') + + call assert_fails('delmarks', 'E471:') + call assert_fails('delmarks /', 'E475:') + + " Test delmarks! + norm mx + norm `x + delmarks! + call assert_fails('norm `x', 'E20:') + call assert_fails('delmarks! x', 'E474:') + + bwipe! +endfunc + +func Test_mark_error() + call assert_fails('mark', 'E471:') + call assert_fails('mark xx', 'E488:') + call assert_fails('mark _', 'E191:') +endfunc diff --git a/src/nvim/testdir/test_profile.vim b/src/nvim/testdir/test_profile.vim index 4cbd800da5..8996e86b43 100644 --- a/src/nvim/testdir/test_profile.vim +++ b/src/nvim/testdir/test_profile.vim @@ -181,3 +181,44 @@ func Test_profile_errors() call assert_fails("profile pause", 'E750:') call assert_fails("profile continue", 'E750:') endfunc + +func Test_profile_truncate_mbyte() + if !has('multi_byte') || &enc !=# 'utf-8' + return + endif + + let lines = [ + \ 'scriptencoding utf-8', + \ 'func! Foo()', + \ ' return [', + \ ' \ "' . join(map(range(0x4E00, 0x4E00 + 340), 'nr2char(v:val)'), '') . '",', + \ ' \ "' . join(map(range(0x4F00, 0x4F00 + 340), 'nr2char(v:val)'), '') . '",', + \ ' \ ]', + \ 'endfunc', + \ 'call Foo()', + \ ] + + call writefile(lines, 'Xprofile_file.vim') + call system(v:progpath + \ . ' -es --cmd "set enc=utf-8"' + \ . ' -c "profile start Xprofile_file.log"' + \ . ' -c "profile file Xprofile_file.vim"' + \ . ' -c "so Xprofile_file.vim"' + \ . ' -c "qall!"') + call assert_equal(0, v:shell_error) + + split Xprofile_file.log + if &fenc != '' + call assert_equal('utf-8', &fenc) + endif + /func! Foo() + let lnum = line('.') + call assert_match('^\s*return \[$', getline(lnum + 1)) + call assert_match("\u4F52$", getline(lnum + 2)) + call assert_match("\u5052$", getline(lnum + 3)) + call assert_match('^\s*\\ \]$', getline(lnum + 4)) + bwipe! + + call delete('Xprofile_file.vim') + call delete('Xprofile_file.log') +endfunc diff --git a/src/nvim/testdir/test_regex_char_classes.vim b/src/nvim/testdir/test_regex_char_classes.vim index 2192b5e8fc..7873502943 100644 --- a/src/nvim/testdir/test_regex_char_classes.vim +++ b/src/nvim/testdir/test_regex_char_classes.vim @@ -1,5 +1,11 @@ " Tests for regexp with backslash and other special characters inside [] " Also test backslash for hex/octal numbered character. +" +if !has('multi_byte') + finish +endif + +scriptencoding utf-8 function RunSTest(value, calls, expected) new @@ -56,3 +62,237 @@ function Test_s_search() call RunSTest(" xyz", "s/~/bcd/", " bcd") call RunSTest(" bcdbcdbcd", "s/~\\+/BB/", " BB") endfunction + +" Test character classes in regexp using regexpengine 0, 1, 2. +func Test_regex_char_classes() + new + let save_enc = &encoding + set encoding=utf-8 + + let input = "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé" + + " Format is [cmd_to_run, expected_output] + let tests = [ + \ [':s/\%#=0\d//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1\d//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2\d//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0[0-9]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1[0-9]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2[0-9]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0\D//g', + \ "0123456789"], + \ [':s/\%#=1\D//g', + \ "0123456789"], + \ [':s/\%#=2\D//g', + \ "0123456789"], + \ [':s/\%#=0[^0-9]//g', + \ "0123456789"], + \ [':s/\%#=1[^0-9]//g', + \ "0123456789"], + \ [':s/\%#=2[^0-9]//g', + \ "0123456789"], + \ [':s/\%#=0\o//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1\o//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2\o//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0[0-7]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1[0-7]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2[0-7]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0\O//g', + \ "01234567"], + \ [':s/\%#=1\O//g', + \ "01234567"], + \ [':s/\%#=2\O//g', + \ "01234567"], + \ [':s/\%#=0[^0-7]//g', + \ "01234567"], + \ [':s/\%#=1[^0-7]//g', + \ "01234567"], + \ [':s/\%#=2[^0-7]//g', + \ "01234567"], + \ [':s/\%#=0\x//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1\x//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2\x//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0[0-9A-Fa-f]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1[0-9A-Fa-f]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2[0-9A-Fa-f]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0\X//g', + \ "0123456789ABCDEFabcdef"], + \ [':s/\%#=1\X//g', + \ "0123456789ABCDEFabcdef"], + \ [':s/\%#=2\X//g', + \ "0123456789ABCDEFabcdef"], + \ [':s/\%#=0[^0-9A-Fa-f]//g', + \ "0123456789ABCDEFabcdef"], + \ [':s/\%#=1[^0-9A-Fa-f]//g', + \ "0123456789ABCDEFabcdef"], + \ [':s/\%#=2[^0-9A-Fa-f]//g', + \ "0123456789ABCDEFabcdef"], + \ [':s/\%#=0\w//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1\w//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2\w//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0[0-9A-Za-z_]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1[0-9A-Za-z_]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2[0-9A-Za-z_]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0\W//g', + \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"], + \ [':s/\%#=1\W//g', + \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"], + \ [':s/\%#=2\W//g', + \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"], + \ [':s/\%#=0[^0-9A-Za-z_]//g', + \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"], + \ [':s/\%#=1[^0-9A-Za-z_]//g', + \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"], + \ [':s/\%#=2[^0-9A-Za-z_]//g', + \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"], + \ [':s/\%#=0\h//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1\h//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2\h//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0[A-Za-z_]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1[A-Za-z_]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2[A-Za-z_]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0\H//g', + \ "ABCDEFGHIXYZ_abcdefghiwxyz"], + \ [':s/\%#=1\H//g', + \ "ABCDEFGHIXYZ_abcdefghiwxyz"], + \ [':s/\%#=2\H//g', + \ "ABCDEFGHIXYZ_abcdefghiwxyz"], + \ [':s/\%#=0[^A-Za-z_]//g', + \ "ABCDEFGHIXYZ_abcdefghiwxyz"], + \ [':s/\%#=1[^A-Za-z_]//g', + \ "ABCDEFGHIXYZ_abcdefghiwxyz"], + \ [':s/\%#=2[^A-Za-z_]//g', + \ "ABCDEFGHIXYZ_abcdefghiwxyz"], + \ [':s/\%#=0\a//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1\a//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2\a//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0[A-Za-z]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1[A-Za-z]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2[A-Za-z]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0\A//g', + \ "ABCDEFGHIXYZabcdefghiwxyz"], + \ [':s/\%#=1\A//g', + \ "ABCDEFGHIXYZabcdefghiwxyz"], + \ [':s/\%#=2\A//g', + \ "ABCDEFGHIXYZabcdefghiwxyz"], + \ [':s/\%#=0[^A-Za-z]//g', + \ "ABCDEFGHIXYZabcdefghiwxyz"], + \ [':s/\%#=1[^A-Za-z]//g', + \ "ABCDEFGHIXYZabcdefghiwxyz"], + \ [':s/\%#=2[^A-Za-z]//g', + \ "ABCDEFGHIXYZabcdefghiwxyz"], + \ [':s/\%#=0\l//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1\l//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2\l//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0[a-z]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1[a-z]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2[a-z]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0\L//g', + \ "abcdefghiwxyz"], + \ [':s/\%#=1\L//g', + \ "abcdefghiwxyz"], + \ [':s/\%#=2\L//g', + \ "abcdefghiwxyz"], + \ [':s/\%#=0[^a-z]//g', + \ "abcdefghiwxyz"], + \ [':s/\%#=1[^a-z]//g', + \ "abcdefghiwxyz"], + \ [':s/\%#=2[^a-z]//g', + \ "abcdefghiwxyz"], + \ [':s/\%#=0\u//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1\u//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2\u//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0[A-Z]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1[A-Z]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2[A-Z]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0\U//g', + \ "ABCDEFGHIXYZ"], + \ [':s/\%#=1\U//g', + \ "ABCDEFGHIXYZ"], + \ [':s/\%#=2\U//g', + \ "ABCDEFGHIXYZ"], + \ [':s/\%#=0[^A-Z]//g', + \ "ABCDEFGHIXYZ"], + \ [':s/\%#=1[^A-Z]//g', + \ "ABCDEFGHIXYZ"], + \ [':s/\%#=2[^A-Z]//g', + \ "ABCDEFGHIXYZ"], + \ [':s/\%#=0\%' . line('.') . 'l^\t...//g', + \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1\%' . line('.') . 'l^\t...//g', + \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2\%' . line('.') . 'l^\t...//g', + \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0[0-z]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=1[0-z]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=2[0-z]//g', + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ [':s/\%#=0[^0-z]//g', + \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"], + \ [':s/\%#=1[^0-z]//g', + \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"], + \ [':s/\%#=2[^0-z]//g', + \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"] + \] + + for [cmd, expected] in tests + call append(0, input) + call cursor(1, 1) + exe cmd + call assert_equal(expected, getline(1), cmd) + endfor + + let &encoding = save_enc + enew! + close +endfunc diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index eb8f69ef15..0619e9c027 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -47,3 +47,29 @@ func Test_get_equi_class() s/.*/[[. call assert_equal(1, search(getline(1))) endfunc + +func Test_rex_init() + set noincsearch + set re=1 + new + setlocal iskeyword=a-z + call setline(1, ['abc', 'ABC']) + call assert_equal(1, search('[[:keyword:]]')) + new + setlocal iskeyword=A-Z + call setline(1, ['abc', 'ABC']) + call assert_equal(2, search('[[:keyword:]]')) + bwipe! + bwipe! + set re=0 +endfunc + +func Test_backref() + new + call setline(1, ['one', 'two', 'three', 'four', 'five']) + call assert_equal(3, search('\%#=1\(e\)\1')) + call assert_equal(3, search('\%#=2\(e\)\1')) + call assert_fails('call search("\\%#=1\\(e\\1\\)")', 'E65:') + call assert_fails('call search("\\%#=2\\(e\\1\\)")', 'E65:') + bwipe! +endfunc diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index cf85bd58ac..351b119acd 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -223,7 +223,7 @@ func Test_statusline() set statusline=ab%(cd%q%)de call assert_match('^abde\s*$', s:get_statusline()) copen - call assert_match('^abcd\[Quickfix List\1]de\s*$', s:get_statusline()) + call assert_match('^abcd\[Quickfix List]de\s*$', s:get_statusline()) cclose " %#: Set highlight group. The name must follow and then a # again. diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim index dbd26be089..d02454fbf0 100644 --- a/src/nvim/testdir/test_substitute.vim +++ b/src/nvim/testdir/test_substitute.vim @@ -322,6 +322,90 @@ func Test_sub_cmd_8() set titlestring& endfunc +" Test %s/\n// which is implemented as a special case to use a +" more efficient join rather than doing a regular substitution. +func Test_substitute_join() + new + + call setline(1, ["foo\tbar", "bar\<C-H>foo"]) + let a = execute('%s/\n//') + call assert_equal("", a) + call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$')) + call assert_equal('\n', histget("search", -1)) + + call setline(1, ["foo\tbar", "bar\<C-H>foo"]) + let a = execute('%s/\n//g') + call assert_equal("", a) + call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$')) + call assert_equal('\n', histget("search", -1)) + + call setline(1, ["foo\tbar", "bar\<C-H>foo"]) + let a = execute('%s/\n//p') + call assert_equal("\nfoo barbar^Hfoo", a) + call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$')) + call assert_equal('\n', histget("search", -1)) + + call setline(1, ["foo\tbar", "bar\<C-H>foo"]) + let a = execute('%s/\n//l') + call assert_equal("\nfoo^Ibarbar^Hfoo$", a) + call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$')) + call assert_equal('\n', histget("search", -1)) + + call setline(1, ["foo\tbar", "bar\<C-H>foo"]) + let a = execute('%s/\n//#') + call assert_equal("\n 1 foo barbar^Hfoo", a) + call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$')) + call assert_equal('\n', histget("search", -1)) + + bwipe! +endfunc + +func Test_substitute_count() + new + call setline(1, ['foo foo', 'foo foo', 'foo foo', 'foo foo', 'foo foo']) + 2 + + s/foo/bar/3 + call assert_equal(['foo foo', 'bar foo', 'bar foo', 'bar foo', 'foo foo'], + \ getline(1, '$')) + + call assert_fails('s/foo/bar/0', 'E939:') + + bwipe! +endfunc + +" Test substitute 'n' flag (report number of matches, do not substitute). +func Test_substitute_flag_n() + new + let lines = ['foo foo', 'foo foo', 'foo foo', 'foo foo', 'foo foo'] + call setline(1, lines) + + call assert_equal("\n3 matches on 3 lines", execute('2,4s/foo/bar/n')) + call assert_equal("\n6 matches on 3 lines", execute('2,4s/foo/bar/gn')) + + " c flag (confirm) should be ignored when using n flag. + call assert_equal("\n3 matches on 3 lines", execute('2,4s/foo/bar/nc')) + + " No substitution should have been done. + call assert_equal(lines, getline(1, '$')) + + bwipe! +endfunc + +func Test_substitute_errors() + new + call setline(1, 'foobar') + + call assert_fails('s/FOO/bar/', 'E486:') + call assert_fails('s/foo/bar/@', 'E488:') + call assert_fails('s/\(/bar/', 'E476:') + + setl nomodifiable + call assert_fails('s/foo/bar/', 'E21:') + + bwipe! +endfunc + " Test for *sub-replace-special* and *sub-replace-expression* on substitute(). func Test_sub_replace_1() " Run the tests with 'magic' on diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index 999566c6ac..13fb50b985 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -163,6 +163,329 @@ func Test_text_format() \ '# 1 xxxxx', \ '# foobar'], getline(1, 2)) + " Test the 'p' flag for 'formatoptions' + " First test without the flag: that it will break "Mr. Feynman" at the space + normal ggdG + setl tw=28 fo=tcq + call setline('.', 'Surely you''re joking, Mr. Feynman!') + normal gqq + call assert_equal([ + \ 'Surely you''re joking, Mr.', + \ 'Feynman!'], getline(1, 2)) + " Now test with the flag: that it will push the name with the title onto the + " next line + normal ggdG + setl fo+=p + call setline('.', 'Surely you''re joking, Mr. Feynman!') + normal gqq + call assert_equal([ + \ 'Surely you''re joking,', + \ 'Mr. Feynman!'], getline(1, 2)) + " Ensure that it will still break if two spaces are entered + normal ggdG + call setline('.', 'Surely you''re joking, Mr. Feynman!') + normal gqq + call assert_equal([ + \ 'Surely you''re joking, Mr.', + \ 'Feynman!'], getline(1, 2)) + setl ai& tw& fo& si& comments& enew! endfunc + +" Tests for :right, :center and :left on text with embedded TAB. +func Test_format_align() + enew! + set tw=65 + + " :left alignment + call append(0, [ + \ " test for :left", + \ " a a", + \ " fa a", + \ " dfa a", + \ " sdfa a", + \ " asdfa a", + \ " xasdfa a", + \ "asxxdfa a", + \ ]) + %left + call assert_equal([ + \ "test for :left", + \ "a a", + \ "fa a", + \ "dfa a", + \ "sdfa a", + \ "asdfa a", + \ "xasdfa a", + \ "asxxdfa a", + \ "" + \ ], getline(1, '$')) + enew! + + " :center alignment + call append(0, [ + \ " test for :center", + \ " a a", + \ " fa afd asdf", + \ " dfa a", + \ " sdfa afd asdf", + \ " asdfa a", + \ " xasdfa asdfasdfasdfasdfasdf", + \ "asxxdfa a" + \ ]) + %center + call assert_equal([ + \ " test for :center", + \ " a a", + \ " fa afd asdf", + \ " dfa a", + \ " sdfa afd asdf", + \ " asdfa a", + \ " xasdfa asdfasdfasdfasdfasdf", + \ " asxxdfa a", + \ "" + \ ], getline(1, '$')) + enew! + + " :right alignment + call append(0, [ + \ " test for :right", + \ " a a", + \ " fa a", + \ " dfa a", + \ " sdfa a", + \ " asdfa a", + \ " xasdfa a", + \ " asxxdfa a", + \ " asxa;ofa a", + \ " asdfaqwer a", + \ " a ax", + \ " fa ax", + \ " dfa ax", + \ " sdfa ax", + \ " asdfa ax", + \ " xasdfa ax", + \ " asxxdfa ax", + \ " asxa;ofa ax", + \ " asdfaqwer ax", + \ " a axx", + \ " fa axx", + \ " dfa axx", + \ " sdfa axx", + \ " asdfa axx", + \ " xasdfa axx", + \ " asxxdfa axx", + \ " asxa;ofa axx", + \ " asdfaqwer axx", + \ " a axxx", + \ " fa axxx", + \ " dfa axxx", + \ " sdfa axxx", + \ " asdfa axxx", + \ " xasdfa axxx", + \ " asxxdfa axxx", + \ " asxa;ofa axxx", + \ " asdfaqwer axxx", + \ " a axxxo", + \ " fa axxxo", + \ " dfa axxxo", + \ " sdfa axxxo", + \ " asdfa axxxo", + \ " xasdfa axxxo", + \ " asxxdfa axxxo", + \ " asxa;ofa axxxo", + \ " asdfaqwer axxxo", + \ " a axxxoi", + \ " fa axxxoi", + \ " dfa axxxoi", + \ " sdfa axxxoi", + \ " asdfa axxxoi", + \ " xasdfa axxxoi", + \ " asxxdfa axxxoi", + \ " asxa;ofa axxxoi", + \ " asdfaqwer axxxoi", + \ " a axxxoik", + \ " fa axxxoik", + \ " dfa axxxoik", + \ " sdfa axxxoik", + \ " asdfa axxxoik", + \ " xasdfa axxxoik", + \ " asxxdfa axxxoik", + \ " asxa;ofa axxxoik", + \ " asdfaqwer axxxoik", + \ " a axxxoike", + \ " fa axxxoike", + \ " dfa axxxoike", + \ " sdfa axxxoike", + \ " asdfa axxxoike", + \ " xasdfa axxxoike", + \ " asxxdfa axxxoike", + \ " asxa;ofa axxxoike", + \ " asdfaqwer axxxoike", + \ " a axxxoikey", + \ " fa axxxoikey", + \ " dfa axxxoikey", + \ " sdfa axxxoikey", + \ " asdfa axxxoikey", + \ " xasdfa axxxoikey", + \ " asxxdfa axxxoikey", + \ " asxa;ofa axxxoikey", + \ " asdfaqwer axxxoikey", + \ ]) + %right + call assert_equal([ + \ "\t\t\t\t test for :right", + \ "\t\t\t\t a a", + \ "\t\t\t\t fa a", + \ "\t\t\t\t dfa a", + \ "\t\t\t\t sdfa a", + \ "\t\t\t\t asdfa a", + \ "\t\t\t\t xasdfa a", + \ "\t\t\t\t asxxdfa a", + \ "\t\t\t\t asxa;ofa a", + \ "\t\t\t\t asdfaqwer a", + \ "\t\t\t\t a ax", + \ "\t\t\t\t fa ax", + \ "\t\t\t\t dfa ax", + \ "\t\t\t\t sdfa ax", + \ "\t\t\t\t asdfa ax", + \ "\t\t\t\t xasdfa ax", + \ "\t\t\t\t asxxdfa ax", + \ "\t\t\t\t asxa;ofa ax", + \ "\t\t\t\t asdfaqwer ax", + \ "\t\t\t\t a axx", + \ "\t\t\t\t fa axx", + \ "\t\t\t\t dfa axx", + \ "\t\t\t\t sdfa axx", + \ "\t\t\t\t asdfa axx", + \ "\t\t\t\t xasdfa axx", + \ "\t\t\t\t asxxdfa axx", + \ "\t\t\t\t asxa;ofa axx", + \ "\t\t\t\t asdfaqwer axx", + \ "\t\t\t\t a axxx", + \ "\t\t\t\t fa axxx", + \ "\t\t\t\t dfa axxx", + \ "\t\t\t\t sdfa axxx", + \ "\t\t\t\t asdfa axxx", + \ "\t\t\t\t xasdfa axxx", + \ "\t\t\t\t asxxdfa axxx", + \ "\t\t\t\t asxa;ofa axxx", + \ "\t\t\t\t asdfaqwer axxx", + \ "\t\t\t\t a axxxo", + \ "\t\t\t\t fa axxxo", + \ "\t\t\t\t dfa axxxo", + \ "\t\t\t\t sdfa axxxo", + \ "\t\t\t\t asdfa axxxo", + \ "\t\t\t\t xasdfa axxxo", + \ "\t\t\t\t asxxdfa axxxo", + \ "\t\t\t\t asxa;ofa axxxo", + \ "\t\t\t\t asdfaqwer axxxo", + \ "\t\t\t\t a axxxoi", + \ "\t\t\t\t fa axxxoi", + \ "\t\t\t\t dfa axxxoi", + \ "\t\t\t\t sdfa axxxoi", + \ "\t\t\t\t asdfa axxxoi", + \ "\t\t\t\t xasdfa axxxoi", + \ "\t\t\t\t asxxdfa axxxoi", + \ "\t\t\t\t asxa;ofa axxxoi", + \ "\t\t\t\t asdfaqwer axxxoi", + \ "\t\t\t\t a axxxoik", + \ "\t\t\t\t fa axxxoik", + \ "\t\t\t\t dfa axxxoik", + \ "\t\t\t\t sdfa axxxoik", + \ "\t\t\t\t asdfa axxxoik", + \ "\t\t\t\t xasdfa axxxoik", + \ "\t\t\t\t asxxdfa axxxoik", + \ "\t\t\t\t asxa;ofa axxxoik", + \ "\t\t\t\t asdfaqwer axxxoik", + \ "\t\t\t\t a axxxoike", + \ "\t\t\t\t fa axxxoike", + \ "\t\t\t\t dfa axxxoike", + \ "\t\t\t\t sdfa axxxoike", + \ "\t\t\t\t asdfa axxxoike", + \ "\t\t\t\t xasdfa axxxoike", + \ "\t\t\t\t asxxdfa axxxoike", + \ "\t\t\t\t asxa;ofa axxxoike", + \ "\t\t\t\t asdfaqwer axxxoike", + \ "\t\t\t\t a axxxoikey", + \ "\t\t\t\t fa axxxoikey", + \ "\t\t\t\t dfa axxxoikey", + \ "\t\t\t\t sdfa axxxoikey", + \ "\t\t\t\t asdfa axxxoikey", + \ "\t\t\t\t xasdfa axxxoikey", + \ "\t\t\t\t asxxdfa axxxoikey", + \ "\t\t\t\t asxa;ofa axxxoikey", + \ "\t\t\t\t asdfaqwer axxxoikey", + \ "" + \ ], getline(1, '$')) + enew! + + set tw& +endfunc + +" Test formatting a paragraph. +func Test_format_para() + enew! + set fo+=tcroql tw=72 + + call append(0, [ + \ "xxxxx xx xxxxxx ", + \ "xxxxxxx xxxxxxxxx xxx xxxx xxxxx xxxxx xxx xx", + \ "xxxxxxxxxxxxxxxxxx xxxxx xxxx, xxxx xxxx xxxx xxxx xxx xx xx", + \ "xx xxxxxxx. xxxx xxxx.", + \ "", + \ "> xx xx, xxxx xxxx xxx xxxx xxx xxxxx xxx xxx xxxxxxx xxx xxxxx", + \ "> xxxxxx xxxxxxx: xxxx xxxxxxx, xx xxxxxx xxxx xxxxxxxxxx" + \ ]) + exe "normal /xxxxxxxx$\<CR>" + normal 0gq6kk + call assert_equal([ + \ "xxxxx xx xxxxxx xxxxxxx xxxxxxxxx xxx xxxx xxxxx xxxxx xxx xx", + \ "xxxxxxxxxxxxxxxxxx xxxxx xxxx, xxxx xxxx xxxx xxxx xxx xx xx xx xxxxxxx.", + \ "xxxx xxxx.", + \ "", + \ "> xx xx, xxxx xxxx xxx xxxx xxx xxxxx xxx xxx xxxxxxx xxx xxxxx xxxxxx", + \ "> xxxxxxx: xxxx xxxxxxx, xx xxxxxx xxxx xxxxxxxxxx", + \ "" + \ ], getline(1, '$')) + + set fo& tw& + enew! +endfunc + +" Test undo after ":%s" and formatting. +func Test_format_undo() + enew! + map gg :.,.+2s/^/x/<CR>kk:set tw=3<CR>gqq + + call append(0, [ + \ "aa aa aa aa", + \ "bb bb bb bb", + \ "cc cc cc cc" + \ ]) + " undo/redo here to make the next undo only work on the following changes + exe "normal i\<C-G>u" + call cursor(1,1) + normal ggu + call assert_equal([ + \ "aa aa aa aa", + \ "bb bb bb bb", + \ "cc cc cc cc", + \ "" + \ ], getline(1, '$')) + + unmap gg + set tw& + enew! +endfunc + +func Test_format_list_auto() + new + call setline(1, ['1. abc', '2. def', '3. ghi']) + set fo=tan ai bs=2 + call feedkeys("3G0lli\<BS>\<BS>x\<Esc>", 'tx') + call assert_equal('2. defx ghi', getline(2)) + bwipe! + set fo& ai& bs& +endfunc diff --git a/src/nvim/testdir/test_true_false.vim b/src/nvim/testdir/test_true_false.vim index 84aca737ac..4a5d47471d 100644 --- a/src/nvim/testdir/test_true_false.vim +++ b/src/nvim/testdir/test_true_false.vim @@ -57,6 +57,9 @@ endfunc " Test using TRUE or FALSE values for an argument. func Test_true_false_arg() + let shellslash = &shellslash + let wildignore = &wildignore + set shellslash call Try_arg_true_false('count(["a", "A"], "a", %v%)', 1, 2) set wildignore=*.swp @@ -110,6 +113,8 @@ func Test_true_false_arg() let here_id = synID(1, 3, 0) call Try_arg_true_false('synID(1, 3, %v%)', here_id, brackets_id) bwipe! + let &wildignore = wildignore + let &shellslash = shellslash endfunc function Try_arg_non_zero(expr, false_val, true_val) diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 4a143d665d..74afc72f03 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1,8 +1,4 @@ " Tests for various Visual mode. -if !has('visual') - finish -endif - func Test_block_shift_multibyte() " Uses double-wide character. @@ -278,9 +274,46 @@ func Test_visual_mode_reset() set belloff& endfunc +func Test_Visual_word_textobject() + new + call setline(1, ['First sentence. Second sentence.']) + + " When start and end of visual area are identical, 'aw' or 'iw' select + " the whole word. + norm! 1go2fcvawy + call assert_equal('Second ', @") + norm! 1go2fcviwy + call assert_equal('Second', @") + + " When start and end of visual area are not identical, 'aw' or 'iw' + " extend the word in direction of the end of the visual area. + norm! 1go2fcvlawy + call assert_equal('cond ', @") + norm! gv2awy + call assert_equal('cond sentence.', @") + + norm! 1go2fcvliwy + call assert_equal('cond', @") + norm! gv2iwy + call assert_equal('cond sentence', @") + + " Extend visual area in opposite direction. + norm! 1go2fcvhawy + call assert_equal(' Sec', @") + norm! gv2awy + call assert_equal(' sentence. Sec', @") + + norm! 1go2fcvhiwy + call assert_equal('Sec', @") + norm! gv2iwy + call assert_equal('. Sec', @") + + bwipe! +endfunc + func Test_Visual_sentence_textobject() new - call setline(1, ['First sentence. Second sentence. Third', 'sentence. Fouth sentence']) + call setline(1, ['First sentence. Second sentence. Third', 'sentence. Fourth sentence']) " When start and end of visual area are identical, 'as' or 'is' select " the whole sentence. @@ -318,3 +351,63 @@ func Test_Visual_sentence_textobject() bwipe! endfunc + +func Test_curswant_not_changed() + new + call setline(1, ['one', 'two']) + au InsertLeave * call getcurpos() + call feedkeys("gg0\<C-V>jI123 \<Esc>j", 'xt') + call assert_equal([0, 2, 1, 0, 1], getcurpos()) + + bwipe! + au! InsertLeave +endfunc + +func Test_Visual_paragraph_textobject() + new + call setline(1, ['First line.', + \ '', + \ 'Second line.', + \ 'Third line.', + \ 'Fourth line.', + \ 'Fifth line.', + \ '', + \ 'Sixth line.']) + + " When start and end of visual area are identical, 'ap' or 'ip' select + " the whole paragraph. + norm! 4ggvapy + call assert_equal("Second line.\nThird line.\nFourth line.\nFifth line.\n\n", @") + norm! 4ggvipy + call assert_equal("Second line.\nThird line.\nFourth line.\nFifth line.\n", @") + + " When start and end of visual area are not identical, 'ap' or 'ip' + " extend the sentence in direction of the end of the visual area. + " FIXME: actually, it is not sufficient to have different start and + " end of visual selection, the start line and end line have to differ, + " which is not consistent with the documentation. + norm! 4ggVjapy + call assert_equal("Third line.\nFourth line.\nFifth line.\n\n", @") + norm! gvapy + call assert_equal("Third line.\nFourth line.\nFifth line.\n\nSixth line.\n", @") + norm! 4ggVjipy + call assert_equal("Third line.\nFourth line.\nFifth line.\n", @") + norm! gvipy + call assert_equal("Third line.\nFourth line.\nFifth line.\n\n", @") + norm! gvipy + call assert_equal("Third line.\nFourth line.\nFifth line.\n\nSixth line.\n", @") + + " Extend visual area in opposite direction. + norm! 5ggVkapy + call assert_equal("\nSecond line.\nThird line.\nFourth line.\n", @") + norm! gvapy + call assert_equal("First line.\n\nSecond line.\nThird line.\nFourth line.\n", @") + norm! 5ggVkipy + call assert_equal("Second line.\nThird line.\nFourth line.\n", @") + norma gvipy + call assert_equal("\nSecond line.\nThird line.\nFourth line.\n", @") + norm! gvipy + call assert_equal("First line.\n\nSecond line.\nThird line.\nFourth line.\n", @") + + bwipe! +endfunc diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index b3ab6957dc..57fb36abb8 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -518,4 +518,132 @@ func Test_winrestcmd() only endfunc +func Test_relative_cursor_position_in_one_line_window() + new + only + call setline(1, range(1, 10000)) + normal 50% + let lnum = getcurpos()[1] + split + split + " make third window take as many lines as possible, other windows will + " become one line + 3wincmd w + for i in range(1, &lines - 6) + wincmd + + redraw! + endfor + + " first and second window should show cursor line + let wininfo = getwininfo() + call assert_equal(lnum, wininfo[0].topline) + call assert_equal(lnum, wininfo[1].topline) + + only! + bwipe! +endfunc + +func Test_relative_cursor_position_after_move_and_resize() + let so_save = &so + set so=0 + enew + call setline(1, range(1, 10000)) + normal 50% + split + 1wincmd w + " Move cursor to first line in window + normal H + redraw! + " Reduce window height to two lines + let height = winheight(0) + while winheight(0) > 2 + wincmd - + redraw! + endwhile + " move cursor to second/last line in window + normal j + " restore previous height + while winheight(0) < height + wincmd + + redraw! + endwhile + " make window two lines again + while winheight(0) > 2 + wincmd - + redraw! + endwhile + + " cursor should be at bottom line + let info = getwininfo(win_getid())[0] + call assert_equal(info.topline + 1, getcurpos()[1]) + + only! + bwipe! + let &so = so_save +endfunc + +func Test_relative_cursor_position_after_resize() + let so_save = &so + set so=0 + enew + call setline(1, range(1, 10000)) + normal 50% + split + 1wincmd w + let winid1 = win_getid() + let info = getwininfo(winid1)[0] + " Move cursor to second line in window + exe "normal " . (info.topline + 1) . "G" + redraw! + let lnum = getcurpos()[1] + + " Make the window only two lines high, cursor should end up in top line + 2wincmd w + exe (info.height - 2) . "wincmd +" + redraw! + let info = getwininfo(winid1)[0] + call assert_equal(lnum, info.topline) + + only! + bwipe! + let &so = so_save +endfunc + +func Test_relative_cursor_second_line_after_resize() + let so_save = &so + set so=0 + enew + call setline(1, range(1, 10000)) + normal 50% + split + 1wincmd w + let winid1 = win_getid() + let info = getwininfo(winid1)[0] + + " Make the window only two lines high + 2wincmd _ + + " Move cursor to second line in window + normal H + normal j + + " Make window size bigger, then back to 2 lines + for i in range(1, 10) + wincmd + + redraw! + endfor + for i in range(1, 10) + wincmd - + redraw! + endfor + + " cursor should end up in bottom line + let info = getwininfo(winid1)[0] + call assert_equal(info.topline + 1, getcurpos()[1]) + + only! + bwipe! + let &so = so_save +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 3eb88366d6..0bdda17299 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -10,6 +10,7 @@ #include "nvim/charset.h" #include "nvim/main.h" #include "nvim/aucmd.h" +#include "nvim/ex_docmd.h" #include "nvim/option.h" #include "nvim/os/os.h" #include "nvim/os/input.h" @@ -357,15 +358,17 @@ static bool handle_forced_escape(TermInput *input) static void set_bg_deferred(void **argv) { char *bgvalue = argv[0]; - if (starting) { - // Wait until after startup, so OptionSet is triggered. - loop_schedule(&main_loop, event_create(set_bg_deferred, 1, bgvalue)); - return; - } if (!option_was_set("bg") && !strequal((char *)p_bg, bgvalue)) { // Value differs, apply it. - set_option_value("bg", 0L, bgvalue, 0); - reset_option_was_set("bg"); + if (starting) { + // Wait until after startup, so OptionSet is triggered. + do_cmdline_cmd((bgvalue[0] == 'l') + ? "autocmd VimEnter * ++once ++nested set bg=light" + : "autocmd VimEnter * ++once ++nested set bg=dark"); + } else { + set_option_value("bg", 0L, bgvalue, 0); + reset_option_was_set("bg"); + } } } @@ -424,7 +427,8 @@ static bool handle_background_color(TermInput *input) double luminance = (0.299 * r) + (0.587 * g) + (0.114 * b); // CCIR 601 char *bgvalue = luminance < 0.5 ? "dark" : "light"; DLOG("bg response: %s", bgvalue); - loop_schedule(&main_loop, event_create(set_bg_deferred, 1, bgvalue)); + loop_schedule_deferred(&main_loop, + event_create(set_bg_deferred, 1, bgvalue)); } else { DLOG("failed to parse bg response"); } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index e20cf15a79..3e7a3b1ba1 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -2000,7 +2000,7 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value, } else if (strequal(name, "key_dc")) { DLOG("libtermkey:kdch1=%s", value); // Vim: "If <BS> and <DEL> are now the same, redefine <DEL>." - if (value != NULL && strequal(stty_erase, value)) { + if (value != NULL && value != (char *)-1 && strequal(stty_erase, value)) { return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR; } } else if (strequal(name, "key_mouse")) { diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 9ad3f851ad..50432dd119 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -158,6 +158,7 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width, 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; } diff --git a/src/nvim/window.c b/src/nvim/window.c index 18fad76a95..edb5b06a2e 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -541,9 +541,7 @@ static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, win_T *win_new_float(win_T *wp, int width, int height, FloatConfig config, Error *err) { - bool new = false; if (wp == NULL) { - new = true; wp = win_alloc(lastwin_nofloating(), false); win_init(wp, curwin, 0); } else { @@ -569,12 +567,13 @@ win_T *win_new_float(win_T *wp, int width, int height, FloatConfig config, wp->w_floating = 1; wp->w_status_height = 0; wp->w_vsep_width = 0; + + // TODO(bfredl): use set_option_to() after merging #9110 ? + wp->w_p_nu = false; + wp->w_allbuf_opt.wo_nu = false; win_config_float(wp, width, height, config); wp->w_pos_changed = true; redraw_win_later(wp, VALID); - if (new) { - win_enter(wp, false); - } return wp; } @@ -591,6 +590,7 @@ void win_config_float(win_T *wp, int width, int height, config.window = curwin->handle; } + bool change_external = config.external != wp->w_float_config.external; wp->w_float_config = config; if (!ui_has(kUIMultigrid)) { @@ -601,6 +601,10 @@ void win_config_float(win_T *wp, int width, int height, win_set_inner_size(wp); must_redraw = MAX(must_redraw, VALID); wp->w_pos_changed = true; + if (change_external) { + wp->w_hl_needs_update = true; + redraw_win_later(wp, NOT_VALID); + } } static void ui_ext_win_position(win_T *wp) @@ -806,12 +810,12 @@ bool parse_float_config(Dictionary config, FloatConfig *out, bool reconf, api_set_error(err, kErrorTypeValidation, "Only one of 'relative' and 'external' should be used"); return false; - } else if (has_relative) { - out->external = false; } else if (!reconf && !has_relative && !has_external) { api_set_error(err, kErrorTypeValidation, "One of 'relative' and 'external' must be used"); return false; + } else if (has_relative) { + out->external = false; } if (out->external && !ui_has(kUIMultigrid)) { @@ -5332,7 +5336,10 @@ void win_drag_vsep_line(win_T *dragwin, int offset) void set_fraction(win_T *wp) { if (wp->w_height_inner > 1) { - wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height_inner / 2) + // When cursor is in the first line the percentage is computed as if + // it's halfway that line. Thus with two lines it is 25%, with three + // lines 17%, etc. Similarly for the last line: 75%, 83%, etc. + wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + FRACTION_MULT / 2) / (long)wp->w_height_inner; } } @@ -5364,8 +5371,8 @@ void scroll_to_fraction(win_T *wp, int prev_height) int sline, line_size; int height = wp->w_height_inner; - /* Don't change w_topline when height is zero. Don't set w_topline when - * 'scrollbind' is set and this isn't the current window. */ + // Don't change w_topline when height is zero. Don't set w_topline when + // 'scrollbind' is set and this isn't the current window. if (height > 0 && (!wp->w_p_scb || wp == curwin) ) { @@ -5376,8 +5383,7 @@ void scroll_to_fraction(win_T *wp, int prev_height) lnum = wp->w_cursor.lnum; if (lnum < 1) /* can happen when starting up */ lnum = 1; - wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L + FRACTION_MULT / 2) - / FRACTION_MULT; + wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT; line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1; sline = wp->w_wrow - line_size; @@ -5408,7 +5414,6 @@ void scroll_to_fraction(win_T *wp, int prev_height) wp->w_wrow--; } } - set_topline(wp, lnum); } else if (sline > 0) { while (sline > 0 && lnum > 1) { (void)hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); @@ -5437,12 +5442,12 @@ void scroll_to_fraction(win_T *wp, int prev_height) lnum++; wp->w_wrow -= line_size + sline; } else if (sline > 0) { - /* First line of file reached, use that as topline. */ + // First line of file reached, use that as topline. lnum = 1; wp->w_wrow -= sline; } - set_topline(wp, lnum); } + set_topline(wp, lnum); } if (wp == curwin) { @@ -5546,10 +5551,11 @@ void command_height(void) * p_ch was changed in another tab page. */ curtab->tp_ch_used = p_ch; - /* Find bottom frame with width of screen. */ - frp = lastwin->w_frame; - while (frp->fr_width != Columns && frp->fr_parent != NULL) + // Find bottom frame with width of screen. + frp = lastwin_nofloating()->w_frame; + while (frp->fr_width != Columns && frp->fr_parent != NULL) { frp = frp->fr_parent; + } /* Avoid changing the height of a window with 'winfixheight' set. */ while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF @@ -5708,9 +5714,9 @@ file_name_in_line ( len = 0; while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ') || ((options & FNAME_HYP) && path_is_url((char *)ptr + len)) - || (is_url && vim_strchr((char_u *)"?&=", ptr[len]) != NULL)) { - // After type:// we also include ?, & and = as valid characters, so that - // http://google.com?q=this&that=ok works. + || (is_url && vim_strchr((char_u *)":?&=", ptr[len]) != NULL)) { + // After type:// we also include :, ?, & and = as valid characters, so that + // http://google.com:8080?q=this&that=ok works. if ((ptr[len] >= 'A' && ptr[len] <= 'Z') || (ptr[len] >= 'a' && ptr[len] <= 'z')) { if (in_type && path_is_url((char *)ptr + len + 1)) { diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index d9412f0f13..93599c04f1 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -1,7 +1,9 @@ local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local clear, nvim, buffer = helpers.clear, helpers.nvim, helpers.buffer local curbuf, curwin, eq = helpers.curbuf, helpers.curwin, helpers.eq local curbufmeths, ok = helpers.curbufmeths, helpers.ok +local meths = helpers.meths local funcs = helpers.funcs local request = helpers.request local exc_exec = helpers.exc_exec @@ -11,6 +13,7 @@ local NIL = helpers.NIL local meth_pcall = helpers.meth_pcall local command = helpers.command local bufmeths = helpers.bufmeths +local feed = helpers.feed describe('api/buf', function() before_each(clear) @@ -299,6 +302,38 @@ describe('api/buf', function() local retval = exc_exec("call nvim_buf_set_lines(1, 0, 1, v:false, ['test'])") eq(0, retval) end) + + it("set_lines of invisible buffer doesn't move cursor in current window", function() + local screen = Screen.new(20, 5) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {bold = true}, + }) + screen:attach() + + insert([[ + Who would win? + A real window + with proper text]]) + local buf = meths.create_buf(false,true) + screen:expect([[ + Who would win? | + A real window | + with proper tex^t | + {1:~ }| + | + ]]) + + meths.buf_set_lines(buf, 0, -1, true, {'or some', 'scratchy text'}) + feed('i') -- provoke redraw + screen:expect([[ + Who would win? | + A real window | + with proper tex^t | + {1:~ }| + {2:-- INSERT --} | + ]]) + end) end) describe('get_offset', function() diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index b10076c6da..75b9fb71c9 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1359,6 +1359,9 @@ describe('API', function() eq({id=1}, meths.get_current_buf()) local screen = Screen.new(20, 4) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + }) screen:attach() -- @@ -1373,7 +1376,7 @@ describe('API', function() end -- - -- Visiting a scratch-buffer DOES change its properties. + -- Visiting a scratch-buffer DOES NOT change its properties. -- meths.set_current_buf(edited_buf) screen:expect([[ @@ -1381,12 +1384,19 @@ describe('API', function() {1:~ }| {1:~ }| | - ]], { - [1] = {bold = true, foreground = Screen.colors.Blue1}, - }) - eq('', meths.buf_get_option(edited_buf, 'buftype')) - eq('', meths.buf_get_option(edited_buf, 'bufhidden')) + ]]) + eq('nofile', meths.buf_get_option(edited_buf, 'buftype')) + eq('hide', meths.buf_get_option(edited_buf, 'bufhidden')) eq(false, meths.buf_get_option(edited_buf, 'swapfile')) + + -- scratch buffer can be wiped without error + command('bwipe') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + | + ]]) end) end) end) diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua index 8ee9462a8d..814624d8e2 100644 --- a/test/functional/autocmd/autocmd_spec.lua +++ b/test/functional/autocmd/autocmd_spec.lua @@ -1,15 +1,18 @@ local helpers = require('test.functional.helpers')(after_each) +local dedent = helpers.dedent local eq = helpers.eq local eval = helpers.eval +local feed = helpers.feed local clear = helpers.clear local meths = helpers.meths +local funcs = helpers.funcs local expect = helpers.expect local command = helpers.command local exc_exec = helpers.exc_exec local curbufmeths = helpers.curbufmeths -describe('autocmds:', function() +describe('autocmd', function() before_each(clear) it(':tabnew triggers events in the correct order', function() @@ -55,4 +58,90 @@ describe('autocmds:', function() end of test file xx]]) end) end) + + it('++once', function() -- :help autocmd-once + -- + -- ":autocmd ... ++once" executes its handler once, then removes the handler. + -- + local expected = { + 'Many1', + 'Once1', + 'Once2', + 'Many2', + 'Once3', + 'Many1', + 'Many2', + 'Many1', + 'Many2', + } + command('let g:foo = []') + command('autocmd TabNew * :call add(g:foo, "Many1")') + command('autocmd TabNew * ++once :call add(g:foo, "Once1")') + command('autocmd TabNew * ++once :call add(g:foo, "Once2")') + command('autocmd TabNew * :call add(g:foo, "Many2")') + command('autocmd TabNew * ++once :call add(g:foo, "Once3")') + eq(dedent([[ + + --- Autocommands --- + TabNew + * :call add(g:foo, "Many1") + :call add(g:foo, "Once1") + :call add(g:foo, "Once2") + :call add(g:foo, "Many2") + :call add(g:foo, "Once3")]]), + funcs.execute('autocmd Tabnew')) + command('tabnew') + command('tabnew') + command('tabnew') + eq(expected, eval('g:foo')) + eq(dedent([[ + + --- Autocommands --- + TabNew + * :call add(g:foo, "Many1") + :call add(g:foo, "Many2")]]), + funcs.execute('autocmd Tabnew')) + + -- + -- ":autocmd ... ++once" handlers can be deleted. + -- + expected = {} + command('let g:foo = []') + command('autocmd TabNew * ++once :call add(g:foo, "Once1")') + command('autocmd! TabNew') + command('tabnew') + eq(expected, eval('g:foo')) + + -- + -- ":autocmd ... <buffer> ++once ++nested" + -- + expected = { + 'OptionSet-Once', + 'CursorMoved-Once', + } + command('let g:foo = []') + command('autocmd OptionSet binary ++nested ++once :call add(g:foo, "OptionSet-Once")') + command('autocmd CursorMoved <buffer> ++once ++nested setlocal binary|:call add(g:foo, "CursorMoved-Once")') + command("put ='foo bar baz'") + feed('0llhlh') + eq(expected, eval('g:foo')) + + -- + -- :autocmd should not show empty section after ++once handlers expire. + -- + expected = { + 'Once1', + 'Once2', + } + command('let g:foo = []') + command('autocmd! TabNew') -- Clear all TabNew handlers. + command('autocmd TabNew * ++once :call add(g:foo, "Once1")') + command('autocmd TabNew * ++once :call add(g:foo, "Once2")') + command('tabnew') + eq(expected, eval('g:foo')) + eq(dedent([[ + + --- Autocommands ---]]), + funcs.execute('autocmd Tabnew')) + end) end) diff --git a/test/functional/autocmd/textyankpost_spec.lua b/test/functional/autocmd/textyankpost_spec.lua index 486a3346b1..5849679dd2 100644 --- a/test/functional/autocmd/textyankpost_spec.lua +++ b/test/functional/autocmd/textyankpost_spec.lua @@ -23,6 +23,7 @@ describe('TextYankPost', function() it('is executed after yank and handles register types', function() feed('yy') eq({ + inclusive = false, operator = 'y', regcontents = { 'foo\nbar' }, regname = '', @@ -35,6 +36,7 @@ describe('TextYankPost', function() feed('+yw') eq({ + inclusive = false, operator = 'y', regcontents = { 'baz ' }, regname = '', @@ -44,6 +46,7 @@ describe('TextYankPost', function() feed('<c-v>eky') eq({ + inclusive = true, operator = 'y', regcontents = { 'foo', 'baz' }, regname = '', @@ -55,6 +58,7 @@ describe('TextYankPost', function() it('makes v:event immutable', function() feed('yy') eq({ + inclusive = false, operator = 'y', regcontents = { 'foo\nbar' }, regname = '', @@ -84,6 +88,7 @@ describe('TextYankPost', function() command('autocmd TextYankPost * normal "+yy') feed('yy') eq({ + inclusive = false, operator = 'y', regcontents = { 'foo\nbar' }, regname = '', @@ -96,6 +101,7 @@ describe('TextYankPost', function() it('is executed after delete and change', function() feed('dw') eq({ + inclusive = false, operator = 'd', regcontents = { 'foo' }, regname = '', @@ -105,6 +111,7 @@ describe('TextYankPost', function() feed('dd') eq({ + inclusive = false, operator = 'd', regcontents = { '\nbar' }, regname = '', @@ -114,6 +121,7 @@ describe('TextYankPost', function() feed('cwspam<esc>') eq({ + inclusive = true, operator = 'c', regcontents = { 'baz' }, regname = '', @@ -141,6 +149,7 @@ describe('TextYankPost', function() it('gives the correct register name', function() feed('$"byiw') eq({ + inclusive = true, operator = 'y', regcontents = { 'bar' }, regname = 'b', @@ -149,6 +158,7 @@ describe('TextYankPost', function() feed('"*yy') eq({ + inclusive = true, operator = 'y', regcontents = { 'foo\nbar' }, regname = '*', @@ -160,6 +170,7 @@ describe('TextYankPost', function() -- regname still shows the name the user requested feed('yy') eq({ + inclusive = true, operator = 'y', regcontents = { 'foo\nbar' }, regname = '', @@ -168,6 +179,7 @@ describe('TextYankPost', function() feed('"*yy') eq({ + inclusive = true, operator = 'y', regcontents = { 'foo\nbar' }, regname = '*', @@ -178,6 +190,7 @@ describe('TextYankPost', function() it('works with Ex commands', function() command('1delete +') eq({ + inclusive = false, operator = 'd', regcontents = { 'foo\nbar' }, regname = '+', @@ -187,6 +200,7 @@ describe('TextYankPost', function() command('yank') eq({ + inclusive = false, operator = 'y', regcontents = { 'baz text' }, regname = '', @@ -196,6 +210,7 @@ describe('TextYankPost', function() command('normal yw') eq({ + inclusive = false, operator = 'y', regcontents = { 'baz ' }, regname = '', @@ -205,6 +220,7 @@ describe('TextYankPost', function() command('normal! dd') eq({ + inclusive = false, operator = 'd', regcontents = { 'baz text' }, regname = '', diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index 3d550588e7..48e7e05e4c 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -1,12 +1,13 @@ local helpers = require('test.functional.helpers')(after_each) local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source +local insert = helpers.insert local eq, next_msg = helpers.eq, helpers.next_msg local exc_exec = helpers.exc_exec local command = helpers.command local eval = helpers.eval -describe('dictionary change notifications', function() +describe('VimL dictionary notifications', function() local channel before_each(function() @@ -338,4 +339,22 @@ describe('dictionary change notifications', function() eq({'notification', '2', {'foo', {old = 'baz', new = 'bar'}}}, next_msg()) end) end) + + it('for b:changedtick', function() + source([[ + function! OnTickChanged(dict, key, value) + call rpcnotify(g:channel, 'SendChangeTick', a:key, a:value) + endfunction + call dictwatcheradd(b:, 'changedtick', 'OnTickChanged') + ]]) + + insert('t'); + eq({'notification', 'SendChangeTick', {'changedtick', {old = 2, new = 3}}}, + next_msg()) + + command([[call dictwatcherdel(b:, 'changedtick', 'OnTickChanged')]]) + insert('t'); + eq(2, eval('1+1')) -- Still alive? + end) + end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index a0adb45630..9d0eb5e40e 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -839,8 +839,7 @@ describe('TUI background color', function() it("triggers OptionSet event on terminal-response", function() feed_data('\027:autocmd OptionSet background echo "did OptionSet, yay!"\n') - -- The child Nvim is running asynchronously; wait for it to register the - -- OptionSet handler. + -- Wait for the child Nvim to register the OptionSet handler. feed_data('\027:autocmd OptionSet\n') screen:expect({any='--- Autocommands ---'}) @@ -860,16 +859,23 @@ describe('TUI background color', function() local function assert_bg(color, bg) it('handles '..color..' as '..bg, function() - feed_data('\027]11;rgb:'..color..'\007:echo &background\n') - screen:expect(string.format([[ - {1: } | - {4:~ }| - {4:~ }| - {4:~ }| - {5:[No Name] 0,0-1 All}| - %-5s | - {3:-- TERMINAL --} | - ]], bg)) + feed_data('\027]11;rgb:'..color..'\007') + -- Retry until the terminal response is handled. + retry(100, nil, function() + feed_data(':echo &background\n') + screen:expect({ + timeout=40, + grid=string.format([[ + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] 0,0-1 All}| + %-5s | + {3:-- TERMINAL --} | + ]], bg) + }) + end) end) end diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua index f3d0b45d09..c0ce656bb1 100644 --- a/test/functional/terminal/window_split_tab_spec.lua +++ b/test/functional/terminal/window_split_tab_spec.lua @@ -39,10 +39,11 @@ describe(':terminal', function() it('does not change size on WinEnter', function() if helpers.pending_win32(pending) then return end feed('<c-\\><c-n>') + feed('k') feed_command('2split') screen:expect([[ - tty ready | - ^rows: 5, cols: 50 | + ^tty ready | + rows: 5, cols: 50 | ========== | tty ready | rows: 5, cols: 50 | @@ -57,8 +58,8 @@ describe(':terminal', function() tty ready | rows: 5, cols: 50 | ========== | - tty ready | - ^rows: 5, cols: 50 | + ^tty ready | + rows: 5, cols: 50 | {2: } | | | diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 3e0370db14..4dc86f1e1f 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -216,10 +216,10 @@ describe('ui/cursor', function() if m.blinkwait then m.blinkwait = 700 end end if m.hl_id then - m.hl_id = 49 + m.hl_id = 50 m.attr = {background = Screen.colors.DarkGray} end - if m.id_lm then m.id_lm = 50 end + if m.id_lm then m.id_lm = 51 end end -- Assert the new expectation. diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 784c2d98f5..bb3255840e 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -29,7 +29,10 @@ describe('floating windows', function() [10] = {background = Screen.colors.LightGrey, underline = true, bold = true, foreground = Screen.colors.Magenta}, [11] = {bold = true, foreground = Screen.colors.Magenta}, [12] = {background = Screen.colors.Red, bold = true, foreground = Screen.colors.Blue1}, - [13] = {background = Screen.colors.WebGray} + [13] = {background = Screen.colors.WebGray}, + [14] = {foreground = Screen.colors.Brown}, + [15] = {background = Screen.colors.Grey20}, + [16] = {background = Screen.colors.Grey20, bold = true, foreground = Screen.colors.Blue1}, } local function with_ext_multigrid(multigrid) @@ -43,12 +46,10 @@ describe('floating windows', function() it('can be created and reconfigured', function() local buf = meths.create_buf(false,false) local win = meths.open_win(buf, false, 20, 2, {relative='editor', row=2, col=5}) - meths.win_set_option(win , 'winhl', 'Normal:PMenu') local expected_pos = { [3]={{id=1001}, 'NW', 1, 2, 5, true}, } - if multigrid then screen:expect{grid=[[ ## grid 1 @@ -151,6 +152,83 @@ describe('floating windows', function() end end) + it('defaults to nonumber and NormalFloat highlight', function() + command('set number') + command('hi NormalFloat guibg=#333333') + feed('ix<cr>y<cr><esc>gg') + local win = meths.open_win(0, false, 20, 4, {relative='editor', row=4, col=10}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + {14: 1 }^x | + {14: 2 }y | + {14: 3 } | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {15:x }| + {15:y }| + {15: }| + {16:~ }| + ]], float_pos={[3] = {{id = 1001}, "NW", 1, 4, 10, true}}} + else + screen:expect([[ + {14: 1 }^x | + {14: 2 }y | + {14: 3 } {15:x } | + {0:~ }{15:y }{0: }| + {0:~ }{15: }{0: }| + {0:~ }{16:~ }{0: }| + | + ]]) + end + + local buf = meths.create_buf(false, true) + meths.win_set_buf(win, buf) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {4:[No Name] [+] }| + | + ## grid 2 + {14: 1 }^x | + {14: 2 }y | + {14: 3 } | + {0:~ }| + {0:~ }| + ## grid 3 + {15: }| + {16:~ }| + {16:~ }| + {16:~ }| + ]], float_pos={[3] = {{id = 1001}, "NW", 1, 4, 10, true}}} + else + screen:expect([[ + {14: 1 }^x | + {14: 2 }y | + {14: 3 } {15: } | + {0:~ }{16:~ }{0: }| + {0:~ }{16:~ }{0: }| + {4:[No Name] }{16:~ }{4: }| + | + ]]) + end + end) + it('API has proper error messages', function() local buf = meths.create_buf(false,false) eq({false, "Invalid options key 'bork'"}, @@ -211,7 +289,6 @@ describe('floating windows', function() local buf = meths.create_buf(false,false) -- no 'win' arg, relative default window local win = meths.open_win(buf, false, 20, 2, {relative='win', row=0, col=10}) - meths.win_set_option(win, 'winhl', 'Normal:PMenu') if multigrid then screen:expect{grid=[[ ## grid 1 @@ -467,8 +544,7 @@ describe('floating windows', function() screen2:attach(nil, session2) screen2:set_default_attr_ids(attrs) local buf = meths.create_buf(false,false) - local win = meths.open_win(buf, true, 20, 2, {relative='editor', row=2, col=5}) - meths.win_set_option(win, 'winhl', 'Normal:PMenu') + meths.open_win(buf, true, 20, 2, {relative='editor', row=2, col=5}) local expected_pos = { [2]={{id=1001}, 'NW', 1, 2, 5} } @@ -502,9 +578,8 @@ describe('floating windows', function() local buf = meths.create_buf(false,false) meths.buf_set_lines(buf, 0, -1, true, {'such', 'very', 'float'}) local win = meths.open_win(buf, false, 15, 4, {relative='editor', row=2, col=10}) - meths.win_set_option(win , 'winhl', 'Normal:PMenu') local expected_pos = { - [3]={{id=1001}, 'NW', 1, 2, 10, true}, + [4]={{id=1002}, 'NW', 1, 2, 10, true}, } if multigrid then screen:expect{grid=[[ @@ -523,7 +598,7 @@ describe('floating windows', function() {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 4 {1:such }| {1:very }| {1:float }| @@ -555,7 +630,7 @@ describe('floating windows', function() {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 4 {1:such }| {1:very }| {1:float }| @@ -583,7 +658,7 @@ describe('floating windows', function() ^ | {0:~ }| {0:~ }| - ## grid 3 + ## grid 4 {1:such }| {1:very }| {1:float }| @@ -608,7 +683,7 @@ describe('floating windows', function() ## grid 2 ^ | {0:~ }| - ## grid 3 + ## grid 4 {1:such }| {1:very }| {1:float }| @@ -631,7 +706,7 @@ describe('floating windows', function() ## grid 2 | {0:~ }| - ## grid 3 + ## grid 4 {1:such }| {1:very }| {1:^float }| @@ -663,7 +738,7 @@ describe('floating windows', function() {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 4 {1:such }| {1:very }| {1:^float }| @@ -700,7 +775,7 @@ describe('floating windows', function() {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 4 {1:^such }| {1:very }| {1:float }| @@ -735,7 +810,7 @@ describe('floating windows', function() {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 4 {1:^such }| {1:very }| {1:float }| @@ -770,7 +845,7 @@ describe('floating windows', function() {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 4 {1:^such }| {1:very }| {1:float }| @@ -805,7 +880,7 @@ describe('floating windows', function() {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 4 {1:^such }| {1:very }| {1:float }| @@ -840,7 +915,7 @@ describe('floating windows', function() {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 4 {1:^such }| {1:very }| {1:float }| @@ -875,7 +950,7 @@ describe('floating windows', function() {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 4 {1:^such }| {1:very }| {1:float }| @@ -910,7 +985,7 @@ describe('floating windows', function() {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 4 {1:^such }| {1:very }| {1:float }| @@ -945,7 +1020,7 @@ describe('floating windows', function() {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 4 {1:^such }| {1:very }| {1:float }| @@ -971,7 +1046,7 @@ describe('floating windows', function() | ## grid 2 | - ## grid 3 + ## grid 4 {1:^such }| {1:very }| {1:float }| @@ -1001,7 +1076,7 @@ describe('floating windows', function() {0:~ }| {0:~ }| {0:~ }| - ## grid 3 + ## grid 4 {1:^such }| {1:very }| {1:float }| @@ -1019,6 +1094,13 @@ describe('floating windows', function() end end) + it('does not crash when set cmdheight #9680', function() + local buf = meths.create_buf(false,false) + meths.open_win(buf, false, 20, 2, {relative='editor', row=2, col=5}) + command("set cmdheight=2") + eq(1, meths.eval('1')) + end) + describe('and completion', function() before_each(function() local buf = meths.create_buf(false,false) @@ -1395,9 +1477,233 @@ describe('floating windows', function() ]]) end end) - end) + describe('float shown after pum', function() + local win + before_each(function() + command('hi NormalFloat guibg=#333333') + feed('i') + funcs.complete(1, {'aa', 'word', 'longtext'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + aa^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {13:aa }| + {1:word }| + {1:longtext }| + ]], float_pos={ + [3] = {{id = -1}, "NW", 2, 1, 0, false}} + } + else + screen:expect([[ + aa^ | + {13:aa }{0: }| + {1:word }{0: }| + {1:longtext }{0: }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end + + local buf = meths.create_buf(false,true) + meths.buf_set_lines(buf,0,-1,true,{"some info", "about item"}) + win = meths.open_win(buf, false, 12, 2, {relative='cursor', row=1, col=10}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + aa^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {13:aa }| + {1:word }| + {1:longtext }| + ## grid 5 + {15:some info }| + {15:about item }| + ]], float_pos={ + [3] = {{id = -1}, "NW", 2, 1, 0, false}, + [5] = {{id = 1002}, "NW", 2, 1, 12, true}, + }} + else + screen:expect([[ + aa^ | + {13:aa }{15:e info }{0: }| + {1:word }{15:ut item }{0: }| + {1:longtext }{0: }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end + end) + + it('and close pum first', function() + feed('<c-y>') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + aa^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 5 + {15:some info }| + {15:about item }| + ]], float_pos={ + [5] = {{id = 1002}, "NW", 2, 1, 12, true}, + }} + else + screen:expect([[ + aa^ | + {0:~ }{15:some info }{0: }| + {0:~ }{15:about item }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end + + meths.win_close(win, false) + if multigrid then + screen:expect([[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + aa^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ]]) + else + screen:expect([[ + aa^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end + end) + + it('and close float first', function() + meths.win_close(win, false) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + aa^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {13:aa }| + {1:word }| + {1:longtext }| + ]], float_pos={ + [3] = {{id = -1}, "NW", 2, 1, 0, false}, + }} + else + screen:expect([[ + aa^ | + {13:aa }{0: }| + {1:word }{0: }| + {1:longtext }{0: }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end + + feed('<c-y>') + if multigrid then + screen:expect([[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + aa^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ]]) + else + screen:expect([[ + aa^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end + end) + end) describe("handles :wincmd", function() local win @@ -1410,7 +1716,6 @@ describe('floating windows', function() local buf = meths.create_buf(false,false) win = meths.open_win(buf, false, 20, 2, {relative='editor', row=2, col=5}) meths.buf_set_lines(buf,0,-1,true,{"y"}) - meths.win_set_option(win , 'winhl', 'Normal:PMenu') expected_pos = { [3]={{id=1001}, 'NW', 1, 2, 5, true} } @@ -2067,39 +2372,6 @@ describe('floating windows', function() {1:y }| {2:~ }| ## grid 4 - {1:^y }| - {2:~ }| - ]], float_pos=expected_pos} - else - screen:expect([[ - {1:^y }| - {2:~ }| - {4:[No N}{1:y }{4: }| - x {2:~ } | - {0:~ }| - {5:[No Name] [+] }| - | - ]]) - end - - feed(":set winhighlight=<cr><c-l>") - if multigrid then - screen:expect{grid=[[ - ## grid 1 - [4:----------------------------------------]| - [4:----------------------------------------]| - {4:[No Name] [+] }| - [2:----------------------------------------]| - [2:----------------------------------------]| - {5:[No Name] [+] }| - | - ## grid 2 - x | - {0:~ }| - ## grid 3 - {1:y }| - {2:~ }| - ## grid 4 ^y | {0:~ }| ]], float_pos=expected_pos} @@ -2115,7 +2387,6 @@ describe('floating windows', function() ]]) end - feed("<c-w>j") if multigrid then screen:expect{grid=[[ @@ -2652,16 +2923,16 @@ describe('floating windows', function() x | {0:~ }| ## grid 3 - {1:^y }| - {2:~ }| + ^y | + {0:~ }| ]]} else screen:expect([[ x | {0:~ }| {5:[No Name] [+] }| - {1:^y }| - {2:~ }| + ^y | + {0:~ }| {4:[No Name] [+] }| | ]]) @@ -2686,8 +2957,8 @@ describe('floating windows', function() {0:~ }| {0:~ }| ## grid 3 - {1:^y }| - {2:~ }| + ^y | + {0:~ }| ]], float_pos=expected_pos} else eq({false, "UI doesn't support external windows"}, @@ -2710,11 +2981,10 @@ describe('floating windows', function() x | {0:~ }| ## grid 3 - {1:^y }| - {2:~ }| + ^y | + {0:~ }| ]]) end - end) it('movements with nested split layout', function() @@ -2779,8 +3049,8 @@ describe('floating windows', function() 4 | {0:~ }| ## grid 3 - ^5 | - {0:~ }| + {1:^5 }| + {2:~ }| ## grid 4 2 | {0:~ }| @@ -2795,8 +3065,8 @@ describe('floating windows', function() screen:expect([[ 1 {5:│}2 | {0:~ }{5:│}{0:~ }| - {5:[No N}^5 {5:ame] [+] }| - 3 {0:~ } | + {5:[No N}{1:^5 }{5:ame] [+] }| + 3 {2:~ } | {0:~ }{5:│}{0:~ }| {5:[No Name] [+] [No Name] [+] }| :enew | @@ -2978,8 +3248,8 @@ describe('floating windows', function() {0:~ }| {0:~ }| ## grid 3 - {1:y }| - {2:~ }| + y | + {0:~ }| ## grid 4 ^ | {0:~ }| @@ -3009,8 +3279,8 @@ describe('floating windows', function() {0:~ }| {0:~ }| ## grid 3 - {1:y }| - {2:~ }| + y | + {0:~ }| ## grid 4 | {0:~ }| @@ -3037,8 +3307,8 @@ describe('floating windows', function() {0:~ }| {0:~ }| ## grid 3 - {1:y }| - {2:~ }| + y | + {0:~ }| ## grid 4 ^ | {0:~ }| diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 39170337d7..3ee3f173d6 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -1274,17 +1274,18 @@ describe("'winhighlight' highlight", function() command('set number') command('set colorcolumn=2') command('set cursorcolumn') + feed('k') command('split') command('set winhl=LineNr:Background1,CursorColumn:Background2,' ..'ColorColumn:ErrorMsg') screen:expect([[ - {1: 1 }v{15:e}ry tex{5:t} | - {1: 2 }m{15:o}re tex^t | + {1: 1 }v{15:e}ry tex^t | + {1: 2 }m{15:o}re tex{5:t} | {0:~ }| {3:[No Name] [+] }| - {9: 1 }v{17:e}ry tex{18:t} | - {9: 2 }m{17:o}re text | + {9: 1 }v{17:e}ry text | + {9: 2 }m{17:o}re tex{18:t} | {4:[No Name] [+] }| | ]]) diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 536264019c..c215ece2f2 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -938,11 +938,11 @@ describe(":substitute, inccommand=split", function() feed(":%s/tw") -- 'cursorline' is NOT active during preview. screen:expect([[ - Inc substitution on | {12:tw}o lines | Inc substitution on | {12:tw}o lines | | + {15:~ }| {11:[No Name] [+] }| |2| {12:tw}o lines | |4| {12:tw}o lines | @@ -1185,6 +1185,7 @@ describe(":substitute, inccommand=split", function() end) it("clears preview if non-previewable command is edited #5585", function() + feed('gg') -- Put a non-previewable command in history. feed_command("echo 'foo'") -- Start an incomplete :substitute command. @@ -2084,11 +2085,11 @@ describe(":substitute", function() feed(":%s/[0-9]\\n\\zs[A-Z]/OKO") screen:expect([[ - 1 2 3 | {12:OKO} B C | 4 5 6 | {12:OKO} Y Z | 7 8 9 | + | {11:[No Name] [+] }| |1| 1 2 3 | |2| {12:OKO} B C | @@ -2204,11 +2205,11 @@ describe(":substitute", function() feed("/KKK") screen:expect([[ - x | afa {12:KKK}adf la;lkd {12:KKK}alx | | {15:~ }| {15:~ }| + {15:~ }| {11:[No Name] [+] }| |3| afa {12:KKK}adf la;lkd {12:KKK}alx | {15:~ }| @@ -2256,11 +2257,11 @@ describe(":substitute", function() common_setup(screen, "split", multibyte_text) feed(":%s/£.*Ñ«/X¥¥") screen:expect([[ - {12:X¥¥} | a{12:X¥¥}Â¥KOL | £ Â¥ libm | £ Â¥ | | + {15:~ }| {11:[No Name] [+] }| |1| {12:X¥¥} PEPPERS | |2| {12:X¥¥} | @@ -2275,11 +2276,11 @@ describe(":substitute", function() feed("\\ra££ Â¥") screen:expect([[ - {12:a££ Â¥} | a{12:X¥¥} | {12:a££ Â¥}Â¥KOL | £ Â¥ libm | £ Â¥ | + | {11:[No Name] [+] }| |1| {12:X¥¥} | |2|{12: a££ Â¥} PEPPERS | @@ -2378,7 +2379,6 @@ describe(":substitute", function() feed("\\rÑ« ab \\rXXXX") screen:expect([[ - 7 8 9 | K L M | {12:JLKR £} | {12:Ñ« ab } | @@ -2387,6 +2387,7 @@ describe(":substitute", function() {12:Ñ« ab } | {12:XXXX} e f | {12:JLKR £} | + {12:Ñ« ab } | {11:[No Name] [+] }| | 7| {12:JLKR £} | | 8|{12: Ñ« ab } | @@ -2480,14 +2481,15 @@ describe(":substitute", function() ]]) feed("<C-c>") + feed('gg') wait() feed([[:%s/\(some\)\@<lt>!thing/one/]]) screen:expect([[ - something | every{12:one} | someone | {15:~ }| {15:~ }| + {15:~ }| {11:[No Name] [+] }| |2| every{12:one} | {15:~ }| @@ -2525,11 +2527,11 @@ describe(":substitute", function() wait() feed([[:%s/some\(thing\)\@!/every/]]) screen:expect([[ - everything | {12:every}one | {15:~ }| {15:~ }| {15:~ }| + {15:~ }| {11:[No Name] [+] }| |3| {12:every}one | {15:~ }| diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 7805ed3cb9..c1a350dc34 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -649,13 +649,14 @@ describe('ui/mouse/input', function() mouse scrolling ]]) screen:try_resize(53, 14) + feed('k') feed_command('sp', 'vsp') screen:expect([[ lines {4:│}lines | to {4:│}to | test {4:│}test | - mouse scrolling {4:│}mouse scrolling | - ^ {4:│} | + ^mouse scrolling {4:│}mouse scrolling | + {4:│} | {0:~ }{4:│}{0:~ }| {5:[No Name] [+] }{4:[No Name] [+] }| to | @@ -672,8 +673,8 @@ describe('ui/mouse/input', function() feed('<ScrollWheelDown><0,0>') end screen:expect([[ - mouse scrolling {4:│}lines | - ^ {4:│}to | + ^mouse scrolling {4:│}lines | + {4:│}to | {0:~ }{4:│}test | {0:~ }{4:│}mouse scrolling | {0:~ }{4:│} | @@ -693,8 +694,8 @@ describe('ui/mouse/input', function() feed('<ScrollWheelUp><27,0>') end screen:expect([[ - mouse scrolling {4:│}text | - ^ {4:│}with | + ^mouse scrolling {4:│}text | + {4:│}with | {0:~ }{4:│}many | {0:~ }{4:│}lines | {0:~ }{4:│}to | @@ -715,8 +716,8 @@ describe('ui/mouse/input', function() feed('<ScrollWheelUp><27,7><ScrollWheelUp>') end screen:expect([[ - mouse scrolling {4:│}text | - ^ {4:│}with | + ^mouse scrolling {4:│}text | + {4:│}with | {0:~ }{4:│}many | {0:~ }{4:│}lines | {0:~ }{4:│}to | diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 87b489fd71..38c4527a5b 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -51,7 +51,8 @@ describe("shell command :!", function() end) it("throttles shell-command output greater than ~10KB", function() - if helpers.skip_fragile(pending) then + if helpers.skip_fragile(pending, + (os.getenv("TRAVIS") and helpers.os_name() == "osx")) then return end child_session.feed_data( diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 9a8c5a5789..1e6ebb87f5 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -1198,20 +1198,20 @@ describe('builtin popupmenu', function() command("split") screen:expect([[ - xx | choice^ | + {1:~ }| {n:word }{1: }| {s:choice }{4: }| {n:text } | - {n:thing } | + {n:thing }{1: }| {3:[No Name] [+] }| {2:-- INSERT --} | ]]) meths.input_mouse('wheel', 'down', '', 0, 6, 15) screen:expect([[ - xx | choice^ | + {1:~ }| {n:word }{1: }| {s:choice }{4: }| {n:text } | diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua index d678784dc9..00e94ef94b 100644 --- a/test/functional/ui/syntax_conceal_spec.lua +++ b/test/functional/ui/syntax_conceal_spec.lua @@ -356,16 +356,17 @@ describe('Screen', function() end) it('between windows', function() + feed('k') command("split") screen:expect([[ foo {1:b} bar {1:b} eggs | - foo {1:b} bar {1:b} eggs | foo barf bar barf egg^s | + foo {1:b} bar {1:b} eggs | | {2:[No Name] [+] }| foo {1:b} bar {1:b} eggs | foo {1:b} bar {1:b} eggs | - | + foo {1:b} bar {1:b} eggs | {3:[No Name] [+] }| | ]]) @@ -379,7 +380,7 @@ describe('Screen', function() {3:[No Name] [+] }| foo {1:b} bar {1:b} eggs | foo barf bar barf egg^s | - | + foo {1:b} bar {1:b} eggs | {2:[No Name] [+] }| | ]]) diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 18c73aa9c1..0b90ea52a4 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -143,8 +143,8 @@ set(LUAROCKS_SHA256 9eb3d0738fd02ad8bf39bcedccac4e83e9b5fff2bcca247c3584b925b207 set(UNIBILIUM_URL https://github.com/mauke/unibilium/archive/v2.0.0.tar.gz) set(UNIBILIUM_SHA256 78997d38d4c8177c60d3d0c1aa8c53fd0806eb21825b7b335b1768d7116bc1c1) -set(LIBTERMKEY_URL http://www.leonerd.org.uk/code/libtermkey/libtermkey-0.20.tar.gz) -set(LIBTERMKEY_SHA256 6c0d87c94ab9915e76ecd313baec08dedf3bd56de83743d9aa923a081935d2f5) +set(LIBTERMKEY_URL http://www.leonerd.org.uk/code/libtermkey/libtermkey-0.21.1.tar.gz) +set(LIBTERMKEY_SHA256 cecbf737f35d18f433c8d7864f63c0f878af41f8bd0255a3ebb16010dc044d5f) set(LIBVTERM_URL https://github.com/neovim/libvterm/archive/b45b648cab73f9667bde7c0c6045b285e22b3ecd.tar.gz) set(LIBVTERM_SHA256 37cc123deff29327efa654358c2ebaaf8589da03754ca5adb8ec47be386a0433) diff --git a/third-party/cmake/BuildLibtermkey.cmake b/third-party/cmake/BuildLibtermkey.cmake index 0b56674ad2..da65c87972 100644 --- a/third-party/cmake/BuildLibtermkey.cmake +++ b/third-party/cmake/BuildLibtermkey.cmake @@ -11,10 +11,6 @@ ExternalProject_Add(libtermkey -DTARGET=libtermkey -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake - PATCH_COMMAND ${GIT_EXECUTABLE} -C ${DEPS_BUILD_DIR}/src/libtermkey init - COMMAND ${GIT_EXECUTABLE} -C ${DEPS_BUILD_DIR}/src/libtermkey apply --ignore-whitespace - ${CMAKE_CURRENT_SOURCE_DIR}/patches/libtermkey-Add-support-for-Windows.patch - ${CMAKE_CURRENT_SOURCE_DIR}/patches/libtermkey-Fix-escape-sequences-for-MSVC.patch CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/cmake/libtermkeyCMakeLists.txt ${DEPS_BUILD_DIR}/src/libtermkey/CMakeLists.txt diff --git a/third-party/patches/libtermkey-Add-support-for-Windows.patch b/third-party/patches/libtermkey-Add-support-for-Windows.patch deleted file mode 100644 index b839e94d05..0000000000 --- a/third-party/patches/libtermkey-Add-support-for-Windows.patch +++ /dev/null @@ -1,170 +0,0 @@ -From fbe91a958816d85fa93665eb8f7a7a8e05eb9650 Mon Sep 17 00:00:00 2001 -From: Rui Abreu Ferreira <raf-ep@gmx.com> -Date: Tue, 5 Apr 2016 00:12:41 +0100 -Subject: [PATCH] Add support for Windows - -Ported termkey for windows. - -- The TERMKEY_FLAG_NOTERMIOS is ignore in Windows, since there is no termios. -- The termkey_waitkey() function is not implemented in windows, since there - is no poll() alternative. -- The CMake recipe only supports unibilium, not curses. ---- - driver-ti.c | 8 +++++++- - termkey-internal.h | 11 ++++++++++- - termkey.c | 22 ++++++++++++++++++---- - 3 files changed, 35 insertions(+), 6 deletions(-) - -diff --git a/driver-ti.c b/driver-ti.c -index e673ab7..f5f8052 100644 ---- a/driver-ti.c -+++ b/driver-ti.c -@@ -17,7 +17,9 @@ - #include <ctype.h> - #include <stdio.h> - #include <string.h> --#include <unistd.h> -+#ifndef _WIN32 -+# include <unistd.h> -+#endif - #include <sys/types.h> - #include <sys/stat.h> - -@@ -338,8 +340,10 @@ static int start_driver(TermKey *tk, void *info) - if(fstat(tk->fd, &statbuf) == -1) - return 0; - -+#ifndef _WIN32 - if(S_ISFIFO(statbuf.st_mode)) - return 1; -+#endif - - // Can't call putp or tputs because they suck and don't give us fd control - len = strlen(start_string); -@@ -367,8 +371,10 @@ static int stop_driver(TermKey *tk, void *info) - if(fstat(tk->fd, &statbuf) == -1) - return 0; - -+#ifndef _WIN32 - if(S_ISFIFO(statbuf.st_mode)) - return 1; -+#endif - - /* The terminfo database will contain keys in application cursor key mode. - * We may need to enable that mode -diff --git a/termkey-internal.h b/termkey-internal.h -index 52829b3..b796729 100644 ---- a/termkey-internal.h -+++ b/termkey-internal.h -@@ -4,7 +4,14 @@ - #include "termkey.h" - - #include <stdint.h> --#include <termios.h> -+#ifndef _WIN32 -+# include <termios.h> -+#endif -+ -+#ifdef _MSC_VER -+#include <BaseTsd.h> -+typedef SSIZE_T ssize_t; -+#endif - - struct TermKeyDriver - { -@@ -41,8 +48,10 @@ struct TermKey { - size_t hightide; /* Position beyond buffstart at which peekkey() should next start - * normally 0, but see also termkey_interpret_csi */ - -+#ifndef _WIN32 - struct termios restore_termios; - char restore_termios_valid; -+#endif - - TermKey_Terminfo_Getstr_Hook *ti_getstr_hook; - void *ti_getstr_hook_data; -diff --git a/termkey.c b/termkey.c -index 2f01f3a..145b99f 100644 ---- a/termkey.c -+++ b/termkey.c -@@ -3,14 +3,20 @@ - - #include <ctype.h> - #include <errno.h> --#include <poll.h> --#include <unistd.h> -+#ifndef _WIN32 -+# include <poll.h> -+# include <unistd.h> -+# include <strings.h> -+#endif - #include <string.h> --#include <strings.h> - - #include <stdio.h> - --#define strcaseeq(a,b) (strcasecmp(a,b) == 0) -+#ifdef _MSC_VER -+# define strcaseeq(a,b) (_stricmp(a,b) == 0) -+#else -+# define strcaseeq(a,b) (strcasecmp(a,b) == 0) -+#endif - - void termkey_check_version(int major, int minor) - { -@@ -282,7 +288,9 @@ static TermKey *termkey_alloc(void) - tk->buffsize = 256; /* bytes */ - tk->hightide = 0; - -+#ifndef _WIN32 - tk->restore_termios_valid = 0; -+#endif - - tk->ti_getstr_hook = NULL; - tk->ti_getstr_hook_data = NULL; -@@ -483,6 +491,7 @@ int termkey_start(TermKey *tk) - if(tk->is_started) - return 1; - -+#ifndef _WIN32 - if(tk->fd != -1 && !(tk->flags & TERMKEY_FLAG_NOTERMIOS)) { - struct termios termios; - if(tcgetattr(tk->fd, &termios) == 0) { -@@ -517,6 +526,7 @@ int termkey_start(TermKey *tk) - tcsetattr(tk->fd, TCSANOW, &termios); - } - } -+#endif - - struct TermKeyDriverNode *p; - for(p = tk->drivers; p; p = p->next) -@@ -542,8 +552,10 @@ int termkey_stop(TermKey *tk) - if(p->driver->stop_driver) - (*p->driver->stop_driver)(tk, p->info); - -+#ifndef _WIN32 - if(tk->restore_termios_valid) - tcsetattr(tk->fd, TCSANOW, &tk->restore_termios); -+#endif - - tk->is_started = 0; - -@@ -1046,6 +1058,7 @@ TermKeyResult termkey_getkey_force(TermKey *tk, TermKeyKey *key) - return ret; - } - -+#ifndef _WIN32 - TermKeyResult termkey_waitkey(TermKey *tk, TermKeyKey *key) - { - if(tk->fd == -1) { -@@ -1105,6 +1118,7 @@ retry: - - /* UNREACHABLE */ - } -+#endif - - TermKeyResult termkey_advisereadable(TermKey *tk) - { --- -2.16.1.windows.4 - diff --git a/third-party/patches/libtermkey-Fix-escape-sequences-for-MSVC.patch b/third-party/patches/libtermkey-Fix-escape-sequences-for-MSVC.patch deleted file mode 100644 index c1099bd3c4..0000000000 --- a/third-party/patches/libtermkey-Fix-escape-sequences-for-MSVC.patch +++ /dev/null @@ -1,189 +0,0 @@ -diff --git a/t/30mouse.c b/t/30mouse.c ---- a/t/30mouse.c -+++ b/t/30mouse.c -@@ -14,7 +14,7 @@ int main(int argc, char *argv[]) - - tk = termkey_new_abstract("vt100", 0); - -- termkey_push_bytes(tk, "\e[M !!", 6); -+ termkey_push_bytes(tk, "\x1b[M !!", 6); - - key.type = -1; - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mouse press"); -@@ -38,7 +38,7 @@ int main(int argc, char *argv[]) - is_int(len, 21, "string length for press"); - is_str(buffer, "MousePress(1) @ (1,1)", "string buffer for press"); - -- termkey_push_bytes(tk, "\e[M@\"!", 6); -+ termkey_push_bytes(tk, "\x1b[M@\"!", 6); - - key.type = -1; - ev = -1; button = -1; line = -1; col = -1; -@@ -51,7 +51,7 @@ int main(int argc, char *argv[]) - is_int(col, 2, "mouse column for drag"); - is_int(key.modifiers, 0, "modifiers for press"); - -- termkey_push_bytes(tk, "\e[M##!", 6); -+ termkey_push_bytes(tk, "\x1b[M##!", 6); - - key.type = -1; - ev = -1; button = -1; line = -1; col = -1; -@@ -63,7 +63,7 @@ int main(int argc, char *argv[]) - is_int(col, 3, "mouse column for release"); - is_int(key.modifiers, 0, "modifiers for press"); - -- termkey_push_bytes(tk, "\e[M0++", 6); -+ termkey_push_bytes(tk, "\x1b[M0++", 6); - - key.type = -1; - ev = -1; button = -1; line = -1; col = -1; -@@ -81,7 +81,7 @@ int main(int argc, char *argv[]) - is_str(buffer, "C-MousePress(1)", "string buffer for Ctrl-press"); - - // rxvt protocol -- termkey_push_bytes(tk, "\e[0;20;20M", 10); -+ termkey_push_bytes(tk, "\x1b[0;20;20M", 10); - - key.type = -1; - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mouse press rxvt protocol"); -@@ -96,7 +96,7 @@ int main(int argc, char *argv[]) - is_int(col, 20, "mouse column for press rxvt protocol"); - is_int(key.modifiers, 0, "modifiers for press rxvt protocol"); - -- termkey_push_bytes(tk, "\e[3;20;20M", 10); -+ termkey_push_bytes(tk, "\x1b[3;20;20M", 10); - - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mouse release rxvt protocol"); - -@@ -111,7 +111,7 @@ int main(int argc, char *argv[]) - is_int(key.modifiers, 0, "modifiers for release rxvt protocol"); - - // SGR protocol -- termkey_push_bytes(tk, "\e[<0;30;30M", 11); -+ termkey_push_bytes(tk, "\x1b[<0;30;30M", 11); - - key.type = -1; - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mouse press SGR encoding"); -@@ -127,7 +127,7 @@ int main(int argc, char *argv[]) - is_int(col, 30, "mouse column for press SGR"); - is_int(key.modifiers, 0, "modifiers for press SGR"); - -- termkey_push_bytes(tk, "\e[<0;30;30m", 11); -+ termkey_push_bytes(tk, "\x1b[<0;30;30m", 11); - - key.type = -1; - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mouse release SGR encoding"); -@@ -139,7 +139,7 @@ int main(int argc, char *argv[]) - - is_int(ev, TERMKEY_MOUSE_RELEASE, "mouse event for release SGR"); - -- termkey_push_bytes(tk, "\e[<0;500;300M", 13); -+ termkey_push_bytes(tk, "\x1b[<0;500;300M", 13); - - key.type = -1; - ev = -1; button = -1; line = -1; col = -1; -diff --git a/t/31position.c b/t/31position.c -index 1748211..86ad1bc 100644 ---- a/t/31position.c -+++ b/t/31position.c -@@ -11,7 +11,7 @@ int main(int argc, char *argv[]) - - tk = termkey_new_abstract("vt100", 0); - -- termkey_push_bytes(tk, "\e[?15;7R", 8); -+ termkey_push_bytes(tk, "\x1b[?15;7R", 8); - - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for position report"); - -@@ -25,7 +25,7 @@ int main(int argc, char *argv[]) - /* A plain CSI R is likely to be <F3> though. - * This is tricky :/ - */ -- termkey_push_bytes(tk, "\e[R", 3); -+ termkey_push_bytes(tk, "\x1b[R", 3); - - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for <F3>"); - -diff --git a/t/32modereport.c b/t/32modereport.c -index 31de400..5e49705 100644 ---- a/t/32modereport.c -+++ b/t/32modereport.c -@@ -11,7 +11,7 @@ int main(int argc, char *argv[]) - - tk = termkey_new_abstract("vt100", 0); - -- termkey_push_bytes(tk, "\e[?1;2$y", 8); -+ termkey_push_bytes(tk, "\x1b[?1;2$y", 8); - - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mode report"); - -@@ -23,7 +23,7 @@ int main(int argc, char *argv[]) - is_int(mode, 1, "mode number from mode report"); - is_int(value, 2, "mode value from mode report"); - -- termkey_push_bytes(tk, "\e[4;1$y", 7); -+ termkey_push_bytes(tk, "\x1b[4;1$y", 7); - - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for mode report"); - -diff --git a/t/38csi.c b/t/38csi.c -index 9d186f6..fd592d5 100644 ---- a/t/38csi.c -+++ b/t/38csi.c -@@ -13,7 +13,7 @@ int main(int argc, char *argv[]) - - tk = termkey_new_abstract("vt100", 0); - -- termkey_push_bytes(tk, "\e[5;25v", 7); -+ termkey_push_bytes(tk, "\x1b[5;25v", 7); - - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for CSI v"); - -@@ -26,14 +26,14 @@ int main(int argc, char *argv[]) - is_int(args[1], 25, "args[1] for unknown CSI"); - is_int(command, 'v', "command for unknown CSI"); - -- termkey_push_bytes(tk, "\e[?w", 4); -+ termkey_push_bytes(tk, "\x1b[?w", 4); - - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for CSI ? w"); - is_int(key.type, TERMKEY_TYPE_UNKNOWN_CSI, "key.type for unknown CSI"); - is_int(termkey_interpret_csi(tk, &key, args, &nargs, &command), TERMKEY_RES_KEY, "interpret_csi yields RES_KEY"); - is_int(command, '?'<<8 | 'w', "command for unknown CSI"); - -- termkey_push_bytes(tk, "\e[?$x", 5); -+ termkey_push_bytes(tk, "\x1b[?$x", 5); - - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for CSI ? $x"); - is_int(key.type, TERMKEY_TYPE_UNKNOWN_CSI, "key.type for unknown CSI"); -diff --git a/t/39dcs.c b/t/39dcs.c -index c517411..f065477 100644 ---- a/t/39dcs.c -+++ b/t/39dcs.c -@@ -12,7 +12,7 @@ int main(int argc, char *argv[]) - tk = termkey_new_abstract("xterm", 0); - - // 7bit DCS -- termkey_push_bytes(tk, "\eP1$r1 q\e\\", 10); -+ termkey_push_bytes(tk, "\x1bP1$r1 q\x1b\\", 10); - - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for DCS"); - -@@ -38,7 +38,7 @@ int main(int argc, char *argv[]) - is_int(termkey_getkey(tk, &key), TERMKEY_RES_NONE, "getkey again yields RES_NONE"); - - // 7bit OSC -- termkey_push_bytes(tk, "\e]15;abc\e\\", 10); -+ termkey_push_bytes(tk, "\x1b]15;abc\x1b\\", 10); - - is_int(termkey_getkey(tk, &key), TERMKEY_RES_KEY, "getkey yields RES_KEY for OSC"); - -@@ -51,7 +51,7 @@ int main(int argc, char *argv[]) - is_int(termkey_getkey(tk, &key), TERMKEY_RES_NONE, "getkey again yields RES_NONE"); - - // False alarm -- termkey_push_bytes(tk, "\eP", 2); -+ termkey_push_bytes(tk, "\x1bP", 2); - - is_int(termkey_getkey(tk, &key), TERMKEY_RES_AGAIN, "getkey yields RES_AGAIN for false alarm"); - |