diff options
137 files changed, 3582 insertions, 1054 deletions
diff --git a/ci/build.ps1 b/ci/build.ps1 index 36570be7ae..08fc76393d 100644 --- a/ci/build.ps1 +++ b/ci/build.ps1 @@ -116,7 +116,7 @@ if (-not $NoTests) { python3 -c "import pynvim; print(str(pynvim))" ; exitIfFailed $env:PATH = "C:\Ruby24\bin;$env:PATH" - gem.cmd install neovim + gem.cmd install --pre neovim Get-Command -CommandType Application neovim-ruby-host.bat npm.cmd install -g neovim diff --git a/ci/install.sh b/ci/install.sh index a4dfc87a1b..efb37cea4e 100755 --- a/ci/install.sh +++ b/ci/install.sh @@ -19,7 +19,7 @@ echo "Install neovim module for Python 2." CC=cc python2 -m pip -q install --user --upgrade pynvim echo "Install neovim RubyGem." -gem install --no-document --version ">= 0.8.0" neovim +gem install --no-document --pre neovim echo "Install neovim npm package" source ~/.nvm/nvm.sh diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index b80ca9edd7..0c726ddd86 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -529,14 +529,6 @@ nvim__id_float({flt}) *nvim__id_float()* nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* TODO: Documentation - *nvim__put_attr()* -nvim__put_attr({id}, {start_row}, {start_col}, {end_row}, {end_col}) - Set attrs in nvim__buf_set_lua_hl callbacks - - TODO(bfredl): This is rather pedestrian. The final interface - should probably be derived from a reformed bufhl/virttext - interface with full support for multi-line ranges etc - nvim__screenshot({path}) *nvim__screenshot()* TODO: Documentation @@ -1480,6 +1472,53 @@ nvim_set_current_win({window}) *nvim_set_current_win()* Parameters: ~ {window} Window handle + *nvim_set_decoration_provider()* +nvim_set_decoration_provider({ns_id}, {opts}) + Set or change decoration provider for a namespace + + This is a very general purpose interface for having lua + callbacks being triggered during the redraw code. + + The expected usage is to set extmarks for the currently + redrawn buffer. |nvim_buf_set_extmark| can be called to add + marks on a per-window or per-lines basis. Use the `ephemeral` + key to only use the mark for the current screen redraw (the + callback will be called again for the next redraw ). + + Note: this function should not be called often. Rather, the + callbacks themselves can be used to throttle unneeded + callbacks. the `on_start` callback can return `false` to + disable the provider until the next redraw. Similarily, return + `false` in `on_win` will skip the `on_lines` calls for that + window (but any extmarks set in `on_win` will still be used). + A plugin managing multiple sources of decorations should + ideally only set one provider, and merge the sources + internally. You can use multiple `ns_id` for the extmarks + set/modified inside the callback anyway. + + Note: doing anything other than setting extmarks is considered + experimental. Doing things like changing options are not + expliticly forbidden, but is likely to have unexpected + consequences (such as 100% CPU consumption). doing + `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite + dubious for the moment. + + Parameters: ~ + {ns_id} Namespace id from |nvim_create_namespace()| + {opts} Callbacks invoked during redraw: + • on_start: called first on each screen redraw + ["start", tick] + • on_buf: called for each buffer being redrawn + (before window callbacks) ["buf", bufnr, tick] + • on_win: called when starting to redraw a + specific window. ["win", winid, bufnr, topline, + botline_guess] + • on_line: called for each buffer line being + redrawn. (The interation with fold lines is + subject to change) ["win", winid, bufnr, row] + • on_end: called at the end of a redraw cycle + ["end", tick] + nvim_set_keymap({mode}, {lhs}, {rhs}, {opts}) *nvim_set_keymap()* Sets a global |mapping| for the given mode. @@ -1575,18 +1614,6 @@ to check whether a buffer is loaded. nvim__buf_redraw_range({buffer}, {first}, {last}) TODO: Documentation -nvim__buf_set_luahl({buffer}, {opts}) *nvim__buf_set_luahl()* - Unstabilized interface for defining syntax hl in lua. - - This is not yet safe for general use, lua callbacks will need - to be restricted, like textlock and probably other stuff. - - The API on_line/nvim__put_attr is quite raw and not intended - to be the final shape. Ideally this should operate on chunks - larger than a single line to reduce interpreter overhead, and - generate annotation objects (bufhl/virttext) on the fly but - using the same representation. - nvim__buf_stats({buffer}) *nvim__buf_stats()* TODO: Documentation @@ -1690,6 +1717,29 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* |nvim_buf_detach()| |api-buffer-updates-lua| +nvim_buf_call({buffer}, {fun}) *nvim_buf_call()* + call a function with buffer as temporary current buffer + + This temporarily switches current buffer to "buffer". If the + current window already shows "buffer", the window is not + switched If a window inside the current tabpage (including a + float) already shows the buffer One of these windows will be + set as current window temporarily. Otherwise a temporary + scratch window (calleed the "autocmd window" for historical + reasons) will be used. + + This is useful e.g. to call vimL functions that only work with + the current buffer/window currently, like |termopen()|. + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {fun} Function to call inside the buffer (currently + lua callable only) + + Return: ~ + Return value of function. NB: will deepcopy lua values + currently, use upvalues to send lua references in and out. + *nvim_buf_clear_namespace()* nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end}) Clears namespaced objects (highlights, extmarks, virtual text) @@ -1733,6 +1783,17 @@ nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()* {buffer} Buffer handle, or 0 for current buffer {name} Variable name +nvim_buf_delete({buffer}, {opts}) *nvim_buf_delete()* + Deletes the buffer. See |:bwipeout| + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {opts} Optional parameters. Keys: + • force: Force deletion and ignore unsaved + changes. + • unload: Unloaded only, do not delete. See + |:bunload| + nvim_buf_detach({buffer}) *nvim_buf_detach()* Deactivates buffer-update events on the channel. @@ -1986,6 +2047,11 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts}) • hl_group : name of the highlight group used to highlight this mark. • virt_text : virtual text to link to this mark. + • ephemeral : for use with + |nvim_set_decoration_provider| callbacks. The + mark will only be used for the current redraw + cycle, and not be permantently stored in the + buffer. Return: ~ Id of the created/updated extmark diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index a6872d0af5..a728593c40 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -700,9 +700,14 @@ InsertEnter Just before starting Insert mode. Also for The cursor is restored afterwards. If you do not want that set |v:char| to a non-empty string. + *InsertLeavePre* +InsertLeavePre Just before leaving Insert mode. Also when + using CTRL-O |i_CTRL-O|. Be caseful not to + change mode or use `:normal`, it will likely + cause trouble. *InsertLeave* -InsertLeave When leaving Insert mode. Also when using - CTRL-O |i_CTRL-O|. But not for |i_CTRL-C|. +InsertLeave Just after leaving Insert mode. Also when + using CTRL-O |i_CTRL-O|. But not for |i_CTRL-C|. *MenuPopup* MenuPopup Just before showing the popup menu (under the right mouse button). Useful for adjusting the diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index dcebbc524c..5c67359002 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1615,6 +1615,10 @@ B When joining lines, don't insert a space between two multi-byte characters. Overruled by the 'M' flag. 1 Don't break a line after a one-letter word. It's broken before it instead (if possible). +] Respect textwidth rigorously. With this flag set, no line can be + longer than textwidth, unless line-break-prohibition rules make this + impossible. Mainly for CJK scripts and works only if 'encoding' is + "utf-8". j Where it makes sense, remove a comment leader when joining lines. For example, joining: int i; // the index ~ diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 0f848d0c27..800de63a55 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2336,6 +2336,7 @@ repeat({expr}, {count}) String repeat {expr} {count} times resolve({filename}) String get filename a shortcut points to reverse({list}) List reverse {list} in-place round({expr}) Float round off {expr} +rubyeval({expr}) any evaluate |Ruby| expression rpcnotify({channel}, {event}[, {args}...]) Sends an |RPC| notification to {channel} rpcrequest({channel}, {method}[, {args}...]) @@ -2468,7 +2469,8 @@ tolower({expr}) String the String {expr} switched to lowercase toupper({expr}) String the String {expr} switched to uppercase tr({src}, {fromstr}, {tostr}) String translate chars of {src} in {fromstr} to chars in {tostr} -trim({text} [, {mask}]) String trim characters in {mask} from {text} +trim({text} [, {mask} [, {dir}]]) + String trim characters in {mask} from {text} trunc({expr}) Float truncate Float {expr} type({name}) Number type of variable {name} undofile({name}) String undo file name for {name} @@ -3839,7 +3841,7 @@ feedkeys({string} [, {mode}]) *feedkeys()* stuck, waiting for a character to be typed before the script continues. Note that if you manage to call feedkeys() while - executing commands, thus calling it recursively, the + executing commands, thus calling it recursively, then all typehead will be consumed by the last call. '!' When used with 'x' will not end Insert mode. Can be used in a test when a timer is set to exit Insert mode @@ -4642,7 +4644,7 @@ getloclist({nr},[, {what}]) *getloclist()* If {what} contains 'filewinid', then returns the id of the window used to display files from the location list. This field is applicable only when called from a location list - window. + window. See |location-list-file-window| for more details. getmatches([{win}]) *getmatches()* Returns a |List| with all matches previously defined for the @@ -4733,7 +4735,9 @@ getqflist([{what}]) *getqflist()* id get information for the quickfix list with |quickfix-ID|; zero means the id for the current list or the list specified by "nr" - idx index of the current entry in the list + idx index of the current entry in the quickfix + list specified by 'id' or 'nr'. + See |quickfix-index| items quickfix list entries lines parse a list of lines using 'efm' and return the resulting entries. Only a |List| type is @@ -4926,6 +4930,19 @@ getwinpos([{timeout}]) *getwinpos()* {timeout} can be used to specify how long to wait in msec for a response from the terminal. When omitted 100 msec is used. + Use a longer time for a remote terminal. + When using a value less than 10 and no response is received + within that time, a previously reported position is returned, + if available. This can be used to poll for the position and + do some work in the meantime: > + while 1 + let res = getwinpos(1) + if res[0] >= 0 + break + endif + " Do some work here + endwhile +< *getwinposx()* getwinposx() The result is a Number, which is the X coordinate in pixels of the left hand side of the GUI Vim window. The result will be @@ -6263,6 +6280,7 @@ mode([expr]) Return a string that indicates the current mode. nov Operator-pending (forced charwise |o_v|) noV Operator-pending (forced linewise |o_V|) noCTRL-V Operator-pending (forced blockwise |o_CTRL-V|) + CTRL-V is one character niI Normal using |i_CTRL-O| in |Insert-mode| niR Normal using |i_CTRL-O| in |Replace-mode| niV Normal using |i_CTRL-O| in |Virtual-Replace-mode| @@ -7053,6 +7071,17 @@ rpcstart({prog}[, {argv}]) *rpcstart()* < with > :let id = jobstart(['prog', 'arg1', 'arg2'], {'rpc': v:true}) +rubyeval({expr}) *rubyeval()* + Evaluate Ruby expression {expr} and return its result + converted to Vim data structures. + Numbers, floats and strings are returned as they are (strings + are copied though). + Arrays are represented as Vim |List| type. + Hashes are represented as Vim |Dictionary| type. + Other objects are represented as strings resulted from their + "Object#to_s" method. + {only available when compiled with the |+ruby| feature} + screenattr({row}, {col}) *screenattr()* Like |screenchar()|, but return the attribute. This is a rather arbitrary number that can only be used to compare to the @@ -7622,16 +7651,22 @@ setqflist({list} [, {action}[, {what}]]) *setqflist()* efm errorformat to use when parsing text from "lines". If this is not present, then the 'errorformat' option value is used. + See |quickfix-parse| id quickfix list identifier |quickfix-ID| + idx index of the current entry in the quickfix + list specified by 'id' or 'nr'. If set to '$', + then the last entry in the list is set as the + current entry. See |quickfix-index| items list of quickfix entries. Same as the {list} argument. lines use 'errorformat' to parse a list of lines and add the resulting entries to the quickfix list {nr} or {id}. Only a |List| value is supported. + See |quickfix-parse| nr list number in the quickfix stack; zero means the current quickfix list and "$" means - the last quickfix list - title quickfix list title text + the last quickfix list. + title quickfix list title text. See |quickfix-title| Unsupported keys in {what} are ignored. If the "nr" item is not present, then the current quickfix list is modified. When creating a new quickfix list, "nr" can be @@ -9015,21 +9050,28 @@ tr({src}, {fromstr}, {tostr}) *tr()* echo tr("<blob>", "<>", "{}") < returns "{blob}" -trim({text} [, {mask}]) *trim()* +trim({text} [, {mask} [, {dir}]]) *trim()* Return {text} as a String where any character in {mask} is - removed from the beginning and end of {text}. + removed from the beginning and/or end of {text}. If {mask} is not given, {mask} is all characters up to 0x20, which includes Tab, space, NL and CR, plus the non-breaking space character 0xa0. - This code deals with multibyte characters properly. - + The optional {dir} argument specifies where to remove the + characters: + 0 remove from the beginning and end of {text} + 1 remove only at the beginning of {text} + 2 remove only at the end of {text} + When omitted both ends are trimmed. + This function deals with multibyte characters properly. Examples: > echo trim(" some text ") < returns "some text" > echo trim(" \r\t\t\r RESERVE \t\n\x0B\xA0") . "_TAIL" < returns "RESERVE_TAIL" > echo trim("rm<Xrm<>X>rrm", "rm<>") -< returns "Xrm<>X" (characters in the middle are not removed) +< returns "Xrm<>X" (characters in the middle are not removed) > + echo trim(" vim ", " ", 2) +< returns " vim" trunc({expr}) *trunc()* Return the largest integral value with magnitude less than or diff --git a/runtime/doc/if_ruby.txt b/runtime/doc/if_ruby.txt index 6468e4c81e..c8d2409549 100644 --- a/runtime/doc/if_ruby.txt +++ b/runtime/doc/if_ruby.txt @@ -136,7 +136,7 @@ self[{n}] Returns the buffer object for the number {n}. The first number Methods: -name Returns the name of the buffer. +name Returns the full name of the buffer. number Returns the number of the buffer. count Returns the number of lines. length Returns the number of lines. @@ -172,6 +172,7 @@ height = {n} Sets the window height to {n}. width Returns the width of the window. width = {n} Sets the window width to {n}. cursor Returns a [row, col] array for the cursor position. + First line number is 1 and first column number is 0. cursor = [{row}, {col}] Sets the cursor position to {row} and {col}. @@ -184,4 +185,13 @@ $curwin The current window object. $curbuf The current buffer object. ============================================================================== +6. rubyeval() Vim function *ruby-rubyeval* + +To facilitate bi-directional interface, you can use |rubyeval()| function to +evaluate Ruby expressions and pass their values to Vim script. + +The Ruby value "true", "false" and "nil" are converted to v:true, v:false and +v:null, respectively. + +============================================================================== vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 44b611c2cf..33d65406a1 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -488,8 +488,8 @@ get_active_clients() *vim.lsp.get_active_clients()* Table of |vim.lsp.client| objects get_client_by_id({client_id}) *vim.lsp.get_client_by_id()* - Gets an active client by id, or nil if the id is invalid or - the client is not yet initialized. + Gets a client by id, or nil if the id is invalid. + The returned client may not yet be fully initialized. Parameters: ~ {client_id} client id number @@ -792,6 +792,20 @@ outgoing_calls() *vim.lsp.buf.outgoing_calls()* cursor in the |quickfix| window. If the symbol can resolve to multiple items, the user can pick one in the |inputlist|. + *vim.lsp.buf.range_code_action()* +range_code_action({context}, {start_pos}, {end_pos}) + Performs |vim.lsp.buf.code_action()| for a given range. + + Parameters: ~ + {context} (table, optional) Valid `CodeActionContext` + object + {start_pos} ({number, number}, optional) mark-indexed + position. Defaults to the start of the last + visual selection. + {end_pos} ({number, number}, optional) mark-indexed + position. Defaults to the end of the last + visual selection. + *vim.lsp.buf.range_formatting()* range_formatting({options}, {start_pos}, {end_pos}) Formats a given range. @@ -1285,6 +1299,23 @@ make_formatting_params({options}) See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting + *vim.lsp.util.make_given_range_params()* +make_given_range_params({start_pos}, {end_pos}) + Using the given range in the current buffer, creates an object + that is similar to |vim.lsp.util.make_range_params()|. + + Parameters: ~ + {start_pos} ({number, number}, optional) mark-indexed + position. Defaults to the start of the last + visual selection. + {end_pos} ({number, number}, optional) mark-indexed + position. Defaults to the end of the last + visual selection. + + Return: ~ + { textDocument = { uri = `current_file_uri` }, range = { + start = `start_position` , end = `end_position` } } + make_position_params() *vim.lsp.util.make_position_params()* Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index a53024d420..334bb33c1e 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1031,6 +1031,9 @@ is_callable({f}) *vim.is_callable()* Return: ~ true if `f` is callable, else false +is_valid({opt}) *vim.is_valid()* + TODO: Documentation + list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()* Extends a list-like table with the values of another list-like table. @@ -1286,7 +1289,9 @@ validate({opt}) *vim.validate()* • arg_value: argument value • fn: any function accepting one argument, returns true if and only if the argument is - valid + valid. Can optionally return an additional + informative error message as the second + returned value. • msg: (optional) error string if validation fails diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 1514f03c55..edec4a8de7 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1136,9 +1136,10 @@ scripts. :com[mand] *:com* *:command* List all user-defined commands. When listing commands, - the characters in the first two columns are + the characters in the first columns are: ! Command has the -bang attribute " Command has the -register attribute + | Command has the -bar attribute b Command is local to current buffer (see below for details on attributes) The list can be filtered on command name with @@ -1342,14 +1343,15 @@ It is possible that the special characters in the range like `.`, `$` or `%` which by default correspond to the current line, last line and the whole buffer, relate to arguments, (loaded) buffers, windows or tab pages. -Possible values are: - -addr=lines Range of lines (this is the default) - -addr=arguments Range for arguments - -addr=buffers Range for buffers (also not loaded buffers) - -addr=loaded_buffers Range for loaded buffers - -addr=windows Range for windows - -addr=tabs Range for tab pages - -addr=other other kind of range +Possible values are (second column is the short name used in listing): + -addr=lines Range of lines (this is the default) + -addr=arguments arg Range for arguments + -addr=buffers buf Range for buffers (also not loaded buffers) + -addr=loaded_buffers load Range for loaded buffers + -addr=windows win Range for windows + -addr=tabs tab Range for tab pages + -addr=quickfix qf Range for quickfix entries + -addr=other ? other kind of range Special cases ~ diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 188cfc91b6..9da11a553d 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -43,6 +43,7 @@ A location list is a window-local quickfix list. You get one after commands like `:lvimgrep`, `:lgrep`, `:lhelpgrep`, `:lmake`, etc., which create a location list instead of a quickfix list as the corresponding `:vimgrep`, `:grep`, `:helpgrep`, `:make` do. + *location-list-file-window* A location list is associated with a window and each window can have a separate location list. A location list can be associated with only one window. The location list is independent of the quickfix list. @@ -718,6 +719,9 @@ using these functions are below: " get the location list window id of the third window :echo getloclist(3, {'winid' : 0}).winid + + " get the file window id of a location list window (winnr: 4) + :echo getloclist(4, {'filewinid' : 0}).filewinid < *setqflist-examples* The |setqflist()| and |setloclist()| functions can be used to set the various @@ -732,6 +736,9 @@ using these functions are below: " set the title of the current quickfix list :call setqflist([], 'a', {'title' : 'Mytitle'}) + " change the current entry in the list specified by an identifier + :call setqflist([], 'a', {'id' : qfid, 'idx' : 10}) + " set the context of a quickfix list specified by an identifier :call setqflist([], 'a', {'id' : qfid, 'context' : {'val' : 100}}) diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index ae60c1c5e8..1fcb6611b4 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -450,6 +450,9 @@ Eval: *js_decode()* *v:none* (used by Vim to represent JavaScript "undefined"); use |v:null| instead. +Events: + *SigUSR1* Use |Signal| to detect `SIGUSR1` signal instead. + Highlight groups: *hl-StatusLineTerm* *hl-StatusLineTermNC* are unnecessary because Nvim supports 'winhighlight' window-local highlights. diff --git a/runtime/ftplugin/markdown.vim b/runtime/ftplugin/markdown.vim index 277ba94e8b..fc1d9e068b 100644 --- a/runtime/ftplugin/markdown.vim +++ b/runtime/ftplugin/markdown.vim @@ -1,7 +1,7 @@ " Vim filetype plugin " Language: Markdown " Maintainer: Tim Pope <vimNOSPAM@tpope.org> -" Last Change: 2016 Aug 29 +" Last Change: 2019 Dec 05 if exists("b:did_ftplugin") finish @@ -9,7 +9,7 @@ endif runtime! ftplugin/html.vim ftplugin/html_*.vim ftplugin/html/*.vim -setlocal comments=fb:*,fb:-,fb:+,n:> commentstring=>\ %s +setlocal comments=fb:*,fb:-,fb:+,n:> commentstring=<!--%s--> setlocal formatoptions+=tcqln formatoptions-=r formatoptions-=o setlocal formatlistpat=^\\s*\\d\\+\\.\\s\\+\\\|^[-*+]\\s\\+\\\|^\\[^\\ze[^\\]]\\+\\]: @@ -19,32 +19,56 @@ else let b:undo_ftplugin = "setl cms< com< fo< flp<" endif -function! MarkdownFold() +function! s:NotCodeBlock(lnum) abort + return synIDattr(synID(v:lnum, 1, 1), 'name') !=# 'markdownCode' +endfunction + +function! MarkdownFold() abort let line = getline(v:lnum) - " Regular headers - let depth = match(line, '\(^#\+\)\@<=\( .*$\)\@=') - if depth > 0 - return ">" . depth + if line =~# '^#\+ ' && s:NotCodeBlock(v:lnum) + return ">" . match(line, ' ') endif - " Setext style headings let nextline = getline(v:lnum + 1) - if (line =~ '^.\+$') && (nextline =~ '^=\+$') + if (line =~ '^.\+$') && (nextline =~ '^=\+$') && s:NotCodeBlock(v:lnum + 1) return ">1" endif - if (line =~ '^.\+$') && (nextline =~ '^-\+$') + if (line =~ '^.\+$') && (nextline =~ '^-\+$') && s:NotCodeBlock(v:lnum + 1) return ">2" endif return "=" endfunction +function! s:HashIndent(lnum) abort + let hash_header = matchstr(getline(a:lnum), '^#\{1,6}') + if len(hash_header) + return hash_header + else + let nextline = getline(a:lnum + 1) + if nextline =~# '^=\+\s*$' + return '#' + elseif nextline =~# '^-\+\s*$' + return '##' + endif + endif +endfunction + +function! MarkdownFoldText() abort + let hash_indent = s:HashIndent(v:foldstart) + let title = substitute(getline(v:foldstart), '^#\+\s*', '', '') + let foldsize = (v:foldend - v:foldstart + 1) + let linecount = '['.foldsize.' lines]' + return hash_indent.' '.title.' '.linecount +endfunction + if has("folding") && exists("g:markdown_folding") setlocal foldexpr=MarkdownFold() setlocal foldmethod=expr - let b:undo_ftplugin .= " foldexpr< foldmethod<" + setlocal foldtext=MarkdownFoldText() + let b:undo_ftplugin .= " foldexpr< foldmethod< foldtext<" endif " vim:set sw=2: diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 585528dd5a..fad213212a 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -25,6 +25,27 @@ local lsp = { -- format_rpc_error = lsp_rpc.format_rpc_error; } +-- maps request name to the required resolved_capability in the client. +lsp._request_name_to_capability = { + ['textDocument/hover'] = 'hover'; + ['textDocument/signatureHelp'] = 'signature_help'; + ['textDocument/definition'] = 'goto_definition'; + ['textDocument/implementation'] = 'implementation'; + ['textDocument/declaration'] = 'declaration'; + ['textDocument/typeDefinition'] = 'type_definition'; + ['textDocument/documentSymbol'] = 'document_symbol'; + ['textDocument/workspaceSymbol'] = 'workspace_symbol'; + ['textDocument/prepareCallHierarchy'] = 'call_hierarchy'; + ['textDocument/rename'] = 'rename'; + ['textDocument/codeAction'] = 'code_action'; + ['workspace/executeCommand'] = 'execute_command'; + ['textDocument/references'] = 'find_references'; + ['textDocument/rangeFormatting'] = 'document_range_formatting'; + ['textDocument/formatting'] = 'document_formatting'; + ['textDocument/completion'] = 'completion'; + ['textDocument/documentHighlight'] = 'document_highlight'; +} + -- TODO improve handling of scratch buffers with LSP attached. --@private @@ -51,6 +72,16 @@ local function resolve_bufnr(bufnr) end --@private +--- callback called by the client when trying to call a method that's not +--- supported in any of the servers registered for the current buffer. +--@param method (string) name of the method +function lsp._unsupported_method(method) + local msg = string.format("method %s is not supported by any of the servers registered for the current buffer", method) + log.warn(msg) + return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg) +end + +--@private --- Checks whether a given path is a directory. --- --@param filename (string) path to check @@ -397,9 +428,8 @@ end --@param trace: "off" | "messages" | "verbose" | nil passed directly to the language --- server in the initialize request. Invalid/empty values will default to "off" --- ---@returns Client id. |vim.lsp.get_client_by_id()| Note: client is only ---- available after it has been initialized, which may happen after a small ---- delay (or never if there is an error). Use `on_init` to do any actions once +--@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be +--- fully initialized. Use `on_init` to do any actions once --- the client has been initialized. function lsp.start_client(config) local cleaned_config = validate_client_config(config) @@ -576,6 +606,15 @@ function lsp.start_client(config) -- These are the cleaned up capabilities we use for dynamically deciding -- when to send certain events to clients. client.resolved_capabilities = protocol.resolve_capabilities(client.server_capabilities) + client.supports_method = function(method) + local required_capability = lsp._request_name_to_capability[method] + -- if we don't know about the method, assume that the client supports it. + if not required_capability then + return true + end + + return client.resolved_capabilities[required_capability] + end if config.on_init then local status, err = pcall(config.on_init, client, result) if not status then @@ -599,19 +638,6 @@ function lsp.start_client(config) end --@private - --- Throws error for a method that is not supported by the current LSP - --- server. - --- - --@param method (string) an LSP method name not supported by the LSP server. - --@returns (error) a 'MethodNotFound' JSON-RPC error response. - local function unsupported_method(method) - local msg = "server doesn't support "..method - local _ = log.warn() and log.warn(msg) - err_message(msg) - return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg) - end - - --@private --- Sends a request to the server. --- --- This is a thin wrapper around {client.rpc.request} with some additional @@ -638,20 +664,6 @@ function lsp.start_client(config) or error(string.format("not found: %q request callback for client %q.", method, client.name)) end local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback, bufnr) - -- TODO keep these checks or just let it go anyway? - if (not client.resolved_capabilities.hover and method == 'textDocument/hover') - or (not client.resolved_capabilities.signature_help and method == 'textDocument/signatureHelp') - or (not client.resolved_capabilities.goto_definition and method == 'textDocument/definition') - or (not client.resolved_capabilities.implementation and method == 'textDocument/implementation') - or (not client.resolved_capabilities.declaration and method == 'textDocument/declaration') - or (not client.resolved_capabilities.type_definition and method == 'textDocument/typeDefinition') - or (not client.resolved_capabilities.document_symbol and method == 'textDocument/documentSymbol') - or (not client.resolved_capabilities.workspace_symbol and method == 'textDocument/workspaceSymbol') - or (not client.resolved_capabilities.call_hierarchy and method == 'textDocument/prepareCallHierarchy') - then - callback(unsupported_method(method), method, nil, client_id, bufnr) - return - end return rpc.request(method, params, function(err, result) callback(err, method, result, client_id, bufnr) end) @@ -910,14 +922,14 @@ function lsp.buf_is_attached(bufnr, client_id) return (all_buffer_active_clients[bufnr] or {})[client_id] == true end ---- Gets an active client by id, or nil if the id is invalid or the ---- client is not yet initialized. ---- +--- Gets a client by id, or nil if the id is invalid. +--- The returned client may not yet be fully initialized. +-- --@param client_id client id number --- --@returns |vim.lsp.client| object, or nil function lsp.get_client_by_id(client_id) - return active_clients[client_id] + return active_clients[client_id] or uninitialized_clients[client_id] end --- Stops a client(s). @@ -998,16 +1010,32 @@ function lsp.buf_request(bufnr, method, params, callback) callback = { callback, 'f', true }; } local client_request_ids = {} - for_each_buffer_client(bufnr, function(client, client_id, resolved_bufnr) - local request_success, request_id = client.request(method, params, callback, resolved_bufnr) - -- This could only fail if the client shut down in the time since we looked - -- it up and we did the request, which should be rare. - if request_success then - client_request_ids[client_id] = request_id + local method_supported = false + for_each_buffer_client(bufnr, function(client, client_id, resolved_bufnr) + if client.supports_method(method) then + method_supported = true + local request_success, request_id = client.request(method, params, callback, resolved_bufnr) + + -- This could only fail if the client shut down in the time since we looked + -- it up and we did the request, which should be rare. + if request_success then + client_request_ids[client_id] = request_id + end end end) + -- if no clients support the given method, call the callback with the proper + -- error message. + if not method_supported then + local unsupported_err = lsp._unsupported_method(method) + local cb = callback or lsp.callbacks['method'] + if cb then + cb(unsupported_err, method, bufnr) + end + return + end + local function _cancel_all_requests() for client_id, request_id in pairs(client_request_ids) do local client = active_clients[client_id] diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 4e7a8333a0..3270d1d2a9 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -82,18 +82,6 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) return end - -- Unloaded buffers should not handle diagnostics. - -- When the buffer is loaded, we'll call on_attach, which sends textDocument/didOpen. - -- This should trigger another publish of the diagnostics. - -- - -- In particular, this stops a ton of spam when first starting a server for current - -- unloaded buffers. - if not api.nvim_buf_is_loaded(bufnr) then - return - end - - util.buf_clear_diagnostics(bufnr) - -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic -- The diagnostic's severity. Can be omitted. If omitted it is up to the -- client to interpret diagnostics as error, warning, info or hint. @@ -104,7 +92,23 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) end end + util.buf_clear_diagnostics(bufnr) + + -- Always save the diagnostics, even if the buf is not loaded. + -- Language servers may report compile or build errors via diagnostics + -- Users should be able to find these, even if they're in files which + -- are not loaded. util.buf_diagnostics_save_positions(bufnr, result.diagnostics) + + -- Unloaded buffers should not handle diagnostics. + -- When the buffer is loaded, we'll call on_attach, which sends textDocument/didOpen. + -- This should trigger another publish of the diagnostics. + -- + -- In particular, this stops a ton of spam when first starting a server for current + -- unloaded buffers. + if not api.nvim_buf_is_loaded(bufnr) then + return + end util.buf_diagnostics_underline(bufnr, result.diagnostics) util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) util.buf_diagnostics_signs(bufnr, result.diagnostics) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 4e926381e0..2773f59b45 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -703,6 +703,10 @@ function protocol.make_client_capabilities() }; hierarchicalDocumentSymbolSupport = true; }; + rename = { + dynamicRegistration = false; + prepareSupport = true; + }; }; workspace = { symbol = { @@ -914,6 +918,7 @@ function protocol.resolve_capabilities(server_capabilities) return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync)) end end + general_properties.completion = server_capabilities.completionProvider ~= nil general_properties.hover = server_capabilities.hoverProvider or false general_properties.goto_definition = server_capabilities.definitionProvider or false general_properties.find_references = server_capabilities.referencesProvider or false @@ -923,6 +928,15 @@ function protocol.resolve_capabilities(server_capabilities) general_properties.document_formatting = server_capabilities.documentFormattingProvider or false general_properties.document_range_formatting = server_capabilities.documentRangeFormattingProvider or false general_properties.call_hierarchy = server_capabilities.callHierarchyProvider or false + general_properties.execute_command = server_capabilities.executeCommandProvider ~= nil + + if server_capabilities.renameProvider == nil then + general_properties.rename = false + elseif type(server_capabilities.renameProvider) == 'boolean' then + general_properties.rename = server_capabilities.renameProvider + else + general_properties.rename = true + end if server_capabilities.codeActionProvider == nil then general_properties.code_action = false diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 53f88dea7d..b5f171a985 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -807,7 +807,7 @@ function M.fancy_floating_markdown(contents, opts) h.start = h.start + i - 1 h.finish = h.finish + i - 1 if h.finish + 1 <= #stripped then - table.insert(stripped, h.finish + 1, string.rep("─", math.min(width, opts.wrap_at))) + table.insert(stripped, h.finish + 1, string.rep("─", math.min(width, opts.wrap_at or width))) height = height + 1 end end diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 5c89c63f7b..995c52e8ed 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -477,48 +477,77 @@ end --- 2. (arg_value, fn, msg) --- - arg_value: argument value --- - fn: any function accepting one argument, returns true if and ---- only if the argument is valid +--- only if the argument is valid. Can optionally return an additional +--- informative error message as the second returned value. --- - msg: (optional) error string if validation fails function vim.validate(opt) end -- luacheck: no unused -vim.validate = (function() + +do local type_names = { - t='table', s='string', n='number', b='boolean', f='function', c='callable', - ['table']='table', ['string']='string', ['number']='number', - ['boolean']='boolean', ['function']='function', ['callable']='callable', - ['nil']='nil', ['thread']='thread', ['userdata']='userdata', + ['table'] = 'table', t = 'table', + ['string'] = 'string', s = 'string', + ['number'] = 'number', n = 'number', + ['boolean'] = 'boolean', b = 'boolean', + ['function'] = 'function', f = 'function', + ['callable'] = 'callable', c = 'callable', + ['nil'] = 'nil', + ['thread'] = 'thread', + ['userdata'] = 'userdata', } - local function _type_name(t) - local tname = type_names[t] - if tname == nil then - error(string.format('invalid type name: %s', tostring(t))) - end - return tname - end + local function _is_type(val, t) return t == 'callable' and vim.is_callable(val) or type(val) == t end - return function(opt) - assert(type(opt) == 'table', string.format('opt: expected table, got %s', type(opt))) + local function is_valid(opt) + if type(opt) ~= 'table' then + return false, string.format('opt: expected table, got %s', type(opt)) + end + for param_name, spec in pairs(opt) do - assert(type(spec) == 'table', string.format('%s: expected table, got %s', param_name, type(spec))) + if type(spec) ~= 'table' then + return false, string.format('opt[%s]: expected table, got %s', param_name, type(spec)) + end local val = spec[1] -- Argument value. local t = spec[2] -- Type name, or callable. local optional = (true == spec[3]) - if not vim.is_callable(t) then -- Check type name. - if (not optional or val ~= nil) and not _is_type(val, _type_name(t)) then - error(string.format("%s: expected %s, got %s", param_name, _type_name(t), type(val))) + if type(t) == 'string' then + local t_name = type_names[t] + if not t_name then + return false, string.format('invalid type name: %s', t) + end + + if (not optional or val ~= nil) and not _is_type(val, t_name) then + return false, string.format("%s: expected %s, got %s", param_name, t_name, type(val)) + end + elseif vim.is_callable(t) then + -- Check user-provided validation function. + local valid, optional_message = t(val) + if not valid then + local error_message = string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), val) + if optional_message ~= nil then + error_message = error_message .. string.format(". Info: %s", optional_message) + end + + return false, error_message end - elseif not t(val) then -- Check user-provided validation function. - error(string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), val)) + else + return false, string.format("invalid type name: %s", tostring(t)) end end - return true + + return true, nil end -end)() + function vim.validate(opt) + local ok, err_msg = is_valid(opt) + if not ok then + error(debug.traceback(err_msg, 2), 2) + end + end +end --- Returns true if object `f` can be called as a function. --- --@param f Any object diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 77bbfaa3ad..0de3388356 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -59,6 +59,24 @@ function Parser:_on_bytes(bufnr, changed_tick, end end +--- Registers callbacks for the parser +-- @param cbs An `nvim_buf_attach`-like table argument with the following keys : +-- `on_bytes` : see `nvim_buf_attach`, but this will be called _after_ the parsers callback. +-- `on_changedtree` : a callback that will be called everytime the tree has syntactical changes. +-- it will only be passed one argument, that is a table of the ranges (as node ranges) that +-- changed. +function Parser:register_cbs(cbs) + if not cbs then return end + + if cbs.on_changedtree then + table.insert(self.changedtree_cbs, cbs.on_changedtree) + end + + if cbs.on_bytes then + table.insert(self.bytes_cbs, cbs.on_bytes) + end +end + --- Sets the included ranges for the current parser -- -- @param ranges A table of nodes that will be used as the ranges the parser should include. @@ -68,6 +86,11 @@ function Parser:set_included_ranges(ranges) self.valid = false end +--- Gets the included ranges for the parsers +function Parser:included_ranges() + return self._parser:included_ranges() +end + local M = vim.tbl_extend("error", query, language) setmetatable(M, { @@ -127,11 +150,7 @@ end -- -- @param bufnr The buffer the parser should be tied to -- @param ft The filetype of this parser --- @param buf_attach_cbs An `nvim_buf_attach`-like table argument with the following keys : --- `on_lines` : see `nvim_buf_attach`, but this will be called _after_ the parsers callback. --- `on_changedtree` : a callback that will be called everytime the tree has syntactical changes. --- it will only be passed one argument, that is a table of the ranges (as node ranges) that --- changed. +-- @param buf_attach_cbs See Parser:register_cbs -- -- @returns The parser function M.get_parser(bufnr, lang, buf_attach_cbs) @@ -147,13 +166,7 @@ function M.get_parser(bufnr, lang, buf_attach_cbs) parsers[id] = M._create_parser(bufnr, lang, id) end - if buf_attach_cbs and buf_attach_cbs.on_changedtree then - table.insert(parsers[id].changedtree_cbs, buf_attach_cbs.on_changedtree) - end - - if buf_attach_cbs and buf_attach_cbs.on_bytes then - table.insert(parsers[id].bytes_cbs, buf_attach_cbs.on_bytes) - end + parsers[id]:register_cbs(buf_attach_cbs) return parsers[id] end diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 0f497fe434..decde08019 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -8,7 +8,7 @@ TSHighlighter.active = TSHighlighter.active or {} local ns = a.nvim_create_namespace("treesitter/highlighter") --- These are conventions defined by tree-sitter, though it +-- These are conventions defined by nvim-treesitter, though it -- needs to be user extensible also. TSHighlighter.hl_map = { ["error"] = "Error", @@ -56,21 +56,14 @@ TSHighlighter.hl_map = { ["include"] = "Include", } -function TSHighlighter.new(query, bufnr, ft) - if bufnr == nil or bufnr == 0 then - bufnr = a.nvim_get_current_buf() - end - +function TSHighlighter.new(parser, query) local self = setmetatable({}, TSHighlighter) - self.parser = vim.treesitter.get_parser( - bufnr, - ft, - { - on_changedtree = function(...) self:on_changedtree(...) end, - } - ) - - self.buf = self.parser.bufnr + + self.parser = parser + parser:register_cbs { + on_changedtree = function(...) self:on_changedtree(...) end + } + self:set_query(query) self.edit_count = 0 self.redraw_count = 0 @@ -79,7 +72,11 @@ function TSHighlighter.new(query, bufnr, ft) a.nvim_buf_set_option(self.buf, "syntax", "") -- TODO(bfredl): can has multiple highlighters per buffer???? - TSHighlighter.active[bufnr] = self + if not TSHighlighter.active[parser.bufnr] then + TSHighlighter.active[parser.bufnr] = {} + end + + TSHighlighter.active[parser.bufnr][parser.lang] = self -- Tricky: if syntax hasn't been enabled, we need to reload color scheme -- but use synload.vim rather than syntax.vim to not enable @@ -119,13 +116,6 @@ end function TSHighlighter:set_query(query) if type(query) == "string" then query = vim.treesitter.parse_query(self.parser.lang, query) - elseif query == nil then - query = vim.treesitter.get_query(self.parser.lang, 'highlights') - - if query == nil then - a.nvim_err_writeln("No highlights.scm query found for " .. self.parser.lang) - query = vim.treesitter.parse_query(self.parser.lang, "") - end end self.query = query @@ -139,12 +129,16 @@ function TSHighlighter:set_query(query) end }) - a.nvim__buf_redraw_range(self.buf, 0, a.nvim_buf_line_count(self.buf)) + a.nvim__buf_redraw_range(self.parser.bufnr, 0, a.nvim_buf_line_count(self.parser.bufnr)) end -function TSHighlighter._on_line(_, _win, buf, line) - -- on_line is only called when this is non-nil - local self = TSHighlighter.active[buf] +local function iter_active_tshl(buf, fn) + for _, hl in pairs(TSHighlighter.active[buf] or {}) do + fn(hl) + end +end + +local function on_line_impl(self, buf, line) if self.root == nil then return -- parser bought the farm already end @@ -172,24 +166,38 @@ function TSHighlighter._on_line(_, _win, buf, line) end end -function TSHighlighter._on_buf(_, buf) - local self = TSHighlighter.active[buf] - if self then - local tree = self.parser:parse() - self.root = (tree and tree:root()) or nil +function TSHighlighter._on_line(_, _win, buf, line, highlighter) + -- on_line is only called when this is non-nil + if highlighter then + on_line_impl(highlighter, buf, line) + else + iter_active_tshl(buf, function(self) + on_line_impl(self, buf, line) + end) end end +function TSHighlighter._on_buf(_, buf) + iter_active_tshl(buf, function(self) + if self then + local tree = self.parser:parse() + self.root = (tree and tree:root()) or nil + end + end) +end + function TSHighlighter._on_win(_, _win, buf, _topline, botline) - local self = TSHighlighter.active[buf] - if not self then - return false - end + iter_active_tshl(buf, function(self) + if not self then + return false + end - self.iter = nil - self.nextrow = 0 - self.botline = botline - self.redraw_count = self.redraw_count + 1 + self.iter = nil + self.nextrow = 0 + self.botline = botline + self.redraw_count = self.redraw_count + 1 + return true + end) return true end diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 494fb59fa7..2903c5905c 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -8,6 +8,104 @@ Query.__index = Query local M = {} +-- Filter the runtime query files, the spec is like regular runtime files but in the new `queries` +-- directory. They resemble ftplugins, that is that you can override queries by adding things in the +-- `queries` directory, and extend using the `after/queries` directory. +local function filter_files(file_list) + local main = nil + local after = {} + + for _, fname in ipairs(file_list) do + -- Only get the name of the directory containing the queries directory + if vim.fn.fnamemodify(fname, ":p:h:h:h:t") == "after" then + table.insert(after, fname) + -- The first one is the one with most priority + elseif not main then + main = fname + end + end + + return { main, unpack(after) } +end + +local function runtime_query_path(lang, query_name) + return string.format('queries/%s/%s.scm', lang, query_name) +end + +local function filtered_runtime_queries(lang, query_name) + return filter_files(a.nvim_get_runtime_file(runtime_query_path(lang, query_name), true) or {}) +end + +local function get_query_files(lang, query_name, is_included) + local lang_files = filtered_runtime_queries(lang, query_name) + local query_files = lang_files + + if #query_files == 0 then return {} end + + local base_langs = {} + + -- Now get the base languages by looking at the first line of every file + -- The syntax is the folowing : + -- ;+ inherits: ({language},)*{language} + -- + -- {language} ::= {lang} | ({lang}) + local MODELINE_FORMAT = "^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$" + + for _, file in ipairs(query_files) do + local modeline = vim.fn.readfile(file, "", 1) + + if #modeline == 1 then + local langlist = modeline[1]:match(MODELINE_FORMAT) + + if langlist then + for _, incllang in ipairs(vim.split(langlist, ',', true)) do + local is_optional = incllang:match("%(.*%)") + + if is_optional then + if not is_included then + table.insert(base_langs, incllang:sub(2, #incllang - 1)) + end + else + table.insert(base_langs, incllang) + end + end + end + end + end + + for _, base_lang in ipairs(base_langs) do + local base_files = get_query_files(base_lang, query_name, true) + vim.list_extend(query_files, base_files) + end + + return query_files +end + +local function read_query_files(filenames) + local contents = {} + + for _,filename in ipairs(filenames) do + vim.list_extend(contents, vim.fn.readfile(filename)) + end + + return table.concat(contents, '\n') +end + +--- Returns the runtime query {query_name} for {lang}. +-- +-- @param lang The language to use for the query +-- @param query_name The name of the query (i.e. "highlights") +-- +-- @return The corresponding query, parsed. +function M.get_query(lang, query_name) + local query_files = get_query_files(lang, query_name) + local query_string = read_query_files(query_files) + + if #query_string > 0 then + return M.parse_query(lang, query_string) + end +end + --- Parses a query. -- -- @param language The language diff --git a/runtime/queries/c/highlights.scm b/runtime/queries/c/highlights.scm new file mode 100644 index 0000000000..96b43cf0d0 --- /dev/null +++ b/runtime/queries/c/highlights.scm @@ -0,0 +1,151 @@ +(identifier) @variable + +[ + "const" + "default" + "enum" + "extern" + "inline" + "return" + "sizeof" + "static" + "struct" + "typedef" + "union" + "volatile" + "goto" +] @keyword + +[ + "while" + "for" + "do" + "continue" + "break" +] @repeat + +[ + "if" + "else" + "case" + "switch" +] @conditional + +"#define" @constant.macro +[ + "#if" + "#ifdef" + "#ifndef" + "#else" + "#elif" + "#endif" + (preproc_directive) +] @keyword + +"#include" @include + +[ + "=" + + "-" + "*" + "/" + "+" + "%" + + "~" + "|" + "&" + "^" + "<<" + ">>" + + "->" + + "<" + "<=" + ">=" + ">" + "==" + "!=" + + "!" + "&&" + "||" + + "-=" + "+=" + "*=" + "/=" + "%=" + "|=" + "&=" + "^=" + "--" + "++" +] @operator + +[ + (true) + (false) +] @boolean + +[ "." ";" ":" "," ] @punctuation.delimiter + +(conditional_expression [ "?" ":" ] @conditional) + + +[ "(" ")" "[" "]" "{" "}"] @punctuation.bracket + +(string_literal) @string +(system_lib_string) @string + +(null) @constant.builtin +(number_literal) @number +(char_literal) @number + +(call_expression + function: (identifier) @function) +(call_expression + function: (field_expression + field: (field_identifier) @function)) +(function_declarator + declarator: (identifier) @function) +(preproc_function_def + name: (identifier) @function.macro) +[ + (preproc_arg) + (preproc_defined) +] @function.macro +; TODO (preproc_arg) @embedded + +(field_identifier) @property +(statement_identifier) @label + +[ +(type_identifier) +(primitive_type) +(sized_type_specifier) +(type_descriptor) + ] @type + +(declaration type: [(identifier) (type_identifier)] @type) +(cast_expression type: [(identifier) (type_identifier)] @type) +(sizeof_expression value: (parenthesized_expression (identifier) @type)) + +((identifier) @constant + (#match? @constant "^[A-Z][A-Z0-9_]+$")) + +(comment) @comment + +;; Parameters +(parameter_declaration + declarator: (identifier) @parameter) + +(parameter_declaration + declarator: (pointer_declarator) @parameter) + +(preproc_params + (identifier)) @parameter + +(ERROR) @error diff --git a/runtime/syntax/markdown.vim b/runtime/syntax/markdown.vim index 1955a7443e..17b61c2fa4 100644 --- a/runtime/syntax/markdown.vim +++ b/runtime/syntax/markdown.vim @@ -2,7 +2,7 @@ " Language: Markdown " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " Filenames: *.markdown -" Last Change: 2016 Aug 29 +" Last Change: 2020 Jan 14 if exists("b:current_syntax") finish @@ -18,37 +18,46 @@ unlet! b:current_syntax if !exists('g:markdown_fenced_languages') let g:markdown_fenced_languages = [] endif +let s:done_include = {} for s:type in map(copy(g:markdown_fenced_languages),'matchstr(v:val,"[^=]*$")') + if has_key(s:done_include, matchstr(s:type,'[^.]*')) + continue + endif if s:type =~ '\.' let b:{matchstr(s:type,'[^.]*')}_subtype = matchstr(s:type,'\.\zs.*') endif exe 'syn include @markdownHighlight'.substitute(s:type,'\.','','g').' syntax/'.matchstr(s:type,'[^.]*').'.vim' unlet! b:current_syntax + let s:done_include[matchstr(s:type,'[^.]*')] = 1 endfor unlet! s:type +unlet! s:done_include -syn sync minlines=10 +if !exists('g:markdown_minlines') + let g:markdown_minlines = 50 +endif +execute 'syn sync minlines=' . g:markdown_minlines syn case ignore -syn match markdownValid '[<>]\c[a-z/$!]\@!' -syn match markdownValid '&\%(#\=\w*;\)\@!' +syn match markdownValid '[<>]\c[a-z/$!]\@!' transparent contains=NONE +syn match markdownValid '&\%(#\=\w*;\)\@!' transparent contains=NONE syn match markdownLineStart "^[<@]\@!" nextgroup=@markdownBlock,htmlSpecialChar syn cluster markdownBlock contains=markdownH1,markdownH2,markdownH3,markdownH4,markdownH5,markdownH6,markdownBlockquote,markdownListMarker,markdownOrderedListMarker,markdownCodeBlock,markdownRule -syn cluster markdownInline contains=markdownLineBreak,markdownLinkText,markdownItalic,markdownBold,markdownCode,markdownEscape,@htmlTop,markdownError +syn cluster markdownInline contains=markdownLineBreak,markdownLinkText,markdownItalic,markdownBold,markdownCode,markdownEscape,@htmlTop,markdownError,markdownValid syn match markdownH1 "^.\+\n=\+$" contained contains=@markdownInline,markdownHeadingRule,markdownAutomaticLink syn match markdownH2 "^.\+\n-\+$" contained contains=@markdownInline,markdownHeadingRule,markdownAutomaticLink syn match markdownHeadingRule "^[=-]\+$" contained -syn region markdownH1 matchgroup=markdownHeadingDelimiter start="##\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained -syn region markdownH2 matchgroup=markdownHeadingDelimiter start="###\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained -syn region markdownH3 matchgroup=markdownHeadingDelimiter start="####\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained -syn region markdownH4 matchgroup=markdownHeadingDelimiter start="#####\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained -syn region markdownH5 matchgroup=markdownHeadingDelimiter start="######\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained -syn region markdownH6 matchgroup=markdownHeadingDelimiter start="#######\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained +syn region markdownH1 matchgroup=markdownH1Delimiter start="##\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained +syn region markdownH2 matchgroup=markdownH2Delimiter start="###\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained +syn region markdownH3 matchgroup=markdownH3Delimiter start="####\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained +syn region markdownH4 matchgroup=markdownH4Delimiter start="#####\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained +syn region markdownH5 matchgroup=markdownH5Delimiter start="######\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained +syn region markdownH6 matchgroup=markdownH6Delimiter start="#######\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained syn match markdownBlockquote ">\%(\s\|$\)" contained nextgroup=@markdownBlock @@ -70,31 +79,40 @@ syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+"+ end=+ syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+'+ end=+'+ keepend contained syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+(+ end=+)+ keepend contained -syn region markdownLinkText matchgroup=markdownLinkTextDelimiter start="!\=\[\%(\_[^]]*]\%( \=[[(]\)\)\@=" end="\]\%( \=[[(]\)\@=" nextgroup=markdownLink,markdownId skipwhite contains=@markdownInline,markdownLineStart +syn region markdownLinkText matchgroup=markdownLinkTextDelimiter start="!\=\[\%(\%(\_[^][]\|\[\_[^][]*\]\)*]\%( \=[[(]\)\)\@=" end="\]\%( \=[[(]\)\@=" nextgroup=markdownLink,markdownId skipwhite contains=@markdownInline,markdownLineStart syn region markdownLink matchgroup=markdownLinkDelimiter start="(" end=")" contains=markdownUrl keepend contained syn region markdownId matchgroup=markdownIdDelimiter start="\[" end="\]" keepend contained syn region markdownAutomaticLink matchgroup=markdownUrlDelimiter start="<\%(\w\+:\|[[:alnum:]_+-]\+@\)\@=" end=">" keepend oneline -let s:concealends = has('conceal') ? ' concealends' : '' -exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter start="\S\@<=\*\|\*\S\@=" end="\S\@<=\*\|\*\S\@=" keepend contains=markdownLineStart' . s:concealends -exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter start="\S\@<=_\|_\S\@=" end="\S\@<=_\|_\S\@=" keepend contains=markdownLineStart' . s:concealends -exe 'syn region markdownBold matchgroup=markdownBoldDelimiter start="\S\@<=\*\*\|\*\*\S\@=" end="\S\@<=\*\*\|\*\*\S\@=" keepend contains=markdownLineStart,markdownItalic' . s:concealends -exe 'syn region markdownBold matchgroup=markdownBoldDelimiter start="\S\@<=__\|__\S\@=" end="\S\@<=__\|__\S\@=" keepend contains=markdownLineStart,markdownItalic' . s:concealends -exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\S\@<=\*\*\*\|\*\*\*\S\@=" end="\S\@<=\*\*\*\|\*\*\*\S\@=" keepend contains=markdownLineStart' . s:concealends -exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\S\@<=___\|___\S\@=" end="\S\@<=___\|___\S\@=" keepend contains=markdownLineStart' . s:concealends +let s:concealends = '' +if has('conceal') && get(g:, 'markdown_syntax_conceal', 1) == 1 + let s:concealends = ' concealends' +endif +exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter start="\S\@<=\*\|\*\S\@=" end="\S\@<=\*\|\*\S\@=" skip="\\\*" contains=markdownLineStart,@Spell' . s:concealends +exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter start="\w\@<!_\S\@=" end="\S\@<=_\w\@!" skip="\\_" contains=markdownLineStart,@Spell' . s:concealends +exe 'syn region markdownBold matchgroup=markdownBoldDelimiter start="\S\@<=\*\*\|\*\*\S\@=" end="\S\@<=\*\*\|\*\*\S\@=" skip="\\\*" contains=markdownLineStart,markdownItalic,@Spell' . s:concealends +exe 'syn region markdownBold matchgroup=markdownBoldDelimiter start="\w\@<!__\S\@=" end="\S\@<=__\w\@!" skip="\\_" contains=markdownLineStart,markdownItalic,@Spell' . s:concealends +exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\S\@<=\*\*\*\|\*\*\*\S\@=" end="\S\@<=\*\*\*\|\*\*\*\S\@=" skip="\\\*" contains=markdownLineStart,@Spell' . s:concealends +exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\w\@<!___\S\@=" end="\S\@<=___\w\@!" skip="\\_" contains=markdownLineStart,@Spell' . s:concealends syn region markdownCode matchgroup=markdownCodeDelimiter start="`" end="`" keepend contains=markdownLineStart syn region markdownCode matchgroup=markdownCodeDelimiter start="`` \=" end=" \=``" keepend contains=markdownLineStart -syn region markdownCode matchgroup=markdownCodeDelimiter start="^\s*```.*$" end="^\s*```\ze\s*$" keepend +syn region markdownCode matchgroup=markdownCodeDelimiter start="^\s*````*.*$" end="^\s*````*\ze\s*$" keepend syn match markdownFootnote "\[^[^\]]\+\]" syn match markdownFootnoteDefinition "^\[^[^\]]\+\]:" if main_syntax ==# 'markdown' + let s:done_include = {} for s:type in g:markdown_fenced_languages - exe 'syn region markdownHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\..*','','').' matchgroup=markdownCodeDelimiter start="^\s*```\s*'.matchstr(s:type,'[^=]*').'\>.*$" end="^\s*```\ze\s*$" keepend contains=@markdownHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\.','','g') + if has_key(s:done_include, matchstr(s:type,'[^.]*')) + continue + endif + exe 'syn region markdownHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\..*','','').' matchgroup=markdownCodeDelimiter start="^\s*````*\s*\%({.\{-}\.\)\='.matchstr(s:type,'[^=]*').'}\=\S\@!.*$" end="^\s*````*\ze\s*$" keepend contains=@markdownHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\.','','g') . s:concealends + let s:done_include[matchstr(s:type,'[^.]*')] = 1 endfor unlet! s:type + unlet! s:done_include endif syn match markdownEscape "\\[][\\`*_{}()<>#+.!-]" @@ -107,6 +125,12 @@ hi def link markdownH4 htmlH4 hi def link markdownH5 htmlH5 hi def link markdownH6 htmlH6 hi def link markdownHeadingRule markdownRule +hi def link markdownH1Delimiter markdownHeadingDelimiter +hi def link markdownH2Delimiter markdownHeadingDelimiter +hi def link markdownH3Delimiter markdownHeadingDelimiter +hi def link markdownH4Delimiter markdownHeadingDelimiter +hi def link markdownH5Delimiter markdownHeadingDelimiter +hi def link markdownH6Delimiter markdownHeadingDelimiter hi def link markdownHeadingDelimiter Delimiter hi def link markdownOrderedListMarker markdownListMarker hi def link markdownListMarker htmlTagName diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index 03f52bd162..8287958ab5 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -597,8 +597,11 @@ list_missing_previous_vimpatches_for_patch() { set -u local -a missing_unique + local stat while IFS= read -r line; do - missing_unique+=("$line") + local commit="${line%%:*}" + stat="$(git -C "${VIM_SOURCE_DIR}" show --format= --shortstat "${commit}")" + missing_unique+=("$(printf '%s\n %s' "$line" "$stat")") done < <(printf '%s\n' "${missing_list[@]}" | sort -u) msg_err "$(printf '%d missing previous Vim patches:' ${#missing_unique[@]})" diff --git a/src/clint.py b/src/clint.py index 8dc41fdb93..9b4128a0c9 100755 --- a/src/clint.py +++ b/src/clint.py @@ -350,7 +350,7 @@ def IsErrorInSuppressedErrorsList(category, linenum): category: str, the category of the error. linenum: int, the current line number. Returns: - bool, True iff the error should be suppressed due to presense in + bool, True iff the error should be suppressed due to presence in suppressions file. """ return (category, linenum) in _error_suppressions_2 diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index c8dd85b39d..cad4c8314f 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -953,6 +953,53 @@ Boolean nvim_buf_is_loaded(Buffer buffer) return buf && buf->b_ml.ml_mfp != NULL; } +/// Deletes the buffer. See |:bwipeout| +/// +/// @param buffer Buffer handle, or 0 for current buffer +/// @param opts Optional parameters. Keys: +/// - force: Force deletion and ignore unsaved changes. +/// - unload: Unloaded only, do not delete. See |:bunload| +void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err) + FUNC_API_SINCE(7) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (ERROR_SET(err)) { + return; + } + + bool force = false; + bool unload = false; + for (size_t i = 0; i < opts.size; i++) { + String k = opts.items[i].key; + Object v = opts.items[i].value; + if (strequal("force", k.data)) { + force = api_coerce_to_bool(v, "force", false, err); + } else if (strequal("unload", k.data)) { + unload = api_coerce_to_bool(v, "unload", false, err); + } else { + api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); + return; + } + } + + if (ERROR_SET(err)) { + return; + } + + int result = do_buffer( + unload ? DOBUF_UNLOAD : DOBUF_WIPE, + DOBUF_FIRST, + FORWARD, + buf->handle, + force); + + if (result == FAIL) { + api_set_error(err, kErrorTypeException, "Failed to unload buffer."); + return; + } +} + /// Checks if a buffer is valid. /// /// @note Even if a buffer is valid it may have been unloaded. See |api-buffer| @@ -1394,7 +1441,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, goto error; } } else if (strequal("ephemeral", k.data)) { - ephemeral = api_is_truthy(*v, "ephemeral", false, err); + ephemeral = api_coerce_to_bool(*v, "ephemeral", false, err); if (ERROR_SET(err)) { goto error; } diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 84517c99fc..981d41ae6e 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1619,14 +1619,24 @@ free_exit: return virt_text; } -bool api_is_truthy(Object obj, const char *what, bool nil_truthy, Error *err) +/// Force obj to bool. +/// If it fails, returns false and sets err +/// @param obj The object to coerce to a boolean +/// @param what The name of the object, used for error message +/// @param nil_value What to return if the type is nil. +/// @param err Set if there was an error in converting to a bool +bool api_coerce_to_bool( + Object obj, + const char *what, + bool nil_value, + Error *err) { if (obj.type == kObjectTypeBoolean) { return obj.data.boolean; } else if (obj.type == kObjectTypeInteger) { return obj.data.integer; // C semantics: non-zero int is true } else if (obj.type == kObjectTypeNil) { - return nil_truthy; // caller decides what NIL (missing retval in lua) means + return nil_value; // caller decides what NIL (missing retval in lua) means } else { api_set_error(err, kErrorTypeValidation, "%s is not an boolean", what); return false; diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index ef5e90bf5c..e934d5dc92 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -1,7 +1,7 @@ #ifndef NVIM_API_UI_EVENTS_IN_H #define NVIM_API_UI_EVENTS_IN_H -// This file is not compiled, just parsed for definitons +// This file is not compiled, just parsed for definitions #ifdef INCLUDE_GENERATED_DECLARATIONS # error "don't include this file, include nvim/ui.h" #endif @@ -44,7 +44,7 @@ void option_set(String name, Object value) void stop(void) FUNC_API_NOEXPORT; -// First revison of the grid protocol, used by default +// First revision of the grid protocol, used by default void update_fg(Integer fg) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; void update_bg(Integer bg) @@ -68,7 +68,7 @@ void set_scroll_region(Integer top, Integer bot, Integer left, Integer right) void scroll(Integer count) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; -// Second revison of the grid protocol, used with ext_linegrid ui option +// Second revision of the grid protocol, used with ext_linegrid ui option void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, Integer cterm_fg, Integer cterm_bg) FUNC_API_SINCE(4) FUNC_API_REMOTE_IMPL; @@ -91,7 +91,7 @@ void grid_scroll(Integer grid, Integer top, Integer bot, void grid_destroy(Integer grid) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; -// For perfomance and simplicity, we use the dense screen representation +// For performance and simplicity, we use the dense screen representation // in internal code, such as compositor and TUI. The remote_ui module will // translate this in to the public grid_line format. void raw_line(Integer grid, Integer row, Integer startcol, diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 4391d997a7..10647c01a4 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -65,7 +65,8 @@ return { 'InsertChange', -- when changing Insert/Replace mode 'InsertCharPre', -- before inserting a char 'InsertEnter', -- when entering Insert mode - 'InsertLeave', -- when leaving Insert mode + 'InsertLeave', -- just after leaving Insert mode + 'InsertLeavePre', -- just before leaving Insert mode 'MenuPopup', -- just before popup menu is displayed 'OptionSet', -- after setting any option 'QuickFixCmdPost', -- after :make, :grep etc. diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index ec633dcc26..8f631ae13b 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5397,13 +5397,11 @@ bool buf_hide(const buf_T *const buf) char_u *buf_spname(buf_T *buf) { if (bt_quickfix(buf)) { - win_T *win; - tabpage_T *tp; + win_T *win; + tabpage_T *tp; - /* - * For location list window, w_llist_ref points to the location list. - * For quickfix window, w_llist_ref is NULL. - */ + // For location list window, w_llist_ref points to the location list. + // For quickfix window, w_llist_ref is NULL. if (find_win_for_buf(buf, &win, &tp) && win->w_llist_ref != NULL) { return (char_u *)_(msg_loclist); } else { diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 5e5a20e8f2..1223f2bdab 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -91,6 +91,7 @@ typedef struct { #define BF_READERR 0x40 // got errors while reading the file #define BF_DUMMY 0x80 // dummy buffer, only used internally #define BF_PRESERVED 0x100 // ":preserve" was used +#define BF_SYN_SET 0x200 // 'syntax' option was set // Mask to check for flags that prevent normal writing #define BF_WRITE_MASK (BF_NOTEDITED + BF_NEW + BF_READERR) diff --git a/src/nvim/change.c b/src/nvim/change.c index 71614363d2..be52750c44 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -142,7 +142,6 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra) { int i; - int cols; pos_T *p; int add; @@ -170,7 +169,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, if (p->lnum != lnum) { add = true; } else { - cols = comp_textwidth(false); + int cols = comp_textwidth(false); if (cols == 0) { cols = 79; } diff --git a/src/nvim/charset.c b/src/nvim/charset.c index f9d5adbc12..fb158f377a 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -509,7 +509,7 @@ char_u* str_foldcase(char_u *str, int orglen, char_u *buf, int buflen) // Does NOT work for multi-byte characters, c must be <= 255. // Also doesn't work for the first byte of a multi-byte, "c" must be a // character! -static char_u transchar_buf[11]; +static char_u transchar_charbuf[11]; /// Translate a character into a printable one, leaving printable ASCII intact /// @@ -520,11 +520,17 @@ static char_u transchar_buf[11]; /// @return translated character into a static buffer. char_u *transchar(int c) { + return transchar_buf(curbuf, c); +} + +char_u *transchar_buf(const buf_T *buf, int c) + FUNC_ATTR_NONNULL_ALL +{ int i = 0; if (IS_SPECIAL(c)) { // special key code, display as ~@ char - transchar_buf[0] = '~'; - transchar_buf[1] = '@'; + transchar_charbuf[0] = '~'; + transchar_charbuf[1] = '@'; i = 2; c = K_SECOND(c); } @@ -532,14 +538,14 @@ char_u *transchar(int c) if ((!chartab_initialized && (((c >= ' ') && (c <= '~')))) || ((c <= 0xFF) && vim_isprintc_strict(c))) { // printable character - transchar_buf[i] = (char_u)c; - transchar_buf[i + 1] = NUL; + transchar_charbuf[i] = (char_u)c; + transchar_charbuf[i + 1] = NUL; } else if (c <= 0xFF) { - transchar_nonprint(transchar_buf + i, c); + transchar_nonprint(buf, transchar_charbuf + i, c); } else { - transchar_hex((char *)transchar_buf + i, c); + transchar_hex((char *)transchar_charbuf + i, c); } - return transchar_buf; + return transchar_charbuf; } /// Like transchar(), but called with a byte instead of a character @@ -548,13 +554,13 @@ char_u *transchar(int c) /// /// @param[in] c Byte to translate. /// -/// @return pointer to translated character in transchar_buf. +/// @return pointer to translated character in transchar_charbuf. char_u *transchar_byte(const int c) FUNC_ATTR_WARN_UNUSED_RESULT { if (c >= 0x80) { - transchar_nonprint(transchar_buf, c); - return transchar_buf; + transchar_nonprint(curbuf, transchar_charbuf, c); + return transchar_charbuf; } return transchar(c); } @@ -563,16 +569,18 @@ char_u *transchar_byte(const int c) /// /// @warning Does not work for multi-byte characters, c must be <= 255. /// -/// @param[out] buf Buffer to store result in, must be able to hold at least -/// 5 bytes (conversion result + NUL). +/// @param[in] buf Required to check the file format +/// @param[out] charbuf Buffer to store result in, must be able to hold +/// at least 5 bytes (conversion result + NUL). /// @param[in] c Character to convert. NUL is assumed to be NL according to /// `:h NL-used-for-NUL`. -void transchar_nonprint(char_u *buf, int c) +void transchar_nonprint(const buf_T *buf, char_u *charbuf, int c) + FUNC_ATTR_NONNULL_ALL { if (c == NL) { // we use newline in place of a NUL c = NUL; - } else if ((c == CAR) && (get_fileformat(curbuf) == EOL_MAC)) { + } else if ((c == CAR) && (get_fileformat(buf) == EOL_MAC)) { // we use CR in place of NL in this case c = NL; } @@ -580,14 +588,14 @@ void transchar_nonprint(char_u *buf, int c) if (dy_flags & DY_UHEX || c > 0x7f) { // 'display' has "uhex" - transchar_hex((char *)buf, c); + transchar_hex((char *)charbuf, c); } else { // 0x00 - 0x1f and 0x7f - buf[0] = '^'; + charbuf[0] = '^'; // DEL displayed as ^? - buf[1] = (char_u)(c ^ 0x40); + charbuf[1] = (char_u)(c ^ 0x40); - buf[2] = NUL; + charbuf[2] = NUL; } } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index de2346a9d8..b3261cfce6 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -5549,13 +5549,11 @@ void insertchar( int second_indent // indent for second line if >= 0 ) { - int textwidth; char_u *p; - int fo_ins_blank; int force_format = flags & INSCHAR_FORMAT; - textwidth = comp_textwidth(force_format); - fo_ins_blank = has_format_option(FO_INS_BLANK); + const int textwidth = comp_textwidth(force_format); + const bool fo_ins_blank = has_format_option(FO_INS_BLANK); /* * Try to break the line in two or more pieces when: @@ -5756,10 +5754,11 @@ internal_format ( int cc; int save_char = NUL; bool haveto_redraw = false; - int fo_ins_blank = has_format_option(FO_INS_BLANK); - int fo_multibyte = has_format_option(FO_MBYTE_BREAK); - int fo_white_par = has_format_option(FO_WHITE_PAR); - int first_line = TRUE; + const bool fo_ins_blank = has_format_option(FO_INS_BLANK); + const bool fo_multibyte = has_format_option(FO_MBYTE_BREAK); + const bool fo_rigor_tw = has_format_option(FO_RIGOROUS_TW); + const bool fo_white_par = has_format_option(FO_WHITE_PAR); + bool first_line = true; colnr_T leader_len; bool no_leader = false; int do_comments = (flags & INSCHAR_DO_COM); @@ -5838,6 +5837,7 @@ internal_format ( curwin->w_cursor.col = startcol; foundcol = 0; + int skip_pos = 0; /* * Find position to break at. @@ -5907,7 +5907,11 @@ internal_format ( foundcol = curwin->w_cursor.col; if (curwin->w_cursor.col <= (colnr_T)wantcol) break; - } else if (cc >= 0x100 && fo_multibyte) { + } else if ((cc >= 0x100 || !utf_allow_break_before(cc)) + && fo_multibyte) { + int ncc; + bool allow_break; + // Break after or before a multi-byte character. if (curwin->w_cursor.col != startcol) { // Don't break until after the comment leader @@ -5916,8 +5920,11 @@ internal_format ( } col = curwin->w_cursor.col; inc_cursor(); - // Don't change end_foundcol if already set. - if (foundcol != curwin->w_cursor.col) { + ncc = gchar_cursor(); + allow_break = utf_allow_break(cc, ncc); + + // If we have already checked this position, skip! + if (curwin->w_cursor.col != skip_pos && allow_break) { foundcol = curwin->w_cursor.col; end_foundcol = foundcol; if (curwin->w_cursor.col <= (colnr_T)wantcol) @@ -5929,6 +5936,7 @@ internal_format ( if (curwin->w_cursor.col == 0) break; + ncc = cc; col = curwin->w_cursor.col; dec_cursor(); @@ -5937,17 +5945,56 @@ internal_format ( if (WHITECHAR(cc)) { continue; // break with space } - // Don't break until after the comment leader + // Don't break until after the comment leader. if (curwin->w_cursor.col < leader_len) { break; } curwin->w_cursor.col = col; + skip_pos = curwin->w_cursor.col; - foundcol = curwin->w_cursor.col; - end_foundcol = foundcol; - if (curwin->w_cursor.col <= (colnr_T)wantcol) - break; + allow_break = utf_allow_break(cc, ncc); + + // Must handle this to respect line break prohibition. + if (allow_break) { + foundcol = curwin->w_cursor.col; + end_foundcol = foundcol; + } + if (curwin->w_cursor.col <= (colnr_T)wantcol) { + const bool ncc_allow_break = utf_allow_break_before(ncc); + + if (allow_break) { + break; + } + if (!ncc_allow_break && !fo_rigor_tw) { + // Enable at most 1 punct hang outside of textwidth. + if (curwin->w_cursor.col == startcol) { + // We are inserting a non-breakable char, postpone + // line break check to next insert. + end_foundcol = foundcol = 0; + break; + } + + // Neither cc nor ncc is NUL if we are here, so + // it's safe to inc_cursor. + col = curwin->w_cursor.col; + + inc_cursor(); + cc = ncc; + ncc = gchar_cursor(); + // handle insert + ncc = (ncc != NUL) ? ncc : c; + + allow_break = utf_allow_break(cc, ncc); + + if (allow_break) { + // Break only when we are not at end of line. + end_foundcol = foundcol = ncc == NUL? 0 : curwin->w_cursor.col; + break; + } + curwin->w_cursor.col = col; + } + } } if (curwin->w_cursor.col == 0) break; @@ -6049,7 +6096,7 @@ internal_format ( } } } - first_line = FALSE; + first_line = false; } if (State & VREPLACE_FLAG) { @@ -6236,12 +6283,10 @@ static void check_auto_format( * Set default to window width (maximum 79) for "gq" operator. */ int comp_textwidth( - int ff // force formatting (for "gq" command) + bool ff // force formatting (for "gq" command) ) { - int textwidth; - - textwidth = curbuf->b_p_tw; + int textwidth = curbuf->b_p_tw; if (textwidth == 0 && curbuf->b_p_wm) { // The width is the window width minus 'wrapmargin' minus all the // things that add to the margin. @@ -7691,6 +7736,10 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) undisplay_dollar(); } + if (cmdchar != 'r' && cmdchar != 'v') { + ins_apply_autocmds(EVENT_INSERTLEAVEPRE); + } + // When an autoindent was removed, curswant stays after the // indent if (restart_edit == NUL && (colnr_T)temp == curwin->w_cursor.col) { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a2490be355..cccf1e50ff 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2994,7 +2994,6 @@ char_u *get_user_var_name(expand_T *xp, int idx) static size_t tdone; static size_t vidx; static hashitem_T *hi; - hashtab_T *ht; if (idx == 0) { gdone = bdone = wdone = vidx = 0; @@ -3015,7 +3014,10 @@ char_u *get_user_var_name(expand_T *xp, int idx) } // b: variables - ht = &curbuf->b_vars->dv_hashtab; + // In cmdwin, the alternative buffer should be used. + hashtab_T *ht = (cmdwin_type != 0 && get_cmdline_type() == NUL) + ? &prevwin->w_buffer->b_vars->dv_hashtab + : &curbuf->b_vars->dv_hashtab; if (bdone < ht->ht_used) { if (bdone++ == 0) hi = ht->ht_array; @@ -3027,7 +3029,10 @@ char_u *get_user_var_name(expand_T *xp, int idx) } // w: variables - ht = &curwin->w_vars->dv_hashtab; + // In cmdwin, the alternative window should be used. + ht = (cmdwin_type != 0 && get_cmdline_type() == NUL) + ? &prevwin->w_vars->dv_hashtab + : &curwin->w_vars->dv_hashtab; if (wdone < ht->ht_used) { if (wdone++ == 0) hi = ht->ht_array; diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 372c950825..6c316bb1fe 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -275,6 +275,7 @@ return { rpcrequest={args=varargs(2)}, rpcstart={args={1, 2}}, rpcstop={args=1}, + rubyeval={args=1}, screenattr={args=2}, screenchar={args=2}, screencol={}, @@ -371,7 +372,7 @@ return { tolower={args=1}, toupper={args=1}, tr={args=3}, - trim={args={1,2}}, + trim={args={1,3}}, trunc={args=1, func="float_op_wrapper", data="&trunc"}, type={args=1}, undofile={args=1}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 83ad948a93..d2e9c68965 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6381,6 +6381,12 @@ static void f_perleval(typval_T *argvars, typval_T *rettv, FunPtr fptr) script_host_eval("perl", argvars, rettv); } +// "rubyeval()" function +static void f_rubyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + script_host_eval("ruby", argvars, rettv); +} + /* * "range()" function */ @@ -6936,7 +6942,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) } ptrdiff_t len = (ptrdiff_t)strlen(p); - if (len > 0 && after_pathsep(p, p + len)) { + if (len > 1 && after_pathsep(p, p + len)) { has_trailing_pathsep = true; p[len - 1] = NUL; // The trailing slash breaks readlink(). } @@ -7668,7 +7674,7 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) } retval = do_searchpair( - (char_u *)spat, (char_u *)mpat, (char_u *)epat, dir, skip, + spat, mpat, epat, dir, skip, flags, match_pos, lnum_stop, time_limit); theend: @@ -7712,9 +7718,9 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ long do_searchpair( - char_u *spat, // start pattern - char_u *mpat, // middle pattern - char_u *epat, // end pattern + const char *spat, // start pattern + const char *mpat, // middle pattern + const char *epat, // end pattern int dir, // BACKWARD or FORWARD const typval_T *skip, // skip expression int flags, // SP_SETPCMARK and other SP_ values @@ -7722,6 +7728,7 @@ do_searchpair( linenr_T lnum_stop, // stop at this line if not zero long time_limit // stop after this many msec ) + FUNC_ATTR_NONNULL_ARG(1, 2, 3) { char_u *save_cpo; char_u *pat, *pat2 = NULL, *pat3 = NULL; @@ -7736,8 +7743,6 @@ do_searchpair( bool use_skip = false; int options = SEARCH_KEEP; proftime_T tm; - size_t pat2_len; - size_t pat3_len; // Make 'cpoptions' empty, the 'l' flag should not be used here. save_cpo = p_cpo; @@ -7748,9 +7753,9 @@ do_searchpair( // Make two search patterns: start/end (pat2, for in nested pairs) and // start/middle/end (pat3, for the top pair). - pat2_len = STRLEN(spat) + STRLEN(epat) + 17; + const size_t pat2_len = strlen(spat) + strlen(epat) + 17; pat2 = xmalloc(pat2_len); - pat3_len = STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 25; + const size_t pat3_len = strlen(spat) + strlen(mpat) + strlen(epat) + 25; pat3 = xmalloc(pat3_len); snprintf((char *)pat2, pat2_len, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); if (*mpat == NUL) { @@ -8142,15 +8147,17 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// Create quickfix/location list from VimL values /// /// Used by `setqflist()` and `setloclist()` functions. Accepts invalid -/// list_arg, action_arg and what_arg arguments in which case errors out, -/// including VAR_UNKNOWN parameters. +/// args argument in which case errors out, including VAR_UNKNOWN parameters. /// /// @param[in,out] wp Window to create location list for. May be NULL in /// which case quickfix list will be created. -/// @param[in] list_arg Quickfix list contents. -/// @param[in] action_arg Action to perform: append to an existing list, -/// replace its content or create a new one. -/// @param[in] title_arg New list title. Defaults to caller function name. +/// @param[in] args [list, action, what] +/// @param[in] args[0] Quickfix list contents. +/// @param[in] args[1] Optional. Action to perform: +/// append to an existing list, replace its content, +/// or create a new one. +/// @param[in] args[2] Optional. Quickfix list properties or title. +/// Defaults to caller function name. /// @param[out] rettv Return value: 0 in case of success, -1 otherwise. static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) FUNC_ATTR_NONNULL_ARG(2, 3) @@ -8160,7 +8167,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) int action = ' '; static int recursive = 0; rettv->vval.v_number = -1; - dict_T *d = NULL; + dict_T *what = NULL; typval_T *list_arg = &args[0]; if (list_arg->v_type != VAR_LIST) { @@ -8188,18 +8195,18 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) return; } - typval_T *title_arg = &args[2]; - if (title_arg->v_type == VAR_UNKNOWN) { + typval_T *const what_arg = &args[2]; + if (what_arg->v_type == VAR_UNKNOWN) { // Option argument was not given. goto skip_args; - } else if (title_arg->v_type == VAR_STRING) { - title = tv_get_string_chk(title_arg); + } else if (what_arg->v_type == VAR_STRING) { + title = tv_get_string_chk(what_arg); if (!title) { // Type error. Error already printed by tv_get_string_chk(). return; } - } else if (title_arg->v_type == VAR_DICT) { - d = title_arg->vval.v_dict; + } else if (what_arg->v_type == VAR_DICT && what_arg->vval.v_dict != NULL) { + what = what_arg->vval.v_dict; } else { EMSG(_(e_dictreq)); return; @@ -8212,7 +8219,7 @@ skip_args: recursive++; list_T *const l = list_arg->vval.v_list; - if (set_errorlist(wp, l, action, (char_u *)title, d) == OK) { + if (set_errorlist(wp, l, action, (char_u *)title, what) == OK) { rettv->vval.v_number = 0; } recursive--; @@ -10824,52 +10831,72 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char_u *prev; const char_u *p; int c1; + int dir = 0; rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; if (head == NULL) { - rettv->vval.v_string = NULL; return; } if (argvars[1].v_type == VAR_STRING) { mask = (const char_u *)tv_get_string_buf_chk(&argvars[1], buf2); + if (argvars[2].v_type != VAR_UNKNOWN) { + bool error = false; + // leading or trailing characters to trim + dir = (int)tv_get_number_chk(&argvars[2], &error); + if (error) { + return; + } + if (dir < 0 || dir > 2) { + emsgf(_(e_invarg2), tv_get_string(&argvars[2])); + return; + } + } } - while (*head != NUL) { - c1 = PTR2CHAR(head); - if (mask == NULL) { - if (c1 > ' ' && c1 != 0xa0) { - break; - } - } else { - for (p = mask; *p != NUL; MB_PTR_ADV(p)) { - if (c1 == PTR2CHAR(p)) { + if (dir == 0 || dir == 1) { + // Trim leading characters + while (*head != NUL) { + c1 = PTR2CHAR(head); + if (mask == NULL) { + if (c1 > ' ' && c1 != 0xa0) { + break; + } + } else { + for (p = mask; *p != NUL; MB_PTR_ADV(p)) { + if (c1 == PTR2CHAR(p)) { + break; + } + } + if (*p == NUL) { break; } } - if (*p == NUL) { - break; - } + MB_PTR_ADV(head); } - MB_PTR_ADV(head); } - for (tail = head + STRLEN(head); tail > head; tail = prev) { - prev = tail; - MB_PTR_BACK(head, prev); - c1 = PTR2CHAR(prev); - if (mask == NULL) { - if (c1 > ' ' && c1 != 0xa0) { - break; - } - } else { - for (p = mask; *p != NUL; MB_PTR_ADV(p)) { - if (c1 == PTR2CHAR(p)) { + tail = head + STRLEN(head); + if (dir == 0 || dir == 2) { + // Trim trailing characters + for (; tail > head; tail = prev) { + prev = tail; + MB_PTR_BACK(head, prev); + c1 = PTR2CHAR(prev); + if (mask == NULL) { + if (c1 > ' ' && c1 != 0xa0) { + break; + } + } else { + for (p = mask; *p != NUL; MB_PTR_ADV(p)) { + if (c1 == PTR2CHAR(p)) { + break; + } + } + if (*p == NUL) { break; } - } - if (*p == NUL) { - break; } } } diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index e0361048bc..dc94bc698d 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1052,7 +1052,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, // A Lambda always has the command "return {expr}". It is much faster // to evaluate {expr} directly. ex_nesting_level++; - eval1(&p, rettv, true); + (void)eval1(&p, rettv, true); ex_nesting_level--; } else { // call do_cmdline() to execute the lines @@ -2920,8 +2920,10 @@ void ex_call(exarg_T *eap) if (!failed || eap->cstack->cs_trylevel > 0) { // Check for trailing illegal characters and a following command. if (!ends_excmd(*arg)) { - emsg_severe = true; - EMSG(_(e_trailing)); + if (!failed) { + emsg_severe = true; + EMSG(_(e_trailing)); + } } else { eap->nextcmd = check_nextcmd(arg); } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index bb4e92efc0..3669cbbd2d 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -135,7 +135,7 @@ void do_ascii(const exarg_T *const eap) char buf1[20]; if (vim_isprintc_strict(c) && (c < ' ' || c > '~')) { char_u buf3[7]; - transchar_nonprint(buf3, c); + transchar_nonprint(curbuf, buf3, c); vim_snprintf(buf1, sizeof(buf1), " <%s>", (char *)buf3); } else { buf1[0] = NUL; @@ -2240,11 +2240,9 @@ int do_ecmd( goto theend; } - /* - * if the file was changed we may not be allowed to abandon it - * - if we are going to re-edit the same file - * - or if we are the only window on this file and if ECMD_HIDE is FALSE - */ + // If the file was changed we may not be allowed to abandon it: + // - if we are going to re-edit the same file + // - or if we are the only window on this file and if ECMD_HIDE is FALSE if ( ((!other_file && !(flags & ECMD_OLDBUF)) || (curbuf->b_nwindows == 1 && !(flags & (ECMD_HIDE | ECMD_ADDBUF)))) diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 3e169f7a4e..503fd8e0d0 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2058,6 +2058,10 @@ void ex_listdo(exarg_T *eap) // Don't do syntax HL autocommands. Skipping the syntax file is a // great speed improvement. save_ei = au_event_disable(",Syntax"); + + FOR_ALL_BUFFERS(buf) { + buf->b_flags &= ~BF_SYN_SET; + } } if (eap->cmdidx == CMD_windo @@ -2252,9 +2256,32 @@ void ex_listdo(exarg_T *eap) } if (save_ei != NULL) { + buf_T *bnext; + aco_save_T aco; + au_event_restore(save_ei); - apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, - curbuf->b_fname, true, curbuf); + + for (buf_T *buf = firstbuf; buf != NULL; buf = bnext) { + bnext = buf->b_next; + if (buf->b_nwindows > 0 && (buf->b_flags & BF_SYN_SET)) { + buf->b_flags &= ~BF_SYN_SET; + + // buffer was opened while Syntax autocommands were disabled, + // need to trigger them now. + if (buf == curbuf) { + apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, + curbuf->b_fname, true, curbuf); + } else { + aucmd_prepbuf(&aco, buf); + apply_autocmds(EVENT_SYNTAX, buf->b_p_syn, + buf->b_fname, true, buf); + aucmd_restbuf(&aco); + } + + // start over, in case autocommands messed things up. + bnext = firstbuf; + } + } } } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index ccaa0b0e52..211791c19d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2400,6 +2400,7 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) } break; case ADDR_TABS_RELATIVE: + case ADDR_OTHER: *errormsg = (char_u *)_(e_invrange); return FAIL; case ADDR_ARGUMENTS: @@ -5057,16 +5058,18 @@ fail: static struct { int expand; char *name; + char *shortname; } addr_type_complete[] = { - { ADDR_ARGUMENTS, "arguments" }, - { ADDR_LINES, "lines" }, - { ADDR_LOADED_BUFFERS, "loaded_buffers" }, - { ADDR_TABS, "tabs" }, - { ADDR_BUFFERS, "buffers" }, - { ADDR_WINDOWS, "windows" }, - { ADDR_QUICKFIX, "quickfix" }, - { -1, NULL } + { ADDR_ARGUMENTS, "arguments", "arg" }, + { ADDR_LINES, "lines", "line" }, + { ADDR_LOADED_BUFFERS, "loaded_buffers", "load" }, + { ADDR_TABS, "tabs", "tab" }, + { ADDR_BUFFERS, "buffers", "buf" }, + { ADDR_WINDOWS, "windows", "win" }, + { ADDR_QUICKFIX, "quickfix", "qf" }, + { ADDR_OTHER, "other", "?" }, + { -1, NULL, NULL } }; /* @@ -5151,7 +5154,7 @@ static void uc_list(char_u *name, size_t name_len) // Put out the title first time if (!found) { MSG_PUTS_TITLE(_("\n Name Args Address " - "Complete Definition")); + "Complete Definition")); } found = true; msg_putchar('\n'); @@ -5237,13 +5240,13 @@ static void uc_list(char_u *name, size_t name_len) do { IObuff[len++] = ' '; - } while (len < 9 - over); + } while (len < 8 - over); // Address Type for (j = 0; addr_type_complete[j].expand != -1; j++) { if (addr_type_complete[j].expand != ADDR_LINES && addr_type_complete[j].expand == cmd->uc_addr_type) { - STRCPY(IObuff + len, addr_type_complete[j].name); + STRCPY(IObuff + len, addr_type_complete[j].shortname); len += (int)STRLEN(IObuff + len); break; } @@ -5262,13 +5265,13 @@ static void uc_list(char_u *name, size_t name_len) do { IObuff[len++] = ' '; - } while (len < 24 - over); + } while (len < 25 - over); IObuff[len] = '\0'; msg_outtrans(IObuff); msg_outtrans_special(cmd->uc_rep, false, - name_len == 0 ? Columns - 46 : 0); + name_len == 0 ? Columns - 47 : 0); if (p_verbose > 0) { last_set_msg(cmd->uc_script_ctx); } @@ -5397,7 +5400,7 @@ invalid_count: if (parse_addr_type_arg(val, (int)vallen, argt, addr_type_arg) == FAIL) { return FAIL; } - if (addr_type_arg != ADDR_LINES) { + if (*addr_type_arg != ADDR_LINES) { *argt |= (ZEROR | NOTADR); } } else { @@ -6926,8 +6929,9 @@ void ex_splitview(exarg_T *eap) { win_T *old_curwin = curwin; char_u *fname = NULL; - - + const bool use_tab = eap->cmdidx == CMD_tabedit + || eap->cmdidx == CMD_tabfind + || eap->cmdidx == CMD_tabnew; /* A ":split" in the quickfix window works like ":new". Don't want two * quickfix windows. But it's OK when doing ":tab split". */ @@ -6949,9 +6953,7 @@ void ex_splitview(exarg_T *eap) /* * Either open new tab page or split the window. */ - if (eap->cmdidx == CMD_tabedit - || eap->cmdidx == CMD_tabfind - || eap->cmdidx == CMD_tabnew) { + if (use_tab) { if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab : eap->addr_count == 0 ? 0 : (int)eap->line2 + 1, eap->arg) != FAIL) { do_exedit(eap, old_curwin); @@ -7136,14 +7138,14 @@ static void ex_resize(exarg_T *eap) n = atol((char *)eap->arg); if (cmdmod.split & WSP_VERT) { if (*eap->arg == '-' || *eap->arg == '+') { - n += curwin->w_width; + n += wp->w_width; } else if (n == 0 && eap->arg[0] == NUL) { // default is very wide n = Columns; } win_setwidth_win(n, wp); } else { if (*eap->arg == '-' || *eap->arg == '+') { - n += curwin->w_height; + n += wp->w_height; } else if (n == 0 && eap->arg[0] == NUL) { // default is very high n = Rows-1; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index f9ca7bfa42..940f446a7b 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -5193,7 +5193,7 @@ ExpandFromContext ( * obtain strings, one by one. The strings are matched against a regexp * program. Matching strings are copied into an array, which is returned. */ -void ExpandGeneric( +static void ExpandGeneric( expand_T *xp, regmatch_T *regmatch, int *num_file, diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 1b4a89fb6c..ad856b588a 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3593,7 +3593,7 @@ restore_backup: * the backup file our 'original' file. */ if (*p_pm && dobackup) { - char *org = modname((char *)fname, (char *)p_pm, FALSE); + char *const org = modname((char *)fname, (char *)p_pm, false); if (backup != NULL) { /* diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua index 075d8ba9cc..849c82f50e 100644 --- a/src/nvim/generators/gen_ex_cmds.lua +++ b/src/nvim/generators/gen_ex_cmds.lua @@ -24,8 +24,6 @@ local defsfile = io.open(defsfname, 'w') local defs = require('ex_cmds') -local first = true - local byte_a = string.byte('a') local byte_z = string.byte('z') local a_to_z = byte_z - byte_a + 1 @@ -41,8 +39,7 @@ static const uint16_t cmdidxs1[%u] = { -- fit in a byte. local cmdidxs2_out = string.format([[ static const char_u cmdidxs2[%u][%u] = { -/* a b c d e f g h i j k l m n o p q r s t u v w x y z */ - + /* a b c d e f g h i j k l m n o p q r s t u v w x y z */ ]], a_to_z, a_to_z) enumfile:write([[ @@ -50,10 +47,8 @@ typedef enum CMD_index { ]]) defsfile:write(string.format([[ static const int command_count = %u; -]], #defs)) -defsfile:write(string.format([[ static CommandDefinition cmdnames[%u] = { -]], #defs)) +]], #defs, #defs)) local cmds, cmdidxs1, cmdidxs2 = {}, {}, {} for _, cmd in ipairs(defs) do local enumname = cmd.enum or ('CMD_' .. cmd.command) @@ -61,11 +56,6 @@ for _, cmd in ipairs(defs) do if byte_a <= byte_cmd and byte_cmd <= byte_z then table.insert(cmds, cmd.command) end - if first then - first = false - else - defsfile:write(',\n') - end enumfile:write(' ' .. enumname .. ',\n') defsfile:write(string.format([[ [%s] = { @@ -73,7 +63,8 @@ for _, cmd in ipairs(defs) do .cmd_func = (ex_func_T)&%s, .cmd_argt = %uL, .cmd_addr_type = %i - }]], enumname, cmd.command, cmd.func, cmd.flags, cmd.addr_type)) + }, +]], enumname, cmd.command, cmd.func, cmd.flags, cmd.addr_type)) end for i = #cmds, 1, -1 do local cmd = cmds[i] @@ -104,15 +95,14 @@ for i = byte_a, byte_z do end cmdidxs2_out = cmdidxs2_out .. ' },\n' end -defsfile:write([[ - -}; -]]) enumfile:write([[ CMD_SIZE, CMD_USER = -1, CMD_USER_BUF = -2 } cmdidx_T; ]]) -defsfile:write(cmdidxs1_out .. '};\n') -defsfile:write(cmdidxs2_out .. '};\n') +defsfile:write(string.format([[ +}; +%s}; +%s}; +]], cmdidxs1_out, cmdidxs2_out)) diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua index a8cf496cb9..d80a6219eb 100644 --- a/src/nvim/generators/gen_options.lua +++ b/src/nvim/generators/gen_options.lua @@ -141,9 +141,6 @@ local dump_option = function(i, o) elseif #o.scope == 1 and o.scope[1] == 'window' then w(' .var=VAR_WIN') end - if o.enable_if then - w('#endif') - end if #o.scope == 1 and o.scope[1] == 'global' then w(' .indir=PV_NONE') else @@ -163,6 +160,12 @@ local dump_option = function(i, o) defines['PV_' .. varname:sub(3):upper()] = pv_name w(' .indir=' .. pv_name) end + if o.enable_if then + w('#else') + w(' .var=NULL') + w(' .indir=PV_NONE') + w('#endif') + end if o.defaults then if o.defaults.condition then w(get_cond(o.defaults.condition)) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index cbd9582f8b..456979be00 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -4204,7 +4204,6 @@ int put_escstr(FILE *fd, char_u *strstart, int what) { char_u *str = strstart; int c; - int modifiers; // :map xx <Nop> if (*str == NUL && what == 1) { @@ -4231,7 +4230,7 @@ int put_escstr(FILE *fd, char_u *strstart, int what) * when they are read back. */ if (c == K_SPECIAL && what != 2) { - modifiers = 0x0; + int modifiers = 0; if (str[1] == KS_MODIFIER) { modifiers = str[2]; str += 3; diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 33d9772bec..5258352e72 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -43,6 +43,7 @@ static struct luaL_Reg parser_meta[] = { { "edit", parser_edit }, { "tree", parser_tree }, { "set_included_ranges", parser_set_ranges }, + { "included_ranges", parser_get_ranges }, { NULL, NULL } }; @@ -314,6 +315,26 @@ static const char *input_cb(void *payload, uint32_t byte_index, #undef BUFSIZE } +static void push_ranges(lua_State *L, + const TSRange *ranges, + const unsigned int length) +{ + lua_createtable(L, length, 0); + for (size_t i = 0; i < length; i++) { + lua_createtable(L, 4, 0); + lua_pushinteger(L, ranges[i].start_point.row); + lua_rawseti(L, -2, 1); + lua_pushinteger(L, ranges[i].start_point.column); + lua_rawseti(L, -2, 2); + lua_pushinteger(L, ranges[i].end_point.row); + lua_rawseti(L, -2, 3); + lua_pushinteger(L, ranges[i].end_point.column); + lua_rawseti(L, -2, 4); + + lua_rawseti(L, -2, i+1); + } +} + static int parser_parse(lua_State *L) { TSLua_parser *p = parser_check(L); @@ -363,20 +384,8 @@ static int parser_parse(lua_State *L) tslua_push_tree(L, p->tree); - lua_createtable(L, n_ranges, 0); - for (size_t i = 0; i < n_ranges; i++) { - lua_createtable(L, 4, 0); - lua_pushinteger(L, changed[i].start_point.row); - lua_rawseti(L, -2, 1); - lua_pushinteger(L, changed[i].start_point.column); - lua_rawseti(L, -2, 2); - lua_pushinteger(L, changed[i].end_point.row); - lua_rawseti(L, -2, 3); - lua_pushinteger(L, changed[i].end_point.column); - lua_rawseti(L, -2, 4); + push_ranges(L, changed, n_ranges); - lua_rawseti(L, -2, i+1); - } xfree(changed); return 2; } @@ -474,6 +483,21 @@ static int parser_set_ranges(lua_State *L) return 0; } +static int parser_get_ranges(lua_State *L) +{ + TSLua_parser *p = parser_check(L); + if (!p || !p->parser) { + return 0; + } + + unsigned int len; + const TSRange *ranges = ts_parser_included_ranges(p->parser, &len); + + push_ranges(L, ranges, len); + + return 1; +} + // Tree methods diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index e67be60aa6..ec4f4cbc21 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -512,7 +512,7 @@ int utf_ptr2cells(const char_u *p) { int c; - /* Need to convert to a wide character. */ + // Need to convert to a character number. if (*p >= 0x80) { c = utf_ptr2char(p); /* An illegal byte is displayed as <xx>. */ @@ -582,7 +582,7 @@ size_t mb_string2cells_len(const char_u *str, size_t size) return clen; } -/// Convert a UTF-8 byte sequence to a wide character +/// Convert a UTF-8 byte sequence to a character number. /// /// If the sequence is illegal or truncated by a NUL then the first byte is /// returned. @@ -1624,6 +1624,146 @@ int utf_head_off(const char_u *base, const char_u *p) return (int)(p - q); } +// Whether space is NOT allowed before/after 'c'. +bool utf_eat_space(int cc) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + return (cc >= 0x2000 && cc <= 0x206F) // General punctuations + || (cc >= 0x2e00 && cc <= 0x2e7f) // Supplemental punctuations + || (cc >= 0x3000 && cc <= 0x303f) // CJK symbols and punctuations + || (cc >= 0xff01 && cc <= 0xff0f) // Full width ASCII punctuations + || (cc >= 0xff1a && cc <= 0xff20) // .. + || (cc >= 0xff3b && cc <= 0xff40) // .. + || (cc >= 0xff5b && cc <= 0xff65); // .. +} + +// Whether line break is allowed before "cc". +bool utf_allow_break_before(int cc) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + static const int BOL_prohibition_punct[] = { + '!', + '%', + ')', + ',', + ':', + ';', + '>', + '?', + ']', + '}', + 0x2019, // ’ right single quotation mark + 0x201d, // ” right double quotation mark + 0x2020, // † dagger + 0x2021, // ‡ double dagger + 0x2026, // … horizontal ellipsis + 0x2030, // ‰ per mille sign + 0x2031, // ‱ per then thousand sign + 0x203c, // ‼ double exclamation mark + 0x2047, // ⁇ double question mark + 0x2048, // ⁈ question exclamation mark + 0x2049, // ⁉ exclamation question mark + 0x2103, // ℃ degree celsius + 0x2109, // ℉ degree fahrenheit + 0x3001, // 、 ideographic comma + 0x3002, // 。 ideographic full stop + 0x3009, // 〉 right angle bracket + 0x300b, // 》 right double angle bracket + 0x300d, // 」 right corner bracket + 0x300f, // 』 right white corner bracket + 0x3011, // 】 right black lenticular bracket + 0x3015, // 〕 right tortoise shell bracket + 0x3017, // 〗 right white lenticular bracket + 0x3019, // 〙 right white tortoise shell bracket + 0x301b, // 〛 right white square bracket + 0xff01, // ! fullwidth exclamation mark + 0xff09, // ) fullwidth right parenthesis + 0xff0c, // , fullwidth comma + 0xff0e, // . fullwidth full stop + 0xff1a, // : fullwidth colon + 0xff1b, // ; fullwidth semicolon + 0xff1f, // ? fullwidth question mark + 0xff3d, // ] fullwidth right square bracket + 0xff5d, // } fullwidth right curly bracket + }; + + int first = 0; + int last = ARRAY_SIZE(BOL_prohibition_punct) - 1; + + while (first < last) { + const int mid = (first + last) / 2; + + if (cc == BOL_prohibition_punct[mid]) { + return false; + } else if (cc > BOL_prohibition_punct[mid]) { + first = mid + 1; + } else { + last = mid - 1; + } + } + + return cc != BOL_prohibition_punct[first]; +} + +// Whether line break is allowed after "cc". +bool utf_allow_break_after(int cc) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + static const int EOL_prohibition_punct[] = { + '(', + '<', + '[', + '`', + '{', + // 0x2014, // — em dash + 0x2018, // ‘ left single quotation mark + 0x201c, // “ left double quotation mark + // 0x2053, // ~ swung dash + 0x3008, // 〈 left angle bracket + 0x300a, // 《 left double angle bracket + 0x300c, // 「 left corner bracket + 0x300e, // 『 left white corner bracket + 0x3010, // 【 left black lenticular bracket + 0x3014, // 〔 left tortoise shell bracket + 0x3016, // 〖 left white lenticular bracket + 0x3018, // 〘 left white tortoise shell bracket + 0x301a, // 〚 left white square bracket + 0xff08, // ( fullwidth left parenthesis + 0xff3b, // [ fullwidth left square bracket + 0xff5b, // { fullwidth left curly bracket + }; + + int first = 0; + int last = ARRAY_SIZE(EOL_prohibition_punct) - 1; + + while (first < last) { + const int mid = (first + last)/2; + + if (cc == EOL_prohibition_punct[mid]) { + return false; + } else if (cc > EOL_prohibition_punct[mid]) { + first = mid + 1; + } else { + last = mid - 1; + } + } + + return cc != EOL_prohibition_punct[first]; +} + +// Whether line break is allowed between "cc" and "ncc". +bool utf_allow_break(int cc, int ncc) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + // don't break between two-letter punctuations + if (cc == ncc + && (cc == 0x2014 // em dash + || cc == 0x2026)) { // horizontal ellipsis + return false; + } + return utf_allow_break_after(cc) && utf_allow_break_before(ncc); +} + /// Copy a character, advancing the pointers /// /// @param[in,out] fp Source of the character to copy. diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 2a75f13cc2..57ed0d6588 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1360,7 +1360,7 @@ recover_names ( * Try finding a swap file by simply adding ".swp" to the file name. */ if (*dirp == NUL && file_count + num_files == 0 && fname != NULL) { - char_u *swapname = (char_u *)modname((char *)fname_res, ".swp", TRUE); + char_u *swapname = (char_u *)modname((char *)fname_res, ".swp", true); if (swapname != NULL) { if (os_path_exists(swapname)) { files = xmalloc(sizeof(char_u *)); @@ -1638,10 +1638,11 @@ static int recov_file_names(char_u **names, char_u *path, int prepend_dot) // May also add the file name with a dot prepended, for swap file in same // dir as original file. if (prepend_dot) { - names[num_names] = (char_u *)modname((char *)path, ".sw?", TRUE); - if (names[num_names] == NULL) + names[num_names] = (char_u *)modname((char *)path, ".sw?", true); + if (names[num_names] == NULL) { return num_names; - ++num_names; + } + num_names++; } // Form the normal swap file name pattern by appending ".sw?". @@ -2422,17 +2423,17 @@ int ml_replace(linenr_T lnum, char_u *line, bool copy) return ml_replace_buf(curbuf, lnum, line, copy); } -/* - * Replace line lnum, with buffering, in current buffer. - * - * If "copy" is TRUE, make a copy of the line, otherwise the line has been - * copied to allocated memory already. - * - * Check: The caller of this function should probably also call - * changed_lines(), unless update_screen(NOT_VALID) is used. - * - * return FAIL for failure, OK otherwise - */ +// Replace line "lnum", with buffering, in current buffer. +// +// If "copy" is true, make a copy of the line, otherwise the line has been +// copied to allocated memory already. +// If "copy" is false the "line" may be freed to add text properties! +// Do not use it after calling ml_replace(). +// +// Check: The caller of this function should probably also call +// changed_lines(), unless update_screen(NOT_VALID) is used. +// +// return FAIL for failure, OK otherwise int ml_replace_buf(buf_T *buf, linenr_T lnum, char_u *line, bool copy) { if (line == NULL) /* just checking... */ @@ -3193,6 +3194,12 @@ char_u *makeswapname(char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name char_u *fname_res = fname; #ifdef HAVE_READLINK char_u fname_buf[MAXPATHL]; + + // Expand symlink in the file name, so that we put the swap file with the + // actual file instead of with the symlink. + if (resolve_symlink(fname, fname_buf) == OK) { + fname_res = fname_buf; + } #endif int len = (int)STRLEN(dir_name); @@ -3201,20 +3208,14 @@ char_u *makeswapname(char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name && len > 1 && s[-1] == s[-2]) { // Ends with '//', Use Full path r = NULL; - if ((s = (char_u *)make_percent_swname((char *)dir_name, (char *)fname)) != NULL) { - r = (char_u *)modname((char *)s, ".swp", FALSE); + s = (char_u *)make_percent_swname((char *)dir_name, (char *)fname_res); + if (s != NULL) { + r = (char_u *)modname((char *)s, ".swp", false); xfree(s); } return r; } -#ifdef HAVE_READLINK - /* Expand symlink in the file name, so that we put the swap file with the - * actual file instead of with the symlink. */ - if (resolve_symlink(fname, fname_buf) == OK) - fname_res = fname_buf; -#endif - // Prepend a '.' to the swap file name for the current directory. r = (char_u *)modname((char *)fname_res, ".swp", dir_name[0] == '.' && dir_name[1] == NUL); diff --git a/src/nvim/message.c b/src/nvim/message.c index e4f47917ec..6cd5616acf 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1621,7 +1621,7 @@ const char *str2special(const char **const sp, const bool replace_spaces, // Check for an illegal byte. if (MB_BYTE2LEN((uint8_t)(*str)) > len) { - transchar_nonprint((char_u *)buf, c); + transchar_nonprint(curbuf, (char_u *)buf, c); *sp = str + 1; return buf; } @@ -1886,6 +1886,7 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) // wait-return prompt later. Needed when scrolling, resetting // need_wait_return after some prompt, and then outputting something // without scrolling + // Not needed when only using CR to move the cursor. bool overflow = false; if (ui_has(kUIMessages)) { int count = msg_ext_visible + (msg_ext_overwrite ? 0 : 1); @@ -1897,7 +1898,7 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) overflow = msg_scrolled != 0; } - if (overflow && !msg_scrolled_ign) { + if (overflow && !msg_scrolled_ign && strcmp(str, "\r") != 0) { need_wait_return = true; } msg_didany = true; // remember that something was outputted @@ -1999,7 +2000,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, || (*s == TAB && msg_col <= 7) || (utf_ptr2cells(s) > 1 && msg_col <= 2)) - : (msg_col + t_col >= Columns - 1 + : ((*s != '\r' && msg_col + t_col >= Columns - 1) || (*s == TAB && msg_col + t_col >= ((Columns - 1) & ~7)) || (utf_ptr2cells(s) > 1 diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 6dafbafb3e..1cd9ff2c4d 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -983,7 +983,7 @@ void preserve_exit(void) FOR_ALL_BUFFERS(buf) { if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) { - mch_errmsg((uint8_t *)"Vim: preserving files...\n"); + mch_errmsg("Vim: preserving files...\r\n"); ui_flush(); ml_sync_all(false, false, true); // preserve all swap files break; @@ -992,7 +992,7 @@ void preserve_exit(void) ml_close_all(false); // close all memfiles, without deleting - mch_errmsg("Vim: Finished.\n"); + mch_errmsg("Vim: Finished.\r\n"); getout(1); } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index a51aa0dc07..1cc400166c 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -6841,7 +6841,7 @@ static void nv_g_cmd(cmdarg_T *cap) } else { if (cap->count1 > 1) { // if it fails, let the cursor still move to the last char - cursor_down(cap->count1 - 1, false); + (void)cursor_down(cap->count1 - 1, false); } i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1; coladvance((colnr_T)i); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 8329daf5f1..939cde0ba1 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3833,7 +3833,8 @@ int do_join(size_t count, && (!has_format_option(FO_MBYTE_JOIN) || (utf_ptr2char(curr) < 0x100 && endcurr1 < 0x100)) && (!has_format_option(FO_MBYTE_JOIN2) - || utf_ptr2char(curr) < 0x100 || endcurr1 < 0x100) + || (utf_ptr2char(curr) < 0x100 && !utf_eat_space(endcurr1)) + || (endcurr1 < 0x100 && !utf_eat_space(utf_ptr2char(curr)))) ) { /* don't add a space if the line is ending in a space */ if (endcurr1 == ' ') @@ -4158,49 +4159,41 @@ format_lines( int avoid_fex /* don't use 'formatexpr' */ ) { - int max_len; - int is_not_par; /* current line not part of parag. */ - int next_is_not_par; /* next line not part of paragraph */ - int is_end_par; /* at end of paragraph */ - int prev_is_end_par = FALSE; /* prev. line not part of parag. */ - int next_is_start_par = FALSE; - int leader_len = 0; /* leader len of current line */ - int next_leader_len; /* leader len of next line */ - char_u *leader_flags = NULL; /* flags for leader of current line */ - char_u *next_leader_flags; /* flags for leader of next line */ - int do_comments; /* format comments */ - int do_comments_list = 0; /* format comments with 'n' or '2' */ - int advance = TRUE; - int second_indent = -1; /* indent for second line (comment - * aware) */ - int do_second_indent; - int do_number_indent; - int do_trail_white; - int first_par_line = TRUE; + bool is_not_par; // current line not part of parag. + bool next_is_not_par; // next line not part of paragraph + bool is_end_par; // at end of paragraph + bool prev_is_end_par = false; // prev. line not part of parag. + bool next_is_start_par = false; + int leader_len = 0; // leader len of current line + int next_leader_len; // leader len of next line + char_u *leader_flags = NULL; // flags for leader of current line + char_u *next_leader_flags; // flags for leader of next line + bool advance = true; + int second_indent = -1; // indent for second line (comment aware) + bool first_par_line = true; int smd_save; long count; - int need_set_indent = TRUE; /* set indent of next paragraph */ - int force_format = FALSE; - int old_State = State; - - /* length of a line to force formatting: 3 * 'tw' */ - max_len = comp_textwidth(TRUE) * 3; - - /* check for 'q', '2' and '1' in 'formatoptions' */ - do_comments = has_format_option(FO_Q_COMS); - do_second_indent = has_format_option(FO_Q_SECOND); - do_number_indent = has_format_option(FO_Q_NUMBER); - do_trail_white = has_format_option(FO_WHITE_PAR); - - /* - * Get info about the previous and current line. - */ - if (curwin->w_cursor.lnum > 1) - is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1 - , &leader_len, &leader_flags, do_comments - ); - else - is_not_par = TRUE; + bool need_set_indent = true; // set indent of next paragraph + bool force_format = false; + const int old_State = State; + + // length of a line to force formatting: 3 * 'tw' + const int max_len = comp_textwidth(true) * 3; + + // check for 'q', '2' and '1' in 'formatoptions' + const bool do_comments = has_format_option(FO_Q_COMS); // format comments + int do_comments_list = 0; // format comments with 'n' or '2' + const bool do_second_indent = has_format_option(FO_Q_SECOND); + const bool do_number_indent = has_format_option(FO_Q_NUMBER); + const bool do_trail_white = has_format_option(FO_WHITE_PAR); + + // Get info about the previous and current line. + if (curwin->w_cursor.lnum > 1) { + is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1, + &leader_len, &leader_flags, do_comments); + } else { + is_not_par = true; + } next_is_not_par = fmt_check_par(curwin->w_cursor.lnum , &next_leader_len, &next_leader_flags, do_comments ); @@ -4225,7 +4218,7 @@ format_lines( * The last line to be formatted. */ if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) { - next_is_not_par = TRUE; + next_is_not_par = true; next_leader_len = 0; next_leader_flags = NULL; } else { @@ -4236,7 +4229,7 @@ format_lines( next_is_start_par = (get_number_indent(curwin->w_cursor.lnum + 1) > 0); } - advance = TRUE; + advance = true; is_end_par = (is_not_par || next_is_not_par || next_is_start_par); if (!is_end_par && do_trail_white) is_end_par = !ends_in_white(curwin->w_cursor.lnum); @@ -4287,7 +4280,7 @@ format_lines( leader_len, leader_flags, next_leader_len, next_leader_flags) ) - is_end_par = TRUE; + is_end_par = true; /* * If we have got to the end of a paragraph, or the line is @@ -4324,9 +4317,9 @@ format_lines( * end of the paragraph. */ if (line_count < 0) break; - first_par_line = TRUE; + first_par_line = true; } - force_format = FALSE; + force_format = false; } /* @@ -4334,7 +4327,7 @@ format_lines( * first delete the leader from the second line. */ if (!is_end_par) { - advance = FALSE; + advance = false; curwin->w_cursor.lnum++; curwin->w_cursor.col = 0; if (line_count < 0 && u_save_cursor() == FAIL) @@ -4357,12 +4350,13 @@ format_lines( beep_flush(); break; } - first_par_line = FALSE; - /* If the line is getting long, format it next time */ - if (STRLEN(get_cursor_line_ptr()) > (size_t)max_len) - force_format = TRUE; - else - force_format = FALSE; + first_par_line = false; + // If the line is getting long, format it next time + if (STRLEN(get_cursor_line_ptr()) > (size_t)max_len) { + force_format = true; + } else { + force_format = false; + } } } line_breakcheck(); @@ -4423,11 +4417,10 @@ static int fmt_check_par(linenr_T lnum, int *leader_len, char_u **leader_flags, int paragraph_start(linenr_T lnum) { char_u *p; - int leader_len = 0; /* leader len of current line */ - char_u *leader_flags = NULL; /* flags for leader of current line */ - int next_leader_len = 0; /* leader len of next line */ - char_u *next_leader_flags = NULL; /* flags for leader of next line */ - int do_comments; /* format comments */ + int leader_len = 0; // leader len of current line + char_u *leader_flags = NULL; // flags for leader of current line + int next_leader_len = 0; // leader len of next line + char_u *next_leader_flags = NULL; // flags for leader of next line if (lnum <= 1) return TRUE; /* start of the file */ @@ -4436,7 +4429,7 @@ int paragraph_start(linenr_T lnum) if (*p == NUL) return TRUE; /* after empty line */ - do_comments = has_format_option(FO_Q_COMS); + const bool do_comments = has_format_option(FO_Q_COMS); // format comments if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments)) { return true; // after non-paragraph line } diff --git a/src/nvim/option.c b/src/nvim/option.c index 6ae9378236..4569eb1dda 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3438,6 +3438,7 @@ ambw_end: // recursively, to avoid endless recurrence. apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, curbuf->b_fname, value_changed || syn_recursive == 1, curbuf); + curbuf->b_flags |= BF_SYN_SET; syn_recursive--; } else if (varp == &(curbuf->b_p_ft)) { // 'filetype' is set, trigger the FileType autocommand @@ -6802,7 +6803,8 @@ static void langmap_set(void) /// Return true if format option 'x' is in effect. /// Take care of no formatting when 'paste' is set. -int has_format_option(int x) +bool has_format_option(int x) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { if (p_paste) { return false; @@ -7269,7 +7271,8 @@ unsigned int get_bkc_value(buf_T *buf) } /// Return the current end-of-line type: EOL_DOS, EOL_UNIX or EOL_MAC. -int get_fileformat(buf_T *buf) +int get_fileformat(const buf_T *buf) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { int c = *buf->b_p_ff; diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index a09811c8fb..6630bda710 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -77,12 +77,13 @@ #define FO_ONE_LETTER '1' #define FO_WHITE_PAR 'w' // trailing white space continues paragr. #define FO_AUTO 'a' // automatic formatting +#define FO_RIGOROUS_TW ']' // respect textwidth rigorously #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,awjp" // for do_set() +#define FO_ALL "tcroq2vlb1mMBn,aw]jp" // for do_set() // characters for the p_cpo option: #define CPO_ALTREAD 'a' // ":read" sets alternate file name diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index bfe230b521..bc774b8ebc 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -161,8 +161,8 @@ static void deadly_signal(int signum) WLOG("got signal %d (%s)", signum, signal_name(signum)); - snprintf((char *)IObuff, sizeof(IObuff), "Vim: Caught deadly signal '%s'\n", - signal_name(signum)); + snprintf((char *)IObuff, sizeof(IObuff), "Vim: Caught deadly signal '%s'\r\n", + signal_name(signum)); // Preserve files and exit. preserve_exit(); diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 3beada5bc9..d2d20852aa 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -301,49 +301,49 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, if (pum_width < p_pw) { pum_width = (int)p_pw; } - } - } else if (((cursor_col > p_pw || cursor_col > max_width) && !pum_rl) - || (pum_rl && (cursor_col < Columns - p_pw - || cursor_col < Columns - max_width))) { - // align pum edge with "cursor_col" - if (pum_rl && W_ENDCOL(curwin) < max_width + pum_scrollbar + 1) { - pum_col = cursor_col + max_width + pum_scrollbar + 1; - if (pum_col >= Columns) { - pum_col = Columns - 1; - } - } else if (!pum_rl) { - if (curwin->w_wincol > Columns - max_width - pum_scrollbar - && max_width <= p_pw) { - // use full width to end of the screen - pum_col = cursor_col - max_width - pum_scrollbar; - if (pum_col < 0) { - pum_col = 0; + } else if (((cursor_col > p_pw || cursor_col > max_width) && !pum_rl) + || (pum_rl && (cursor_col < Columns - p_pw + || cursor_col < Columns - max_width))) { + // align pum edge with "cursor_col" + if (pum_rl && W_ENDCOL(curwin) < max_width + pum_scrollbar + 1) { + pum_col = cursor_col + max_width + pum_scrollbar + 1; + if (pum_col >= Columns) { + pum_col = Columns - 1; + } + } else if (!pum_rl) { + if (curwin->w_wincol > Columns - max_width - pum_scrollbar + && max_width <= p_pw) { + // use full width to end of the screen + pum_col = Columns - max_width - pum_scrollbar; + if (pum_col < 0) { + pum_col = 0; + } } } - } - - if (pum_rl) { - pum_width = pum_col - pum_scrollbar + 1; - } else { - pum_width = Columns - pum_col - pum_scrollbar; - } - if (pum_width < p_pw) { - pum_width = (int)p_pw; if (pum_rl) { - if (pum_width > pum_col) { - pum_width = pum_col; - } + pum_width = pum_col - pum_scrollbar + 1; } else { - if (pum_width >= Columns - pum_col) { - pum_width = Columns - pum_col - 1; - } + pum_width = Columns - pum_col - pum_scrollbar; } - } else if (pum_width > max_width + pum_kind_width + pum_extra_width + 1 - && pum_width > p_pw) { - pum_width = max_width + pum_kind_width + pum_extra_width + 1; + if (pum_width < p_pw) { pum_width = (int)p_pw; + if (pum_rl) { + if (pum_width > pum_col) { + pum_width = pum_col; + } + } else { + if (pum_width >= Columns - pum_col) { + pum_width = Columns - pum_col - 1; + } + } + } else if (pum_width > max_width + pum_kind_width + pum_extra_width + 1 + && pum_width > p_pw) { + pum_width = max_width + pum_kind_width + pum_extra_width + 1; + if (pum_width < p_pw) { + pum_width = (int)p_pw; + } } } } else if (Columns < def_width) { @@ -919,11 +919,11 @@ void pum_set_event_info(dict_T *dict) r = (double)pum_row; c = (double)pum_col; } - tv_dict_add_float(dict, S_LEN("height"), h); - tv_dict_add_float(dict, S_LEN("width"), w); - tv_dict_add_float(dict, S_LEN("row"), r); - tv_dict_add_float(dict, S_LEN("col"), c); - tv_dict_add_nr(dict, S_LEN("size"), pum_size); - tv_dict_add_bool(dict, S_LEN("scrollbar"), - pum_scrollbar ? kBoolVarTrue : kBoolVarFalse); + (void)tv_dict_add_float(dict, S_LEN("height"), h); + (void)tv_dict_add_float(dict, S_LEN("width"), w); + (void)tv_dict_add_float(dict, S_LEN("row"), r); + (void)tv_dict_add_float(dict, S_LEN("col"), c); + (void)tv_dict_add_nr(dict, S_LEN("size"), pum_size); + (void)tv_dict_add_bool(dict, S_LEN("scrollbar"), + pum_scrollbar ? kBoolVarTrue : kBoolVarFalse); } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index fc2e1a4295..1cd7879dc0 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1092,6 +1092,7 @@ qf_init_ext( ) FUNC_ATTR_NONNULL_ARG(1) { + qf_list_T *qfl; qfstate_T state = { 0 }; qffields_T fields = { 0 }; qfline_T *old_last = NULL; @@ -1115,15 +1116,16 @@ qf_init_ext( // make place for a new list qf_new_list(qi, qf_title); qf_idx = qi->qf_curlist; + qfl = qf_get_list(qi, qf_idx); } else { // Adding to existing list, use last entry. adding = true; - if (!qf_list_empty(qf_get_list(qi, qf_idx) )) { - old_last = qi->qf_lists[qf_idx].qf_last; + qfl = qf_get_list(qi, qf_idx); + if (!qf_list_empty(qfl)) { + old_last = qfl->qf_last; } } - qf_list_T *qfl = qf_get_list(qi, qf_idx); // Use the local value of 'errorformat' if it's set. if (errorformat == p_efm && tv == NULL && buf && *buf->b_p_efm != NUL) { @@ -1665,8 +1667,8 @@ static int qf_parse_multiline_pfx(int idx, qf_list_T *qfl, qffields_T *fields) } if (!qfprev->qf_col) { qfprev->qf_col = fields->col; + qfprev->qf_viscol = fields->use_viscol; } - qfprev->qf_viscol = fields->use_viscol; if (!qfprev->qf_fnum) { qfprev->qf_fnum = qf_get_fnum(qfl, qfl->qf_directory, *fields->namebuf || qfl->qf_directory @@ -1775,7 +1777,7 @@ static void decr_quickfix_busy(void) void check_quickfix_busy(void) { if (quickfix_busy != 0) { - EMSGN("quickfix_busy not zero on exit: %ld", (long)quickfix_busy); + EMSGN("quickfix_busy not zero on exit: %" PRId64, (int64_t)quickfix_busy); # ifdef ABORT_ON_INTERNAL_ERROR abort(); # endif @@ -2453,12 +2455,13 @@ static void win_set_loclist(win_T *wp, qf_info_T *qi) qi->qf_refcount++; } -/// Find a help window or open one. -static int jump_to_help_window(qf_info_T *qi, int *opened_window) +/// Find a help window or open one. If 'newwin' is true, then open a new help +/// window. +static int jump_to_help_window(qf_info_T *qi, bool newwin, int *opened_window) { win_T *wp = NULL; - if (cmdmod.tab != 0) { + if (cmdmod.tab != 0 || newwin) { wp = NULL; } else { wp = qf_find_help_win(); @@ -2476,8 +2479,10 @@ static int jump_to_help_window(qf_info_T *qi, int *opened_window) flags |= WSP_TOP; } - if (IS_LL_STACK(qi)) { - flags |= WSP_NEWLOC; // don't copy the location list + // If the user asks to open a new window, then copy the location list. + // Otherwise, don't copy the location list. + if (IS_LL_STACK(qi) && !newwin) { + flags |= WSP_NEWLOC; } if (win_split(0, flags) == FAIL) { @@ -2490,8 +2495,10 @@ static int jump_to_help_window(qf_info_T *qi, int *opened_window) win_setheight((int)p_hh); } - if (IS_LL_STACK(qi)) { // not a quickfix list - // The new window should use the supplied location list + // When using location list, the new window should use the supplied + // location list. If the user asks to open a new window, then the new + // window will get a copy of the location list. + if (IS_LL_STACK(qi) && !newwin) { win_set_loclist(curwin, qi); } } @@ -2652,14 +2659,19 @@ static void qf_goto_win_with_qfl_file(int qf_fnum) // Find a suitable window for opening a file (qf_fnum) from the // quickfix/location list and jump to it. If the file is already opened in a -// window, jump to it. Otherwise open a new window to display the file. This is -// called from either a quickfix or a location list window. -static int qf_jump_to_usable_window(int qf_fnum, int *opened_window) +// window, jump to it. Otherwise open a new window to display the file. If +// 'newwin' is true, then always open a new window. This is called from either +// a quickfix or a location list window. +static int qf_jump_to_usable_window(int qf_fnum, bool newwin, + int *opened_window) { win_T *usable_win_ptr = NULL; bool usable_win = false; - qf_info_T *ll_ref = curwin->w_llist_ref; + // If opening a new window, then don't use the location list referred by + // the current window. Otherwise two windows will refer to the same + // location list. + qf_info_T *ll_ref = newwin ? NULL : curwin->w_llist_ref; if (ll_ref != NULL) { // Find a non-quickfix window with this location list usable_win_ptr = qf_find_win_with_loclist(ll_ref); @@ -2684,7 +2696,7 @@ static int qf_jump_to_usable_window(int qf_fnum, int *opened_window) // If there is only one window and it is the quickfix window, create a // new one above the quickfix window. - if ((ONE_WINDOW && bt_quickfix(curbuf)) || !usable_win) { + if ((ONE_WINDOW && bt_quickfix(curbuf)) || !usable_win || newwin) { if (qf_open_new_file_win(ll_ref) != OK) { return FAIL; } @@ -2823,11 +2835,12 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, } /// Find a usable window for opening a file from the quickfix/location list. If -/// a window is not found then open a new window. +/// a window is not found then open a new window. If 'newwin' is true, then open +/// a new window. /// Returns OK if successfully jumped or opened a window. Returns FAIL if not /// able to jump/open a window. Returns NOTDONE if a file is not associated /// with the entry. -static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, +static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int *opened_window) { qf_list_T *qfl = qf_get_curlist(qi); @@ -2837,7 +2850,7 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, // For ":helpgrep" find a help window or open one. if (qf_ptr->qf_type == 1 && (!bt_help(curwin->w_buffer) || cmdmod.tab != 0)) { - if (jump_to_help_window(qi, opened_window) == FAIL) { + if (jump_to_help_window(qi, newwin, opened_window) == FAIL) { return FAIL; } } @@ -2861,7 +2874,8 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, return NOTDONE; } - if (qf_jump_to_usable_window(qf_ptr->qf_fnum, opened_window) == FAIL) { + if (qf_jump_to_usable_window(qf_ptr->qf_fnum, newwin, opened_window) + == FAIL) { return FAIL; } } @@ -2924,15 +2938,24 @@ static int qf_jump_to_buffer(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, return retval; } -/// jump to a quickfix ltne -/// if dir == FORWARD go "errornr" valid entries forward -/// if dir == BACKWARD go "errornr" valid entries backward -/// if dir == FORWARD_FILE go "errornr" valid entries files backward -/// if dir == BACKWARD_FILE go "errornr" valid entries files backward -/// else if "errornr" is zero, redisplay the same line -/// else go to entry "errornr" +/// Jump to a quickfix line and try to use an existing window. void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit) { + qf_jump_newwin(qi, dir, errornr, forceit, false); +} + +// Jump to a quickfix line. +// If dir == 0 go to entry "errornr". +// If dir == FORWARD go "errornr" valid entries forward. +// If dir == BACKWARD go "errornr" valid entries backward. +// If dir == FORWARD_FILE go "errornr" valid entries files backward. +// If dir == BACKWARD_FILE go "errornr" valid entries files backward +// else if "errornr" is zero, redisplay the same line +// If 'forceit' is true, then can discard changes to the current buffer. +// If 'newwin' is true, then open the file in a new window. +static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, + bool newwin) +{ qf_list_T *qfl; qfline_T *qf_ptr; qfline_T *old_qf_ptr; @@ -2975,7 +2998,7 @@ void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit) print_message = false; } - retval = qf_jump_open_window(qi, qf_ptr, &opened_window); + retval = qf_jump_open_window(qi, qf_ptr, newwin, &opened_window); if (retval == FAIL) { goto failed; } @@ -3277,7 +3300,7 @@ void qf_history(exarg_T *eap) qf_info_T *qi = qf_cmd_get_stack(eap, false); int i; - if (qf_stack_empty(qi) || qf_list_empty(qf_get_curlist(qi))) { + if (qf_stack_empty(qi)) { MSG(_("No entries")); } else { for (i = 0; i < qi->qf_listcount; i++) { @@ -3445,14 +3468,9 @@ void qf_view_result(bool split) } if (split) { - char cmd[32]; - - snprintf(cmd, sizeof(cmd), "split +%" PRId64 "%s", - (int64_t)curwin->w_cursor.lnum, - IS_LL_WINDOW(curwin) ? "ll" : "cc"); - if (do_cmdline_cmd(cmd) == OK) { - do_cmdline_cmd("clearjumps"); - } + // Open the selected entry in a new window + qf_jump_newwin(qi, 0, (int)curwin->w_cursor.lnum, false, true); + do_cmdline_cmd("clearjumps"); return; } @@ -3483,7 +3501,7 @@ void ex_cwindow(exarg_T *eap) // it if we have errors; otherwise, leave it closed. if (qf_stack_empty(qi) || qfl->qf_nonevalid - || qf_list_empty(qf_get_curlist(qi))) { + || qf_list_empty(qfl)) { if (win != NULL) { ex_cclose(eap); } @@ -3536,10 +3554,23 @@ static int qf_goto_cwindow(const qf_info_T *qi, bool resize, int sz, return OK; } +// Set options for the buffer in the quickfix or location list window. +static void qf_set_cwindow_options(void) +{ + // switch off 'swapfile' + set_option_value("swf", 0L, NULL, OPT_LOCAL); + set_option_value("bt", 0L, "quickfix", OPT_LOCAL); + set_option_value("bh", 0L, "wipe", OPT_LOCAL); + RESET_BINDING(curwin); + curwin->w_p_diff = false; + set_option_value("fdm", 0L, "manual", OPT_LOCAL); +} + // Open a new quickfix or location list window, load the quickfix buffer and // set the appropriate options for the window. // Returns FAIL if the window could not be opened. -static int qf_open_new_cwindow(const qf_info_T *qi, int height) +static int qf_open_new_cwindow(qf_info_T *qi, int height) + FUNC_ATTR_NONNULL_ALL { win_T *oldwin = curwin; const tabpage_T *const prevtab = curtab; @@ -3582,14 +3613,13 @@ static int qf_open_new_cwindow(const qf_info_T *qi, int height) } else { // Create a new quickfix buffer (void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, oldwin); + } - // switch off 'swapfile' - set_option_value("swf", 0L, NULL, OPT_LOCAL); - set_option_value("bt", 0L, "quickfix", OPT_LOCAL); - set_option_value("bh", 0L, "wipe", OPT_LOCAL); - RESET_BINDING(curwin); - curwin->w_p_diff = false; - set_option_value("fdm", 0L, "manual", OPT_LOCAL); + // Set the options for the quickfix buffer/window (if not already done) + // Do this even if the quickfix buffer was already present, as an autocmd + // might have previously deleted (:bdelete) the quickfix buffer. + if (curbuf->b_p_bt[0] != 'q') { + qf_set_cwindow_options(); } // Only set the height when still in the same tab page and there is no @@ -3778,8 +3808,8 @@ static win_T *qf_find_win(const qf_info_T *qi) // Find a quickfix buffer. // Searches in windows opened in all the tabs. -static buf_T *qf_find_buf(const qf_info_T *qi) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +static buf_T *qf_find_buf(qf_info_T *qi) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { FOR_ALL_TAB_WINDOWS(tp, win) { if (is_qf_win(win, qi)) { @@ -4922,7 +4952,7 @@ static bool vgr_qflist_valid(win_T *wp, qf_info_T *qi, unsigned qfid, /// Search for a pattern in all the lines in a buffer and add the matching lines /// to a quickfix list. -static bool vgr_match_buflines(qf_info_T *qi, char_u *fname, buf_T *buf, +static bool vgr_match_buflines(qf_list_T *qfl, char_u *fname, buf_T *buf, regmmatch_T *regmatch, long *tomatch, int duplicate_name, int flags) FUNC_ATTR_NONNULL_ARG(1, 3, 4, 5) @@ -4936,7 +4966,7 @@ static bool vgr_match_buflines(qf_info_T *qi, char_u *fname, buf_T *buf, // 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. - if (qf_add_entry(qf_get_curlist(qi), + if (qf_add_entry(qfl, NULL, // dir fname, NULL, @@ -4997,6 +5027,20 @@ static void vgr_jump_to_match(qf_info_T *qi, int forceit, int *redraw_for_dummy, } } +// Return true if "buf" had an existing swap file, the current swap file does +// not end in ".swp". +static bool existing_swapfile(const buf_T *buf) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) { + const char_u *const fname = buf->b_ml.ml_mfp->mf_fname; + const size_t len = STRLEN(fname); + + return fname[len - 1] != 'p' || fname[len - 2] != 'w'; + } + return false; +} + // ":vimgrep {pattern} file(s)" // ":vimgrepadd {pattern} file(s)" // ":lvimgrep {pattern} file(s)" @@ -5129,7 +5173,8 @@ void ex_vimgrep(exarg_T *eap) } else { // Try for a match in all lines of the buffer. // For ":1vimgrep" look for first match only. - found_match = vgr_match_buflines(qi, fname, buf, ®match, &tomatch, + found_match = vgr_match_buflines(qf_get_curlist(qi), + fname, buf, ®match, &tomatch, duplicate_name, flags); if (using_dummy) { @@ -5153,7 +5198,9 @@ void ex_vimgrep(exarg_T *eap) if (!found_match) { wipe_dummy_buffer(buf, dirname_start); buf = NULL; - } else if (buf != first_match_buf || (flags & VGR_NOJUMP)) { + } else if (buf != first_match_buf + || (flags & VGR_NOJUMP) + || existing_swapfile(buf)) { unload_dummy_buffer(buf, dirname_start); // Keeping the buffer, remove the dummy flag. buf->b_flags &= ~BF_DUMMY; @@ -6119,6 +6166,49 @@ static int qf_setprop_context(qf_list_T *qfl, dictitem_T *di) return OK; } +// Set the current index in the specified quickfix list +static int qf_setprop_curidx(qf_info_T *qi, qf_list_T *qfl, + const dictitem_T *di) + FUNC_ATTR_NONNULL_ALL +{ + int newidx; + + // If the specified index is '$', then use the last entry + if (di->di_tv.v_type == VAR_STRING + && di->di_tv.vval.v_string != NULL + && STRCMP(di->di_tv.vval.v_string, "$") == 0) { + newidx = qfl->qf_count; + } else { + // Otherwise use the specified index + bool denote = false; + newidx = (int)tv_get_number_chk(&di->di_tv, &denote); + if (denote) { + return FAIL; + } + } + + if (newidx < 1) { // sanity check + return FAIL; + } + if (newidx > qfl->qf_count) { + newidx = qfl->qf_count; + } + const int old_qfidx = qfl->qf_index; + qfline_T *const qf_ptr = get_nth_entry(qfl, newidx, &newidx); + if (qf_ptr == NULL) { + return FAIL; + } + qfl->qf_ptr = qf_ptr; + qfl->qf_index = newidx; + + // If the current list is modified and it is displayed in the quickfix + // window, then Update it. + if (qi->qf_lists[qi->qf_curlist].qf_id == qfl->qf_id) { + qf_win_pos_update(qi, old_qfidx); + } + return OK; +} + /// Set quickfix/location list properties (title, items, context). /// Also used to add items from parsing a list of lines. /// Used by the setqflist() and setloclist() Vim script functions. @@ -6154,6 +6244,9 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, if ((di = tv_dict_find(what, S_LEN("context"))) != NULL) { retval = qf_setprop_context(qfl, di); } + if ((di = tv_dict_find(what, S_LEN("idx"))) != NULL) { + retval = qf_setprop_curidx(qi, qfl, di); + } if (retval == OK) { qf_list_changed(qfl); @@ -6164,7 +6257,8 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, /// Find the non-location list window with the specified location list stack in /// the current tabpage. -static win_T * find_win_with_ll(qf_info_T *qi) +static win_T *find_win_with_ll(const qf_info_T *qi) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if ((wp->w_llist == qi) && !bt_quickfix(wp->w_buffer)) { @@ -6223,6 +6317,7 @@ static void qf_free_stack(win_T *wp, qf_info_T *qi) // Populate the quickfix list with the items supplied in the list // of dictionaries. "title" will be copied to w:quickfix_title // "action" is 'a' for add, 'r' for replace. Otherwise create a new list. +// When "what" is not NULL then only set some properties. int set_errorlist(win_T *wp, list_T *list, int action, char_u *title, dict_T *what) { @@ -6239,6 +6334,12 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title, return OK; } + // A dict argument cannot be specified with a non-empty list argument + if (list != NULL && tv_list_len(list) != 0 && what != NULL) { + EMSG2(_(e_invarg2), _("cannot have both a list and a \"what\" argument")); + return FAIL; + } + incr_quickfix_busy(); if (what != NULL) { @@ -6538,7 +6639,7 @@ static qf_info_T *hgr_get_ll(bool *new_ll) // Search for a pattern in a help file. static void hgr_search_file( - qf_info_T *qi, + qf_list_T *qfl, char_u *fname, regmatch_T *p_regmatch) FUNC_ATTR_NONNULL_ARG(1, 3) @@ -6560,7 +6661,7 @@ static void hgr_search_file( line[--l] = NUL; } - if (qf_add_entry(qf_get_curlist(qi), + if (qf_add_entry(qfl, NULL, // dir fname, NULL, @@ -6593,7 +6694,7 @@ static void hgr_search_file( // Search for a pattern in all the help files in the doc directory under // the given directory. static void hgr_search_files_in_dir( - qf_info_T *qi, + qf_list_T *qfl, char_u *dirname, regmatch_T *p_regmatch, const char_u *lang) @@ -6618,7 +6719,7 @@ static void hgr_search_files_in_dir( continue; } - hgr_search_file(qi, fnames[fi], p_regmatch); + hgr_search_file(qfl, fnames[fi], p_regmatch); } FreeWild(fcount, fnames); } @@ -6628,7 +6729,7 @@ static void hgr_search_files_in_dir( // and add the matches to a quickfix list. // 'lang' is the language specifier. If supplied, then only matches in the // specified language are found. -static void hgr_search_in_rtp(qf_info_T *qi, regmatch_T *p_regmatch, +static void hgr_search_in_rtp(qf_list_T *qfl, regmatch_T *p_regmatch, const char_u *lang) FUNC_ATTR_NONNULL_ARG(1, 2) { @@ -6637,7 +6738,7 @@ static void hgr_search_in_rtp(qf_info_T *qi, regmatch_T *p_regmatch, while (*p != NUL && !got_int) { copy_option_part(&p, NameBuff, MAXPATHL, ","); - hgr_search_files_in_dir(qi, NameBuff, p_regmatch, lang); + hgr_search_files_in_dir(qfl, NameBuff, p_regmatch, lang); } } @@ -6679,12 +6780,12 @@ void ex_helpgrep(exarg_T *eap) if (regmatch.regprog != NULL) { // Create a new quickfix list. qf_new_list(qi, qf_cmdtitle(*eap->cmdlinep)); + qf_list_T *const qfl = qf_get_curlist(qi); - hgr_search_in_rtp(qi, ®match, lang); + hgr_search_in_rtp(qfl, ®match, lang); vim_regfree(regmatch.regprog); - qf_list_T *qfl = qf_get_curlist(qi); qfl->qf_nonevalid = false; qfl->qf_ptr = qfl->qf_start; qfl->qf_index = 1; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 1191886888..df0c5ce791 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -176,7 +176,7 @@ static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, textlock--; if (!ERROR_SET(&err) - && api_is_truthy(ret, "provider %s retval", default_true, &err)) { + && api_coerce_to_bool(ret, "provider %s retval", default_true, &err)) { return true; } @@ -3634,7 +3634,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, mb_utf8 = false; // don't draw as UTF-8 } } else if (c != NUL) { - p_extra = transchar(c); + p_extra = transchar_buf(wp->w_buffer, c); if (n_extra == 0) { n_extra = byte2cells(c) - 1; } diff --git a/src/nvim/search.c b/src/nvim/search.c index ea2107c5c7..9458e6333f 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -3436,7 +3436,6 @@ current_tagblock( pos_T start_pos; pos_T end_pos; pos_T old_start, old_end; - char_u *spat, *epat; char_u *p; char_u *cp; int len; @@ -3490,9 +3489,9 @@ again: */ for (long n = 0; n < count; n++) { if (do_searchpair( - (char_u *)"<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)", - (char_u *)"", - (char_u *)"</[^>]*>", BACKWARD, NULL, 0, + "<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)", + "", + "</[^>]*>", BACKWARD, NULL, 0, NULL, (linenr_T)0, 0L) <= 0) { curwin->w_cursor = old_pos; goto theend; @@ -3514,12 +3513,15 @@ again: curwin->w_cursor = old_pos; goto theend; } - spat = xmalloc(len + 31); - epat = xmalloc(len + 9); - sprintf((char *)spat, "<%.*s\\>\\%%(\\s\\_[^>]\\{-}[^/]>\\|>\\)\\c", len, p); - sprintf((char *)epat, "</%.*s>\\c", len, p); - - const int r = do_searchpair(spat, (char_u *)"", epat, FORWARD, NULL, + const size_t spat_len = len + 39; + char *const spat = xmalloc(spat_len); + const size_t epat_len = len + 9; + char *const epat = xmalloc(epat_len); + snprintf(spat, spat_len, + "<%.*s\\>\\%%(\\_s\\_[^>]\\{-}\\_[^/]>\\|\\_s\\?>\\)\\c", len, p); + snprintf(epat, epat_len, "</%.*s>\\c", len, p); + + const int r = do_searchpair(spat, "", epat, FORWARD, NULL, 0, NULL, (linenr_T)0, 0L); xfree(spat); @@ -4097,7 +4099,7 @@ abort_search: int current_search( long count, - int forward // true for forward, false for backward + bool forward // true for forward, false for backward ) { bool old_p_ws = p_ws; @@ -4112,6 +4114,11 @@ current_search( pos_T pos; // position after the pattern int result; // result of various function calls + // When searching forward and the cursor is at the start of the Visual + // area, skip the first search backward, otherwise it doesn't move. + const bool skip_first_backward = forward && VIsual_active + && lt(curwin->w_cursor, VIsual); + orig_pos = pos = curwin->w_cursor; if (VIsual_active) { // Searching further will extend the match. @@ -4129,13 +4136,20 @@ current_search( return FAIL; // pattern not found } - /* - * The trick is to first search backwards and then search forward again, - * so that a match at the current cursor position will be correctly - * captured. - */ + // The trick is to first search backwards and then search forward again, + // so that a match at the current cursor position will be correctly + // captured. When "forward" is false do it the other way around. for (int i = 0; i < 2; i++) { - int dir = forward ? i : !i; + int dir; + if (forward) { + if (i == 0 && skip_first_backward) { + continue; + } + dir = i; + } else { + dir = !i; + } + int flags = 0; if (!dir && !zero_width) { @@ -4182,11 +4196,17 @@ current_search( VIsual = start_pos; } - // put cursor on last character of match + // put the cursor after the match curwin->w_cursor = end_pos; if (lt(VIsual, end_pos) && forward) { - dec_cursor(); - } else if (VIsual_active && lt(curwin->w_cursor, VIsual)) { + if (skip_first_backward) { + // put the cursor on the start of the match + curwin->w_cursor = pos; + } else { + // put the cursor on last character of match + dec_cursor(); + } + } else if (VIsual_active && lt(curwin->w_cursor, VIsual) && forward) { curwin->w_cursor = pos; // put the cursor on the start of the match } VIsual_active = true; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index f036d7fe04..1984a357c3 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -396,8 +396,7 @@ size_t spell_check( mi.mi_word = ptr; mi.mi_fend = ptr; if (spell_iswordp(mi.mi_fend, wp)) { - int prev_upper; - int this_upper; + bool this_upper = false; // init for gcc if (use_camel_case) { c = PTR2CHAR(mi.mi_fend); @@ -407,7 +406,7 @@ size_t spell_check( do { MB_PTR_ADV(mi.mi_fend); if (use_camel_case) { - prev_upper = this_upper; + const bool prev_upper = this_upper; c = PTR2CHAR(mi.mi_fend); this_upper = SPELL_ISUPPER(c); camel_case = !prev_upper && this_upper; diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 765ba2cbb6..b02514143c 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -78,6 +78,9 @@ set encoding=utf-8 let s:test_script_fname = expand('%') au! SwapExists * call HandleSwapExists() func HandleSwapExists() + if exists('g:ignoreSwapExists') + return + endif " Ignore finding a swap file for the test script (the user might be " editing it and do ":make test_name") and the output file. " Report finding another swap file and chose 'q' to avoid getting stuck. diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim index 9bc037a59f..4f1ddcd7b9 100644 --- a/src/nvim/testdir/shared.vim +++ b/src/nvim/testdir/shared.vim @@ -56,6 +56,9 @@ endfunc " Run "cmd". Returns the job if using a job. func RunCommand(cmd) + " Running an external command can occasionally be slow or fail. + let g:test_is_flaky = 1 + let job = 0 if has('job') let job = job_start(a:cmd, {"stoponexit": "hup"}) diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 5668f45dea..7647475427 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -13,7 +13,6 @@ source test_ex_undo.vim source test_ex_z.vim source test_execute_func.vim source test_expand_func.vim -source test_expr.vim source test_feedkeys.vim source test_filter_cmd.vim source test_filter_map.vim @@ -50,6 +49,7 @@ source test_tagjump.vim source test_taglist.vim source test_true_false.vim source test_unlet.vim +source test_version.vim source test_virtualedit.vim source test_window_cmd.vim source test_wnext.vim diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 094bb3ebd1..04a678eeb8 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1897,4 +1897,17 @@ func Test_autocmd_FileReadCmd() delfunc ReadFileCmd endfunc +" Tests for SigUSR1 autocmd event, which is only available on posix systems. +func Test_autocmd_sigusr1() + CheckUnix + + let g:sigusr1_passed = 0 + au Signal SIGUSR1 let g:sigusr1_passed = 1 + call system('/bin/kill -s usr1 ' . getpid()) + call WaitForAssert({-> assert_true(g:sigusr1_passed)}) + + au! Signal + unlet g:sigusr1_passed +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_cjk_linebreak.vim b/src/nvim/testdir/test_cjk_linebreak.vim new file mode 100644 index 0000000000..dfaa8fa1af --- /dev/null +++ b/src/nvim/testdir/test_cjk_linebreak.vim @@ -0,0 +1,97 @@ +scriptencoding utf-8 + +func Run_cjk_linebreak_after(rigorous) + set textwidth=12 + for punct in [ + \ '!', '%', ')', ',', ':', ';', '>', '?', ']', '}', '’', '”', '†', '‡', + \ '…', '‰', '‱', '‼', '⁇', '⁈', '⁉', '℃', '℉', '、', '。', '〉', '》', + \ '」', '』', '】', '〕', '〗', '〙', '〛', '!', ')', ',', '.', ':', + \ ';', '?', ']', '}'] + call setline('.', '这是一个测试' .. punct.'试试 CJK 行禁则补丁。') + normal gqq + if a:rigorous + call assert_equal('这是一个测', getline(1)) + else + call assert_equal('这是一个测试' .. punct, getline(1)) + endif + %d_ + endfor +endfunc + +func Test_cjk_linebreak_after() + set formatoptions=croqn2mB1j + call Run_cjk_linebreak_after(0) +endfunc + +func Test_cjk_linebreak_after_rigorous() + set formatoptions=croqn2mB1j] + call Run_cjk_linebreak_after(1) +endfunc + +func Run_cjk_linebreak_before() + set textwidth=12 + for punct in [ + \ '(', '<', '[', '`', '{', '‘', '“', '〈', '《', '「', '『', '【', '〔', + \ '〖', '〘', '〚', '(', '[', '{'] + call setline('.', '这是个测试' .. punct.'试试 CJK 行禁则补丁。') + normal gqq + call assert_equal('这是个测试', getline(1)) + %d_ + endfor +endfunc + +func Test_cjk_linebreak_before() + set formatoptions=croqn2mB1j + call Run_cjk_linebreak_before() +endfunc + +func Test_cjk_linebreak_before_rigorous() + set formatoptions=croqn2mB1j] + call Run_cjk_linebreak_before() +endfunc + +func Run_cjk_linebreak_nobetween(rigorous) + " …… must not start a line + call setline('.', '这是个测试……试试 CJK 行禁则补丁。') + set textwidth=12 ambiwidth=double + normal gqq + if a:rigorous + call assert_equal('这是个测', getline(1)) + else + call assert_equal('这是个测试……', getline(1)) + endif + %d_ + + call setline('.', '这是一个测试……试试 CJK 行禁则补丁。') + set textwidth=12 ambiwidth=double + normal gqq + call assert_equal('这是一个测', getline(1)) + %d_ + + " but —— can + call setline('.', '这是个测试——试试 CJK 行禁则补丁。') + set textwidth=12 ambiwidth=double + normal gqq + call assert_equal('这是个测试', getline(1)) +endfunc + +func Test_cjk_linebreak_nobetween() + set formatoptions=croqn2mB1j + call Run_cjk_linebreak_nobetween(0) +endfunc + +func Test_cjk_linebreak_nobetween_rigorous() + set formatoptions=croqn2mB1j] + call Run_cjk_linebreak_nobetween(1) +endfunc + +func Test_cjk_linebreak_join_punct() + for punct in ['——', '〗', ',', '。', '……'] + call setline(1, '文本文本' .. punct) + call setline(2, 'English') + set formatoptions=croqn2mB1j + normal ggJ + call assert_equal('文本文本' .. punct.'English', getline(1)) + %d_ + endfor +endfunc diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim index e853b046dc..c702b44b88 100644 --- a/src/nvim/testdir/test_display.vim +++ b/src/nvim/testdir/test_display.vim @@ -185,6 +185,47 @@ func Test_scroll_CursorLineNr_update() call delete(filename) endfunc +" check a long file name does not result in the hit-enter prompt +func Test_edit_long_file_name() + CheckScreendump + + let longName = 'x'->repeat(min([&columns, 255])) + call writefile([], longName) + let buf = RunVimInTerminal('-N -u NONE ' .. longName, #{rows: 8}) + + call VerifyScreenDump(buf, 'Test_long_file_name_1', {}) + + call term_sendkeys(buf, ":q\<cr>") + + " clean up + call StopVimInTerminal(buf) + call delete(longName) +endfunc + +func Test_unprintable_fileformats() + CheckScreendump + + call writefile(["unix\r", "two"], 'Xunix.txt') + call writefile(["mac\r", "two"], 'Xmac.txt') + let lines =<< trim END + edit Xunix.txt + split Xmac.txt + edit ++ff=mac + END + let filename = 'Xunprintable' + call writefile(lines, filename) + let buf = RunVimInTerminal('-S '.filename, #{rows: 9, cols: 50}) + call VerifyScreenDump(buf, 'Test_display_unprintable_01', {}) + call term_sendkeys(buf, "\<C-W>\<C-W>\<C-L>") + call VerifyScreenDump(buf, 'Test_display_unprintable_02', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xunix.txt') + call delete('Xmac.txt') + call delete(filename) +endfunc + " Test for scrolling that modifies buffer during visual block func Test_visual_block_scroll() " See test/functional/legacy/visual_mode_spec.lua diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index d4b1c63741..15c718b243 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -1441,31 +1441,40 @@ endfunc func Test_edit_InsertLeave() new + au InsertLeavePre * let g:did_au_pre = 1 au InsertLeave * let g:did_au = 1 + let g:did_au_pre = 0 let g:did_au = 0 call feedkeys("afoo\<Esc>", 'tx') + call assert_equal(1, g:did_au_pre) call assert_equal(1, g:did_au) call assert_equal('foo', getline(1)) + let g:did_au_pre = 0 let g:did_au = 0 call feedkeys("Sbar\<C-C>", 'tx') + call assert_equal(1, g:did_au_pre) call assert_equal(0, g:did_au) call assert_equal('bar', getline(1)) inoremap x xx<Esc> + let g:did_au_pre = 0 let g:did_au = 0 call feedkeys("Saax", 'tx') + call assert_equal(1, g:did_au_pre) call assert_equal(1, g:did_au) call assert_equal('aaxx', getline(1)) inoremap x xx<C-C> + let g:did_au_pre = 0 let g:did_au = 0 call feedkeys("Sbbx", 'tx') + call assert_equal(1, g:did_au_pre) call assert_equal(0, g:did_au) call assert_equal('bbxx', getline(1)) bwipe! - au! InsertLeave + au! InsertLeave InsertLeavePre iunmap x endfunc @@ -1559,3 +1568,23 @@ func Test_edit_is_a_directory() call delete(dirname, 'rf') endfunc + +func Test_edit_browse() + " in the GUI this opens a file picker, we only test the terminal behavior + CheckNotGui + + " ":browse xxx" checks for the FileExplorer augroup and assumes editing "." + " works then. + augroup FileExplorer + au! + augroup END + + " When the USE_FNAME_CASE is defined this used to cause a crash. + browse enew + bwipe! + + browse split + bwipe! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index c7ca682c8c..9f79c1b545 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -532,6 +532,7 @@ let s:filename_case_checks = { \ } func CheckItems(checks) + set noswapfile for [ft, names] in items(a:checks) for i in range(0, len(names) - 1) new @@ -548,6 +549,7 @@ func CheckItems(checks) bwipe! endfor endfor + set swapfile& endfunc func Test_filetype_detection() diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 8fa70a5313..917a5e8eca 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -214,7 +214,7 @@ func Test_strftime() endif endfunc -func Test_resolve() +func Test_resolve_unix() if !has('unix') return endif @@ -258,6 +258,8 @@ func Test_resolve() call assert_equal('Xlink2', resolve('Xlink1')) call assert_equal('./Xlink2', resolve('./Xlink1')) call delete('Xlink1') + + call assert_equal('/', resolve('/')) endfunc func Test_simplify() @@ -1083,6 +1085,12 @@ func Test_trim() call assert_equal("", trim("", "")) call assert_equal("a", trim("a", "")) call assert_equal("", trim("", "a")) + call assert_equal("vim", trim(" vim ", " ", 0)) + call assert_equal("vim ", trim(" vim ", " ", 1)) + call assert_equal(" vim", trim(" vim ", " ", 2)) + call assert_fails('call trim(" vim ", " ", [])', 'E745:') + call assert_fails('call trim(" vim ", " ", -1)', 'E475:') + call assert_fails('call trim(" vim ", " ", 3)', 'E475:') let chars = join(map(range(1, 0x20) + [0xa0], {n -> nr2char(n)}), '') call assert_equal("x", trim(chars . "x" . chars)) diff --git a/src/nvim/testdir/test_gn.vim b/src/nvim/testdir/test_gn.vim index d41675be0c..9acec51913 100644 --- a/src/nvim/testdir/test_gn.vim +++ b/src/nvim/testdir/test_gn.vim @@ -158,7 +158,32 @@ func Test_gn_command() set wrapscan&vim set belloff&vim -endfu +endfunc + +func Test_gN_repeat() + new + call setline(1, 'this list is a list with a list of a list.') + /list + normal $gNgNgNx + call assert_equal('list with a list of a list', @") + bwipe! +endfunc + +func Test_gN_then_gn() + new + + call setline(1, 'this list is a list with a list of a last.') + /l.st + normal $gNgNgnx + call assert_equal('last', @") + + call setline(1, 'this list is a list with a lust of a last.') + /l.st + normal $gNgNgNgnx + call assert_equal('lust of a last', @") + + bwipe! +endfunc func Test_gn_multi_line() new diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 1339a9f25d..b8632b9595 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -327,7 +327,10 @@ func Test_compl_in_cmdwin() set wildmenu wildchar=<Tab> com! -nargs=1 -complete=command GetInput let input = <q-args> com! -buffer TestCommand echo 'TestCommand' + let w:test_winvar = 'winvar' + let b:test_bufvar = 'bufvar' + " User-defined commands let input = '' call feedkeys("q:iGetInput T\<C-x>\<C-v>\<CR>", 'tx!') call assert_equal('TestCommand', input) @@ -336,8 +339,29 @@ func Test_compl_in_cmdwin() call feedkeys("q::GetInput T\<Tab>\<CR>:q\<CR>", 'tx!') call assert_equal('T', input) + com! -nargs=1 -complete=var GetInput let input = <q-args> + " Window-local variables + let input = '' + call feedkeys("q:iGetInput w:test_\<C-x>\<C-v>\<CR>", 'tx!') + call assert_equal('w:test_winvar', input) + + let input = '' + call feedkeys("q::GetInput w:test_\<Tab>\<CR>:q\<CR>", 'tx!') + call assert_equal('w:test_', input) + + " Buffer-local variables + let input = '' + call feedkeys("q:iGetInput b:test_\<C-x>\<C-v>\<CR>", 'tx!') + call assert_equal('b:test_bufvar', input) + + let input = '' + call feedkeys("q::GetInput b:test_\<Tab>\<CR>:q\<CR>", 'tx!') + call assert_equal('b:test_', input) + delcom TestCommand delcom GetInput + unlet w:test_winvar + unlet b:test_bufvar set wildmenu& wildchar& endfunc diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 7fbf04311d..ca14494248 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -74,6 +74,7 @@ func Test_echomsg() call assert_equal("\n12345", execute(':echomsg 12345')) call assert_equal("\n[]", execute(':echomsg []')) call assert_equal("\n[1, 2, 3]", execute(':echomsg [1, 2, 3]')) + call assert_equal("\n[1, 2, []]", execute(':echomsg [1, 2, v:_null_list]')) call assert_equal("\n{}", execute(':echomsg {}')) call assert_equal("\n{'a': 1, 'b': 2}", execute(':echomsg {"a": 1, "b": 2}')) if has('float') diff --git a/src/nvim/testdir/test_perl.vim b/src/nvim/testdir/test_perl.vim index 2343f389fa..872194a804 100644 --- a/src/nvim/testdir/test_perl.vim +++ b/src/nvim/testdir/test_perl.vim @@ -4,6 +4,7 @@ if !has('perl') || has('win32') finish endif +" FIXME: RunTest don't see any error when Perl abort... perl $SIG{__WARN__} = sub { die "Unexpected warnings from perl: @_" }; func Test_change_buffer() @@ -23,11 +24,15 @@ func Test_evaluate_list() $l = VIM::Eval("l"); $curbuf->Append($curline, $l); EOF + normal j + .perldo s|\n|/|g + " call assert_equal('abc/def/', getline('$')) + call assert_equal('def', getline('$')) endfunc funct Test_VIM_Blob() call assert_equal('0z', perleval('VIM::Blob("")')) - "call assert_equal('0z31326162', 'VIM::Blob("12ab")'->perleval()) + call assert_equal('0z31326162', perleval('VIM::Blob("12ab")')) call assert_equal('0z00010203', perleval('VIM::Blob("\x00\x01\x02\x03")')) call assert_equal('0z8081FEFF', perleval('VIM::Blob("\x80\x81\xfe\xff")')) endfunc @@ -46,7 +51,6 @@ func Test_buffer_Append() new perl $curbuf->Append(1, '1') perl $curbuf->Append(2, '2', '3', '4') - call assert_equal(['', '1', '2', '3', '4'], getline(1, '$')) perl @l = ('5' ..'7') perl $curbuf->Append(0, @l) call assert_equal(['5', '6', '7', '', '1', '2', '3', '4'], getline(1, '$')) @@ -114,7 +118,7 @@ func Test_VIM_Windows() perl $curbuf->Append(0, $winnr, scalar(@winlist)) call assert_equal(['2', '2', ''], getline(1, '$')) - "" VIM::Windows() with window number argument. + " VIM::Windows() with window number argument. perl (VIM::Windows(VIM::Eval('winnr()')))[0]->Buffer()->Set(1, 'bar') call assert_equal('bar', getline(1)) bwipe! @@ -133,10 +137,20 @@ func Test_VIM_Buffers() bwipe! endfunc +func <SID>catch_peval(expr) + try + call perleval(a:expr) + catch + return v:exception + endtry + call assert_report('no exception for `perleval("'.a:expr.'")`') + return '' +endfunc + func Test_perleval() call assert_false(perleval('undef')) - "" scalar + " scalar call assert_equal(0, perleval('0')) call assert_equal(2, perleval('2')) call assert_equal(-2, perleval('-2')) @@ -146,9 +160,12 @@ func Test_perleval() call assert_equal(2, perleval('2.5')) end + " sandbox call assert_equal(2, perleval('2')) + call assert_equal('abc', perleval('"abc"')) + " call assert_equal("abc\ndef", perleval('"abc\0def"')) - "" ref + " ref call assert_equal([], perleval('[]')) call assert_equal(['word', 42, [42],{}], perleval('["word", 42, [42], {}]')) @@ -156,13 +173,19 @@ func Test_perleval() call assert_equal({'foo': 'bar'}, perleval('{foo => "bar"}')) perl our %h; our @a; - let a = perleval('[\%h, \%h, \@a, \@a]') - echo a + let a = perleval('[\(%h, %h, @a, @a)]') + " call assert_true((a[0] is a[1])) call assert_equal(a[0], a[1]) + " call assert_true((a[2] is a[3])) call assert_equal(a[2], a[3]) perl undef %h; undef @a; + " call assert_true(<SID>catch_peval('{"" , 0}') =~ 'Malformed key Dictionary') + " call assert_true(<SID>catch_peval('{"\0" , 0}') =~ 'Malformed key Dictionary') + " call assert_true(<SID>catch_peval('{"foo\0bar" , 0}') =~ 'Malformed key Dictionary') + call assert_equal('*VIM', perleval('"*VIM"')) + " call assert_true(perleval('\\0') =~ 'SCALAR(0x\x\+)') endfunc func Test_perldo() @@ -179,7 +202,7 @@ func Test_perldo() perldo VIM::DoCommand("%d_") bwipe! - "" Check switching to another buffer does not trigger ml_get error. + " Check switching to another buffer does not trigger ml_get error. new let wincount = winnr('$') call setline(1, ['one', 'two', 'three']) @@ -198,6 +221,69 @@ func Test_VIM_package() call assert_true(&et) endfunc +func Test_stdio() + throw 'skipped: TODO: ' + redir =>l:out + perl <<EOF + VIM::Msg("&VIM::Msg"); + print "STDOUT"; + print STDERR "STDERR"; +EOF + redir END + call assert_equal(['&VIM::Msg', 'STDOUT', 'STDERR'], split(l:out, "\n")) +endfunc + +" Run first to get a clean namespace +func Test_000_SvREFCNT() + throw 'skipped: TODO: ' + for i in range(8) + exec 'new X'.i + endfor + new t + perl <<--perl +#line 5 "Test_000_SvREFCNT()" + my ($b, $w); + + my $num = 0; + for ( 0 .. 100 ) { + if ( ++$num >= 8 ) { $num = 0 } + VIM::DoCommand("buffer X$num"); + $b = $curbuf; + } + + VIM::DoCommand("buffer t"); + + $b = $curbuf for 0 .. 100; + $w = $curwin for 0 .. 100; + () = VIM::Buffers for 0 .. 100; + () = VIM::Windows for 0 .. 100; + + VIM::DoCommand('bw! t'); + if (exists &Internals::SvREFCNT) { + my $cb = Internals::SvREFCNT($$b); + my $cw = Internals::SvREFCNT($$w); + VIM::Eval("assert_equal(2, $cb, 'T1')"); + VIM::Eval("assert_equal(2, $cw, 'T2')"); + my $strongref; + foreach ( VIM::Buffers, VIM::Windows ) { + VIM::DoCommand("%bw!"); + my $c = Internals::SvREFCNT($_); + VIM::Eval("assert_equal(2, $c, 'T3')"); + $c = Internals::SvREFCNT($$_); + next if $c == 2 && !$strongref++; + VIM::Eval("assert_equal(1, $c, 'T4')"); + } + $cb = Internals::SvREFCNT($$curbuf); + $cw = Internals::SvREFCNT($$curwin); + VIM::Eval("assert_equal(3, $cb, 'T5')"); + VIM::Eval("assert_equal(3, $cw, 'T6')"); + } + VIM::Eval("assert_false($$b)"); + VIM::Eval("assert_false($$w)"); +--perl + %bw! +endfunc + func Test_set_cursor() " Check that setting the cursor position works. new diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index 884ada7e88..15745d5619 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -22,12 +22,21 @@ endfunc func Test_put_char_block2() new - let a = [ getreg('a'), getregtype('a') ] call setreg('a', ' one ', 'v') call setline(1, ['Line 1', '', 'Line 3', '']) " visually select the first 3 lines and put register a over it exe "norm! ggl\<c-v>2j2l\"ap" - call assert_equal(['L one 1', '', 'L one 3', ''], getline(1,4)) + call assert_equal(['L one 1', '', 'L one 3', ''], getline(1, 4)) + " clean up + bw! +endfunc + +func Test_put_lines() + new + let a = [ getreg('a'), getregtype('a') ] + call setline(1, ['Line 1', 'Line2', 'Line 3', '']) + exe 'norm! gg"add"AddG""p' + call assert_equal(['Line 3', '', 'Line 1', 'Line2'], getline(1, '$')) " clean up bw! call setreg('a', a[0], a[1]) @@ -42,21 +51,10 @@ func Test_put_expr() exec "4norm! \"=\<cr>P" norm! j0. norm! j0. - call assert_equal(['A1','A2','A3','4A','5A','6A'], getline(1,'$')) + call assert_equal(['A1','A2','A3','4A','5A','6A'], getline(1, '$')) bw! endfunc -func Test_put_lines() - new - let a = [ getreg('a'), getregtype('a') ] - call setline(1, ['Line 1', 'Line2', 'Line 3', '']) - exe 'norm! gg"add"AddG""p' - call assert_equal(['Line 3', '', 'Line 1', 'Line2'], getline(1,'$')) - " clean up - bw! - call setreg('a', a[0], a[1]) -endfunc - func Test_put_fails_when_nomodifiable() new setlocal nomodifiable diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 4090db2874..cf0af07528 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1,8 +1,7 @@ " Test for the quickfix commands. -if !has('quickfix') - finish -endif +source check.vim +CheckFeature quickfix set encoding=utf-8 @@ -1916,6 +1915,13 @@ func HistoryTest(cchar) call g:Xsetlist([], 'f') let l = split(execute(a:cchar . 'hist'), "\n") call assert_equal('No entries', l[0]) + + " An empty list should still show the stack history + call g:Xsetlist([]) + let res = split(execute(a:cchar . 'hist'), "\n") + call assert_equal('> error list 1 of 1; 0 ' . common, res[0]) + + call g:Xsetlist([], 'f') endfunc func Test_history() @@ -2166,6 +2172,9 @@ func Xproperty_tests(cchar) call assert_equal(['Colors'], newl2.context) call assert_equal('Line10', newl2.items[0].text) call g:Xsetlist([], 'f') + + " Cannot specify both a non-empty list argument and a dict argument + call assert_fails("call g:Xsetlist([{}], ' ', {})", 'E475:') endfunc func Test_qf_property() @@ -2173,6 +2182,56 @@ func Test_qf_property() call Xproperty_tests('l') endfunc +" Test for setting the current index in the location/quickfix list +func Xtest_setqfidx(cchar) + call s:setup_commands(a:cchar) + + Xgetexpr "F1:10:1:Line1\nF2:20:2:Line2\nF3:30:3:Line3" + Xgetexpr "F4:10:1:Line1\nF5:20:2:Line2\nF6:30:3:Line3" + Xgetexpr "F7:10:1:Line1\nF8:20:2:Line2\nF9:30:3:Line3" + + call g:Xsetlist([], 'a', {'nr' : 3, 'idx' : 2}) + call g:Xsetlist([], 'a', {'nr' : 2, 'idx' : 2}) + call g:Xsetlist([], 'a', {'nr' : 1, 'idx' : 3}) + Xolder 2 + Xopen + call assert_equal(3, line('.')) + Xnewer + call assert_equal(2, line('.')) + Xnewer + call assert_equal(2, line('.')) + " Update the current index with the quickfix window open + wincmd w + call g:Xsetlist([], 'a', {'nr' : 3, 'idx' : 3}) + Xopen + call assert_equal(3, line('.')) + Xclose + + " Set the current index to the last entry + call g:Xsetlist([], 'a', {'nr' : 1, 'idx' : '$'}) + call assert_equal(3, g:Xgetlist({'nr' : 1, 'idx' : 0}).idx) + " A large value should set the index to the last index + call g:Xsetlist([], 'a', {'nr' : 1, 'idx' : 1}) + call g:Xsetlist([], 'a', {'nr' : 1, 'idx' : 999}) + call assert_equal(3, g:Xgetlist({'nr' : 1, 'idx' : 0}).idx) + " Invalid index values + call g:Xsetlist([], 'a', {'nr' : 1, 'idx' : -1}) + call assert_equal(3, g:Xgetlist({'nr' : 1, 'idx' : 0}).idx) + call g:Xsetlist([], 'a', {'nr' : 1, 'idx' : 0}) + call assert_equal(3, g:Xgetlist({'nr' : 1, 'idx' : 0}).idx) + call g:Xsetlist([], 'a', {'nr' : 1, 'idx' : 'xx'}) + call assert_equal(3, g:Xgetlist({'nr' : 1, 'idx' : 0}).idx) + call assert_fails("call g:Xsetlist([], 'a', {'nr':1, 'idx':[]})", 'E745:') + + call g:Xsetlist([], 'f') + new | only +endfunc + +func Test_setqfidx() + call Xtest_setqfidx('c') + call Xtest_setqfidx('l') +endfunc + " Tests for the QuickFixCmdPre/QuickFixCmdPost autocommands func QfAutoCmdHandler(loc, cmd) call add(g:acmds, a:loc . a:cmd) @@ -2508,6 +2567,41 @@ func Test_vimgrep_incsearch() set noincsearch endfunc +" Test vimgrep without swap file +func Test_vimgrep_without_swap_file() + let lines =<< trim [SCRIPT] + vimgrep grep test_c* + call writefile(['done'], 'Xresult') + qall! + [SCRIPT] + call writefile(lines, 'Xscript') + if RunVim([], [], '--clean -n -S Xscript Xscript') + call assert_equal(['done'], readfile('Xresult')) + endif + call delete('Xscript') + call delete('Xresult') +endfunc + +func Test_vimgrep_existing_swapfile() + call writefile(['match apple with apple'], 'Xapple') + call writefile(['swapfile'], '.Xapple.swp') + let g:foundSwap = 0 + let g:ignoreSwapExists = 1 + augroup grep + au SwapExists * let foundSwap = 1 | let v:swapchoice = 'e' + augroup END + vimgrep apple Xapple + call assert_equal(1, g:foundSwap) + call assert_match('.Xapple.swo', swapname('')) + + call delete('Xapple') + call delete('Xapple.swp') + augroup grep + au! SwapExists + augroup END + unlet g:ignoreSwapExists +endfunc + func XfreeTests(cchar) call s:setup_commands(a:cchar) @@ -3869,6 +3963,52 @@ func Test_curswant() cclose | helpclose endfunc +" Test for opening a file from the quickfix window using CTRL-W <Enter> +" doesn't leave an empty buffer around. +func Test_splitview() + call s:create_test_file('Xtestfile1') + call s:create_test_file('Xtestfile2') + new | only + let last_bufnr = bufnr('Test_sv_1', 1) + let l = ['Xtestfile1:2:Line2', 'Xtestfile2:4:Line4'] + cgetexpr l + copen + let numbufs = len(getbufinfo()) + exe "normal \<C-W>\<CR>" + copen + exe "normal j\<C-W>\<CR>" + " Make sure new empty buffers are not created + call assert_equal(numbufs, len(getbufinfo())) + " Creating a new buffer should use the next available buffer number + call assert_equal(last_bufnr + 4, bufnr("Test_sv_2", 1)) + bwipe Test_sv_1 + bwipe Test_sv_2 + new | only + + " When split opening files from location list window, make sure that two + " windows doesn't refer to the same location list + lgetexpr l + let locid = getloclist(0, {'id' : 0}).id + lopen + exe "normal \<C-W>\<CR>" + call assert_notequal(locid, getloclist(0, {'id' : 0}).id) + call assert_equal(0, getloclist(0, {'winid' : 0}).winid) + new | only + + " When split opening files from a helpgrep location list window, a new help + " window should be opend with a copy of the location list. + lhelpgrep window + let locid = getloclist(0, {'id' : 0}).id + lwindow + exe "normal j\<C-W>\<CR>" + call assert_notequal(locid, getloclist(0, {'id' : 0}).id) + call assert_equal(0, getloclist(0, {'winid' : 0}).winid) + new | only + + call delete('Xtestfile1') + call delete('Xtestfile2') +endfunc + " Test for parsing entries using visual screen column func Test_viscol() enew @@ -3920,11 +4060,102 @@ func Test_viscol() cnext call assert_equal([16, 25], [col('.'), virtcol('.')]) + " Use screen column number with a multi-line error message + enew + call writefile(["à test"], 'Xfile1') + set efm=%E===\ %f\ ===,%C%l:%v,%Z%m + cexpr ["=== Xfile1 ===", "1:3", "errormsg"] + call assert_equal('Xfile1', @%) + call assert_equal([0, 1, 4, 0], getpos('.')) + + " Repeat previous test with byte offset %c: ensure that fix to issue #7145 + " does not break this + set efm=%E===\ %f\ ===,%C%l:%c,%Z%m + cexpr ["=== Xfile1 ===", "1:3", "errormsg"] + call assert_equal('Xfile1', @%) + call assert_equal([0, 1, 3, 0], getpos('.')) + enew | only set efm& call delete('Xfile1') endfunc +" Test for the quickfix window buffer +func Xqfbuf_test(cchar) + call s:setup_commands(a:cchar) + + " Quickfix buffer should be reused across closing and opening a quickfix + " window + Xexpr "F1:10:Line10" + Xopen + let qfbnum = bufnr('') + Xclose + " Even after the quickfix window is closed, the buffer should be loaded + call assert_true(bufloaded(qfbnum)) + Xopen + " Buffer should be reused when opening the window again + call assert_equal(qfbnum, bufnr('')) + Xclose + + if a:cchar == 'l' + %bwipe + " For a location list, when both the file window and the location list + " window for the list are closed, then the buffer should be freed. + new | only + lexpr "F1:10:Line10" + let wid = win_getid() + lopen + let qfbnum = bufnr('') + call assert_match(qfbnum . ' %a- "\[Location List]"', execute('ls')) + close + " When the location list window is closed, the buffer name should not + " change to 'Quickfix List' + call assert_match(qfbnum . ' h- "\[Location List]"', execute('ls')) + call assert_true(bufloaded(qfbnum)) + + " After deleting a location list buffer using ":bdelete", opening the + " location list window should mark the buffer as a location list buffer. + exe "bdelete " . qfbnum + lopen + call assert_equal("quickfix", &buftype) + call assert_equal(1, getwininfo(win_getid(winnr()))[0].loclist) + call assert_equal(wid, getloclist(0, {'filewinid' : 0}).filewinid) + call assert_false(&swapfile) + lclose + + " When the location list is cleared for the window, the buffer should be + " removed + call setloclist(0, [], 'f') + call assert_false(bufexists(qfbnum)) + + " When the location list is freed with the location list window open, the + " location list buffer should not be lost. It should be reused when the + " location list is again populated. + lexpr "F1:10:Line10" + lopen + let wid = win_getid() + let qfbnum = bufnr('') + wincmd p + call setloclist(0, [], 'f') + lexpr "F1:10:Line10" + lopen + call assert_equal(wid, win_getid()) + call assert_equal(qfbnum, bufnr('')) + lclose + + " When the window with the location list is closed, the buffer should be + " removed + new | only + call assert_false(bufexists(qfbnum)) + endif +endfunc + +func Test_qfbuf() + throw 'skipped: enable after porting patch 8.1.0877' + call Xqfbuf_test('c') + call Xqfbuf_test('l') +endfunc + " Test to make sure that an empty quickfix buffer is not reused for loading " a normal buffer. func Test_empty_qfbuf() diff --git a/src/nvim/testdir/test_ruby.vim b/src/nvim/testdir/test_ruby.vim index 64199570a9..07ad8561c3 100644 --- a/src/nvim/testdir/test_ruby.vim +++ b/src/nvim/testdir/test_ruby.vim @@ -11,37 +11,6 @@ func Test_ruby_change_buffer() call assert_equal('1 changed line 1', getline('$')) endfunc -func Test_ruby_evaluate_list() - throw 'skipped: TODO: ' - call setline(line('$'), ['2 line 2']) - ruby Vim.command("normal /^2\n") - let l = ["abc", "def"] - ruby << EOF - curline = $curbuf.line_number - l = Vim.evaluate("l"); - $curbuf.append(curline, l.join("\n")) -EOF - normal j - .rubydo $_ = $_.gsub(/\n/, '/') - call assert_equal('abc/def', getline('$')) -endfunc - -func Test_ruby_evaluate_dict() - let d = {'a': 'foo', 'b': 123} - redir => l:out - ruby d = Vim.evaluate("d"); print d - redir END - call assert_equal(['{"a"=>"foo", "b"=>123}'], split(l:out, "\n")) -endfunc - -func Test_ruby_evaluate_special_var() - let l = [v:true, v:false, v:null] - redir => l:out - ruby d = Vim.evaluate("l"); print d - redir END - call assert_equal(['[true, false, nil]'], split(l:out, "\n")) -endfunc - func Test_rubydo() throw 'skipped: TODO: ' " Check deleting lines does not trigger ml_get error. @@ -56,8 +25,7 @@ func Test_rubydo() call setline(1, ['one', 'two', 'three']) rubydo Vim.command("new") call assert_equal(wincount + 1, winnr('$')) - bwipe! - bwipe! + %bwipe! endfunc func Test_rubyfile() @@ -75,8 +43,350 @@ func Test_set_cursor() normal gg rubydo $curwin.cursor = [1, 5] call assert_equal([1, 6], [line('.'), col('.')]) + call assert_equal([1, 5], rubyeval('$curwin.cursor')) " Check that movement after setting cursor position keeps current column. normal j call assert_equal([2, 6], [line('.'), col('.')]) + call assert_equal([2, 5], rubyeval('$curwin.cursor')) + + " call assert_fails('ruby $curwin.cursor = [1]', + " \ 'ArgumentError: array length must be 2') + bwipe! +endfunc + +" Test buffer.count and buffer.length (number of lines in buffer) +func Test_buffer_count() + new + call setline(1, ['one', 'two', 'three']) + call assert_equal(3, rubyeval('$curbuf.count')) + call assert_equal(3, rubyeval('$curbuf.length')) + bwipe! +endfunc + +" Test buffer.name (buffer name) +func Test_buffer_name() + new Xfoo + call assert_equal(expand('%:p'), rubyeval('$curbuf.name')) + bwipe + call assert_equal('', rubyeval('$curbuf.name')) +endfunc + +" Test buffer.number (number of the buffer). +func Test_buffer_number() + new + call assert_equal(bufnr('%'), rubyeval('$curbuf.number')) + new + call assert_equal(bufnr('%'), rubyeval('$curbuf.number')) + + %bwipe +endfunc + +" Test buffer.delete({n}) (delete line {n}) +func Test_buffer_delete() + new + call setline(1, ['one', 'two', 'three']) + ruby $curbuf.delete(2) + call assert_equal(['one', 'three'], getline(1, '$')) + + " call assert_fails('ruby $curbuf.delete(0)', 'IndexError: line number 0 out of range') + " call assert_fails('ruby $curbuf.delete(3)', 'IndexError: line number 3 out of range') + call assert_fails('ruby $curbuf.delete(3)', 'RuntimeError: Index out of bounds') + + bwipe! +endfunc + +" Test buffer.append({str}, str) (append line {str} after line {n}) +func Test_buffer_append() + new + ruby $curbuf.append(0, 'one') + ruby $curbuf.append(1, 'three') + ruby $curbuf.append(1, 'two') + ruby $curbuf.append(4, 'four') + + call assert_equal(['one', 'two', 'three', '', 'four'], getline(1, '$')) + + " call assert_fails('ruby $curbuf.append(-1, "x")', + " \ 'IndexError: line number -1 out of range') + call assert_fails('ruby $curbuf.append(-1, "x")', + \ 'ArgumentError: Index out of bounds') + call assert_fails('ruby $curbuf.append(6, "x")', + \ 'RuntimeError: Index out of bounds') + + bwipe! +endfunc + +" Test buffer.line (get or set the current line) +func Test_buffer_line() + new + call setline(1, ['one', 'two', 'three']) + 2 + call assert_equal('two', rubyeval('$curbuf.line')) + + ruby $curbuf.line = 'TWO' + call assert_equal(['one', 'TWO', 'three'], getline(1, '$')) + + bwipe! +endfunc + +" Test buffer.line_number (get current line number) +func Test_buffer_line_number() + new + call setline(1, ['one', 'two', 'three']) + 2 + call assert_equal(2, rubyeval('$curbuf.line_number')) + + bwipe! +endfunc + +func Test_buffer_get() + new + call setline(1, ['one', 'two']) + call assert_equal('one', rubyeval('$curbuf[1]')) + call assert_equal('two', rubyeval('$curbuf[2]')) + + " call assert_fails('ruby $curbuf[0]', + " \ 'IndexError: line number 0 out of range') + call assert_fails('ruby $curbuf[3]', + \ 'RuntimeError: Index out of bounds') + + bwipe! +endfunc + +func Test_buffer_set() + new + call setline(1, ['one', 'two']) + ruby $curbuf[2] = 'TWO' + ruby $curbuf[1] = 'ONE' + + " call assert_fails('ruby $curbuf[0] = "ZERO"', + " \ 'IndexError: line number 0 out of range') + " call assert_fails('ruby $curbuf[3] = "THREE"', + " \ 'IndexError: line number 3 out of range') + call assert_fails('ruby $curbuf[3] = "THREE"', + \ 'RuntimeError: Index out of bounds') + bwipe! +endfunc + +" Test window.width (get or set window height). +func Test_window_height() + new + + " Test setting window height + ruby $curwin.height = 2 + call assert_equal(2, winheight(0)) + + " Test getting window height + call assert_equal(2, rubyeval('$curwin.height')) + + bwipe +endfunc + +" Test window.width (get or set window width). +func Test_window_width() + vnew + + " Test setting window width + ruby $curwin.width = 2 + call assert_equal(2, winwidth(0)) + + " Test getting window width + call assert_equal(2, rubyeval('$curwin.width')) + + bwipe +endfunc + +" Test window.buffer (get buffer object of a window object). +func Test_window_buffer() + new Xfoo1 + new Xfoo2 + ruby $b2 = $curwin.buffer + ruby $w2 = $curwin + wincmd j + ruby $b1 = $curwin.buffer + ruby $w1 = $curwin + + " call assert_equal(rubyeval('$b1'), rubyeval('$w1.buffer')) + " call assert_equal(rubyeval('$b2'), rubyeval('$w2.buffer')) + call assert_equal(bufnr('Xfoo1'), rubyeval('$w1.buffer.number')) + call assert_equal(bufnr('Xfoo2'), rubyeval('$w2.buffer.number')) + + ruby $b1, $w1, $b2, $w2 = nil + %bwipe +endfunc + +" Test Vim::Window.current (get current window object) +func Test_Vim_window_current() + let cw = rubyeval('$curwin.to_s') + " call assert_equal(cw, rubyeval('Vim::Window.current')) + call assert_match('^#<Neovim::Window:0x\x\+>$', cw) +endfunc + +" Test Vim::Window.count (number of windows) +func Test_Vim_window_count() + new Xfoo1 + new Xfoo2 + split + call assert_equal(4, rubyeval('Vim::Window.count')) + %bwipe + call assert_equal(1, rubyeval('Vim::Window.count')) +endfunc + +" Test Vim::Window[n] (get window object of window n) +func Test_Vim_window_get() + new Xfoo1 + new Xfoo2 + call assert_match('Xfoo2$', rubyeval('Vim::Window[0].buffer.name')) + wincmd j + call assert_match('Xfoo1$', rubyeval('Vim::Window[1].buffer.name')) + wincmd j + call assert_equal('', rubyeval('Vim::Window[2].buffer.name')) + %bwipe +endfunc + +" Test Vim::Buffer.current (return the buffer object of current buffer) +func Test_Vim_buffer_current() + let cb = rubyeval('$curbuf.to_s') + " call assert_equal(cb, rubyeval('Vim::Buffer.current')) + call assert_match('^#<Neovim::Buffer:0x\x\+>$', cb) +endfunc + +" Test Vim::Buffer:.count (return the number of buffers) +func Test_Vim_buffer_count() + new Xfoo1 + new Xfoo2 + call assert_equal(3, rubyeval('Vim::Buffer.count')) + %bwipe + call assert_equal(1, rubyeval('Vim::Buffer.count')) +endfunc + +" Test Vim::buffer[n] (return the buffer object of buffer number n) +func Test_Vim_buffer_get() + new Xfoo1 + new Xfoo2 + + " Index of Vim::Buffer[n] goes from 0 to the number of buffers. + call assert_equal('', rubyeval('Vim::Buffer[0].name')) + call assert_match('Xfoo1$', rubyeval('Vim::Buffer[1].name')) + call assert_match('Xfoo2$', rubyeval('Vim::Buffer[2].name')) + call assert_fails('ruby print Vim::Buffer[3].name', + \ "NoMethodError: undefined method `name' for nil:NilClass") + %bwipe +endfunc + +" Test Vim::command({cmd}) (execute a Ex command)) +" Test Vim::command({cmd}) +func Test_Vim_command() + new + call setline(1, ['one', 'two', 'three', 'four']) + ruby Vim::command('2,3d') + call assert_equal(['one', 'four'], getline(1, '$')) + bwipe! +endfunc + +" Test Vim::set_option (set a vim option) +func Test_Vim_set_option() + call assert_equal(0, &number) + ruby Vim::set_option('number') + call assert_equal(1, &number) + ruby Vim::set_option('nonumber') + call assert_equal(0, &number) +endfunc + +func Test_Vim_evaluate() + call assert_equal(123, rubyeval('Vim::evaluate("123")')) + " Vim::evaluate("123").class gives Integer or Fixnum depending + " on versions of Ruby. + call assert_match('^Integer\|Fixnum$', rubyeval('Vim::evaluate("123").class')) + + call assert_equal(1.23, rubyeval('Vim::evaluate("1.23")')) + call assert_equal('Float', rubyeval('Vim::evaluate("1.23").class')) + + call assert_equal('foo', rubyeval('Vim::evaluate("\"foo\"")')) + call assert_equal('String', rubyeval('Vim::evaluate("\"foo\"").class')) + + call assert_equal([1, 2], rubyeval('Vim::evaluate("[1, 2]")')) + call assert_equal('Array', rubyeval('Vim::evaluate("[1, 2]").class')) + + call assert_equal({'1': 2}, rubyeval('Vim::evaluate("{1:2}")')) + call assert_equal('Hash', rubyeval('Vim::evaluate("{1:2}").class')) + + call assert_equal(v:null, rubyeval('Vim::evaluate("v:null")')) + call assert_equal('NilClass', rubyeval('Vim::evaluate("v:null").class')) + + " call assert_equal(v:null, rubyeval('Vim::evaluate("v:none")')) + " call assert_equal('NilClass', rubyeval('Vim::evaluate("v:none").class')) + + call assert_equal(v:true, rubyeval('Vim::evaluate("v:true")')) + call assert_equal('TrueClass', rubyeval('Vim::evaluate("v:true").class')) + call assert_equal(v:false, rubyeval('Vim::evaluate("v:false")')) + call assert_equal('FalseClass',rubyeval('Vim::evaluate("v:false").class')) +endfunc + +func Test_Vim_evaluate_list() + call setline(line('$'), ['2 line 2']) + ruby Vim.command("normal /^2\n") + let l = ["abc", "def"] + ruby << EOF + curline = $curbuf.line_number + l = Vim.evaluate("l"); + $curbuf.append(curline, l.join("|")) +EOF + normal j + .rubydo $_ = $_.gsub(/\|/, '/') + call assert_equal('abc/def', getline('$')) +endfunc + +func Test_Vim_evaluate_dict() + let d = {'a': 'foo', 'b': 123} + redir => l:out + ruby d = Vim.evaluate("d"); print d + redir END + call assert_equal(['{"a"=>"foo", "b"=>123}'], split(l:out, "\n")) +endfunc + +" Test Vim::message({msg}) (display message {msg}) +func Test_Vim_message() + throw 'skipped: TODO: ' + ruby Vim::message('A message') + let messages = split(execute('message'), "\n") + call assert_equal('A message', messages[-1]) +endfunc + +func Test_print() + func RubyPrint(expr) + return trim(execute('ruby print ' . a:expr)) + endfunc + + call assert_equal('123', RubyPrint('123')) + call assert_equal('1.23', RubyPrint('1.23')) + call assert_equal('Hello World!', RubyPrint('"Hello World!"')) + call assert_equal('[1, 2]', RubyPrint('[1, 2]')) + call assert_equal('{"k1"=>"v1", "k2"=>"v2"}', RubyPrint('({"k1" => "v1", "k2" => "v2"})')) + call assert_equal('true', RubyPrint('true')) + call assert_equal('false', RubyPrint('false')) + call assert_equal('', RubyPrint('nil')) + call assert_match('Vim', RubyPrint('Vim')) + call assert_match('Module', RubyPrint('Vim.class')) + + delfunc RubyPrint +endfunc + +func Test_p() + ruby p 'Just a test' + let messages = split(execute('message'), "\n") + call assert_equal('"Just a test"', messages[-1]) + + " Check return values of p method + + call assert_equal(123, rubyeval('p(123)')) + call assert_equal([1, 2, 3], rubyeval('p(1, 2, 3)')) + + " Avoid the "message maintainer" line. + let $LANG = '' + messages clear + call assert_equal(v:true, rubyeval('p() == nil')) + + let messages = split(execute('message'), "\n") + call assert_equal(0, len(messages)) endfunc diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index e072e9ed7f..f27920d20f 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -1,5 +1,7 @@ " Tests for the swap feature +source check.vim + func s:swapname() return trim(execute('swapname')) endfunc @@ -305,3 +307,61 @@ func Test_swap_recover_ext() augroup END augroup! test_swap_recover_ext endfunc + +" Test for selecting 'q' in the attention prompt +func Test_swap_prompt_splitwin() + CheckRunVimInTerminal + + call writefile(['foo bar'], 'Xfile1') + edit Xfile1 + preserve " should help to make sure the swap file exists + + let buf = RunVimInTerminal('', {'rows': 20}) + call term_sendkeys(buf, ":set nomore\n") + call term_sendkeys(buf, ":set noruler\n") + call term_sendkeys(buf, ":split Xfile1\n") + call term_wait(buf) + call WaitForAssert({-> assert_match('^\[O\]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: $', term_getline(buf, 20))}) + call term_sendkeys(buf, "q") + call term_wait(buf) + call term_sendkeys(buf, ":\<CR>") + call WaitForAssert({-> assert_match('^:$', term_getline(buf, 20))}) + call term_sendkeys(buf, ":echomsg winnr('$')\<CR>") + call term_wait(buf) + call WaitForAssert({-> assert_match('^1$', term_getline(buf, 20))}) + call StopVimInTerminal(buf) + %bwipe! + call delete('Xfile1') +endfunc + +func Test_swap_symlink() + if !has("unix") + return + endif + + call writefile(['text'], 'Xtestfile') + silent !ln -s -f Xtestfile Xtestlink + + set dir=. + + " Test that swap file uses the name of the file when editing through a + " symbolic link (so that editing the file twice is detected) + edit Xtestlink + call assert_match('Xtestfile\.swp$', s:swapname()) + bwipe! + + call mkdir('Xswapdir') + exe 'set dir=' . getcwd() . '/Xswapdir//' + + " Check that this also works when 'directory' ends with '//' + edit Xtestlink + call assert_match('Xtestfile\.swp$', s:swapname()) + bwipe! + + set dir& + call delete('Xtestfile') + call delete('Xtestlink') + call delete('Xswapdir', 'rf') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 2404f113d9..2617aa3945 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -475,6 +475,40 @@ func Test_bg_detection() hi Normal ctermbg=NONE endfunc +func Test_syntax_hangs() + if !has('reltime') || !has('float') || !has('syntax') + return + endif + + " This pattern takes a long time to match, it should timeout. + new + call setline(1, ['aaa', repeat('abc ', 1000), 'ccc']) + let start = reltime() + set nolazyredraw redrawtime=101 + syn match Error /\%#=1a*.*X\@<=b*/ + redraw + let elapsed = reltimefloat(reltime(start)) + call assert_true(elapsed > 0.1) + call assert_true(elapsed < 1.0) + + " second time syntax HL is disabled + let start = reltime() + redraw + let elapsed = reltimefloat(reltime(start)) + call assert_true(elapsed < 0.1) + + " after CTRL-L the timeout flag is reset + let start = reltime() + exe "normal \<C-L>" + redraw + let elapsed = reltimefloat(reltime(start)) + call assert_true(elapsed > 0.1) + call assert_true(elapsed < 1.0) + + set redrawtime& + bwipe! +endfunc + func Test_synstack_synIDtrans() new setfiletype c @@ -550,38 +584,42 @@ func Test_syn_wrong_z_one() bwipe! endfunc -func Test_syntax_hangs() - if !has('reltime') || !has('float') || !has('syntax') - return - endif +func Test_syntax_after_bufdo() + call writefile(['/* aaa comment */'], 'Xaaa.c') + call writefile(['/* bbb comment */'], 'Xbbb.c') + call writefile(['/* ccc comment */'], 'Xccc.c') + call writefile(['/* ddd comment */'], 'Xddd.c') + + let bnr = bufnr('%') + new Xaaa.c + badd Xbbb.c + badd Xccc.c + badd Xddd.c + exe "bwipe " . bnr + let l = [] + bufdo call add(l, bufnr('%')) + call assert_equal(4, len(l)) - " This pattern takes a long time to match, it should timeout. - new - call setline(1, ['aaa', repeat('abc ', 1000), 'ccc']) - let start = reltime() - set nolazyredraw redrawtime=101 - syn match Error /\%#=1a*.*X\@<=b*/ - redraw - let elapsed = reltimefloat(reltime(start)) - call assert_true(elapsed > 0.1) - call assert_true(elapsed < 1.0) - - " second time syntax HL is disabled - let start = reltime() - redraw - let elapsed = reltimefloat(reltime(start)) - call assert_true(elapsed < 0.1) - - " after CTRL-L the timeout flag is reset - let start = reltime() - exe "normal \<C-L>" - redraw - let elapsed = reltimefloat(reltime(start)) - call assert_true(elapsed > 0.1) - call assert_true(elapsed < 1.0) + syntax on - set redrawtime& - bwipe! + " This used to only enable syntax HL in the last buffer. + bufdo tab split + tabrewind + for tab in range(1, 4) + norm fm + call assert_equal(['cComment'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")')) + tabnext + endfor + + bwipe! Xaaa.c + bwipe! Xbbb.c + bwipe! Xccc.c + bwipe! Xddd.c + syntax off + call delete('Xaaa.c') + call delete('Xbbb.c') + call delete('Xccc.c') + call delete('Xddd.c') endfunc func Test_syntax_foldlevel() diff --git a/src/nvim/testdir/test_textobjects.vim b/src/nvim/testdir/test_textobjects.vim index 7863317eb0..f70cc1f70a 100644 --- a/src/nvim/testdir/test_textobjects.vim +++ b/src/nvim/testdir/test_textobjects.vim @@ -152,6 +152,36 @@ func Test_string_html_objects() normal! dit call assert_equal('-<b></b>', getline('.')) + " copy the tag block from leading indentation before the start tag + let t = " <b>\ntext\n</b>" + $put =t + normal! 2kvaty + call assert_equal("<b>\ntext\n</b>", @") + + " copy the tag block from the end tag + let t = "<title>\nwelcome\n</title>" + $put =t + normal! $vaty + call assert_equal("<title>\nwelcome\n</title>", @") + + " copy the outer tag block from a tag without an end tag + let t = "<html>\n<title>welcome\n</html>" + $put =t + normal! k$vaty + call assert_equal("<html>\n<title>welcome\n</html>", @") + + " nested tag that has < in a different line from > + let t = "<div><div\n></div></div>" + $put =t + normal! k0vaty + call assert_equal("<div><div\n></div></div>", @") + + " nested tag with attribute that has < in a different line from > + let t = "<div><div\nattr=\"attr\"\n></div></div>" + $put =t + normal! 2k0vaty + call assert_equal("<div><div\nattr=\"attr\"\n></div></div>", @") + set quoteescape& enew! endfunc diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index fdd3a9abeb..0a89066a2b 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -276,7 +276,7 @@ func Test_CmdCompletion() call assert_equal('"com -nargs=* + 0 1 ?', @:) call feedkeys(":com -addr=\<C-A>\<C-B>\"\<CR>", 'tx') - call assert_equal('"com -addr=arguments buffers lines loaded_buffers quickfix tabs windows', @:) + call assert_equal('"com -addr=arguments buffers lines loaded_buffers other quickfix tabs windows', @:) call feedkeys(":com -complete=co\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"com -complete=color command compiler', @:) @@ -340,3 +340,202 @@ func Test_use_execute_in_completion() call assert_equal('"DoExec hi', @:) delcommand DoExec endfunc + +func Test_addr_all() + throw 'skipped: requires patch v8.1.0341 to pass' + command! -addr=lines DoSomething let g:a1 = <line1> | let g:a2 = <line2> + %DoSomething + call assert_equal(1, g:a1) + call assert_equal(line('$'), g:a2) + + command! -addr=arguments DoSomething let g:a1 = <line1> | let g:a2 = <line2> + args one two three + %DoSomething + call assert_equal(1, g:a1) + call assert_equal(3, g:a2) + + command! -addr=buffers DoSomething let g:a1 = <line1> | let g:a2 = <line2> + %DoSomething + for low in range(1, bufnr('$')) + if buflisted(low) + break + endif + endfor + call assert_equal(low, g:a1) + call assert_equal(bufnr('$'), g:a2) + + command! -addr=loaded_buffers DoSomething let g:a1 = <line1> | let g:a2 = <line2> + %DoSomething + for low in range(1, bufnr('$')) + if bufloaded(low) + break + endif + endfor + call assert_equal(low, g:a1) + for up in range(bufnr('$'), 1, -1) + if bufloaded(up) + break + endif + endfor + call assert_equal(up, g:a2) + + command! -addr=windows DoSomething let g:a1 = <line1> | let g:a2 = <line2> + new + %DoSomething + call assert_equal(1, g:a1) + call assert_equal(winnr('$'), g:a2) + bwipe + + command! -addr=tabs DoSomething let g:a1 = <line1> | let g:a2 = <line2> + tabnew + %DoSomething + call assert_equal(1, g:a1) + call assert_equal(len(gettabinfo()), g:a2) + bwipe + + command! -addr=other DoSomething echo 'nothing' + DoSomething + call assert_fails('%DoSomething') + + delcommand DoSomething +endfunc + +func Test_command_list() + command! DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 :", + \ execute('command DoCmd')) + + " Test with various -range= and -count= argument values. + command! -range DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 . :", + \ execute('command DoCmd')) + command! -range=% DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 % :", + \ execute('command! DoCmd')) + command! -range=2 DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 2 :", + \ execute('command DoCmd')) + command! -count=2 DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 2c :", + \ execute('command DoCmd')) + + " Test with various -addr= argument values. + command! -addr=lines DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 . :", + \ execute('command DoCmd')) + command! -addr=arguments DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 . arg :", + \ execute('command DoCmd')) + command! -addr=buffers DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 . buf :", + \ execute('command DoCmd')) + command! -addr=loaded_buffers DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 . load :", + \ execute('command DoCmd')) + command! -addr=windows DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 . win :", + \ execute('command DoCmd')) + command! -addr=tabs DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 . tab :", + \ execute('command DoCmd')) + command! -addr=other DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 . ? :", + \ execute('command DoCmd')) + + " Test with various -complete= argument values (non-exhaustive list) + command! -complete=arglist DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 arglist :", + \ execute('command DoCmd')) + command! -complete=augroup DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 augroup :", + \ execute('command DoCmd')) + command! -complete=custom,CustomComplete DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 custom :", + \ execute('command DoCmd')) + command! -complete=customlist,CustomComplete DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 customlist :", + \ execute('command DoCmd')) + + " Test with various -narg= argument values. + command! -nargs=0 DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 :", + \ execute('command DoCmd')) + command! -nargs=1 DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 1 :", + \ execute('command DoCmd')) + command! -nargs=* DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd * :", + \ execute('command DoCmd')) + command! -nargs=? DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd ? :", + \ execute('command DoCmd')) + command! -nargs=+ DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd + :", + \ execute('command DoCmd')) + + " Test with other arguments. + command! -bang DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n! DoCmd 0 :", + \ execute('command DoCmd')) + command! -bar DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n| DoCmd 0 :", + \ execute('command DoCmd')) + command! -register DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n\" DoCmd 0 :", + \ execute('command DoCmd')) + command! -buffer DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\nb DoCmd 0 :" + \ .. "\n\" DoCmd 0 :", + \ execute('command DoCmd')) + comclear + + " Test with many args. + command! -bang -bar -register -buffer -nargs=+ -complete=environment -addr=windows -count=3 DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n!\"b|DoCmd + 3c win environment :", + \ execute('command DoCmd')) + comclear + + " Test with special characters in command definition. + command! DoCmd :<cr><tab><c-d> + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd 0 :<CR><Tab><C-D>", + \ execute('command DoCmd')) + + " Test output in verbose mode. + command! DoCmd : + call assert_match("^\n" + \ .. " Name Args Address Complete Definition\n" + \ .. " DoCmd 0 :\n" + \ .. "\tLast set from .*/test_usercommands.vim line \\d\\+$", + \ execute('verbose command DoCmd')) + + comclear + call assert_equal("\nNo user-defined commands found", execute(':command Xxx')) + call assert_equal("\nNo user-defined commands found", execute('command')) +endfunc diff --git a/src/nvim/testdir/test_version.vim b/src/nvim/testdir/test_version.vim new file mode 100644 index 0000000000..46cf34979f --- /dev/null +++ b/src/nvim/testdir/test_version.vim @@ -0,0 +1,12 @@ +" Test :version Ex command + +func Test_version() + " version should always return the same string. + let v1 = execute('version') + let v2 = execute('version') + call assert_equal(v1, v2) + + call assert_match("^\n\nNVIM v[0-9]\\+\\.[0-9]\\+\\.[0-9]\\+.*", v1) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 9f47ee2904..500e3ff088 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -879,6 +879,10 @@ func Test_window_resize() exe other_winnr .. 'resize 10' call assert_equal(10, winheight(other_winnr)) call assert_equal(&lines - 10 - 3, winheight(0)) + exe other_winnr .. 'resize +1' + exe other_winnr .. 'resize +1' + call assert_equal(12, winheight(other_winnr)) + call assert_equal(&lines - 10 - 3 -2, winheight(0)) %bwipe! endfunc diff --git a/src/nvim/window.c b/src/nvim/window.c index e53570edd8..4931221e7a 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2578,9 +2578,10 @@ int win_close(win_T *win, bool free_buf) return OK; } - /* Free independent synblock before the buffer is freed. */ - if (win->w_buffer != NULL) + // Free independent synblock before the buffer is freed. + if (win->w_buffer != NULL) { reset_synblock(win); + } /* * Close the link to the buffer. diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index da7515f012..8ed642b43e 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -534,6 +534,26 @@ describe('api/buf', function() end) end) + describe('nvim_buf_delete', function() + it('allows for just deleting', function() + nvim('command', 'new') + local b = nvim('get_current_buf') + ok(buffer('is_valid', b)) + nvim('buf_delete', b, {}) + ok(not buffer('is_loaded', b)) + ok(not buffer('is_valid', b)) + end) + + it('allows for just unloading', function() + nvim('command', 'new') + local b = nvim('get_current_buf') + ok(buffer('is_valid', b)) + nvim('buf_delete', b, { unload = true }) + ok(not buffer('is_loaded', b)) + ok(buffer('is_valid', b)) + end) + end) + describe('nvim_buf_get_mark', function() it('works', function() curbuf('set_lines', -1, -1, true, {'a', 'bit of', 'text'}) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 72e810e3e4..0b52d06df9 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -449,19 +449,19 @@ describe('API', function() end) it('reports errors', function() - eq([[Error loading lua: [string "<nvim>"]:1: '=' expected near '+']], + eq([[Error loading lua: [string "<nvim>"]:0: '=' expected near '+']], pcall_err(meths.exec_lua, 'a+*b', {})) - eq([[Error loading lua: [string "<nvim>"]:1: unexpected symbol near '1']], + eq([[Error loading lua: [string "<nvim>"]:0: unexpected symbol near '1']], pcall_err(meths.exec_lua, '1+2', {})) - eq([[Error loading lua: [string "<nvim>"]:1: unexpected symbol]], + eq([[Error loading lua: [string "<nvim>"]:0: unexpected symbol]], pcall_err(meths.exec_lua, 'aa=bb\0', {})) - eq([[Error executing lua: [string "<nvim>"]:1: attempt to call global 'bork' (a nil value)]], + eq([[Error executing lua: [string "<nvim>"]:0: attempt to call global 'bork' (a nil value)]], pcall_err(meths.exec_lua, 'bork()', {})) - eq('Error executing lua: [string "<nvim>"]:1: did\nthe\nfail', + eq('Error executing lua: [string "<nvim>"]:0: did\nthe\nfail', pcall_err(meths.exec_lua, 'error("did\\nthe\\nfail")', {})) end) @@ -605,7 +605,7 @@ describe('API', function() end) it('vim.paste() failure', function() nvim('exec_lua', 'vim.paste = (function(lines, phase) error("fake fail") end)', {}) - eq([[Error executing lua: [string "<nvim>"]:1: fake fail]], + eq([[Error executing lua: [string "<nvim>"]:0: fake fail]], pcall_err(request, 'nvim_paste', 'line 1\nline 2\nline 3', false, 1)) end) end) @@ -1252,7 +1252,7 @@ describe('API', function() {0:~ }| {1:very fail} | ]]) - helpers.wait() + helpers.poke_eventloop() -- shows up to &cmdheight lines nvim_async('err_write', 'more fail\ntoo fail\n') diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 8c7c3208c0..7471f50dbd 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -3,7 +3,7 @@ local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq, ok, feed, insert, eval = helpers.clear, helpers.nvim, helpers.curbuf, helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq, helpers.neq, helpers.ok, helpers.feed, helpers.insert, helpers.eval -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local curwinmeths = helpers.curwinmeths local funcs = helpers.funcs local request = helpers.request @@ -82,7 +82,7 @@ describe('API/win', function() insert("epilogue") local win = curwin() feed('gg') - wait() -- let nvim process the 'gg' command + poke_eventloop() -- let nvim process the 'gg' command -- cursor position is at beginning eq({1, 0}, window('get_cursor', win)) @@ -128,7 +128,7 @@ describe('API/win', function() insert("second line") feed('gg') - wait() -- let nvim process the 'gg' command + poke_eventloop() -- let nvim process the 'gg' command -- cursor position is at beginning local win = curwin() @@ -139,7 +139,7 @@ describe('API/win', function() -- move down a line feed('j') - wait() -- let nvim process the 'j' command + poke_eventloop() -- let nvim process the 'j' command -- cursor is still in column 5 eq({2, 5}, window('get_cursor', win)) diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua index 80c65e4544..230b7f8e01 100644 --- a/test/functional/core/exit_spec.lua +++ b/test/functional/core/exit_spec.lua @@ -8,7 +8,7 @@ local run = helpers.run local funcs = helpers.funcs local nvim_prog = helpers.nvim_prog local redir_exec = helpers.redir_exec -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('v:exiting', function() local cid @@ -52,7 +52,7 @@ describe(':cquit', function() local function test_cq(cmdline, exit_code, redir_msg) if redir_msg then eq('\n' .. redir_msg, redir_exec(cmdline)) - wait() + poke_eventloop() eq(2, eval("1+1")) -- Still alive? else funcs.system({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '--cmd', cmdline}) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 1155f12ffc..6d1182478a 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -11,7 +11,7 @@ local os_kill = helpers.os_kill local retry = helpers.retry local meths = helpers.meths local NIL = helpers.NIL -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local iswin = helpers.iswin local get_pathsep = helpers.get_pathsep local pathroot = helpers.pathroot @@ -428,7 +428,7 @@ describe('jobs', function() \ } let job = jobstart(['cat', '-'], g:callbacks) ]]) - wait() + poke_eventloop() source([[ function! g:JobHandler(job_id, data, event) endfunction diff --git a/test/functional/eval/interrupt_spec.lua b/test/functional/eval/interrupt_spec.lua index 7f4ca95317..05b1f4ff57 100644 --- a/test/functional/eval/interrupt_spec.lua +++ b/test/functional/eval/interrupt_spec.lua @@ -4,7 +4,7 @@ local command = helpers.command local meths = helpers.meths local clear = helpers.clear local sleep = helpers.sleep -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local feed = helpers.feed local eq = helpers.eq @@ -39,7 +39,7 @@ describe('List support code', function() feed(':let t_rt = reltime()<CR>:let t_bl = copy(bl)<CR>') sleep(min_dur / 16 * 1000) feed('<C-c>') - wait() + poke_eventloop() command('let t_dur = reltimestr(reltime(t_rt))') local t_dur = tonumber(meths.get_var('t_dur')) if t_dur >= dur / 8 then @@ -50,7 +50,7 @@ describe('List support code', function() feed(':let t_rt = reltime()<CR>:let t_j = join(bl)<CR>') sleep(min_dur / 16 * 1000) feed('<C-c>') - wait() + poke_eventloop() command('let t_dur = reltimestr(reltime(t_rt))') local t_dur = tonumber(meths.get_var('t_dur')) print(('t_dur: %g'):format(t_dur)) diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua index 802c3f68c6..003ab64dd4 100644 --- a/test/functional/ex_cmds/oldfiles_spec.lua +++ b/test/functional/ex_cmds/oldfiles_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local buf, eq, feed_command = helpers.curbufmeths, helpers.eq, helpers.feed_command -local feed, wait = helpers.feed, helpers.wait +local feed, poke_eventloop = helpers.feed, helpers.poke_eventloop local ok = helpers.ok local eval = helpers.eval @@ -90,7 +90,7 @@ describe(':browse oldfiles', function() feed_command('edit testfile2') filename2 = buf.get_name() feed_command('wshada') - wait() + poke_eventloop() _clear() -- Ensure nvim is out of "Press ENTER..." prompt. diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index dca7f35923..a30eb748d0 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -125,6 +125,26 @@ function tests.basic_check_capabilities() } end +function tests.capabilities_for_client_supports_method() + skeleton { + on_init = function(params) + local expected_capabilities = protocol.make_client_capabilities() + assert_eq(params.capabilities, expected_capabilities) + return { + capabilities = { + textDocumentSync = protocol.TextDocumentSyncKind.Full; + completionProvider = true; + hoverProvider = true; + definitionProvider = false; + referencesProvider = false; + } + } + end; + body = function() + end; + } +end + function tests.basic_finish() skeleton { on_init = function(params) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 99cbf30c7c..0bd378d832 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -554,9 +554,9 @@ function module.curbuf(method, ...) return module.buffer(method, 0, ...) end -function module.wait() - -- Execute 'nvim_eval' (a deferred function) to block - -- until all pending input is processed. +function module.poke_eventloop() + -- Execute 'nvim_eval' (a deferred function) to + -- force at least one main_loop iteration session:request('nvim_eval', '1') end @@ -566,7 +566,7 @@ end --@see buf_lines() function module.curbuf_contents() - module.wait() -- Before inspecting the buffer, process all input. + module.poke_eventloop() -- Before inspecting the buffer, do whatever. return table.concat(module.curbuf('get_lines', 0, -1, true), '\n') end diff --git a/test/functional/legacy/005_bufleave_delete_buffer_spec.lua b/test/functional/legacy/005_bufleave_delete_buffer_spec.lua index 8b92c877a6..8e977aa73e 100644 --- a/test/functional/legacy/005_bufleave_delete_buffer_spec.lua +++ b/test/functional/legacy/005_bufleave_delete_buffer_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command, expect = helpers.command, helpers.expect -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('test5', function() setup(clear) @@ -34,7 +34,7 @@ describe('test5', function() command('bwipe') feed('G?this is a<cr>') feed('othis is some more text<esc>') - wait() + poke_eventloop() -- Append some text to this file. @@ -45,7 +45,7 @@ describe('test5', function() command('bwipe!') -- Append an extra line to the output register. feed('ithis is another test line<esc>:yank A<cr>') - wait() + poke_eventloop() -- Output results command('%d') diff --git a/test/functional/legacy/006_argument_list_spec.lua b/test/functional/legacy/006_argument_list_spec.lua index 9f75a91fa8..d269bf8ec9 100644 --- a/test/functional/legacy/006_argument_list_spec.lua +++ b/test/functional/legacy/006_argument_list_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command, dedent, eq = helpers.command, helpers.dedent, helpers.eq local curbuf_contents = helpers.curbuf_contents -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('argument list', function() setup(clear) @@ -17,7 +17,7 @@ describe('argument list', function() this is a test this is a test end of test file Xxx]]) - wait() + poke_eventloop() command('au BufReadPost Xxx2 next Xxx2 Xxx1') command('/^start of') @@ -30,7 +30,7 @@ describe('argument list', function() -- Write test file Xxx3 feed('$r3:.,/end of/w! Xxx3<cr>') - wait() + poke_eventloop() -- Redefine arglist; go to Xxx1 command('next! Xxx1 Xxx2 Xxx3') @@ -43,7 +43,7 @@ describe('argument list', function() -- Append contents of last window (Xxx1) feed('') - wait() + poke_eventloop() command('%yank A') -- should now be in Xxx2 diff --git a/test/functional/legacy/012_directory_spec.lua b/test/functional/legacy/012_directory_spec.lua index cec4f93737..48dd24db9e 100644 --- a/test/functional/legacy/012_directory_spec.lua +++ b/test/functional/legacy/012_directory_spec.lua @@ -8,7 +8,7 @@ local lfs = require('lfs') local eq = helpers.eq local neq = helpers.neq -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local funcs = helpers.funcs local meths = helpers.meths local clear = helpers.clear @@ -64,7 +64,7 @@ describe("'directory' option", function() eq(nil, lfs.attributes('.Xtest1.swp')) command('edit! Xtest1') - wait() + poke_eventloop() eq('Xtest1', funcs.buffer_name('%')) -- Verify that the swapfile exists. In the legacy test this was done by -- reading the output from :!ls. @@ -72,7 +72,7 @@ describe("'directory' option", function() meths.set_option('directory', './Xtest2,.') command('edit Xtest1') - wait() + poke_eventloop() -- swapfile should no longer exist in CWD. eq(nil, lfs.attributes('.Xtest1.swp')) @@ -82,7 +82,7 @@ describe("'directory' option", function() meths.set_option('directory', 'Xtest.je') command('edit Xtest2/Xtest3') eq(true, curbufmeths.get_option('swapfile')) - wait() + poke_eventloop() eq({ "Xtest3" }, ls_dir_sorted("Xtest2")) eq({ "Xtest3.swp" }, ls_dir_sorted("Xtest.je")) diff --git a/test/functional/legacy/023_edit_arguments_spec.lua b/test/functional/legacy/023_edit_arguments_spec.lua index e705397a2b..f59d192c1e 100644 --- a/test/functional/legacy/023_edit_arguments_spec.lua +++ b/test/functional/legacy/023_edit_arguments_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, insert = helpers.clear, helpers.insert local command, expect = helpers.command, helpers.expect -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe(':edit', function() setup(clear) @@ -13,7 +13,7 @@ describe(':edit', function() The result should be in Xfile1: "fooPIPEbar", in Xfile2: "fooSLASHbar" foo|bar foo/bar]]) - wait() + poke_eventloop() -- Prepare some test files command('$-1w! Xfile1') diff --git a/test/functional/legacy/030_fileformats_spec.lua b/test/functional/legacy/030_fileformats_spec.lua index 2fd51602d8..15dbd05cf5 100644 --- a/test/functional/legacy/030_fileformats_spec.lua +++ b/test/functional/legacy/030_fileformats_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, clear, command = helpers.feed, helpers.clear, helpers.command local eq, write_file = helpers.eq, helpers.write_file -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('fileformats option', function() setup(function() @@ -107,7 +107,7 @@ describe('fileformats option', function() command('bwipe XXDosMac') command('e! XXEol') feed('ggO<C-R>=&ffs<CR>:<C-R>=&ff<CR><ESC>') - wait() + poke_eventloop() command('w! XXtt54') command('bwipeout! XXEol') command('set fileformats=dos,mac') @@ -116,7 +116,7 @@ describe('fileformats option', function() command('bwipe XXUxDs') command('e! XXUxMac') feed('ggO<C-R>=&ffs<CR>:<C-R>=&ff<CR><ESC>') - wait() + poke_eventloop() command('w! XXtt62') command('bwipeout! XXUxMac') command('e! XXUxDsMc') @@ -124,7 +124,7 @@ describe('fileformats option', function() command('bwipe XXUxDsMc') command('e! XXMacEol') feed('ggO<C-R>=&ffs<CR>:<C-R>=&ff<CR><ESC>') - wait() + poke_eventloop() command('w! XXtt64') command('bwipeout! XXMacEol') @@ -135,7 +135,7 @@ describe('fileformats option', function() command('bwipe XXUxDsMc') command('e! XXEol') feed('ggO<C-R>=&ffs<CR>:<C-R>=&ff<CR><ESC>') - wait() + poke_eventloop() command('w! XXtt72') command('bwipeout! XXEol') command('set fileformats=mac,dos,unix') @@ -144,7 +144,7 @@ describe('fileformats option', function() command('bwipe XXUxDsMc') command('e! XXEol') feed('ggO<C-R>=&ffs<CR>:<C-R>=&ff<CR><ESC>') - wait() + poke_eventloop() command('w! XXtt82') command('bwipeout! XXEol') -- Try with 'binary' set. @@ -165,7 +165,7 @@ describe('fileformats option', function() -- char was. command('set fileformat=unix nobin') feed('ggdGaEND<esc>') - wait() + poke_eventloop() command('w >>XXtt01') command('w >>XXtt02') command('w >>XXtt11') @@ -204,52 +204,52 @@ describe('fileformats option', function() command('$r XXtt01') command('$r XXtt02') feed('Go1<esc>') - wait() + poke_eventloop() command('$r XXtt11') command('$r XXtt12') command('$r XXtt13') feed('Go2<esc>') - wait() + poke_eventloop() command('$r XXtt21') command('$r XXtt22') command('$r XXtt23') feed('Go3<esc>') - wait() + poke_eventloop() command('$r XXtt31') command('$r XXtt32') command('$r XXtt33') feed('Go4<esc>') - wait() + poke_eventloop() command('$r XXtt41') command('$r XXtt42') command('$r XXtt43') feed('Go5<esc>') - wait() + poke_eventloop() command('$r XXtt51') command('$r XXtt52') command('$r XXtt53') command('$r XXtt54') feed('Go6<esc>') - wait() + poke_eventloop() command('$r XXtt61') command('$r XXtt62') command('$r XXtt63') command('$r XXtt64') feed('Go7<esc>') - wait() + poke_eventloop() command('$r XXtt71') command('$r XXtt72') feed('Go8<esc>') - wait() + poke_eventloop() command('$r XXtt81') command('$r XXtt82') feed('Go9<esc>') - wait() + poke_eventloop() command('$r XXtt91') command('$r XXtt92') command('$r XXtt93') feed('Go10<esc>') - wait() + poke_eventloop() command('$r XXUnix') command('set nobinary ff&') diff --git a/test/functional/legacy/033_lisp_indent_spec.lua b/test/functional/legacy/033_lisp_indent_spec.lua index 5132333a5c..b27de6c16d 100644 --- a/test/functional/legacy/033_lisp_indent_spec.lua +++ b/test/functional/legacy/033_lisp_indent_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command, expect = helpers.command, helpers.expect -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('lisp indent', function() setup(clear) @@ -39,7 +39,7 @@ describe('lisp indent', function() command('set lisp') command('/^(defun') feed('=G:/^(defun/,$yank A<cr>') - wait() + poke_eventloop() -- Put @a and clean empty line command('%d') diff --git a/test/functional/legacy/036_regexp_character_classes_spec.lua b/test/functional/legacy/036_regexp_character_classes_spec.lua index 38e8145d1c..6f66efcb67 100644 --- a/test/functional/legacy/036_regexp_character_classes_spec.lua +++ b/test/functional/legacy/036_regexp_character_classes_spec.lua @@ -15,7 +15,7 @@ end local function diff(text, nodedent) local fname = helpers.tmpname() command('w! '..fname) - helpers.wait() + helpers.poke_eventloop() local data = io.open(fname):read('*all') if nodedent then helpers.eq(text, data) diff --git a/test/functional/legacy/045_folding_spec.lua b/test/functional/legacy/045_folding_spec.lua index 1e5239ceac..7d7856fd37 100644 --- a/test/functional/legacy/045_folding_spec.lua +++ b/test/functional/legacy/045_folding_spec.lua @@ -59,7 +59,7 @@ describe('folding', function() feed('kYpj') feed_command('call append("$", foldlevel("."))') - helpers.wait() + helpers.poke_eventloop() screen:expect([[ dd {{{ | ee {{{ }}} | @@ -88,7 +88,7 @@ describe('folding', function() feed_command('call append("$", foldlevel(2))') feed('zR') - helpers.wait() + helpers.poke_eventloop() screen:expect([[ aa | bb | diff --git a/test/functional/legacy/051_highlight_spec.lua b/test/functional/legacy/051_highlight_spec.lua index 0c9c9621ee..d3f2897493 100644 --- a/test/functional/legacy/051_highlight_spec.lua +++ b/test/functional/legacy/051_highlight_spec.lua @@ -5,7 +5,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed = helpers.clear, helpers.feed local expect = helpers.expect local eq = helpers.eq -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local exc_exec = helpers.exc_exec local feed_command = helpers.feed_command @@ -34,7 +34,7 @@ describe(':highlight', function() -- More --^ | ]]) feed('q') - wait() -- wait until we're back to normal + poke_eventloop() -- wait until we're back to normal feed_command('hi Search') feed_command('hi Normal') diff --git a/test/functional/legacy/057_sort_spec.lua b/test/functional/legacy/057_sort_spec.lua index bdc2c9779c..328d6f6fa0 100644 --- a/test/functional/legacy/057_sort_spec.lua +++ b/test/functional/legacy/057_sort_spec.lua @@ -2,8 +2,8 @@ local helpers = require('test.functional.helpers')(after_each) -local insert, command, clear, expect, eq, wait = helpers.insert, - helpers.command, helpers.clear, helpers.expect, helpers.eq, helpers.wait +local insert, command, clear, expect, eq, poke_eventloop = helpers.insert, + helpers.command, helpers.clear, helpers.expect, helpers.eq, helpers.poke_eventloop local exc_exec = helpers.exc_exec describe(':sort', function() @@ -27,7 +27,7 @@ describe(':sort', function() it('alphabetical', function() insert(text) - wait() + poke_eventloop() command('sort') expect([[ @@ -67,7 +67,7 @@ describe(':sort', function() b321 b321b ]]) - wait() + poke_eventloop() command('sort n') expect([[ abc @@ -92,7 +92,7 @@ describe(':sort', function() it('hexadecimal', function() insert(text) - wait() + poke_eventloop() command('sort x') expect([[ @@ -114,7 +114,7 @@ describe(':sort', function() it('alphabetical, unique', function() insert(text) - wait() + poke_eventloop() command('sort u') expect([[ @@ -135,7 +135,7 @@ describe(':sort', function() it('alphabetical, reverse', function() insert(text) - wait() + poke_eventloop() command('sort!') expect([[ c321d @@ -157,7 +157,7 @@ describe(':sort', function() it('numerical, reverse', function() insert(text) - wait() + poke_eventloop() command('sort! n') expect([[ b322b @@ -179,7 +179,7 @@ describe(':sort', function() it('unique, reverse', function() insert(text) - wait() + poke_eventloop() command('sort! u') expect([[ c321d @@ -200,7 +200,7 @@ describe(':sort', function() it('octal', function() insert(text) - wait() + poke_eventloop() command('sort o') expect([[ abc @@ -222,7 +222,7 @@ describe(':sort', function() it('reverse, hexadecimal', function() insert(text) - wait() + poke_eventloop() command('sort! x') expect([[ c321d @@ -244,7 +244,7 @@ describe(':sort', function() it('alphabetical, skip first character', function() insert(text) - wait() + poke_eventloop() command('sort/./') expect([[ a @@ -266,7 +266,7 @@ describe(':sort', function() it('alphabetical, skip first 2 characters', function() insert(text) - wait() + poke_eventloop() command('sort/../') expect([[ ab @@ -288,7 +288,7 @@ describe(':sort', function() it('alphabetical, unique, skip first 2 characters', function() insert(text) - wait() + poke_eventloop() command('sort/../u') expect([[ ab @@ -309,7 +309,7 @@ describe(':sort', function() it('numerical, skip first character', function() insert(text) - wait() + poke_eventloop() command('sort/./n') expect([[ abc @@ -331,7 +331,7 @@ describe(':sort', function() it('alphabetical, sort on first character', function() insert(text) - wait() + poke_eventloop() command('sort/./r') expect([[ @@ -353,7 +353,7 @@ describe(':sort', function() it('alphabetical, sort on first 2 characters', function() insert(text) - wait() + poke_eventloop() command('sort/../r') expect([[ a @@ -375,7 +375,7 @@ describe(':sort', function() it('numerical, sort on first character', function() insert(text) - wait() + poke_eventloop() command('sort/./rn') expect([[ abc @@ -397,7 +397,7 @@ describe(':sort', function() it('alphabetical, skip past first digit', function() insert(text) - wait() + poke_eventloop() command([[sort/\d/]]) expect([[ abc @@ -419,7 +419,7 @@ describe(':sort', function() it('alphabetical, sort on first digit', function() insert(text) - wait() + poke_eventloop() command([[sort/\d/r]]) expect([[ abc @@ -441,7 +441,7 @@ describe(':sort', function() it('numerical, skip past first digit', function() insert(text) - wait() + poke_eventloop() command([[sort/\d/n]]) expect([[ abc @@ -463,7 +463,7 @@ describe(':sort', function() it('numerical, sort on first digit', function() insert(text) - wait() + poke_eventloop() command([[sort/\d/rn]]) expect([[ abc @@ -485,7 +485,7 @@ describe(':sort', function() it('alphabetical, skip past first 2 digits', function() insert(text) - wait() + poke_eventloop() command([[sort/\d\d/]]) expect([[ abc @@ -507,7 +507,7 @@ describe(':sort', function() it('numerical, skip past first 2 digits', function() insert(text) - wait() + poke_eventloop() command([[sort/\d\d/n]]) expect([[ abc @@ -529,7 +529,7 @@ describe(':sort', function() it('hexadecimal, skip past first 2 digits', function() insert(text) - wait() + poke_eventloop() command([[sort/\d\d/x]]) expect([[ abc @@ -551,7 +551,7 @@ describe(':sort', function() it('alpha, on first 2 digits', function() insert(text) - wait() + poke_eventloop() command([[sort/\d\d/r]]) expect([[ abc @@ -573,7 +573,7 @@ describe(':sort', function() it('numeric, on first 2 digits', function() insert(text) - wait() + poke_eventloop() command([[sort/\d\d/rn]]) expect([[ abc @@ -595,7 +595,7 @@ describe(':sort', function() it('hexadecimal, on first 2 digits', function() insert(text) - wait() + poke_eventloop() command([[sort/\d\d/rx]]) expect([[ abc @@ -638,7 +638,7 @@ describe(':sort', function() 0b100010 0b100100 0b100010]]) - wait() + poke_eventloop() command([[sort b]]) expect([[ 0b000000 @@ -673,7 +673,7 @@ describe(':sort', function() 0b101010 0b000000 b0b111000]]) - wait() + poke_eventloop() command([[sort b]]) expect([[ 0b000000 @@ -700,7 +700,7 @@ describe(':sort', function() 1.15e-6 -1.1e3 -1.01e3]]) - wait() + poke_eventloop() command([[sort f]]) expect([[ -1.1e3 diff --git a/test/functional/legacy/074_global_var_in_viminfo_spec.lua b/test/functional/legacy/074_global_var_in_viminfo_spec.lua index f7f074c61a..445d742c1f 100644 --- a/test/functional/legacy/074_global_var_in_viminfo_spec.lua +++ b/test/functional/legacy/074_global_var_in_viminfo_spec.lua @@ -2,9 +2,9 @@ local helpers = require('test.functional.helpers')(after_each) local lfs = require('lfs') -local clear, command, eq, neq, eval, wait = +local clear, command, eq, neq, eval, poke_eventloop = helpers.clear, helpers.command, helpers.eq, helpers.neq, helpers.eval, - helpers.wait + helpers.poke_eventloop describe('storing global variables in ShaDa files', function() local tempname = 'Xtest-functional-legacy-074' @@ -36,7 +36,7 @@ describe('storing global variables in ShaDa files', function() eq(test_list, eval('MY_GLOBAL_LIST')) command('wsh! ' .. tempname) - wait() + poke_eventloop() -- Assert that the shada file exists. neq(nil, lfs.attributes(tempname)) diff --git a/test/functional/legacy/075_maparg_spec.lua b/test/functional/legacy/075_maparg_spec.lua index ee2b041b51..ad6c190104 100644 --- a/test/functional/legacy/075_maparg_spec.lua +++ b/test/functional/legacy/075_maparg_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed = helpers.clear, helpers.feed local command, expect = helpers.command, helpers.expect -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('maparg()', function() setup(clear) @@ -25,7 +25,7 @@ describe('maparg()', function() command('map abc y<S-char-114>y') command([[call append('$', maparg('abc'))]]) feed('Go<esc>:<cr>') - wait() + poke_eventloop() -- Outside of the range, minimum command('inoremap <Char-0x1040> a') diff --git a/test/functional/legacy/107_adjust_window_and_contents_spec.lua b/test/functional/legacy/107_adjust_window_and_contents_spec.lua index 239f60341a..841eeef0af 100644 --- a/test/functional/legacy/107_adjust_window_and_contents_spec.lua +++ b/test/functional/legacy/107_adjust_window_and_contents_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local clear = helpers.clear local insert = helpers.insert local command = helpers.command @@ -16,7 +16,7 @@ describe('107', function() screen:attach() insert('start:') - wait() + poke_eventloop() command('new') command('call setline(1, range(1,256))') command('let r=[]') diff --git a/test/functional/legacy/autoformat_join_spec.lua b/test/functional/legacy/autoformat_join_spec.lua index 84d661c190..22b1c258fe 100644 --- a/test/functional/legacy/autoformat_join_spec.lua +++ b/test/functional/legacy/autoformat_join_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command, expect = helpers.command, helpers.expect -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('autoformat join', function() setup(clear) @@ -21,7 +21,7 @@ Results:]]) feed('gg') feed('0gqj<cr>') - wait() + poke_eventloop() command([[let a=string(getpos("'[")).'/'.string(getpos("']"))]]) command("g/^This line/;'}-join") diff --git a/test/functional/legacy/close_count_spec.lua b/test/functional/legacy/close_count_spec.lua index 9b932e2ef0..60ae155fbf 100644 --- a/test/functional/legacy/close_count_spec.lua +++ b/test/functional/legacy/close_count_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local eq = helpers.eq -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local eval = helpers.eval local feed = helpers.feed local clear = helpers.clear @@ -110,23 +110,23 @@ describe('close_count', function() command('for i in range(5)|new|endfor') command('4wincmd w') feed('<C-W>c<cr>') - wait() + poke_eventloop() command('let buffers = []') command('windo call add(buffers, bufnr("%"))') eq({25, 24, 23, 21, 1}, eval('buffers')) feed('1<C-W>c<cr>') - wait() + poke_eventloop() command('let buffers = []') command('windo call add(buffers, bufnr("%"))') eq({24, 23, 21, 1}, eval('buffers')) feed('9<C-W>c<cr>') - wait() + poke_eventloop() command('let buffers = []') command('windo call add(buffers, bufnr("%"))') eq({24, 23, 21}, eval('buffers')) command('1wincmd w') feed('2<C-W>c<cr>') - wait() + poke_eventloop() command('let buffers = []') command('windo call add(buffers, bufnr("%"))') eq({24, 21}, eval('buffers')) diff --git a/test/functional/legacy/display_spec.lua b/test/functional/legacy/display_spec.lua index aafcda67dc..3fbbe96947 100644 --- a/test/functional/legacy/display_spec.lua +++ b/test/functional/legacy/display_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear = helpers.clear -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local feed = helpers.feed local feed_command = helpers.feed_command @@ -18,7 +18,7 @@ describe('display', function() }) feed_command([[call setline(1, repeat('a', 21))]]) - wait() + poke_eventloop() feed('O') screen:expect([[ ^ | diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua index 4198ea8bfe..ee9bd29fc4 100644 --- a/test/functional/legacy/eval_spec.lua +++ b/test/functional/legacy/eval_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, insert, source = helpers.feed, helpers.insert, helpers.source local clear, command, expect = helpers.clear, helpers.command, helpers.expect local eq, eval, write_file = helpers.eq, helpers.eval, helpers.write_file -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local exc_exec = helpers.exc_exec local dedent = helpers.dedent @@ -71,7 +71,7 @@ describe('eval', function() command([[call SetReg('I', 'abcI')]]) feed('Go{{{1 Appending single lines with setreg()<esc>') - wait() + poke_eventloop() command([[call SetReg('A', 'abcAc', 'c')]]) command([[call SetReg('A', 'abcAl', 'l')]]) command([[call SetReg('A', 'abcAc2','c')]]) @@ -700,13 +700,13 @@ describe('eval', function() start:]]) command('/^012345678') feed('6l') - wait() + poke_eventloop() command('let sp = getcurpos()') feed('0') - wait() + poke_eventloop() command("call setpos('.', sp)") feed('jyl') - wait() + poke_eventloop() command('$put') expect([[ 012345678 diff --git a/test/functional/legacy/mapping_spec.lua b/test/functional/legacy/mapping_spec.lua index 56a5652184..92a757ca85 100644 --- a/test/functional/legacy/mapping_spec.lua +++ b/test/functional/legacy/mapping_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect, wait = helpers.feed_command, helpers.expect, helpers.wait +local feed_command, expect, poke_eventloop = helpers.feed_command, helpers.expect, helpers.poke_eventloop describe('mapping', function() before_each(clear) @@ -29,9 +29,9 @@ describe('mapping', function() feed_command('cunmap <c-c>') feed('GA<cr>') feed('TEST2: CTRL-C |') - wait() + poke_eventloop() feed('<c-c>A|<cr><esc>') - wait() + poke_eventloop() feed_command('unmap <c-c>') feed_command('unmap! <c-c>') @@ -46,7 +46,7 @@ describe('mapping', function() feed('GV') -- XXX: For some reason the mapping is only triggered -- when <C-c> is in a separate feed command. - wait() + poke_eventloop() feed('<c-c>') feed_command('vunmap <c-c>') diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua index 251e6a5ea4..fb0bacc2d2 100644 --- a/test/functional/legacy/memory_usage_spec.lua +++ b/test/functional/legacy/memory_usage_spec.lua @@ -7,7 +7,7 @@ local iswin = helpers.iswin local retry = helpers.retry local ok = helpers.ok local source = helpers.source -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local uname = helpers.uname local load_adjust = helpers.load_adjust @@ -102,7 +102,7 @@ describe('memory usage', function() call s:f(0) endfor ]]) - wait() + poke_eventloop() local after = monitor_memory_usage(pid) -- Estimate the limit of max usage as 2x initial usage. -- The lower limit can fluctuate a bit, use 97%. @@ -147,11 +147,11 @@ describe('memory usage', function() call s:f() endfor ]]) - wait() + poke_eventloop() local after = monitor_memory_usage(pid) for _ = 1, 3 do feed_command('so '..fname) - wait() + poke_eventloop() end local last = monitor_memory_usage(pid) -- The usage may be a bit less than the last value, use 80%. diff --git a/test/functional/legacy/search_mbyte_spec.lua b/test/functional/legacy/search_mbyte_spec.lua index a365f79cdf..ef7e41aa30 100644 --- a/test/functional/legacy/search_mbyte_spec.lua +++ b/test/functional/legacy/search_mbyte_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local clear = helpers.clear local insert = helpers.insert local expect = helpers.expect @@ -15,7 +15,7 @@ describe('search_mbyte', function() Test bce: A]=]) - wait() + poke_eventloop() command('/^Test bce:/+1') command([[$put =search('A', 'bce', line('.'))]]) diff --git a/test/functional/legacy/search_spec.lua b/test/functional/legacy/search_spec.lua index bb039a585c..4ed08881de 100644 --- a/test/functional/legacy/search_spec.lua +++ b/test/functional/legacy/search_spec.lua @@ -6,7 +6,7 @@ local eq = helpers.eq local eval = helpers.eval local feed = helpers.feed local funcs = helpers.funcs -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('search cmdline', function() local screen @@ -483,9 +483,9 @@ describe('search cmdline', function() -- "interactive". This mimics Vim's test_override("char_avail"). -- (See legacy test: test_search.vim) feed('?the') - wait() + poke_eventloop() feed('<c-g>') - wait() + poke_eventloop() feed('<cr>') screen:expect([[ 1 the first | @@ -496,11 +496,11 @@ describe('search cmdline', function() command('$') feed('?the') - wait() + poke_eventloop() feed('<c-g>') - wait() + poke_eventloop() feed('<c-g>') - wait() + poke_eventloop() feed('<cr>') screen:expect([[ 1 ^the first | @@ -511,13 +511,13 @@ describe('search cmdline', function() command('$') feed('?the') - wait() + poke_eventloop() feed('<c-g>') - wait() + poke_eventloop() feed('<c-g>') - wait() + poke_eventloop() feed('<c-g>') - wait() + poke_eventloop() feed('<cr>') screen:expect([[ 1 the first | @@ -528,9 +528,9 @@ describe('search cmdline', function() command('$') feed('?the') - wait() + poke_eventloop() feed('<c-t>') - wait() + poke_eventloop() feed('<cr>') screen:expect([[ 1 ^the first | @@ -541,11 +541,11 @@ describe('search cmdline', function() command('$') feed('?the') - wait() + poke_eventloop() feed('<c-t>') - wait() + poke_eventloop() feed('<c-t>') - wait() + poke_eventloop() feed('<cr>') screen:expect([[ 1 the first | @@ -556,13 +556,13 @@ describe('search cmdline', function() command('$') feed('?the') - wait() + poke_eventloop() feed('<c-t>') - wait() + poke_eventloop() feed('<c-t>') - wait() + poke_eventloop() feed('<c-t>') - wait() + poke_eventloop() feed('<cr>') screen:expect([[ 1 the first | diff --git a/test/functional/legacy/utf8_spec.lua b/test/functional/legacy/utf8_spec.lua index 5b93f25b24..8b5fc02d11 100644 --- a/test/functional/legacy/utf8_spec.lua +++ b/test/functional/legacy/utf8_spec.lua @@ -5,7 +5,7 @@ local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command, expect = helpers.command, helpers.expect local eq, eval = helpers.eq, helpers.eval local source = helpers.source -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('utf8', function() before_each(clear) @@ -18,7 +18,7 @@ describe('utf8', function() -- Visual block Insert adjusts for multi-byte char feed('gg0l<C-V>jjIx<Esc>') - wait() + poke_eventloop() command('let r = getline(1, "$")') command('bwipeout!') diff --git a/test/functional/legacy/wordcount_spec.lua b/test/functional/legacy/wordcount_spec.lua index 0c8bd2cdcc..826743b0ca 100644 --- a/test/functional/legacy/wordcount_spec.lua +++ b/test/functional/legacy/wordcount_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, insert, source = helpers.feed, helpers.insert, helpers.source local clear, command = helpers.clear, helpers.command local eq, eval = helpers.eq, helpers.eval -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('wordcount', function() before_each(clear) @@ -14,7 +14,7 @@ describe('wordcount', function() insert([=[ RESULT test:]=]) - wait() + poke_eventloop() command('new') source([=[ @@ -127,7 +127,7 @@ describe('wordcount', function() -- -- Start visual mode quickly and select complete buffer. command('0') feed('V2jy<cr>') - wait() + poke_eventloop() command('set stl= ls=1') command('let log=DoRecordWin([3,99,0])') command('let log[1]=g:visual_stat') @@ -144,7 +144,7 @@ describe('wordcount', function() -- Start visual mode quickly and select complete buffer. command('0') feed('v$y<cr>') - wait() + poke_eventloop() command('set stl= ls=1') command('let log=DoRecordWin([3,99,0])') command('let log[1]=g:visual_stat') @@ -161,7 +161,7 @@ describe('wordcount', function() -- Start visual mode quickly and select complete buffer. command('2') feed('0v$y<cr>') - wait() + poke_eventloop() command('set stl= ls=1') command('let log=DoRecordWin([3,99,0])') command('let log[1]=g:visual_stat') diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua index cbc3aee557..f2a1b7dede 100644 --- a/test/functional/lua/commands_spec.lua +++ b/test/functional/lua/commands_spec.lua @@ -43,7 +43,7 @@ describe(':lua command', function() eq({'', 'ETTS', 'TTSE', 'STTE'}, curbufmeths.get_lines(0, 100, false)) end) it('throws catchable errors', function() - eq([[Vim(lua):E5107: Error loading lua [string ":lua"]:1: unexpected symbol near ')']], + eq([[Vim(lua):E5107: Error loading lua [string ":lua"]:0: unexpected symbol near ')']], pcall_err(command, 'lua ()')) eq([[Vim(lua):E5108: Error executing lua [string ":lua"]:1: TEST]], exc_exec('lua error("TEST")')) diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 75966393b1..2ec48777fd 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -477,14 +477,14 @@ describe('v:lua', function() eq(NIL, eval('v:lua.mymod.noisy("eval")')) eq("hey eval", meths.get_current_line()) - eq("Vim:E5108: Error executing lua [string \"<nvim>\"]:10: attempt to call global 'nonexistent' (a nil value)", + eq("Vim:E5108: Error executing lua [string \"<nvim>\"]:0: attempt to call global 'nonexistent' (a nil value)", pcall_err(eval, 'v:lua.mymod.crashy()')) end) it('works in :call', function() command(":call v:lua.mymod.noisy('command')") eq("hey command", meths.get_current_line()) - eq("Vim(call):E5108: Error executing lua [string \"<nvim>\"]:10: attempt to call global 'nonexistent' (a nil value)", + eq("Vim(call):E5108: Error executing lua [string \"<nvim>\"]:0: attempt to call global 'nonexistent' (a nil value)", pcall_err(command, 'call v:lua.mymod.crashy()')) end) diff --git a/test/functional/lua/treesitter_spec.lua b/test/functional/lua/treesitter_spec.lua index 74ae6cde2b..3526b64395 100644 --- a/test/functional/lua/treesitter_spec.lua +++ b/test/functional/lua/treesitter_spec.lua @@ -15,17 +15,16 @@ before_each(clear) describe('treesitter API', function() -- error tests not requiring a parser library it('handles missing language', function() - eq("Error executing lua: .../language.lua: no parser for 'borklang' language, see :help treesitter-parsers", + eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", pcall_err(exec_lua, "parser = vim.treesitter.get_parser(0, 'borklang')")) -- actual message depends on platform matches("Error executing lua: Failed to load parser: uv_dlopen: .+", pcall_err(exec_lua, "parser = vim.treesitter.require_language('borklang', 'borkbork.so')")) - eq("Error executing lua: .../language.lua: no parser for 'borklang' language, see :help treesitter-parsers", + eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", pcall_err(exec_lua, "parser = vim.treesitter.inspect_language('borklang')")) end) - end) describe('treesitter API with C parser', function() @@ -186,6 +185,16 @@ void ui_refresh(void) (field_expression argument: (identifier) @fieldarg) ]] + it("supports runtime queries", function() + if not check_parser() then return end + + local ret = exec_lua [[ + return require"vim.treesitter.query".get_query("c", "highlights").captures[1] + ]] + + eq('variable', ret) + end) + it('support query and iter by capture', function() if not check_parser() then return end @@ -420,9 +429,10 @@ static int nlua_schedule(lua_State *const lstate) ]]} exec_lua([[ + local parser = vim.treesitter.get_parser(0, "c") local highlighter = vim.treesitter.highlighter local query = ... - test_hl = highlighter.new(query, 0, "c") + test_hl = highlighter.new(parser, query) ]], hl_query) screen:expect{grid=[[ {2:/// Schedule Lua callback on main loop's event queue} | @@ -559,6 +569,72 @@ static int nlua_schedule(lua_State *const lstate) ]]} end) + it("supports highlighting with custom parser", function() + if not check_parser() then return end + + local screen = Screen.new(65, 18) + screen:attach() + screen:set_default_attr_ids({ {bold = true, foreground = Screen.colors.SeaGreen4} }) + + insert(test_text) + + screen:expect{ grid= [[ + int width = INT_MAX, height = INT_MAX; | + bool ext_widgets[kUIExtCount]; | + for (UIExtension i = 0; (int)i < kUIExtCount; i++) { | + ext_widgets[i] = true; | + } | + | + bool inclusive = ui_override(); | + for (size_t i = 0; i < ui_count; i++) { | + UI *ui = uis[i]; | + width = MIN(ui->width, width); | + height = MIN(ui->height, height); | + foo = BAR(ui->bazaar, bazaar); | + for (UIExtension j = 0; (int)j < kUIExtCount; j++) { | + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | + } | + } | + ^} | + | + ]] } + + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + query = vim.treesitter.parse_query("c", "(declaration) @decl") + + local nodes = {} + for _, node in query:iter_captures(parser:parse():root(), 0, 0, 19) do + table.insert(nodes, node) + end + + parser:set_included_ranges(nodes) + + local hl = vim.treesitter.highlighter.new(parser, "(identifier) @type") + ]]) + + screen:expect{ grid = [[ + int {1:width} = {1:INT_MAX}, {1:height} = {1:INT_MAX}; | + bool {1:ext_widgets}[{1:kUIExtCount}]; | + for (UIExtension {1:i} = 0; (int)i < kUIExtCount; i++) { | + ext_widgets[i] = true; | + } | + | + bool {1:inclusive} = {1:ui_override}(); | + for (size_t {1:i} = 0; i < ui_count; i++) { | + UI *{1:ui} = {1:uis}[{1:i}]; | + width = MIN(ui->width, width); | + height = MIN(ui->height, height); | + foo = BAR(ui->bazaar, bazaar); | + for (UIExtension {1:j} = 0; (int)j < kUIExtCount; j++) { | + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | + } | + } | + ^} | + | + ]] } + end) + it('inspects language', function() if not check_parser() then return end @@ -604,23 +680,29 @@ static int nlua_schedule(lua_State *const lstate) insert(test_text) - local res = exec_lua([[ + local res = exec_lua [[ parser = vim.treesitter.get_parser(0, "c") return { parser:parse():root():range() } - ]]) + ]] eq({0, 0, 19, 0}, res) -- The following sets the included ranges for the current parser -- As stated here, this only includes the function (thus the whole buffer, without the last line) - local res2 = exec_lua([[ + local res2 = exec_lua [[ local root = parser:parse():root() parser:set_included_ranges({root:child(0)}) parser.valid = false return { parser:parse():root():range() } - ]]) + ]] eq({0, 0, 18, 1}, res2) + + local range = exec_lua [[ + return parser:included_ranges() + ]] + + eq(range, { { 0, 0, 18, 1 } }) end) it("allows to set complex ranges", function() if not check_parser() then return end @@ -628,7 +710,7 @@ static int nlua_schedule(lua_State *const lstate) insert(test_text) - local res = exec_lua([[ + local res = exec_lua [[ parser = vim.treesitter.get_parser(0, "c") query = vim.treesitter.parse_query("c", "(declaration) @decl") @@ -646,7 +728,7 @@ static int nlua_schedule(lua_State *const lstate) table.insert(res, { root:named_child(i):range() }) end return res - ]]) + ]] eq({ { 2, 2, 2, 40 }, diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 63e48a18ca..1cbf6b21e3 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -4,13 +4,14 @@ local Screen = require('test.functional.ui.screen') local funcs = helpers.funcs local meths = helpers.meths +local dedent = helpers.dedent local command = helpers.command local clear = helpers.clear local eq = helpers.eq local ok = helpers.ok local eval = helpers.eval local feed = helpers.feed -local pcall_err = helpers.pcall_err +local pcall_err_withfile = helpers.pcall_err_withfile local exec_lua = helpers.exec_lua local matches = helpers.matches local source = helpers.source @@ -128,8 +129,8 @@ describe('lua stdlib', function() eq(false, funcs.luaeval('vim.startswith("123", "2")')) eq(false, funcs.luaeval('vim.startswith("123", "1234")')) - eq("string", type(pcall_err(funcs.luaeval, 'vim.startswith("123", nil)'))) - eq("string", type(pcall_err(funcs.luaeval, 'vim.startswith(nil, "123")'))) + eq("string", type(pcall_err_withfile(funcs.luaeval, 'vim.startswith("123", nil)'))) + eq("string", type(pcall_err_withfile(funcs.luaeval, 'vim.startswith(nil, "123")'))) end) it('vim.endswith', function() @@ -142,8 +143,8 @@ describe('lua stdlib', function() eq(false, funcs.luaeval('vim.endswith("123", "2")')) eq(false, funcs.luaeval('vim.endswith("123", "1234")')) - eq("string", type(pcall_err(funcs.luaeval, 'vim.endswith("123", nil)'))) - eq("string", type(pcall_err(funcs.luaeval, 'vim.endswith(nil, "123")'))) + eq("string", type(pcall_err_withfile(funcs.luaeval, 'vim.endswith("123", nil)'))) + eq("string", type(pcall_err_withfile(funcs.luaeval, 'vim.endswith(nil, "123")'))) end) it("vim.str_utfindex/str_byteindex", function() @@ -182,10 +183,10 @@ describe('lua stdlib', function() eq({"yy","xx"}, exec_lua("return test_table")) -- Validates args. - eq('Error executing lua: vim.schedule: expected function', - pcall_err(exec_lua, "vim.schedule('stringly')")) - eq('Error executing lua: vim.schedule: expected function', - pcall_err(exec_lua, "vim.schedule()")) + eq('.../helpers.lua:0: Error executing lua: vim.schedule: expected function', + pcall_err_withfile(exec_lua, "vim.schedule('stringly')")) + eq('.../helpers.lua:0: Error executing lua: vim.schedule: expected function', + pcall_err_withfile(exec_lua, "vim.schedule()")) exec_lua([[ vim.schedule(function() @@ -257,17 +258,29 @@ describe('lua stdlib', function() } for _, t in ipairs(loops) do - matches(".*Infinite loop detected", pcall_err(split, t[1], t[2])) + matches(".*Infinite loop detected", pcall_err_withfile(split, t[1], t[2])) end -- Validates args. eq(true, pcall(split, 'string', 'string')) - eq('Error executing lua: .../shared.lua: s: expected string, got number', - pcall_err(split, 1, 'string')) - eq('Error executing lua: .../shared.lua: sep: expected string, got number', - pcall_err(split, 'string', 1)) - eq('Error executing lua: .../shared.lua: plain: expected boolean, got number', - pcall_err(split, 'string', 'string', 1)) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: shared.lua:0: s: expected string, got number + stack traceback: + shared.lua:0: in function 'gsplit' + shared.lua:0: in function <shared.lua:0>]]), + pcall_err_withfile(split, 1, 'string')) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: shared.lua:0: sep: expected string, got number + stack traceback: + shared.lua:0: in function 'gsplit' + shared.lua:0: in function <shared.lua:0>]]), + pcall_err_withfile(split, 'string', 1)) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: shared.lua:0: plain: expected boolean, got number + stack traceback: + shared.lua:0: in function 'gsplit' + shared.lua:0: in function <shared.lua:0>]]), + pcall_err_withfile(split, 'string', 'string', 1)) end) it('vim.trim', function() @@ -287,8 +300,11 @@ describe('lua stdlib', function() end -- Validates args. - eq('Error executing lua: .../shared.lua: s: expected string, got number', - pcall_err(trim, 2)) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: shared.lua:0: s: expected string, got number + stack traceback: + shared.lua:0: in function <shared.lua:0>]]), + pcall_err_withfile(trim, 2)) end) it('vim.inspect', function() @@ -353,8 +369,8 @@ describe('lua stdlib', function() return t1.f() ~= t2.f() ]])) - eq('Error executing lua: .../shared.lua: Cannot deepcopy object of type thread', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: Cannot deepcopy object of type thread', + pcall_err_withfile(exec_lua, [[ local thread = coroutine.create(function () return 0 end) local t = {thr = thread} vim.deepcopy(t) @@ -366,8 +382,11 @@ describe('lua stdlib', function() eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]])) -- Validates args. - eq('Error executing lua: .../shared.lua: s: expected string, got number', - pcall_err(exec_lua, [[return vim.pesc(2)]])) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: shared.lua:0: s: expected string, got number + stack traceback: + shared.lua:0: in function <shared.lua:0>]]), + pcall_err_withfile(exec_lua, [[return vim.pesc(2)]])) end) it('vim.tbl_keys', function() @@ -491,20 +510,20 @@ describe('lua stdlib', function() return c.x.a == 1 and c.x.b == 2 and c.x.c == nil and count == 1 ]])) - eq('Error executing lua: .../shared.lua: invalid "behavior": nil', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: invalid "behavior": nil', + pcall_err_withfile(exec_lua, [[ return vim.tbl_extend() ]]) ) - eq('Error executing lua: .../shared.lua: wrong number of arguments (given 1, expected at least 3)', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: wrong number of arguments (given 1, expected at least 3)', + pcall_err_withfile(exec_lua, [[ return vim.tbl_extend("keep") ]]) ) - eq('Error executing lua: .../shared.lua: wrong number of arguments (given 2, expected at least 3)', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: wrong number of arguments (given 2, expected at least 3)', + pcall_err_withfile(exec_lua, [[ return vim.tbl_extend("keep", {}) ]]) ) @@ -579,20 +598,20 @@ describe('lua stdlib', function() return vim.tbl_islist(c) and count == 0 ]])) - eq('Error executing lua: .../shared.lua: invalid "behavior": nil', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: invalid "behavior": nil', + pcall_err_withfile(exec_lua, [[ return vim.tbl_deep_extend() ]]) ) - eq('Error executing lua: .../shared.lua: wrong number of arguments (given 1, expected at least 3)', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: wrong number of arguments (given 1, expected at least 3)', + pcall_err_withfile(exec_lua, [[ return vim.tbl_deep_extend("keep") ]]) ) - eq('Error executing lua: .../shared.lua: wrong number of arguments (given 2, expected at least 3)', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: wrong number of arguments (given 2, expected at least 3)', + pcall_err_withfile(exec_lua, [[ return vim.tbl_deep_extend("keep", {}) ]]) ) @@ -624,8 +643,11 @@ describe('lua stdlib', function() it('vim.list_extend', function() eq({1,2,3}, exec_lua [[ return vim.list_extend({1}, {2,3}) ]]) - eq('Error executing lua: .../shared.lua: src: expected table, got nil', - pcall_err(exec_lua, [[ return vim.list_extend({1}, nil) ]])) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: shared.lua:0: src: expected table, got nil + stack traceback: + shared.lua:0: in function <shared.lua:0>]]), + pcall_err_withfile(exec_lua, [[ return vim.list_extend({1}, nil) ]])) eq({1,2}, exec_lua [[ return vim.list_extend({1}, {2;a=1}) ]]) eq(true, exec_lua [[ local a = {1} return vim.list_extend(a, {2;a=1}) == a ]]) eq({2}, exec_lua [[ return vim.list_extend({}, {2;a=1}, 1) ]]) @@ -648,8 +670,8 @@ describe('lua stdlib', function() assert(vim.deep_equal(a, { A = 1; [1] = 'A'; })) vim.tbl_add_reverse_lookup(a) ]] - matches('Error executing lua: .../shared.lua: The reverse lookup found an existing value for "[1A]" while processing key "[1A]"', - pcall_err(exec_lua, code)) + matches('.../helpers.lua:0: Error executing lua: shared.lua:0: The reverse lookup found an existing value for "[1A]" while processing key "[1A]"', + pcall_err_withfile(exec_lua, code)) end) it('vim.call, vim.fn', function() @@ -820,34 +842,77 @@ describe('lua stdlib', function() exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}") exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0 end, 'even number' }}") - eq("Error executing lua: .../shared.lua: 1: expected table, got number", - pcall_err(exec_lua, "vim.validate{ 1, 'x' }")) - eq("Error executing lua: .../shared.lua: invalid type name: x", - pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}")) - eq("Error executing lua: .../shared.lua: invalid type name: 1", - pcall_err(exec_lua, "vim.validate{ arg1={ 1, 1 }}")) - eq("Error executing lua: .../shared.lua: invalid type name: nil", - pcall_err(exec_lua, "vim.validate{ arg1={ 1 }}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: opt[1]: expected table, got number + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{ 1, 'x' }")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: invalid type name: x + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{ arg1={ 1, 'x' }}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: invalid type name: 1 + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{ arg1={ 1, 1 }}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: invalid type name: nil + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{ arg1={ 1 }}")) -- Validated parameters are required by default. - eq("Error executing lua: .../shared.lua: arg1: expected string, got nil", - pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's' }}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg1: expected string, got nil + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{ arg1={ nil, 's' }}")) -- Explicitly required. - eq("Error executing lua: .../shared.lua: arg1: expected string, got nil", - pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's', false }}")) - - eq("Error executing lua: .../shared.lua: arg1: expected table, got number", - pcall_err(exec_lua, "vim.validate{arg1={1, 't'}}")) - eq("Error executing lua: .../shared.lua: arg2: expected string, got number", - pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={1, 's'}}")) - eq("Error executing lua: .../shared.lua: arg2: expected string, got nil", - pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) - eq("Error executing lua: .../shared.lua: arg2: expected string, got nil", - pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) - eq("Error executing lua: .../shared.lua: arg1: expected even number, got 3", - pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end, 'even number'}}")) - eq("Error executing lua: .../shared.lua: arg1: expected ?, got 3", - pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end}}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg1: expected string, got nil + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{ arg1={ nil, 's', false }}")) + + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg1: expected table, got number + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={1, 't'}}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg2: expected string, got number + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={1, 's'}}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg2: expected string, got nil + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg2: expected string, got nil + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg1: expected even number, got 3 + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end, 'even number'}}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg1: expected ?, got 3 + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end}}")) + + -- Pass an additional message back. + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg1: expected ?, got 3. Info: TEST_MSG + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={3, function(a) return a == 1, 'TEST_MSG' end}}")) end) it('vim.is_callable', function() @@ -992,10 +1057,10 @@ describe('lua stdlib', function() ]] eq('', funcs.luaeval "vim.bo.filetype") eq(true, funcs.luaeval "vim.bo[BUF].modifiable") - matches("^Error executing lua: .*: Invalid option name: 'nosuchopt'$", - pcall_err(exec_lua, 'return vim.bo.nosuchopt')) - matches("^Error executing lua: .*: Expected lua string$", - pcall_err(exec_lua, 'return vim.bo[0][0].autoread')) + matches("^.../helpers.lua:0: Error executing lua: .*: Invalid option name: 'nosuchopt'$", + pcall_err_withfile(exec_lua, 'return vim.bo.nosuchopt')) + matches("^.../helpers.lua:0: Error executing lua: .*: Expected lua string$", + pcall_err_withfile(exec_lua, 'return vim.bo[0][0].autoread')) end) it('vim.wo', function() @@ -1011,10 +1076,10 @@ describe('lua stdlib', function() eq(0, funcs.luaeval "vim.wo.cole") eq(0, funcs.luaeval "vim.wo[0].cole") eq(0, funcs.luaeval "vim.wo[1001].cole") - matches("^Error executing lua: .*: Invalid option name: 'notanopt'$", - pcall_err(exec_lua, 'return vim.wo.notanopt')) - matches("^Error executing lua: .*: Expected lua string$", - pcall_err(exec_lua, 'return vim.wo[0][0].list')) + matches("^.../helpers.lua:0: Error executing lua: .*: Invalid option name: 'notanopt'$", + pcall_err_withfile(exec_lua, 'return vim.wo.notanopt')) + matches("^.../helpers.lua:0: Error executing lua: .*: Expected lua string$", + pcall_err_withfile(exec_lua, 'return vim.wo[0][0].list')) eq(2, funcs.luaeval "vim.wo[1000].cole") exec_lua [[ vim.wo[1000].cole = 0 diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index f514f4ea6f..067a13ce68 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -270,6 +270,70 @@ describe('LSP', function() test_name = "basic_check_capabilities"; on_init = function(client) client.stop() + local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full") + eq(full_kind, client.resolved_capabilities().text_document_did_change) + end; + on_exit = function(code, signal) + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) + end; + on_callback = function(...) + eq(table.remove(expected_callbacks), {...}, "expected callback") + end; + } + end) + + it('client.supports_methods() should validate capabilities', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + } + test_rpc_server { + test_name = "capabilities_for_client_supports_method"; + on_init = function(client) + client.stop() + local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full") + eq(full_kind, client.resolved_capabilities().text_document_did_change) + eq(true, client.resolved_capabilities().completion) + eq(true, client.resolved_capabilities().hover) + eq(false, client.resolved_capabilities().goto_definition) + eq(false, client.resolved_capabilities().rename) + + -- known methods for resolved capabilities + eq(true, client.supports_method("textDocument/hover")) + eq(false, client.supports_method("textDocument/definition")) + + -- unknown methods are assumed to be supported. + eq(true, client.supports_method("unknown-method")) + end; + on_exit = function(code, signal) + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) + end; + on_callback = function(...) + eq(table.remove(expected_callbacks), {...}, "expected callback") + end; + } + end) + + it('should call unsupported_method when trying to call an unsupported method', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + } + test_rpc_server { + test_name = "capabilities_for_client_supports_method"; + on_setup = function() + exec_lua([=[ + vim.lsp._unsupported_method = function(method) + vim.lsp._last_unsupported_method = method + return 'fake-error' + end + vim.lsp.buf.hover() + ]=]) + end; + on_init = function(client) + client.stop() + local method = exec_lua("return vim.lsp._last_unsupported_method") + eq("textDocument/hover", method) end; on_exit = function(code, signal) eq(0, code, "exit code", fake_lsp_logfile) @@ -747,8 +811,16 @@ describe('LSP', function() end) it('should invalid cmd argument', function() - eq('Error executing lua: .../shared.lua: cmd: expected list, got nvim', pcall_err(_cmd_parts, "nvim")) - eq('Error executing lua: .../shared.lua: cmd argument: expected string, got number', pcall_err(_cmd_parts, {"nvim", 1})) + eq(dedent([[ + Error executing lua: .../lsp.lua:0: cmd: expected list, got nvim + stack traceback: + .../lsp.lua:0: in function .../lsp.lua:0>]]), + pcall_err(_cmd_parts, 'nvim')) + eq(dedent([[ + Error executing lua: .../lsp.lua:0: cmd argument: expected string, got number + stack traceback: + .../lsp.lua:0: in function .../lsp.lua:0>]]), + pcall_err(_cmd_parts, {'nvim', 1})) end) end) end) diff --git a/test/functional/provider/ruby_spec.lua b/test/functional/provider/ruby_spec.lua index bb7d23ede6..2729d8dfa2 100644 --- a/test/functional/provider/ruby_spec.lua +++ b/test/functional/provider/ruby_spec.lua @@ -5,6 +5,7 @@ local command = helpers.command local curbufmeths = helpers.curbufmeths local eq = helpers.eq local eval = helpers.eval +local exc_exec = helpers.exc_exec local expect = helpers.expect local feed = helpers.feed local feed_command = helpers.feed_command @@ -109,3 +110,24 @@ describe('ruby provider', function() eq(2, eval('1+1')) -- Still alive? end) end) + +describe('rubyeval()', function() + it('evaluates ruby objects', function() + eq({1, 2, {['key'] = 'val'}}, funcs.rubyeval('[1, 2, {key: "val"}]')) + end) + + it('returns nil for empty strings', function() + eq(helpers.NIL, funcs.rubyeval('')) + end) + + it('errors out when given non-string', function() + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(10)')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(v:_null_dict)')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(v:_null_list)')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(0.0)')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(function("tr"))')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(v:true)')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(v:false)')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(v:null)')) + end) +end) diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 6372cd935e..8e171d31aa 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local eval, feed_command, source = helpers.eval, helpers.feed_command, helpers.source local eq, neq = helpers.eq, helpers.neq local write_file = helpers.write_file @@ -13,7 +13,7 @@ describe(':terminal buffer', function() before_each(function() clear() feed_command('set modifiable swapfile undolevels=20') - wait() + poke_eventloop() screen = thelpers.screen_setup() end) diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index ef12438ecc..8d70ebf679 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -70,7 +70,7 @@ describe(':terminal cursor', function() :set number | ]]) feed('i') - helpers.wait() + helpers.poke_eventloop() screen:expect([[ {7: 1 }tty ready | {7: 2 }rows: 6, cols: 46 | diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 138befd978..4b512605e1 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local clear, wait, nvim = helpers.clear, helpers.wait, helpers.nvim +local clear, poke_eventloop, nvim = helpers.clear, helpers.poke_eventloop, helpers.nvim local nvim_dir, source, eq = helpers.nvim_dir, helpers.source, helpers.eq local feed = helpers.feed local feed_command, eval = helpers.feed_command, helpers.eval @@ -29,7 +29,7 @@ describe(':terminal', function() -- Invoke a command that emits frequent terminal activity. feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 9999 !terminal_output!<cr>]]) feed([[<C-\><C-N>]]) - wait() + poke_eventloop() -- Wait for some terminal activity. retry(nil, 4000, function() ok(funcs.line('$') > 6) @@ -60,7 +60,7 @@ describe(':terminal', function() feed_command([[terminal while true; do echo foo; sleep .1; done]]) end feed([[<C-\><C-N>M]]) -- move cursor away from last line - wait() + poke_eventloop() eq(3, eval("line('$')")) -- window height eq(2, eval("line('.')")) -- cursor is in the middle feed_command('vsplit') @@ -76,11 +76,11 @@ describe(':terminal', function() -- Create a new line (in the shell). For a normal buffer this -- increments the jumplist; for a terminal-buffer it should not. #3723 feed('i') - wait() + poke_eventloop() feed('<CR><CR><CR><CR>') - wait() + poke_eventloop() feed([[<C-\><C-N>]]) - wait() + poke_eventloop() -- Wait for >=1 lines to be created. retry(nil, 4000, function() ok(funcs.line('$') > lines_before) @@ -210,7 +210,7 @@ describe(':terminal (with fake shell)', function() it('ignores writes if the backing stream closes', function() terminal_with_fake_shell() feed('iiXXXXXXX') - wait() + poke_eventloop() -- Race: Though the shell exited (and streams were closed by SIGCHLD -- handler), :terminal cleanup is pending on the main-loop. -- This write should be ignored (not crash, #5445). diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index 1df8df6f6e..77fdba7fc4 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -6,7 +6,7 @@ local feed, nvim_dir, feed_command = helpers.feed, helpers.nvim_dir, helpers.fee local iswin = helpers.iswin local eval = helpers.eval local command = helpers.command -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local retry = helpers.retry local curbufmeths = helpers.curbufmeths local nvim = helpers.nvim @@ -347,7 +347,7 @@ describe(':terminal prints more lines than the screen height and exits', functio local screen = Screen.new(30, 7) screen:attach({rgb=false}) feed_command('call termopen(["'..nvim_dir..'/tty-test", "10"]) | startinsert') - wait() + poke_eventloop() screen:expect([[ line6 | line7 | @@ -423,7 +423,7 @@ describe("'scrollback' option", function() retry(nil, nil, function() expect_lines(33, 2) end) curbufmeths.set_option('scrollback', 10) - wait() + poke_eventloop() retry(nil, nil, function() expect_lines(16) end) curbufmeths.set_option('scrollback', 10000) retry(nil, nil, function() expect_lines(16) end) diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua index f1c828d17e..9f278fd157 100644 --- a/test/functional/terminal/window_spec.lua +++ b/test/functional/terminal/window_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local feed_data = thelpers.feed_data local feed, clear = helpers.feed, helpers.clear -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local iswin = helpers.iswin local command = helpers.command local retry = helpers.retry @@ -127,7 +127,7 @@ describe(':terminal window', function() it('wont show any folds', function() feed([[<C-\><C-N>ggvGzf]]) - wait() + poke_eventloop() screen:expect([[ ^tty ready | line1 | diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 11fe861de8..eec8eb93d4 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -59,7 +59,7 @@ describe('floatwin', function() end) it('closed immediately by autocmd #11383', function() - eq('Error executing lua: [string "<nvim>"]:4: Window was closed immediately', + eq('Error executing lua: [string "<nvim>"]:0: Window was closed immediately', pcall_err(exec_lua, [[ local a = vim.api local function crashes(contents) diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 16c5477ee4..712c1f377a 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -14,7 +14,7 @@ local neq = helpers.neq local ok = helpers.ok local retry = helpers.retry local source = helpers.source -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local nvim = helpers.nvim local sleep = helpers.sleep local nvim_dir = helpers.nvim_dir @@ -114,7 +114,7 @@ describe(":substitute, inccommand=split interactivity", function() it("no preview if invoked by a script", function() source('%s/tw/MO/g') - wait() + poke_eventloop() eq(1, eval("bufnr('$')")) -- sanity check: assert the buffer state expect(default_text:gsub("tw", "MO")) @@ -123,10 +123,10 @@ describe(":substitute, inccommand=split interactivity", function() it("no preview if invoked by feedkeys()", function() -- in a script... source([[:call feedkeys(":%s/tw/MO/g\<CR>")]]) - wait() + poke_eventloop() -- or interactively... feed([[:call feedkeys(":%s/tw/MO/g\<CR>")<CR>]]) - wait() + poke_eventloop() eq(1, eval("bufnr('$')")) -- sanity check: assert the buffer state expect(default_text:gsub("tw", "MO")) @@ -194,7 +194,7 @@ describe(":substitute, 'inccommand' preserves", function() -- Start typing an incomplete :substitute command. feed([[:%s/e/YYYY/g]]) - wait() + poke_eventloop() -- Cancel the :substitute. feed([[<C-\><C-N>]]) @@ -230,7 +230,7 @@ describe(":substitute, 'inccommand' preserves", function() -- Start typing an incomplete :substitute command. feed([[:%s/e/YYYY/g]]) - wait() + poke_eventloop() -- Cancel the :substitute. feed([[<C-\><C-N>]]) @@ -251,7 +251,7 @@ describe(":substitute, 'inccommand' preserves", function() some text 1 some text 2]]) feed(":%s/e/XXX/") - wait() + poke_eventloop() eq(expected_tick, eval("b:changedtick")) end) @@ -1128,15 +1128,15 @@ describe(":substitute, inccommand=split", function() feed(":%s/tw/Xo/g") -- Delete and re-type the g a few times. feed("<BS>") - wait() + poke_eventloop() feed("g") - wait() + poke_eventloop() feed("<BS>") - wait() + poke_eventloop() feed("g") - wait() + poke_eventloop() feed("<CR>") - wait() + poke_eventloop() feed(":vs tmp<enter>") eq(3, helpers.call('bufnr', '$')) end) @@ -1171,7 +1171,7 @@ describe(":substitute, inccommand=split", function() feed_command("silent edit! test/functional/fixtures/bigfile_oneline.txt") -- Start :substitute with a slow pattern. feed([[:%s/B.*N/x]]) - wait() + poke_eventloop() -- Assert that 'inccommand' is DISABLED in cmdline mode. eq("", eval("&inccommand")) @@ -1360,7 +1360,7 @@ describe("inccommand=nosplit", function() feed("<Esc>") command("set icm=nosplit") feed(":%s/tw/OKOK") - wait() + poke_eventloop() screen:expect([[ Inc substitution on | {12:OKOK}o lines | @@ -2592,7 +2592,7 @@ describe(":substitute", function() feed("<C-c>") feed('gg') - wait() + poke_eventloop() feed([[:%s/\(some\)\@<lt>!thing/one/]]) screen:expect([[ something | @@ -2613,7 +2613,7 @@ describe(":substitute", function() ]]) feed([[<C-c>]]) - wait() + poke_eventloop() feed([[:%s/some\(thing\)\@=/every/]]) screen:expect([[ {12:every}thing | @@ -2634,7 +2634,7 @@ describe(":substitute", function() ]]) feed([[<C-c>]]) - wait() + poke_eventloop() feed([[:%s/some\(thing\)\@!/every/]]) screen:expect([[ something | @@ -2718,7 +2718,7 @@ it(':substitute with inccommand during :terminal activity', function() feed('gg') feed(':%s/foo/ZZZ') sleep(20) -- Allow some terminal activity. - helpers.wait() + helpers.poke_eventloop() screen:expect_unchanged() end) end) diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index d857b57a31..a741136111 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -546,7 +546,7 @@ describe('ui/mouse/input', function() :tabprevious | ]]) feed('<LeftMouse><10,0><LeftRelease>') -- go to second tab - helpers.wait() + helpers.poke_eventloop() feed('<LeftMouse><0,1>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua index e4d1187dea..6601c2d68e 100644 --- a/test/functional/ui/multigrid_spec.lua +++ b/test/functional/ui/multigrid_spec.lua @@ -4,7 +4,7 @@ local clear = helpers.clear local feed, command, insert = helpers.feed, helpers.command, helpers.insert local eq = helpers.eq local meths = helpers.meths -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('ext_multigrid', function() @@ -1846,8 +1846,8 @@ describe('ext_multigrid', function() meths.input_mouse('left', 'press', '', 1,6, 20) -- TODO(bfredl): "batching" input_mouse is formally not supported yet. -- Normally it should work fine in async context when nvim is not blocked, - -- but add a wait be sure. - wait() + -- but add a poke_eventloop be sure. + poke_eventloop() meths.input_mouse('left', 'drag', '', 1, 4, 20) screen:expect{grid=[[ ## grid 1 @@ -1921,7 +1921,7 @@ describe('ext_multigrid', function() ]]} meths.input_mouse('left', 'press', '', 1,8, 26) - wait() + poke_eventloop() meths.input_mouse('left', 'drag', '', 1, 6, 30) screen:expect{grid=[[ ## grid 1 diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index c1c5d1ce2e..3f984ff943 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -1213,10 +1213,10 @@ describe('builtin popupmenu', function() funcs.complete(29, {'word', 'choice', 'text', 'thing'}) screen:expect([[ some long prefix before the ^ | - {n:word }{1: }| - {n:choice }{1: }| - {n:text }{1: }| - {n:thing }{1: }| + {1:~ }{n: word }| + {1:~ }{n: choice}| + {1:~ }{n: text }| + {1:~ }{n: thing }| {1:~ }| {1:~ }| {1:~ }| @@ -1261,10 +1261,10 @@ describe('builtin popupmenu', function() feed('<c-p>') screen:expect([[ some long prefix before the text| - {n:^word }{1: }| - {n:choice }{1: }| - {s:text }{1: }| - {n:thing }{1: }| + {1:^~ }{n: word }| + {1:~ }{n: choice}| + {1:~ }{s: text }| + {1:~ }{n: thing }| {1:~ }| {1:~ }| {1:~ }| @@ -1341,10 +1341,10 @@ describe('builtin popupmenu', function() screen:expect([[ some long prefix | before the text^ | - {1:~ }{n: word }| - {1:~ }{n: choice }| - {1:~ }{s: text }| - {1:~ }{n: thing }| + {1:~ }{n: word }{1: }| + {1:~ }{n: choice }{1: }| + {1:~ }{s: text }{1: }| + {1:~ }{n: thing }{1: }| {1:~ }| {2:-- INSERT --} | ]]) @@ -1358,10 +1358,10 @@ describe('builtin popupmenu', function() funcs.complete(29, {'word', 'choice', 'text', 'thing'}) screen:expect([[ some long prefix before the ^ | - {n:word }{1: }| - {n:choice }{1: }| - {n:text }{1: }| - {n:thing }{1: }| + {1:~ }{n: word }| + {1:~ }{n: choice}| + {1:~ }{n: text }| + {1:~ }{n: thing }| {1:~ }| {1:~ }| {1:~ }| @@ -2168,8 +2168,8 @@ describe('builtin popupmenu', function() funcs.complete(29, {'word', 'choice', 'text', 'thing'}) screen:expect([[ some long prefix before the ^ | - {n:word }{c: }{1: }| - {n:choice }{s: }{1: }| + {1:~ }{n: word }{c: }| + {1:~ }{n: choice}{s: }| {1:~ }| {1:~ }| {1:~ }| @@ -2187,10 +2187,10 @@ describe('builtin popupmenu', function() funcs.complete(29, {'word', 'choice', 'text', 'thing'}) screen:expect([[ some long prefix before the ^ | - {n:word }{1: }| - {n:choice }{1: }| - {n:text }{1: }| - {n:thing }{1: }| + {1:~ }{n: word }| + {1:~ }{n: choice}| + {1:~ }{n: text }| + {1:~ }{n: thing }| {1:~ }| {1:~ }| {2:-- INSERT --} | diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index 635ce7392b..222275eb4d 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -158,7 +158,7 @@ describe('search highlighting', function() bar foo baz ]]) feed('/foo') - helpers.wait() + helpers.poke_eventloop() screen:expect_unchanged() end) diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index 9d4cb325d9..01fc50289d 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -6,7 +6,7 @@ local feed_command, source, expect = helpers.feed_command, helpers.source, helpe local curbufmeths = helpers.curbufmeths local command = helpers.command local meths = helpers.meths -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('completion', function() local screen @@ -737,8 +737,8 @@ describe('completion', function() -- Does not indent when "ind" is typed. feed("in<C-X><C-N>") -- Completion list is generated incorrectly if we send everything at once - -- via nvim_input(). So wait() before sending <BS>. #8480 - wait() + -- via nvim_input(). So poke_eventloop() before sending <BS>. #8480 + poke_eventloop() feed("<BS>d") screen:expect([[ @@ -778,7 +778,7 @@ describe('completion', function() ]]) -- Works for unindenting too. feed("ounin<C-X><C-N>") - helpers.wait() + helpers.poke_eventloop() feed("<BS>d") screen:expect([[ inc uninc indent unindent | @@ -1000,65 +1000,65 @@ describe('completion', function() command('let g:foo = []') feed('o') - wait() + poke_eventloop() feed('<esc>') eq({'I'}, eval('g:foo')) command('let g:foo = []') feed('S') - wait() + poke_eventloop() feed('f') - wait() + poke_eventloop() eq({'I', 'I'}, eval('g:foo')) feed('<esc>') command('let g:foo = []') feed('S') - wait() + poke_eventloop() feed('f') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() eq({'I', 'I', 'P'}, eval('g:foo')) feed('<esc>') command('let g:foo = []') feed('S') - wait() + poke_eventloop() feed('f') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() eq({'I', 'I', 'P', 'P'}, eval('g:foo')) feed('<esc>') command('let g:foo = []') feed('S') - wait() + poke_eventloop() feed('f') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() eq({'I', 'I', 'P', 'P', 'P'}, eval('g:foo')) feed('<esc>') command('let g:foo = []') feed('S') - wait() + poke_eventloop() feed('f') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() feed('<C-N>') eq({'I', 'I', 'P', 'P', 'P', 'P'}, eval('g:foo')) feed('<esc>') diff --git a/test/functional/viml/errorlist_spec.lua b/test/functional/viml/errorlist_spec.lua index c5390cbd12..9acc61e398 100644 --- a/test/functional/viml/errorlist_spec.lua +++ b/test/functional/viml/errorlist_spec.lua @@ -27,7 +27,7 @@ describe('setqflist()', function() setqflist({''}, 'r', 'foo') command('copen') eq('foo', get_cur_win_var('quickfix_title')) - setqflist({''}, 'r', {['title'] = 'qf_title'}) + setqflist({}, 'r', {['title'] = 'qf_title'}) eq('qf_title', get_cur_win_var('quickfix_title')) end) diff --git a/test/helpers.lua b/test/helpers.lua index 5acd2ea0bd..68f0c92244 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -116,8 +116,12 @@ function module.assert_log(pat, logfile) pat, nrlines, logfile, logtail)) end --- Invokes `fn` and returns the error string (may truncate full paths), or --- raises an error if `fn` succeeds. +-- Invokes `fn` and returns the error string (with truncated paths), or raises +-- an error if `fn` succeeds. +-- +-- Replaces line/column numbers with zero: +-- shared.lua:0: in function 'gsplit' +-- shared.lua:0: in function <shared.lua:0>' -- -- Usage: -- -- Match exact string. @@ -125,29 +129,36 @@ end -- -- Match Lua pattern. -- matches('e[or]+$', pcall_err(function(a, b) error('some error') end, 'arg1', 'arg2')) -- -function module.pcall_err(fn, ...) +function module.pcall_err_withfile(fn, ...) assert(type(fn) == 'function') local status, rv = pcall(fn, ...) if status == true then error('expected failure, but got success') end - -- From this: - -- /home/foo/neovim/runtime/lua/vim/shared.lua:186: Expected string, got number - -- to this: - -- Expected string, got number - local errmsg = tostring(rv):gsub('^[^:]+:%d+: ', '') - -- From this: - -- Error executing lua: /very/long/foo.lua:186: Expected string, got number - -- to this: - -- Error executing lua: .../foo.lua:186: Expected string, got number - errmsg = errmsg:gsub([[lua: [a-zA-Z]?:?[^:]-[/\]([^:/\]+):%d+: ]], 'lua: .../%1: ') - -- Compiled modules will not have a path and will just be a name like - -- shared.lua:186, so strip the number. - errmsg = errmsg:gsub([[lua: ([^:/\ ]+):%d+: ]], 'lua: .../%1: ') - -- ^ Windows drive-letter (C:) + -- From: + -- C:/long/path/foo.lua:186: Expected string, got number + -- to: + -- .../foo.lua:0: Expected string, got number + local errmsg = tostring(rv):gsub('[^%s]-[/\\]([^%s:/\\]+):%d+', '.../%1:0') + -- Scrub numbers in paths/stacktraces: + -- shared.lua:0: in function 'gsplit' + -- shared.lua:0: in function <shared.lua:0>' + errmsg = errmsg:gsub('([^%s]):%d+', '%1:0') + -- Scrub tab chars: + errmsg = errmsg:gsub('\t', ' ') + -- In Lua 5.1, we sometimes get a "(tail call): ?" on the last line. + -- We remove this so that the tests are not lua dependent. + errmsg = errmsg:gsub('%s*%(tail call%): %?', '') + return errmsg end +function module.pcall_err(fn, ...) + local errmsg = module.pcall_err_withfile(fn, ...) + + return errmsg:gsub('.../helpers.lua:0: ', '') +end + -- initial_path: directory to recurse into -- re: include pattern (string) -- exc_re: exclude pattern(s) (string or table) diff --git a/test/symbolic/klee/nvim/charset.c b/test/symbolic/klee/nvim/charset.c index 95853a6834..f9bc3fabc4 100644 --- a/test/symbolic/klee/nvim/charset.c +++ b/test/symbolic/klee/nvim/charset.c @@ -69,7 +69,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, && !STRING_ENDED(ptr + 1) && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { pre = ptr[1]; - // Detect hexadecimal: 0x or 0X follwed by hex digit + // Detect hexadecimal: 0x or 0X followed by hex digit if ((what & STR2NR_HEX) && !STRING_ENDED(ptr + 2) && (pre == 'X' || pre == 'x') @@ -77,7 +77,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, ptr += 2; goto vim_str2nr_hex; } - // Detect binary: 0b or 0B follwed by 0 or 1 + // Detect binary: 0b or 0B followed by 0 or 1 if ((what & STR2NR_BIN) && !STRING_ENDED(ptr + 2) && (pre == 'B' || pre == 'b') |