diff options
124 files changed, 2627 insertions, 1158 deletions
diff --git a/ci/build.ps1 b/ci/build.ps1 index 36570be7ae..dbc43aecf3 100644 --- a/ci/build.ps1 +++ b/ci/build.ps1 @@ -91,7 +91,14 @@ if ($compiler -eq 'MINGW') { & C:\msys64\usr\bin\mkdir -p /var/cache/pacman/pkg # Build third-party dependencies - C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm -Su" ; exitIfFailed + C:\msys64\usr\bin\bash -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz" ; exitIfFailed + C:\msys64\usr\bin\bash -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig" ; exitIfFailed + C:\msys64\usr\bin\bash -lc "pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig" ; exitIfFailed + C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm -U msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz" ; exitIfFailed + # If there are still processes using msys-2.0.dll, after the base system update is finished, it will wait for input from the user. + # To prevent this, we will terminate all processes that use msys-2.0.dll. + Get-Process | Where-Object { $_.path -like 'C:\msys64*' } | Stop-Process + C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm -Syu" ; exitIfFailed C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm --needed -S $mingwPackages" ; exitIfFailed } elseif ($compiler -eq 'MSVC') { @@ -116,7 +123,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/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index 275d18a5a9..c2195fa02d 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -91,19 +91,19 @@ function! provider#clipboard#Executable() abort let s:paste['*'] = s:paste['+'] let s:cache_enabled = 0 return 'pbcopy' - elseif exists('$WAYLAND_DISPLAY') && executable('wl-copy') && executable('wl-paste') + elseif !empty($WAYLAND_DISPLAY) && executable('wl-copy') && executable('wl-paste') let s:copy['+'] = ['wl-copy', '--foreground', '--type', 'text/plain'] let s:paste['+'] = ['wl-paste', '--no-newline'] let s:copy['*'] = ['wl-copy', '--foreground', '--primary', '--type', 'text/plain'] let s:paste['*'] = ['wl-paste', '--no-newline', '--primary'] return 'wl-copy' - elseif exists('$DISPLAY') && executable('xclip') + elseif !empty($DISPLAY) && executable('xclip') let s:copy['+'] = ['xclip', '-quiet', '-i', '-selection', 'clipboard'] let s:paste['+'] = ['xclip', '-o', '-selection', 'clipboard'] let s:copy['*'] = ['xclip', '-quiet', '-i', '-selection', 'primary'] let s:paste['*'] = ['xclip', '-o', '-selection', 'primary'] return 'xclip' - elseif exists('$DISPLAY') && executable('xsel') && s:cmd_ok('xsel -o -b') + elseif !empty($DISPLAY) && executable('xsel') && s:cmd_ok('xsel -o -b') let s:copy['+'] = ['xsel', '--nodetach', '-i', '-b'] let s:paste['+'] = ['xsel', '-o', '-b'] let s:copy['*'] = ['xsel', '--nodetach', '-i', '-p'] @@ -132,7 +132,7 @@ function! provider#clipboard#Executable() abort let s:copy['*'] = s:copy['+'] let s:paste['*'] = s:paste['+'] return 'win32yank' - elseif exists('$TMUX') && executable('tmux') + elseif !empty($TMUX) && executable('tmux') let s:copy['+'] = ['tmux', 'load-buffer', '-'] let s:paste['+'] = ['tmux', 'save-buffer', '-'] let s:copy['*'] = s:copy['+'] diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 083128ab80..0c726ddd86 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -1783,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. diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 75cdc71039..66d15d446a 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 a710ef5d14..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}...]) @@ -7070,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 diff --git a/runtime/doc/if_ruby.txt b/runtime/doc/if_ruby.txt index 87047712f3..c8d2409549 100644 --- a/runtime/doc/if_ruby.txt +++ b/runtime/doc/if_ruby.txt @@ -185,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/indent.txt b/runtime/doc/indent.txt index 1df0331239..f2278f8453 100644 --- a/runtime/doc/indent.txt +++ b/runtime/doc/indent.txt @@ -566,9 +566,15 @@ The examples below assume a 'shiftwidth' of 4. with "#" does not work. + PN When N is non-zero recognize C pragmas, and indent them like any + other code; does not concern other preprocessor directives. + When N is zero (default): don't recognize C pragmas, treating + them like every other preprocessor directive. + + The defaults, spelled out in full, are: cinoptions=>s,e0,n0,f0,{0,}0,^0,L-1,:s,=s,l0,b0,gs,hs,N0,E0,ps,ts,is,+s, - c3,C0,/0,(2s,us,U0,w0,W0,k0,m0,j0,J0,)20,*70,#0 + c3,C0,/0,(2s,us,U0,w0,W0,k0,m0,j0,J0,)20,*70,#0,P0 Vim puts a line in column 1 if: - It starts with '#' (preprocessor directives), if 'cinkeys' contains '#0'. diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 5a61017b1d..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 diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index beb5e9f4c2..b83d2c4484 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1386,6 +1386,21 @@ A jump table for the options with a short description can be found at |Q_op|. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. + *'completeslash'* *'csl'* +'completeslash' 'csl' string (default: "") + local to buffer + {not in Vi} {only for MS-Windows} + When this option is set it overrules 'shellslash' for completion: + - When this option is set to "slash", a forward slash is used for path + completion in insert mode. This is useful when editing HTML tag, or + Makefile with 'noshellslash' on Windows. + - When this option is set to "backslash", backslash is used. This is + useful when editing a batch file with 'shellslash' set on Windows. + - When this option is empty, same character is used as for + 'shellslash'. + For Insert mode completion the buffer-local value is used. For + command line completion the global value is used. + *'completeopt'* *'cot'* 'completeopt' 'cot' string (default: "menu,preview") global @@ -2903,6 +2918,8 @@ A jump table for the options with a short description can be found at |Q_op|. *'go-c'* 'c' Use console dialogs instead of popup dialogs for simple choices. + *'go-d'* + 'd' Use dark theme variant if available. *'go-e'* 'e' Add tab pages when indicated with 'showtabline'. 'guitablabel' can be used to change the text in the labels. @@ -5289,7 +5306,8 @@ A jump table for the options with a short description can be found at |Q_op|. 'shellslash' only works when a backslash can be used as a path separator. To test if this is so use: > if exists('+shellslash') -< +< Also see 'completeslash'. + *'shelltemp'* *'stmp'* *'noshelltemp'* *'nostmp'* 'shelltemp' 'stmp' boolean (Vim default on, Vi default off) global @@ -5779,7 +5797,7 @@ A jump table for the options with a short description can be found at |Q_op|. normal text. Each status line item is of the form: %-0{minwid}.{maxwid}{item} All fields except the {item} are optional. A single percent sign can - be given as "%%". Up to 80 items can be specified. *E541* + be given as "%%". When the option starts with "%!" then it is used as an expression, evaluated and the result is used as the option value. Example: > diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 7f644486f7..aaf13d1640 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -136,6 +136,15 @@ tsnode:has_error() *tsnode:has_error()* tsnode:sexpr() *tsnode:sexpr()* Get an S-expression representing the node as a string. +tsnode:id() *tsnode:id()* + Get an unique identier for the node inside its own tree. + + No guarantees are made about this identifer's internal representation, + except for being a primitive lua type with value equality (so not a table). + Presently it is a (non-printable) string. + + NB: the id is not guaranteed to be unique for nodes from different trees. + tsnode:descendant_for_range({start_row}, {start_col}, {end_row}, {end_col}) *tsnode:descendant_for_range()* Get the smallest node within this node that spans the given range of 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..1a0015e2db 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..70862320c5 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -632,15 +632,18 @@ function protocol.make_client_capabilities() codeActionLiteralSupport = { codeActionKind = { - valueSet = {}; + valueSet = vim.tbl_values(protocol.CodeActionKind); }; }; }; completion = { dynamicRegistration = false; completionItem = { + -- Until we can actually expand snippet, move cursor and allow for true snippet experience, + -- this should be disabled out of the box. + -- However, users can turn this back on if they have a snippet plugin. + snippetSupport = false; - snippetSupport = true; commitCharactersSupport = false; preselectSupport = false; deprecatedSupport = false; @@ -703,6 +706,10 @@ function protocol.make_client_capabilities() }; hierarchicalDocumentSymbolSupport = true; }; + rename = { + dynamicRegistration = false; + prepareSupport = true; + }; }; workspace = { symbol = { @@ -914,6 +921,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,14 +931,21 @@ 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 - elseif type(server_capabilities.codeActionProvider) == 'boolean' then + elseif type(server_capabilities.codeActionProvider) == 'boolean' + or type(server_capabilities.codeActionProvider) == 'table' then general_properties.code_action = server_capabilities.codeActionProvider - elseif type(server_capabilities.codeActionProvider) == 'table' then - -- TODO(ashkan) support CodeActionKind - general_properties.code_action = false else error("The server sent invalid codeActionProvider") end diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 749a51fecc..17c411f952 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -42,13 +42,28 @@ local function is_dir(filename) end local NIL = vim.NIL + +--@private +local recursive_convert_NIL +recursive_convert_NIL = function(v, tbl_processed) + if v == NIL then + return nil + elseif not tbl_processed[v] and type(v) == 'table' then + tbl_processed[v] = true + return vim.tbl_map(function(x) + return recursive_convert_NIL(x, tbl_processed) + end, v) + end + + return v +end + --@private --- Returns its argument, but converts `vim.NIL` to Lua `nil`. --@param v (any) Argument --@returns (any) local function convert_NIL(v) - if v == NIL then return nil end - return v + return recursive_convert_NIL(v, {}) end --@private diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index b5f171a985..9ed19b938d 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -214,13 +214,16 @@ end function M.apply_text_document_edit(text_document_edit) local text_document = text_document_edit.textDocument local bufnr = vim.uri_to_bufnr(text_document.uri) - if text_document.version then - -- `VersionedTextDocumentIdentifier`s version may be null https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier - if text_document.version ~= vim.NIL and M.buf_versions[bufnr] ~= nil and M.buf_versions[bufnr] > text_document.version then - print("Buffer ", text_document.uri, " newer than edits.") - return - end + + -- `VersionedTextDocumentIdentifier`s version may be null + -- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier + if text_document.version + and M.buf_versions[bufnr] + and M.buf_versions[bufnr] > text_document.version then + print("Buffer ", text_document.uri, " newer than edits.") + return end + M.apply_text_edits(text_document_edit.edits, bufnr) end @@ -532,7 +535,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help) } --]=] -- TODO highlight parameter - if parameter.documentation and parameter.documentation ~= vim.NIL then + if parameter.documentation then M.convert_input_to_markdown_lines(parameter.documentation, contents) end end diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index decde08019..6714bb6354 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -157,7 +157,7 @@ local function on_line_impl(self, buf, line) a.nvim_buf_set_extmark(buf, ns, start_row, start_col, { end_line = end_row, end_col = end_col, hl_group = hl, - ephemeral = true + ephemeral = true, }) end if start_row > line then diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 2903c5905c..cc7dc2656d 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -25,7 +25,7 @@ local function filter_files(file_list) end end - return { main, unpack(after) } + return main and { main, unpack(after) } or after end local function runtime_query_path(lang, query_name) 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 8287958ab5..551b8fb691 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -211,6 +211,18 @@ preprocess_patch() { LC_ALL=C sed -e 's/\( [ab]\/src\)/\1\/nvim/g' \ "$file" > "$file".tmp && mv "$file".tmp "$file" + # Rename evalfunc.c to eval/funcs.c + LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/evalfunc\.c/\1\/eval\/funcs\.c/g' \ + "$file" > "$file".tmp && mv "$file".tmp "$file" + + # Rename userfunc.c to eval/userfunc.c + LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/userfunc\.c/\1\/eval\/userfunc\.c/g' \ + "$file" > "$file".tmp && mv "$file".tmp "$file" + + # Rename session.c to ex_session.c + LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/session\(\.[ch]\)/\1\/ex_session\2/g' \ + "$file" > "$file".tmp && mv "$file".tmp "$file" + # Rename test_urls.vim to check_urls.vim LC_ALL=C sed -e 's@\( [ab]\)/runtime/doc/test\(_urls.vim\)@\1/scripts/check\2@g' \ "$file" > "$file".tmp && mv "$file".tmp "$file" diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index c8dd85b39d..4569ebc713 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_object_to_bool(v, "force", false, err); + } else if (strequal("unload", k.data)) { + unload = api_object_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_object_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..a9b1676879 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1619,14 +1619,21 @@ 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_object_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; @@ -1644,3 +1651,30 @@ const char *describe_ns(NS ns_id) }) return "(UNKNOWN PLUGIN)"; } + +DecorationProvider *get_provider(NS ns_id, bool force) +{ + ssize_t i; + for (i = 0; i < (ssize_t)kv_size(decoration_providers); i++) { + DecorationProvider *item = &kv_A(decoration_providers, i); + if (item->ns_id == ns_id) { + return item; + } else if (item->ns_id > ns_id) { + break; + } + } + + if (!force) { + return NULL; + } + + for (ssize_t j = (ssize_t)kv_size(decoration_providers)-1; j >= i; j++) { + // allocates if needed: + (void)kv_a(decoration_providers, (size_t)j+1); + kv_A(decoration_providers, (size_t)j+1) = kv_A(decoration_providers, j); + } + DecorationProvider *item = &kv_a(decoration_providers, (size_t)i); + *item = DECORATION_PROVIDER_INIT(ns_id); + + return item; +} diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index df3a263dcf..7c6f07402b 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -52,7 +52,8 @@ .type = kObjectTypeLuaRef, \ .data.luaref = r }) -#define NIL ((Object) {.type = kObjectTypeNil}) +#define NIL ((Object)OBJECT_INIT) +#define NULL_STRING ((String)STRING_INIT) #define PUT(dict, k, v) \ kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v })) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 876b052a8e..725847886a 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -199,6 +199,69 @@ Integer nvim_get_hl_id_by_name(String name) return syn_check_group((const char_u *)name.data, (int)name.size); } +Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) +{ + if (ns_id == 0) { + return get_global_hl_defs(); + } + abort(); +} + +/// Set a highlight group. +/// +/// @param ns_id number of namespace for this highlight +/// @param name highlight group name, like ErrorMsg +/// @param val highlight definiton map, like |nvim_get_hl_by_name|. +/// @param[out] err Error details, if any +/// +/// TODO: ns_id = 0, should modify :highlight namespace +/// TODO val should take update vs reset flag +void nvim_set_hl(Integer ns_id, String name, Dictionary val, Error *err) + FUNC_API_SINCE(7) +{ + int hl_id = syn_check_group( (char_u *)(name.data), (int)name.size); + int link_id = -1; + + HlAttrs attrs = dict2hlattrs(val, true, &link_id, err); + if (!ERROR_SET(err)) { + ns_hl_def((NS)ns_id, hl_id, attrs, link_id); + } +} + +/// Set active namespace for highlights. +/// +/// NB: this function can be called from async contexts, but the +/// semantics are not yet well-defined. To start with +/// |nvim_set_decoration_provider| on_win and on_line callbacks +/// are explicitly allowed to change the namespace during a redraw cycle. +/// +/// @param ns_id the namespace to activate +/// @param[out] err Error details, if any +void nvim_set_hl_ns(Integer ns_id, Error *err) + FUNC_API_SINCE(7) + FUNC_API_FAST +{ + if (ns_id >= 0) { + ns_hl_active = (NS)ns_id; + } + + // TODO(bfredl): this is a little bit hackish. Eventually we want a standard + // event path for redraws caused by "fast" events. This could tie in with + // better throttling of async events causing redraws, such as non-batched + // nvim_buf_set_extmark calls from async contexts. + if (!updating_screen && !ns_hl_changed) { + multiqueue_put(main_loop.events, on_redraw_event, 0); + } + ns_hl_changed = true; +} + +static void on_redraw_event(void **argv) + FUNC_API_NOEXPORT +{ + redraw_all_later(NOT_VALID); +} + + /// Sends input-keys to Nvim, subject to various quirks controlled by `mode` /// flags. This is a blocking call, unlike |nvim_input()|. /// @@ -678,7 +741,11 @@ Integer nvim_strwidth(String text, Error *err) ArrayOf(String) nvim_list_runtime_paths(void) FUNC_API_SINCE(1) { + // TODO(bfredl): this should just work: + // return nvim_get_runtime_file(NULL_STRING, true); + Array rv = ARRAY_DICT_INIT; + char_u *rtp = p_rtp; if (*rtp == NUL) { @@ -725,22 +792,29 @@ ArrayOf(String) nvim_list_runtime_paths(void) /// @param name pattern of files to search for /// @param all whether to return all matches or only the first /// @return list of absolute paths to the found files -ArrayOf(String) nvim_get_runtime_file(String name, Boolean all) +ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Error *err) FUNC_API_SINCE(7) { Array rv = ARRAY_DICT_INIT; - if (!name.data) { + + // TODO(bfredl): + if (name.size == 0) { + api_set_error(err, kErrorTypeValidation, "not yet implemented"); return rv; } + int flags = DIP_START | (all ? DIP_ALL : 0); - do_in_runtimepath((char_u *)name.data, flags, find_runtime_cb, &rv); + do_in_runtimepath(name.size ? (char_u *)name.data : NULL, + flags, find_runtime_cb, &rv); return rv; } static void find_runtime_cb(char_u *fname, void *cookie) { Array *rv = (Array *)cookie; - ADD(*rv, STRING_OBJ(cstr_to_string((char *)fname))); + if (fname != NULL) { + ADD(*rv, STRING_OBJ(cstr_to_string((char *)fname))); + } } String nvim__get_lib_dir(void) @@ -1477,7 +1551,7 @@ void nvim_unsubscribe(uint64_t channel_id, String event) Integer nvim_get_color_by_name(String name) FUNC_API_SINCE(1) { - return name_to_color((char_u *)name.data); + return name_to_color(name.data); } /// Returns a map of color names and RGB values. @@ -2610,33 +2684,6 @@ void nvim__screenshot(String path) ui_call_screenshot(path); } -static DecorationProvider *get_provider(NS ns_id, bool force) -{ - ssize_t i; - for (i = 0; i < (ssize_t)kv_size(decoration_providers); i++) { - DecorationProvider *item = &kv_A(decoration_providers, i); - if (item->ns_id == ns_id) { - return item; - } else if (item->ns_id > ns_id) { - break; - } - } - - if (!force) { - return NULL; - } - - for (ssize_t j = (ssize_t)kv_size(decoration_providers)-1; j >= i; j++) { - // allocates if needed: - (void)kv_a(decoration_providers, (size_t)j+1); - kv_A(decoration_providers, (size_t)j+1) = kv_A(decoration_providers, j); - } - DecorationProvider *item = &kv_a(decoration_providers, (size_t)i); - *item = DECORATION_PROVIDER_INIT(ns_id); - - return item; -} - static void clear_provider(DecorationProvider *p) { NLUA_CLEAR_REF(p->redraw_start); @@ -2695,7 +2742,7 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, clear_provider(p); // regardless of what happens, it seems good idea to redraw - redraw_later(NOT_VALID); // TODO(bfredl): too soon? + redraw_all_later(NOT_VALID); // TODO(bfredl): too soon? struct { const char *name; @@ -2706,6 +2753,7 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, { "on_win", &p->redraw_win }, { "on_line", &p->redraw_line }, { "on_end", &p->redraw_end }, + { "_on_hl_def", &p->hl_def }, { NULL, NULL }, }; diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index ba43bc6cb2..f09a03f592 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -142,7 +142,7 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) // make sure cursor is in visible range even if win != curwin update_topline_win(win); - redraw_win_later(win, VALID); + redraw_later(win, VALID); } /// Gets the window height @@ -471,7 +471,7 @@ void nvim_win_set_config(Window window, Dictionary config, Error *err) if (!win_new_float(win, fconfig, err)) { return; } - redraw_later(NOT_VALID); + redraw_later(win, NOT_VALID); } else { win_config_float(win, fconfig); win->w_pos_changed = true; diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index ea2db41668..9c28398f5b 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 8f631ae13b..469542ec9f 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1623,7 +1623,7 @@ void enter_buffer(buf_T *buf) } curbuf->b_last_used = time(NULL); - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } // Change to the directory of the current buffer. @@ -3440,31 +3440,17 @@ int build_stl_str_hl( int use_sandbox, char_u fillchar, int maxwidth, - struct stl_hlrec *hltab, - StlClickRecord *tabtab + stl_hlrec_t **hltab, + StlClickRecord **tabtab ) { - int groupitems[STL_MAX_ITEM]; - struct stl_item { - // Where the item starts in the status line output buffer - char_u *start; - // Function to run for ClickFunc items. - char *cmd; - // The minimum width of the item - int minwid; - // The maximum width of the item - int maxwid; - enum { - Normal, - Empty, - Group, - Separate, - Highlight, - TabPage, - ClickFunc, - Trunc - } type; - } items[STL_MAX_ITEM]; + static size_t stl_items_len = 20; // Initial value, grows as needed. + static stl_item_t *stl_items = NULL; + static int *stl_groupitems = NULL; + static stl_hlrec_t *stl_hltab = NULL; + static StlClickRecord *stl_tabtab = NULL; + static int *stl_separator_locations = NULL; + #define TMPLEN 70 char_u buf_tmp[TMPLEN]; char_u win_tmp[TMPLEN]; @@ -3472,6 +3458,14 @@ int build_stl_str_hl( const int save_must_redraw = must_redraw; const int save_redr_type = curwin->w_redr_type; + if (stl_items == NULL) { + stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len); + stl_groupitems = xmalloc(sizeof(int) * stl_items_len); + stl_hltab = xmalloc(sizeof(stl_hlrec_t) * stl_items_len); + stl_tabtab = xmalloc(sizeof(stl_hlrec_t) * stl_items_len); + stl_separator_locations = xmalloc(sizeof(int) * stl_items_len); + } + // When the format starts with "%!" then evaluate it as an expression and // use the result as the actual format string. if (fmt[0] == '%' && fmt[1] == '!') { @@ -3540,14 +3534,17 @@ int build_stl_str_hl( // Proceed character by character through the statusline format string // fmt_p is the current positon in the input buffer for (char_u *fmt_p = usefmt; *fmt_p; ) { - if (curitem == STL_MAX_ITEM) { - // There are too many items. Add the error code to the statusline - // to give the user a hint about what went wrong. - if (out_p + 5 < out_end_p) { - memmove(out_p, " E541", (size_t)5); - out_p += 5; - } - break; + if (curitem == (int)stl_items_len) { + size_t new_len = stl_items_len * 3 / 2; + + stl_items = xrealloc(stl_items, sizeof(stl_item_t) * new_len); + stl_groupitems = xrealloc(stl_groupitems, sizeof(int) * new_len); + stl_hltab = xrealloc(stl_hltab, sizeof(stl_hlrec_t) * new_len); + stl_tabtab = xrealloc(stl_tabtab, sizeof(StlClickRecord) * new_len); + stl_separator_locations = + xrealloc(stl_separator_locations, sizeof(int) * new_len); + + stl_items_len = new_len; } if (*fmt_p != NUL && *fmt_p != '%') { @@ -3591,16 +3588,16 @@ int build_stl_str_hl( if (groupdepth > 0) { continue; } - items[curitem].type = Separate; - items[curitem++].start = out_p; + stl_items[curitem].type = Separate; + stl_items[curitem++].start = out_p; continue; } // STL_TRUNCMARK: Where to begin truncating if the statusline is too long. if (*fmt_p == STL_TRUNCMARK) { fmt_p++; - items[curitem].type = Trunc; - items[curitem++].start = out_p; + stl_items[curitem].type = Trunc; + stl_items[curitem++].start = out_p; continue; } @@ -3616,7 +3613,7 @@ int build_stl_str_hl( // Determine how long the group is. // Note: We set the current output position to null // so `vim_strsize` will work. - char_u *t = items[groupitems[groupdepth]].start; + char_u *t = stl_items[stl_groupitems[groupdepth]].start; *out_p = NUL; long group_len = vim_strsize(t); @@ -3626,40 +3623,40 @@ int build_stl_str_hl( // move the output pointer back to where the group started. // Note: This erases any non-item characters that were in the group. // Otherwise there would be no reason to do this step. - if (curitem > groupitems[groupdepth] + 1 - && items[groupitems[groupdepth]].minwid == 0) { + if (curitem > stl_groupitems[groupdepth] + 1 + && stl_items[stl_groupitems[groupdepth]].minwid == 0) { // remove group if all items are empty and highlight group // doesn't change int group_start_userhl = 0; int group_end_userhl = 0; int n; - for (n = groupitems[groupdepth] - 1; n >= 0; n--) { - if (items[n].type == Highlight) { - group_start_userhl = group_end_userhl = items[n].minwid; + for (n = stl_groupitems[groupdepth] - 1; n >= 0; n--) { + if (stl_items[n].type == Highlight) { + group_start_userhl = group_end_userhl = stl_items[n].minwid; break; } } - for (n = groupitems[groupdepth] + 1; n < curitem; n++) { - if (items[n].type == Normal) { + for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) { + if (stl_items[n].type == Normal) { break; } - if (items[n].type == Highlight) { - group_end_userhl = items[n].minwid; + if (stl_items[n].type == Highlight) { + group_end_userhl = stl_items[n].minwid; } } if (n == curitem && group_start_userhl == group_end_userhl) { // empty group out_p = t; group_len = 0; - for (n = groupitems[groupdepth] + 1; n < curitem; n++) { + for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) { // do not use the highlighting from the removed group - if (items[n].type == Highlight) { - items[n].type = Empty; + if (stl_items[n].type == Highlight) { + stl_items[n].type = Empty; } // adjust the start position of TabPage to the next // item position - if (items[n].type == TabPage) { - items[n].start = out_p; + if (stl_items[n].type == TabPage) { + stl_items[n].start = out_p; } } } @@ -3667,18 +3664,19 @@ int build_stl_str_hl( // If the group is longer than it is allowed to be // truncate by removing bytes from the start of the group text. - if (group_len > items[groupitems[groupdepth]].maxwid) { + if (group_len > stl_items[stl_groupitems[groupdepth]].maxwid) { // { Determine the number of bytes to remove long n; if (has_mbyte) { // Find the first character that should be included. n = 0; - while (group_len >= items[groupitems[groupdepth]].maxwid) { + while (group_len >= stl_items[stl_groupitems[groupdepth]].maxwid) { group_len -= ptr2cells(t + n); n += (*mb_ptr2len)(t + n); } } else { - n = (long)(out_p - t) - items[groupitems[groupdepth]].maxwid + 1; + n = (long)(out_p - t) + - stl_items[stl_groupitems[groupdepth]].maxwid + 1; } // } @@ -3689,25 +3687,26 @@ int build_stl_str_hl( memmove(t + 1, t + n, (size_t)(out_p - (t + n))); out_p = out_p - n + 1; // Fill up space left over by half a double-wide char. - while (++group_len < items[groupitems[groupdepth]].minwid) { + while (++group_len < stl_items[stl_groupitems[groupdepth]].minwid) { *out_p++ = fillchar; } // } // correct the start of the items for the truncation - for (int idx = groupitems[groupdepth] + 1; idx < curitem; idx++) { + for (int idx = stl_groupitems[groupdepth] + 1; idx < curitem; idx++) { // Shift everything back by the number of removed bytes - items[idx].start -= n; + stl_items[idx].start -= n; // If the item was partially or completely truncated, set its // start to the start of the group - if (items[idx].start < t) { - items[idx].start = t; + if (stl_items[idx].start < t) { + stl_items[idx].start = t; } } // If the group is shorter than the minimum width, add padding characters. - } else if (abs(items[groupitems[groupdepth]].minwid) > group_len) { - long min_group_width = items[groupitems[groupdepth]].minwid; + } else if ( + abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) { + long min_group_width = stl_items[stl_groupitems[groupdepth]].minwid; // If the group is left-aligned, add characters to the right. if (min_group_width < 0) { min_group_width = 0 - min_group_width; @@ -3726,8 +3725,8 @@ int build_stl_str_hl( // } // Adjust item start positions - for (int n = groupitems[groupdepth] + 1; n < curitem; n++) { - items[n].start += group_len; + for (int n = stl_groupitems[groupdepth] + 1; n < curitem; n++) { + stl_items[n].start += group_len; } // Prepend the fill characters @@ -3763,9 +3762,9 @@ int build_stl_str_hl( // User highlight groups override the min width field // to denote the styling to use. if (*fmt_p == STL_USER_HL) { - items[curitem].type = Highlight; - items[curitem].start = out_p; - items[curitem].minwid = minwid > 9 ? 1 : minwid; + stl_items[curitem].type = Highlight; + stl_items[curitem].start = out_p; + stl_items[curitem].minwid = minwid > 9 ? 1 : minwid; fmt_p++; curitem++; continue; @@ -3799,8 +3798,8 @@ int build_stl_str_hl( if (minwid == 0) { // %X ends the close label, go back to the previous tab label nr. for (long n = curitem - 1; n >= 0; n--) { - if (items[n].type == TabPage && items[n].minwid >= 0) { - minwid = items[n].minwid; + if (stl_items[n].type == TabPage && stl_items[n].minwid >= 0) { + minwid = stl_items[n].minwid; break; } } @@ -3809,9 +3808,9 @@ int build_stl_str_hl( minwid = -minwid; } } - items[curitem].type = TabPage; - items[curitem].start = out_p; - items[curitem].minwid = minwid; + stl_items[curitem].type = TabPage; + stl_items[curitem].start = out_p; + stl_items[curitem].minwid = minwid; fmt_p++; curitem++; continue; @@ -3826,10 +3825,10 @@ int build_stl_str_hl( if (*fmt_p != STL_CLICK_FUNC) { break; } - items[curitem].type = ClickFunc; - items[curitem].start = out_p; - items[curitem].cmd = xmemdupz(t, (size_t)(((char *)fmt_p - t))); - items[curitem].minwid = minwid; + stl_items[curitem].type = ClickFunc; + stl_items[curitem].start = out_p; + stl_items[curitem].cmd = xmemdupz(t, (size_t)(((char *)fmt_p - t))); + stl_items[curitem].minwid = minwid; fmt_p++; curitem++; continue; @@ -3850,11 +3849,11 @@ int build_stl_str_hl( // Denotes the start of a new group if (*fmt_p == '(') { - groupitems[groupdepth++] = curitem; - items[curitem].type = Group; - items[curitem].start = out_p; - items[curitem].minwid = minwid; - items[curitem].maxwid = maxwid; + stl_groupitems[groupdepth++] = curitem; + stl_items[curitem].type = Group; + stl_items[curitem].start = out_p; + stl_items[curitem].minwid = minwid; + stl_items[curitem].maxwid = maxwid; fmt_p++; curitem++; continue; @@ -4149,9 +4148,9 @@ int build_stl_str_hl( // Create a highlight item based on the name if (*fmt_p == '#') { - items[curitem].type = Highlight; - items[curitem].start = out_p; - items[curitem].minwid = -syn_namen2id(t, (int)(fmt_p - t)); + stl_items[curitem].type = Highlight; + stl_items[curitem].start = out_p; + stl_items[curitem].minwid = -syn_namen2id(t, (int)(fmt_p - t)); curitem++; fmt_p++; } @@ -4162,8 +4161,8 @@ int build_stl_str_hl( // If we made it this far, the item is normal and starts at // our current position in the output buffer. // Non-normal items would have `continued`. - items[curitem].start = out_p; - items[curitem].type = Normal; + stl_items[curitem].start = out_p; + stl_items[curitem].type = Normal; // Copy the item string into the output buffer if (str != NULL && *str) { @@ -4321,7 +4320,7 @@ int build_stl_str_hl( // Otherwise, there was nothing to print so mark the item as empty } else { - items[curitem].type = Empty; + stl_items[curitem].type = Empty; } // Only free the string buffer if we allocated it. @@ -4362,13 +4361,13 @@ int build_stl_str_hl( // Otherwise, look for the truncation item } else { // Default to truncating at the first item - trunc_p = items[0].start; + trunc_p = stl_items[0].start; item_idx = 0; for (int i = 0; i < itemcnt; i++) { - if (items[i].type == Trunc) { - // Truncate at %< items. - trunc_p = items[i].start; + if (stl_items[i].type == Trunc) { + // Truncate at %< stl_items. + trunc_p = stl_items[i].start; item_idx = i; break; } @@ -4403,7 +4402,7 @@ int build_stl_str_hl( // Ignore any items in the statusline that occur after // the truncation point for (int i = 0; i < itemcnt; i++) { - if (items[i].start > trunc_p) { + if (stl_items[i].start > trunc_p) { itemcnt = i; break; } @@ -4458,12 +4457,12 @@ int build_stl_str_hl( for (int i = item_idx; i < itemcnt; i++) { // Items starting at or after the end of the truncated section need // to be moved backwards. - if (items[i].start >= trunc_end_p) { - items[i].start -= item_offset; + if (stl_items[i].start >= trunc_end_p) { + stl_items[i].start -= item_offset; // Anything inside the truncated area is set to start // at the `<` truncation character. } else { - items[i].start = trunc_p; + stl_items[i].start = trunc_p; } } // } @@ -4479,7 +4478,7 @@ int build_stl_str_hl( // figuring out how many groups there are. int num_separators = 0; for (int i = 0; i < itemcnt; i++) { - if (items[i].type == Separate) { + if (stl_items[i].type == Separate) { num_separators++; } } @@ -4488,11 +4487,10 @@ int build_stl_str_hl( if (num_separators) { // Create an array of the start location for each // separator mark. - int separator_locations[STL_MAX_ITEM]; int index = 0; for (int i = 0; i < itemcnt; i++) { - if (items[i].type == Separate) { - separator_locations[index] = i; + if (stl_items[i].type == Separate) { + stl_separator_locations[index] = i; index++; } } @@ -4504,16 +4502,17 @@ int build_stl_str_hl( for (int i = 0; i < num_separators; i++) { int dislocation = (i == (num_separators - 1)) ? final_spaces : standard_spaces; - char_u *seploc = items[separator_locations[i]].start + dislocation; - STRMOVE(seploc, items[separator_locations[i]].start); - for (char_u *s = items[separator_locations[i]].start; s < seploc; s++) { + char_u *start = stl_items[stl_separator_locations[i]].start; + char_u *seploc = start + dislocation; + STRMOVE(seploc, start); + for (char_u *s = start; s < seploc; s++) { *s = fillchar; } - for (int item_idx = separator_locations[i] + 1; + for (int item_idx = stl_separator_locations[i] + 1; item_idx < itemcnt; item_idx++) { - items[item_idx].start += dislocation; + stl_items[item_idx].start += dislocation; } } @@ -4523,11 +4522,12 @@ int build_stl_str_hl( // Store the info about highlighting. if (hltab != NULL) { - struct stl_hlrec *sp = hltab; + *hltab = stl_hltab; + stl_hlrec_t *sp = stl_hltab; for (long l = 0; l < itemcnt; l++) { - if (items[l].type == Highlight) { - sp->start = items[l].start; - sp->userhl = items[l].minwid; + if (stl_items[l].type == Highlight) { + sp->start = stl_items[l].start; + sp->userhl = stl_items[l].minwid; sp++; } } @@ -4537,16 +4537,17 @@ int build_stl_str_hl( // Store the info about tab pages labels. if (tabtab != NULL) { - StlClickRecord *cur_tab_rec = tabtab; + *tabtab = stl_tabtab; + StlClickRecord *cur_tab_rec = stl_tabtab; for (long l = 0; l < itemcnt; l++) { - if (items[l].type == TabPage) { - cur_tab_rec->start = (char *)items[l].start; - if (items[l].minwid == 0) { + if (stl_items[l].type == TabPage) { + cur_tab_rec->start = (char *)stl_items[l].start; + if (stl_items[l].minwid == 0) { cur_tab_rec->def.type = kStlClickDisabled; cur_tab_rec->def.tabnr = 0; } else { - int tabnr = items[l].minwid; - if (items[l].minwid > 0) { + int tabnr = stl_items[l].minwid; + if (stl_items[l].minwid > 0) { cur_tab_rec->def.type = kStlClickTabSwitch; } else { cur_tab_rec->def.type = kStlClickTabClose; @@ -4556,11 +4557,11 @@ int build_stl_str_hl( } cur_tab_rec->def.func = NULL; cur_tab_rec++; - } else if (items[l].type == ClickFunc) { - cur_tab_rec->start = (char *)items[l].start; + } else if (stl_items[l].type == ClickFunc) { + cur_tab_rec->start = (char *)stl_items[l].start; cur_tab_rec->def.type = kStlClickFuncRun; - cur_tab_rec->def.tabnr = items[l].minwid; - cur_tab_rec->def.func = items[l].cmd; + cur_tab_rec->def.tabnr = stl_items[l].minwid; + cur_tab_rec->def.func = stl_items[l].cmd; cur_tab_rec++; } } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index ba71ac6b2b..540542f409 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -361,14 +361,36 @@ struct mapblock { sctx_T m_script_ctx; // SCTX where map was defined }; -/* - * Used for highlighting in the status line. - */ +/// Used for highlighting in the status line. +typedef struct stl_hlrec stl_hlrec_t; struct stl_hlrec { char_u *start; int userhl; // 0: no HL, 1-9: User HL, < 0 for syn ID }; +/// Used for building the status line. +typedef struct stl_item stl_item_t; +struct stl_item { + // Where the item starts in the status line output buffer + char_u *start; + // Function to run for ClickFunc items. + char *cmd; + // The minimum width of the item + int minwid; + // The maximum width of the item + int maxwid; + enum { + Normal, + Empty, + Group, + Separate, + Highlight, + TabPage, + ClickFunc, + Trunc + } type; +}; + // values for b_syn_spell: what to do with toplevel text #define SYNSPL_DEFAULT 0 // spell check if @Spell not defined #define SYNSPL_TOP 1 // spell check toplevel text @@ -661,6 +683,9 @@ struct file_buffer { char_u *b_p_com; ///< 'comments' char_u *b_p_cms; ///< 'commentstring' char_u *b_p_cpt; ///< 'complete' +#ifdef BACKSLASH_IN_FILENAME + char_u *b_p_csl; ///< 'completeslash' +#endif char_u *b_p_cfu; ///< 'completefunc' char_u *b_p_ofu; ///< 'omnifunc' char_u *b_p_tfu; ///< 'tagfunc' @@ -770,6 +795,7 @@ struct file_buffer { int b_ind_cpp_namespace; int b_ind_if_for_while; int b_ind_cpp_extern_c; + int b_ind_pragma; linenr_T b_no_eol_lnum; /* non-zero lnum when last line of next binary * write should not have an end-of-line */ diff --git a/src/nvim/change.c b/src/nvim/change.c index 71614363d2..9ee987b45d 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; } @@ -295,7 +294,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, // change. if (wp->w_p_rnu || (wp->w_p_cul && lnum <= wp->w_last_cursorline)) { - redraw_win_later(wp, SOME_VALID); + redraw_later(wp, SOME_VALID); } } } @@ -349,7 +348,7 @@ void changed_bytes(linenr_T lnum, colnr_T col) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_p_diff && wp != curwin) { - redraw_win_later(wp, VALID); + redraw_later(wp, VALID); wlnum = diff_lnum_win(lnum, wp); if (wlnum > 0) { changedOneline(wp->w_buffer, wlnum); @@ -476,7 +475,7 @@ changed_lines( FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_p_diff && wp != curwin) { - redraw_win_later(wp, VALID); + redraw_later(wp, VALID); wlnum = diff_lnum_win(lnum, wp); if (wlnum > 0) { changed_lines_buf(wp->w_buffer, wlnum, diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 036ae32589..d3ffab1759 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -482,7 +482,7 @@ bool leftcol_changed(void) if (retval) curwin->w_set_curswant = true; - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); return retval; } diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 3de5fc49bd..b9c293f6c8 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -643,7 +643,7 @@ void diff_redraw(bool dofold) if (!wp->w_p_diff) { continue; } - redraw_win_later(wp, SOME_VALID); + redraw_later(wp, SOME_VALID); if (dofold && foldmethodIsDiff(wp)) { foldUpdateAll(wp); } @@ -1415,7 +1415,7 @@ void diff_win_options(win_T *wp, int addbuf) if (addbuf) { diff_buf_add(wp->w_buffer); } - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } /// Set options not to show diffs. For the current window or all windows. diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 5ea6716c0a..d7cca9ba36 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -4177,6 +4177,21 @@ static int ins_compl_get_exp(pos_T *ini) EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) { // May change home directory back to "~". tilde_replace(compl_pattern, num_matches, matches); +#ifdef BACKSLASH_IN_FILENAME + if (curbuf->b_p_csl[0] != NUL) { + for (int i = 0; i < num_matches; i++) { + char_u *ptr = matches[i]; + while (*ptr != NUL) { + if (curbuf->b_p_csl[0] == 's' && *ptr == '\\') { + *ptr = '/'; + } else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/') { + *ptr = '\\'; + } + ptr += utfc_ptr2len(ptr); + } + } + } +#endif ins_compl_add_matches(num_matches, matches, p_fic || p_wic); } break; @@ -5555,13 +5570,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: @@ -5762,10 +5775,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); @@ -5844,6 +5858,7 @@ internal_format ( curwin->w_cursor.col = startcol; foundcol = 0; + int skip_pos = 0; /* * Find position to break at. @@ -5913,7 +5928,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 @@ -5922,8 +5941,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) @@ -5935,6 +5957,7 @@ internal_format ( if (curwin->w_cursor.col == 0) break; + ncc = cc; col = curwin->w_cursor.col; dec_cursor(); @@ -5943,17 +5966,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; @@ -6055,7 +6117,7 @@ internal_format ( } } } - first_line = FALSE; + first_line = false; } if (State & VREPLACE_FLAG) { @@ -6242,12 +6304,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. @@ -7697,6 +7757,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) { @@ -8513,7 +8577,7 @@ static void ins_up( if (old_topline != curwin->w_topline || old_topfill != curwin->w_topfill ) - redraw_later(VALID); + redraw_later(curwin, VALID); start_arrow(&tpos); can_cindent = true; } else { @@ -8561,7 +8625,7 @@ static void ins_down( if (old_topline != curwin->w_topline || old_topfill != curwin->w_topfill ) - redraw_later(VALID); + redraw_later(curwin, VALID); start_arrow(&tpos); can_cindent = true; } else { @@ -8955,7 +9019,7 @@ static int ins_ctrl_ey(int tc) scrolldown_clamp(); else scrollup_clamp(); - redraw_later(VALID); + redraw_later(curwin, VALID); } else { c = ins_copychar(curwin->w_cursor.lnum + (c == Ctrl_Y ? -1 : 1)); if (c != NUL) { 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 11f6a350e4..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={}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 38ffa38e20..83ef9c8762 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2071,6 +2071,12 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) expand_T xpc; bool error = false; char_u *result; +#ifdef BACKSLASH_IN_FILENAME + char_u *p_csl_save = p_csl; + + // avoid using 'completeslash' here + p_csl = empty_option; +#endif rettv->v_type = VAR_STRING; if (argvars[1].v_type != VAR_UNKNOWN @@ -2123,6 +2129,9 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = NULL; } } +#ifdef BACKSLASH_IN_FILENAME + p_csl = p_csl_save; +#endif } @@ -4007,7 +4016,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "globpath()" function static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int flags = 0; // Flags for globpath. + int flags = WILD_IGNORE_COMPLETESLASH; // Flags for globpath. bool error = false; // Return a string, or a list if the optional third argument is non-zero. @@ -6381,6 +6390,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 */ @@ -8141,15 +8156,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) @@ -8159,7 +8176,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) { @@ -8187,18 +8204,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; @@ -8211,7 +8228,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--; 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 3669cbbd2d..41137bf2db 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3721,8 +3721,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, update_topline(); validate_cursor(); update_screen(SOME_VALID); - highlight_match = FALSE; - redraw_later(SOME_VALID); + highlight_match = false; + redraw_later(curwin, SOME_VALID); curwin->w_p_fen = save_p_fen; if (msg_row == Rows - 1) @@ -5751,7 +5751,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, } xfree(str); - redraw_later(SOME_VALID); + redraw_later(curwin, SOME_VALID); win_enter(save_curwin, false); // Return to original window update_topline(); diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 503fd8e0d0..713d18b44d 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2363,7 +2363,7 @@ void ex_compiler(exarg_T *eap) do_unlet(S_LEN("b:current_compiler"), true); snprintf((char *)buf, bufsize, "compiler/%s.vim", eap->arg); - if (source_runtime(buf, DIP_ALL) == FAIL) { + if (source_in_path(p_rtp, buf, DIP_ALL) == FAIL) { EMSG2(_("E666: compiler not supported: %s"), eap->arg); } xfree(buf); @@ -2581,6 +2581,7 @@ int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, /// return FAIL when no file could be sourced, OK otherwise. int source_runtime(char_u *name, int flags) { + flags |= (flags & DIP_NORTP) ? 0 : DIP_START; return source_in_path(p_rtp, name, flags); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 71ac542323..d7ed2fe97d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1251,7 +1251,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, char_u *errormsg = NULL; // error message char_u *after_modifier = NULL; exarg_T ea; - int save_msg_scroll = msg_scroll; + const int save_msg_scroll = msg_scroll; cmdmod_T save_cmdmod; const int save_reg_executing = reg_executing; char_u *cmd; @@ -2003,34 +2003,10 @@ doend: ? cmdnames[(int)ea.cmdidx].cmd_name : (char_u *)NULL); - if (ea.verbose_save >= 0) { - p_verbose = ea.verbose_save; - } - free_cmdmod(); - + undo_cmdmod(&ea, save_msg_scroll); cmdmod = save_cmdmod; reg_executing = save_reg_executing; - if (ea.save_msg_silent != -1) { - // messages could be enabled for a serious error, need to check if the - // counters don't become negative - if (!did_emsg || msg_silent > ea.save_msg_silent) { - msg_silent = ea.save_msg_silent; - } - emsg_silent -= ea.did_esilent; - if (emsg_silent < 0) { - emsg_silent = 0; - } - // Restore msg_scroll, it's set by file I/O commands, even when no - // message is actually displayed. - msg_scroll = save_msg_scroll; - - /* "silent reg" or "silent echo x" inside "redir" leaves msg_col - * somewhere in the line. Put it back in the first column. */ - if (redirecting()) - msg_col = 0; - } - if (ea.did_sandbox) { sandbox--; } @@ -2298,9 +2274,14 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only) return OK; } -// Free contents of "cmdmod". -static void free_cmdmod(void) +// Undo and free contents of "cmdmod". +static void undo_cmdmod(const exarg_T *eap, int save_msg_scroll) + FUNC_ATTR_NONNULL_ALL { + if (eap->verbose_save >= 0) { + p_verbose = eap->verbose_save; + } + if (cmdmod.save_ei != NULL) { /* Restore 'eventignore' to the value before ":noautocmd". */ set_string_option_direct((char_u *)"ei", -1, cmdmod.save_ei, @@ -2308,8 +2289,27 @@ static void free_cmdmod(void) free_string_option(cmdmod.save_ei); } - if (cmdmod.filter_regmatch.regprog != NULL) { - vim_regfree(cmdmod.filter_regmatch.regprog); + vim_regfree(cmdmod.filter_regmatch.regprog); + + if (eap->save_msg_silent != -1) { + // messages could be enabled for a serious error, need to check if the + // counters don't become negative + if (!did_emsg || msg_silent > eap->save_msg_silent) { + msg_silent = eap->save_msg_silent; + } + emsg_silent -= eap->did_esilent; + if (emsg_silent < 0) { + emsg_silent = 0; + } + // Restore msg_scroll, it's set by file I/O commands, even when no + // message is actually displayed. + msg_scroll = save_msg_scroll; + + // "silent reg" or "silent echo x" inside "redir" leaves msg_col + // somewhere in the line. Put it back in the first column. + if (redirecting()) { + msg_col = 0; + } } } @@ -4446,6 +4446,9 @@ void separate_nextcmd(exarg_T *eap) else if (p[0] == '`' && p[1] == '=' && (eap->argt & XFILE)) { p += 2; (void)skip_expr(&p); + if (*p == NUL) { // stop at NUL after CTRL-V + break; + } } /* Check for '"': start of comment or '|': next command */ /* :@" does not start a comment! @@ -5400,7 +5403,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 { @@ -7379,7 +7382,7 @@ static void ex_syncbind(exarg_T *eap) else scrolldown(-y, TRUE); curwin->w_scbind_pos = topline; - redraw_later(VALID); + redraw_later(curwin, VALID); cursor_correct(); curwin->w_redr_status = TRUE; } @@ -8504,7 +8507,7 @@ static void ex_pedit(exarg_T *eap) if (curwin != curwin_save && win_valid(curwin_save)) { // Return cursor to where we were validate_cursor(); - redraw_later(VALID); + redraw_later(curwin, VALID); win_enter(curwin_save, true); } g_do_tagpreview = 0; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index f9ca7bfa42..53feffd2d7 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -4990,7 +4990,7 @@ ExpandFromContext ( char_u *pat, int *num_file, char_u ***file, - int options /* EW_ flags */ + int options // WILD_ flags ) { regmatch_T regmatch; @@ -5052,6 +5052,21 @@ ExpandFromContext ( ret = expand_wildcards_eval(&pat, num_file, file, flags); if (free_pat) xfree(pat); +#ifdef BACKSLASH_IN_FILENAME + if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0) { + for (int i = 0; i < *num_file; i++) { + char_u *ptr = (*file)[i]; + while (*ptr != NUL) { + if (p_csl[0] == 's' && *ptr == '\\') { + *ptr = '/'; + } else if (p_csl[0] == 'b' && *ptr == '/') { + *ptr = '\\'; + } + ptr += utfc_ptr2len(ptr); + } + } + } +#endif return ret; } @@ -5193,7 +5208,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, @@ -6468,7 +6483,7 @@ static int open_cmdwin(void) ccline.redraw_state = kCmdRedrawNone; ui_call_cmdline_hide(ccline.level); } - redraw_later(SOME_VALID); + redraw_later(curwin, SOME_VALID); // Save the command line info, can be used recursively. save_cmdline(&save_ccline); diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 6b00b986dc..42a9ef08f9 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -897,8 +897,8 @@ void ex_mkrc(exarg_T *eap) if (!failed && view_session) { if (put_line(fd, - "let s:so_save = &so | let s:siso_save = &siso" - " | set so=0 siso=0") == FAIL) { + "let s:so_save = &g:so | let s:siso_save = &g:siso" + " | setg so=0 siso=0 | setl so=-1 siso=-1") == FAIL) { failed = true; } if (eap->cmdidx == CMD_mksession) { @@ -949,7 +949,7 @@ void ex_mkrc(exarg_T *eap) } if (fprintf(fd, "%s", - "let &so = s:so_save | let &siso = s:siso_save\n" + "let &g:so = s:so_save | let &g:siso = s:siso_save\n" "doautoall SessionLoadPost\n") < 0) { failed = true; diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h index f5ca0ebbb0..382aaf6ed3 100644 --- a/src/nvim/extmark_defs.h +++ b/src/nvim/extmark_defs.h @@ -50,10 +50,13 @@ typedef struct { LuaRef redraw_win; LuaRef redraw_line; LuaRef redraw_end; + LuaRef hl_def; + int hl_valid; } DecorationProvider; #define DECORATION_PROVIDER_INIT(ns_id) (DecorationProvider) \ { ns_id, false, LUA_NOREF, LUA_NOREF, \ - LUA_NOREF, LUA_NOREF, LUA_NOREF } + LUA_NOREF, LUA_NOREF, LUA_NOREF, \ + LUA_NOREF, -1 } #endif // NVIM_EXTMARK_DEFS_H diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 197aedabec..57001f77ed 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -862,7 +862,7 @@ void foldUpdateAfterInsert(void) void foldUpdateAll(win_T *win) { win->w_foldinvalid = true; - redraw_win_later(win, NOT_VALID); + redraw_later(win, NOT_VALID); } // foldMoveTo() {{{2 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/globals.h b/src/nvim/globals.h index 2db8689a56..31dd3fc848 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -92,6 +92,10 @@ EXTERN struct nvim_stats_s { EXTERN int Rows INIT(= DFLT_ROWS); // nr of rows in the screen EXTERN int Columns INIT(= DFLT_COLS); // nr of columns in the screen +EXTERN NS ns_hl_active INIT(= 0); // current ns that defines highlights +EXTERN bool ns_hl_changed INIT(= false); // highlight need update + + // We use 64-bit file functions here, if available. E.g. ftello() returns // off_t instead of long, which helps if long is 32 bit and off_t is 64 bit. // We assume that when fseeko() is available then ftello() is too. @@ -941,8 +945,10 @@ EXTERN char_u e_readonly[] INIT(= N_( EXTERN char_u e_readonlyvar[] INIT(= N_( "E46: Cannot change read-only variable \"%.*s\"")); EXTERN char_u e_dictreq[] INIT(= N_("E715: Dictionary required")); -EXTERN char_u e_toomanyarg[] INIT(= N_("E118: Too many arguments for function: %s")); -EXTERN char_u e_dictkey[] INIT(= N_("E716: Key not present in Dictionary: %s")); +EXTERN char_u e_toomanyarg[] INIT(= N_( + "E118: Too many arguments for function: %s")); +EXTERN char_u e_dictkey[] INIT(= N_( + "E716: Key not present in Dictionary: \"%s\"")); EXTERN char_u e_listreq[] INIT(= N_("E714: List required")); EXTERN char_u e_listdictarg[] INIT(= N_( "E712: Argument of %s must be a List or Dictionary")); diff --git a/src/nvim/hashtab.h b/src/nvim/hashtab.h index 19633d455f..c82a6cc121 100644 --- a/src/nvim/hashtab.h +++ b/src/nvim/hashtab.h @@ -51,6 +51,7 @@ typedef struct hashitem_S { /// Initial size for a hashtable. /// Our items are relatively small and growing is expensive, thus start with 16. /// Must be a power of 2. +/// This allows for storing 10 items (2/3 of 16) before a resize is needed. #define HT_INIT_SIZE 16 /// An array-based hashtable. diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 262afba07a..8403615166 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -14,6 +14,7 @@ #include "nvim/ui.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/lua/executor.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "highlight.c.generated.h" @@ -28,12 +29,16 @@ static Map(int, int) *combine_attr_entries; static Map(int, int) *blend_attr_entries; static Map(int, int) *blendthrough_attr_entries; +/// highlight entries private to a namespace +static Map(ColorKey, ColorItem) *ns_hl; + void highlight_init(void) { attr_entry_ids = map_new(HlEntry, int)(); combine_attr_entries = map_new(int, int)(); blend_attr_entries = map_new(int, int)(); blendthrough_attr_entries = map_new(int, int)(); + ns_hl = map_new(ColorKey, ColorItem)(); // index 0 is no attribute, add dummy entry: kv_push(attr_entries, ((HlEntry){ .attr = HLATTRS_INIT, .kind = kHlUnknown, @@ -129,21 +134,114 @@ void ui_send_all_hls(UI *ui) } /// Get attribute code for a syntax group. -int hl_get_syn_attr(int idx, HlAttrs at_en) +int hl_get_syn_attr(int ns_id, int idx, HlAttrs at_en) { // TODO(bfredl): should we do this unconditionally if (at_en.cterm_fg_color != 0 || at_en.cterm_bg_color != 0 || at_en.rgb_fg_color != -1 || at_en.rgb_bg_color != -1 || at_en.rgb_sp_color != -1 || at_en.cterm_ae_attr != 0 - || at_en.rgb_ae_attr != 0) { + || at_en.rgb_ae_attr != 0 || ns_id != 0) { return get_attr_entry((HlEntry){ .attr = at_en, .kind = kHlSyntax, - .id1 = idx, .id2 = 0 }); + .id1 = idx, .id2 = ns_id }); } else { // If all the fields are cleared, clear the attr field back to default value return 0; } } +static ColorKey colored_key(NS ns_id, int syn_id) +{ + return (ColorKey){ .ns_id = (int)ns_id, .syn_id = syn_id }; +} + +void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id) +{ + DecorationProvider *p = get_provider(ns_id, true); + int attr_id = link_id > 0 ? -1 : hl_get_syn_attr(ns_id, hl_id, attrs); + ColorItem it = { .attr_id = attr_id, + .link_id = link_id, + .version = p->hl_valid }; + map_put(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id), it); +} + +int ns_get_hl(NS ns_id, int hl_id, bool link) +{ + static int recursive = 0; + + if (ns_id < 0) { + if (ns_hl_active <= 0) { + return -1; + } + ns_id = ns_hl_active; + } + + DecorationProvider *p = get_provider(ns_id, true); + ColorItem it = map_get(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id)); + // TODO(bfredl): map_ref true even this? + bool valid_cache = it.version >= p->hl_valid; + + if (!valid_cache && p->hl_def != LUA_NOREF && !recursive) { + FIXED_TEMP_ARRAY(args, 3); + args.items[0] = INTEGER_OBJ((Integer)ns_id); + args.items[1] = STRING_OBJ(cstr_to_string((char *)syn_id2name(hl_id))); + args.items[2] = BOOLEAN_OBJ(link); + // TODO(bfredl): preload the "global" attr dict? + + Error err = ERROR_INIT; + recursive++; + Object ret = nlua_call_ref(p->hl_def, "hl_def", args, true, &err); + recursive--; + + // TODO(bfredl): or "inherit", combine with global value? + bool fallback = true; + int tmp = false; + HlAttrs attrs = HLATTRS_INIT; + if (ret.type == kObjectTypeDictionary) { + Dictionary dict = ret.data.dictionary; + fallback = false; + attrs = dict2hlattrs(dict, true, &it.link_id, &err); + for (size_t i = 0; i < dict.size; i++) { + char *key = dict.items[i].key.data; + Object val = dict.items[i].value; + bool truthy = api_object_to_bool(val, key, false, &err); + + if (strequal(key, "fallback")) { + fallback = truthy; + } else if (strequal(key, "temp")) { + tmp = truthy; + } + } + if (it.link_id >= 0) { + fallback = true; + } + } + + it.attr_id = fallback ? -1 : hl_get_syn_attr((int)ns_id, hl_id, attrs); + it.version = p->hl_valid-tmp; + map_put(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id), it); + } + + if (link) { + return it.attr_id >= 0 ? -1 : it.link_id; + } else { + return it.attr_id; + } +} + + +bool win_check_ns_hl(win_T *wp) +{ + if (ns_hl_changed) { + highlight_changed(); + if (wp) { + update_window_hl(wp, true); + } + ns_hl_changed = false; + return true; + } + return false; +} + /// Get attribute code for a builtin highlight group. /// /// The final syntax group could be modified by hi-link or 'winhighlight'. @@ -204,6 +302,17 @@ void update_window_hl(win_T *wp, bool invalid) wp->w_hl_attr_normal = float_win ? HL_ATTR(HLF_NFLOAT) : 0; } + // NOOOO! You cannot just pretend that "Normal" is just like any other + // syntax group! It needs at least 10 layers of special casing! Noooooo! + // + // haha, theme engine go brrr + int normality = syn_check_group((const char_u *)S_LEN("Normal")); + int ns_attr = ns_get_hl(-1, normality, false); + if (ns_attr > 0) { + // TODO(bfredl): hantera NormalNC and so on + wp->w_hl_attr_normal = ns_attr; + } + // if blend= attribute is not set, 'winblend' value overrides it. if (wp->w_floating && wp->w_p_winbl > 0) { HlEntry entry = kv_A(attr_entries, wp->w_hl_attr_normal); @@ -275,6 +384,7 @@ void clear_hl_tables(bool reinit) map_free(int, int)(combine_attr_entries); map_free(int, int)(blend_attr_entries); map_free(int, int)(blendthrough_attr_entries); + map_free(ColorKey, ColorItem)(ns_hl); } } @@ -663,6 +773,103 @@ Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb) return hl; } +HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, Error *err) +{ + HlAttrs hlattrs = HLATTRS_INIT; + + int32_t fg = -1, bg = -1, sp = -1; + int16_t mask = 0; + for (size_t i = 0; i < dict.size; i++) { + char *key = dict.items[i].key.data; + Object val = dict.items[i].value; + + struct { + const char *name; + int16_t flag; + } flags[] = { + { "bold", HL_BOLD }, + { "standout", HL_STANDOUT }, + { "underline", HL_UNDERLINE }, + { "undercurl", HL_UNDERCURL }, + { "italic", HL_ITALIC }, + { "reverse", HL_INVERSE }, + { NULL, 0 }, + }; + + int j; + for (j = 0; flags[j].name; j++) { + if (strequal(flags[j].name, key)) { + if (api_object_to_bool(val, key, false, err)) { + mask = mask | flags[j].flag; + } + break; + } + } + + struct { + const char *name; + const char *shortname; + int *dest; + } colors[] = { + { "foreground", "fg", &fg }, + { "background", "bg", &bg }, + { "special", "sp", &sp }, + { NULL, NULL, NULL }, + }; + + int k; + for (k = 0; (!flags[j].name) && colors[k].name; k++) { + if (strequal(colors[k].name, key) || strequal(colors[k].shortname, key)) { + if (val.type == kObjectTypeInteger) { + *colors[k].dest = (int)val.data.integer; + } else if (val.type == kObjectTypeString) { + String str = val.data.string; + // TODO(bfredl): be more fancy with "bg", "fg" etc + if (str.size) { + *colors[k].dest = name_to_color(str.data); + } + } else { + api_set_error(err, kErrorTypeValidation, + "'%s' must be string or integer", key); + } + break; + } + } + + + if (flags[j].name || colors[k].name) { + // handled above + } else if (link_id && strequal(key, "link")) { + if (val.type == kObjectTypeString) { + String str = val.data.string; + *link_id = syn_check_group((const char_u *)str.data, (int)str.size); + } else if (val.type == kObjectTypeInteger) { + // TODO(bfredl): validate range? + *link_id = (int)val.data.integer; + } else { + api_set_error(err, kErrorTypeValidation, + "'link' must be string or integer"); + } + } + + if (ERROR_SET(err)) { + return hlattrs; // error set, caller should not use retval + } + } + + if (use_rgb) { + hlattrs.rgb_ae_attr = mask; + hlattrs.rgb_bg_color = bg; + hlattrs.rgb_fg_color = fg; + hlattrs.rgb_sp_color = sp; + } else { + hlattrs.cterm_ae_attr = mask; + hlattrs.cterm_bg_color = bg == -1 ? cterm_normal_bg_color : bg + 1; + hlattrs.cterm_fg_color = fg == -1 ? cterm_normal_fg_color : fg + 1; + } + + return hlattrs; +} Array hl_inspect(int attr) { Array ret = ARRAY_DICT_INIT; diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 36f3181674..6a5c593ab1 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -4,6 +4,7 @@ #include <inttypes.h> #include "nvim/macros.h" +#include "nvim/types.h" typedef int32_t RgbValue; @@ -180,6 +181,20 @@ typedef struct { HlKind kind; int id1; int id2; + int winid; } HlEntry; +typedef struct { + int ns_id; + int syn_id; +} ColorKey; + +typedef struct { + int attr_id; + int link_id; + int version; +} ColorItem; +#define COLOR_ITEM_INITIALIZER { .attr_id = -1, .link_id = -1, .version = -1 } + + #endif // NVIM_HIGHLIGHT_DEFS_H diff --git a/src/nvim/indent.c b/src/nvim/indent.c index bb0fdfec01..9e6693afdf 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -295,13 +295,17 @@ int set_indent(int size, int flags) // Replace the line (unless undo fails). if (!(flags & SIN_UNDO) || (u_savesub(curwin->w_cursor.lnum) == OK)) { + const colnr_T old_offset = (colnr_T)(p - oldline); + const colnr_T new_offset = (colnr_T)(s - newline); + + // this may free "newline" ml_replace(curwin->w_cursor.lnum, newline, false); if (!(flags & SIN_NOMARK)) { extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum-1, skipcols, - (int)(p-oldline) - skipcols, - (int)(s-newline) - skipcols, + old_offset - skipcols, + new_offset - skipcols, kExtmarkUndo); } @@ -311,15 +315,14 @@ int set_indent(int size, int flags) // Correct saved cursor position if it is in this line. if (saved_cursor.lnum == curwin->w_cursor.lnum) { - if (saved_cursor.col >= (colnr_T)(p - oldline)) { + if (saved_cursor.col >= old_offset) { // Cursor was after the indent, adjust for the number of // bytes added/removed. - saved_cursor.col += ind_len - (colnr_T)(p - oldline); - - } else if (saved_cursor.col >= (colnr_T)(s - newline)) { + saved_cursor.col += ind_len - old_offset; + } else if (saved_cursor.col >= new_offset) { // Cursor was in the indent, and is now after it, put it back // at the start of the indent (replacing spaces with TAB). - saved_cursor.col = (colnr_T)(s - newline); + saved_cursor.col = new_offset; } } retval = true; diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index bb443161ef..9298e57411 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -1676,6 +1676,9 @@ void parse_cino(buf_T *buf) // Handle C++ extern "C" or "C++" buf->b_ind_cpp_extern_c = 0; + // Handle C #pragma directives + buf->b_ind_pragma = 0; + for (p = buf->b_p_cino; *p; ) { l = p++; if (*p == '-') { @@ -1747,6 +1750,7 @@ void parse_cino(buf_T *buf) case 'N': buf->b_ind_cpp_namespace = n; break; case 'k': buf->b_ind_if_for_while = n; break; case 'E': buf->b_ind_cpp_extern_c = n; break; + case 'P': buf->b_ind_pragma = n; break; } if (*p == ',') ++p; @@ -1858,12 +1862,14 @@ int get_c_indent(void) goto laterend; } - /* - * #defines and so on always go at the left when included in 'cinkeys'. - */ + // #defines and so on go at the left when included in 'cinkeys', + // exluding pragmas when customized in 'cinoptions' if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', true))) { - amount = curbuf->b_ind_hash_comment; - goto theend; + const char_u *const directive = skipwhite(theline + 1); + if (curbuf->b_ind_pragma == 0 || STRNCMP(directive, "pragma", 6) != 0) { + amount = curbuf->b_ind_hash_comment; + goto theend; + } } /* diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 5c665920b5..a095f298f2 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -549,14 +549,6 @@ static lua_State *nlua_enter(void) // stack: (empty) lua_getglobal(lstate, "vim"); // stack: vim - lua_getfield(lstate, -1, "_update_package_paths"); - // stack: vim, vim._update_package_paths - if (lua_pcall(lstate, 0, 0, 0)) { - // stack: vim, error - nlua_error(lstate, _("E5117: Error while updating package paths: %.*s")); - // stack: vim - } - // stack: vim lua_pop(lstate, 1); // stack: (empty) last_p_rtp = (const void *)p_rtp; @@ -564,14 +556,6 @@ static lua_State *nlua_enter(void) return lstate; } -/// Force an update of lua's package paths if runtime path has changed. -bool nlua_update_package_path(void) -{ - lua_State *const lstate = nlua_enter(); - - return !!lstate; -} - static void nlua_print_event(void **argv) { char *str = argv[0]; @@ -891,6 +875,17 @@ LuaRef nlua_newref(lua_State *lstate, LuaRef original_ref) return new_ref; } +LuaRef api_new_luaref(LuaRef original_ref) +{ + if (original_ref == LUA_NOREF) { + return LUA_NOREF; + } + + lua_State *const lstate = nlua_enter(); + return nlua_newref(lstate, original_ref); +} + + /// Evaluate lua string /// /// Used for luaeval(). diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 5258352e72..c53e208b00 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -58,6 +58,7 @@ static struct luaL_Reg node_meta[] = { { "__tostring", node_tostring }, { "__eq", node_eq }, { "__len", node_child_count }, + { "id", node_id }, { "range", node_range }, { "start", node_start }, { "end_", node_end }, @@ -176,10 +177,11 @@ int tslua_add_language(lua_State *L) } uint32_t lang_version = ts_language_version(lang); - if (lang_version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION) { + if (lang_version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION + || lang_version > TREE_SITTER_LANGUAGE_VERSION) { return luaL_error( L, - "ABI version mismatch : expected %" PRIu32 ", found %" PRIu32, + "ABI version mismatch : expected %d, found %d", TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION, lang_version); } @@ -246,7 +248,12 @@ int tslua_push_parser(lua_State *L) } TSParser *parser = ts_parser_new(); - ts_parser_set_language(parser, lang); + + if (!ts_parser_set_language(parser, lang)) { + ts_parser_delete(parser); + return luaL_error(L, "Failed to load language : %s", lang_name); + } + TSLua_parser *p = lua_newuserdata(L, sizeof(TSLua_parser)); // [udata] p->parser = parser; p->tree = NULL; @@ -342,7 +349,7 @@ static int parser_parse(lua_State *L) return 0; } - TSTree *new_tree; + TSTree *new_tree = NULL; size_t len; const char *str; long bufnr; @@ -374,6 +381,12 @@ static int parser_parse(lua_State *L) return luaL_error(L, "invalid argument to parser:parse()"); } + // Sometimes parsing fails (timeout, or wrong parser ABI) + // In those case, just return an error. + if (!new_tree) { + return luaL_error(L, "An error occured when parsing."); + } + uint32_t n_ranges = 0; TSRange *changed = p->tree ? ts_tree_get_changed_ranges(p->tree, new_tree, &n_ranges) : NULL; @@ -621,6 +634,17 @@ static int node_eq(lua_State *L) return 1; } +static int node_id(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + + lua_pushlstring(L, (const char *)&node.id, sizeof node.id); + return 1; +} + static int node_range(lua_State *L) { TSNode node; diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index bfa8b91208..85d39eaef4 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -92,67 +92,48 @@ function vim._os_proc_children(ppid) return children end --- TODO(ZyX-I): Create compatibility layer. ---{{{1 package.path updater function --- Last inserted paths. Used to clear out items from package.[c]path when they --- are no longer in &runtimepath. -local last_nvim_paths = {} -function vim._update_package_paths() - local cur_nvim_paths = {} - local rtps = vim.api.nvim_list_runtime_paths() - local sep = package.config:sub(1, 1) - for _, key in ipairs({'path', 'cpath'}) do - local orig_str = package[key] .. ';' - local pathtrails_ordered = {} - local orig = {} - -- Note: ignores trailing item without trailing `;`. Not using something - -- simpler in order to preserve empty items (stand for default path). - for s in orig_str:gmatch('[^;]*;') do - s = s:sub(1, -2) -- Strip trailing semicolon - orig[#orig + 1] = s - end - if key == 'path' then - -- /?.lua and /?/init.lua - pathtrails_ordered = {sep .. '?.lua', sep .. '?' .. sep .. 'init.lua'} - else - local pathtrails = {} - for _, s in ipairs(orig) do - -- Find out path patterns. pathtrail should contain something like - -- /?.so, \?.dll. This allows not to bother determining what correct - -- suffixes are. - local pathtrail = s:match('[/\\][^/\\]*%?.*$') - if pathtrail and not pathtrails[pathtrail] then - pathtrails[pathtrail] = true - pathtrails_ordered[#pathtrails_ordered + 1] = pathtrail - end - end - end - local new = {} - for _, rtp in ipairs(rtps) do - if not rtp:match(';') then - for _, pathtrail in pairs(pathtrails_ordered) do - local new_path = rtp .. sep .. 'lua' .. pathtrail - -- Always keep paths from &runtimepath at the start: - -- append them here disregarding orig possibly containing one of them. - new[#new + 1] = new_path - cur_nvim_paths[new_path] = true - end - end +local pathtrails = {} +vim._so_trails = {} +for s in (package.cpath..';'):gmatch('[^;]*;') do + s = s:sub(1, -2) -- Strip trailing semicolon + -- Find out path patterns. pathtrail should contain something like + -- /?.so, \?.dll. This allows not to bother determining what correct + -- suffixes are. + local pathtrail = s:match('[/\\][^/\\]*%?.*$') + if pathtrail and not pathtrails[pathtrail] then + pathtrails[pathtrail] = true + table.insert(vim._so_trails, pathtrail) + end +end + +function vim._load_package(name) + -- tricky: when debugging this function we must let vim.inspect + -- module to be loaded first: + --local inspect = (name == "vim.inspect") and tostring or vim.inspect + + local basename = name:gsub('%.', '/') + local paths = {"lua/"..basename..".lua", "lua/"..basename.."/init.lua"} + for _,path in ipairs(paths) do + local found = vim.api.nvim_get_runtime_file(path, false) + if #found > 0 then + return loadfile(found[1]) end - for _, orig_path in ipairs(orig) do - -- Handle removing obsolete paths originating from &runtimepath: such - -- paths either belong to cur_nvim_paths and were already added above or - -- to last_nvim_paths and should not be added at all if corresponding - -- entry was removed from &runtimepath list. - if not (cur_nvim_paths[orig_path] or last_nvim_paths[orig_path]) then - new[#new + 1] = orig_path - end + end + + for _,trail in ipairs(vim._so_trails) do + local path = "lua/"..trail:gsub('?',basename) + local found = vim.api.nvim_get_runtime_file(path, false) + if #found > 0 then + return package.loadlib(found[1]) end - package[key] = table.concat(new, ';') end - last_nvim_paths = cur_nvim_paths + return nil end +table.insert(package.loaders, 1, vim._load_package) + +-- TODO(ZyX-I): Create compatibility layer. + --- Return a human-readable representation of the given object. --- --@see https://github.com/kikito/inspect.lua diff --git a/src/nvim/main.c b/src/nvim/main.c index a22df9cc69..27bf5c45c6 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -446,7 +446,7 @@ int main(int argc, char **argv) if (exmode_active || use_remote_ui || use_builtin_ui) { // Don't clear the screen when starting in Ex mode, or when a UI might have // displayed messages. - redraw_later(VALID); + redraw_later(curwin, VALID); } else { screenclear(); // clear screen TIME_MSG("clearing screen"); diff --git a/src/nvim/map.c b/src/nvim/map.c index ca8ea76333..7d97b7f13d 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -12,6 +12,9 @@ #include <stdbool.h> #include <string.h> +#include <lua.h> +#include <lauxlib.h> + #include "nvim/map.h" #include "nvim/map_defs.h" #include "nvim/vim.h" @@ -173,6 +176,20 @@ static inline bool HlEntry_eq(HlEntry ae1, HlEntry ae2) return memcmp(&ae1, &ae2, sizeof(ae1)) == 0; } +static inline khint_t ColorKey_hash(ColorKey ae) +{ + const uint8_t *data = (const uint8_t *)&ae; + khint_t h = 0; + for (size_t i = 0; i < sizeof(ae); i++) { + h = (h << 5) - h + data[i]; + } + return h; +} + +static inline bool ColorKey_eq(ColorKey ae1, ColorKey ae2) +{ + return memcmp(&ae1, &ae2, sizeof(ae1)) == 0; +} MAP_IMPL(int, int, DEFAULT_INITIALIZER) @@ -191,6 +208,7 @@ MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) MAP_IMPL(String, handle_T, 0) +MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER) /// Deletes a key:value pair from a string:pointer map, and frees the /// storage of both key and value. diff --git a/src/nvim/map.h b/src/nvim/map.h index 63a18f4129..7bd3d31330 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -55,6 +55,8 @@ MAP_DECLS(String, MsgpackRpcRequestHandler) MAP_DECLS(HlEntry, int) MAP_DECLS(String, handle_T) +MAP_DECLS(ColorKey, ColorItem) + #define map_new(T, U) map_##T##_##U##_new #define map_free(T, U) map_##T##_##U##_free #define map_get(T, U) map_##T##_##U##_get diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 6d188c6cd0..ec4f4cbc21 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -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/menu.c b/src/nvim/menu.c index b060464383..4ba2e36656 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -193,7 +193,7 @@ ex_menu(exarg_T *eap) vimmenu_T **root_menu_ptr = get_root_menu(menu_path); if (root_menu_ptr == &curwin->w_winbar) { // Assume the window toolbar menu will change. - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } if (enable != kNone) { diff --git a/src/nvim/message.c b/src/nvim/message.c index 6cd5616acf..06ba607323 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1216,7 +1216,7 @@ void wait_return(int redraw) ui_refresh(); } else if (!skip_redraw) { if (redraw == true || (msg_scrolled != 0 && redraw != -1)) { - redraw_later(VALID); + redraw_later(curwin, VALID); } if (ui_has(kUIMessages)) { msg_ext_clear(true); @@ -2222,7 +2222,7 @@ void msg_scroll_up(bool may_throttle) /// /// Probably message scrollback storage should reimplented as a file_buffer, and /// message scrolling in TUI be reimplemented as a modal floating window. Then -/// we get throttling "for free" using standard redraw_win_later code paths. +/// we get throttling "for free" using standard redraw_later code paths. void msg_scroll_flush(void) { if (msg_grid.throttled) { diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index fcd9ee4f75..748001dcec 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -286,7 +286,7 @@ retnomove: check_topfill(curwin, false); curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); - redraw_later(VALID); + redraw_later(curwin, VALID); row = 0; } else if (row >= curwin->w_height_inner) { count = 0; @@ -316,7 +316,7 @@ retnomove: } } check_topfill(curwin, false); - redraw_later(VALID); + redraw_later(curwin, VALID); curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); row = curwin->w_height_inner - 1; @@ -450,7 +450,7 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) // skip line number and fold column in front of the line col -= win_col_off(win); - if (col < 0) { + if (col <= 0) { col = 0; } diff --git a/src/nvim/move.c b/src/nvim/move.c index 8084461d3a..218dcd289d 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -107,7 +107,7 @@ void redraw_for_cursorline(win_T *wp) && !pum_visible()) { if (wp->w_p_rnu) { // win_line() will redraw the number column only. - redraw_win_later(wp, VALID); + redraw_later(wp, VALID); } if (win_cursorline_standout(wp)) { if (wp->w_redr_type <= VALID && wp->w_last_cursorline != 0) { @@ -117,7 +117,7 @@ void redraw_for_cursorline(win_T *wp) redrawWinline(wp, wp->w_last_cursorline); redrawWinline(wp, wp->w_cursor.lnum); } else { - redraw_win_later(wp, SOME_VALID); + redraw_later(wp, SOME_VALID); } } } @@ -172,7 +172,7 @@ void update_topline(void) // If the buffer is empty, always set topline to 1. if (BUFEMPTY()) { // special case - file is empty if (curwin->w_topline != 1) { - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } curwin->w_topline = 1; curwin->w_botline = 2; @@ -326,12 +326,14 @@ void update_topline(void) dollar_vcol = -1; if (curwin->w_skipcol != 0) { curwin->w_skipcol = 0; - redraw_later(NOT_VALID); - } else - redraw_later(VALID); - /* May need to set w_skipcol when cursor in w_topline. */ - if (curwin->w_cursor.lnum == curwin->w_topline) + redraw_later(curwin, NOT_VALID); + } else { + redraw_later(curwin, VALID); + } + // May need to set w_skipcol when cursor in w_topline. + if (curwin->w_cursor.lnum == curwin->w_topline) { validate_cursor(); + } } *so_ptr = save_so; @@ -439,7 +441,7 @@ void changed_window_setting_win(win_T *wp) wp->w_lines_valid = 0; changed_line_abv_curs_win(wp); wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP|VALID_TOPLINE); - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } /* @@ -455,8 +457,8 @@ void set_topline(win_T *wp, linenr_T lnum) wp->w_topline_was_set = true; wp->w_topfill = 0; wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_TOPLINE); - /* Don't set VALID_TOPLINE here, 'scrolloff' needs to be checked. */ - redraw_later(VALID); + // Don't set VALID_TOPLINE here, 'scrolloff' needs to be checked. + redraw_later(wp, VALID); } /* @@ -634,7 +636,7 @@ void validate_virtcol_win(win_T *wp) if (wp->w_p_cuc && !pum_visible() ) - redraw_win_later(wp, SOME_VALID); + redraw_later(wp, SOME_VALID); } } @@ -830,7 +832,7 @@ void curs_columns( curwin->w_leftcol = new_leftcol; win_check_anchored_floats(curwin); // screen has to be redrawn with new curwin->w_leftcol - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } } curwin->w_wcol -= curwin->w_leftcol; @@ -934,13 +936,14 @@ void curs_columns( } else { curwin->w_skipcol = 0; } - if (prev_skipcol != curwin->w_skipcol) - redraw_later(NOT_VALID); + if (prev_skipcol != curwin->w_skipcol) { + redraw_later(curwin, NOT_VALID); + } /* Redraw when w_virtcol changes and 'cursorcolumn' is set */ if (curwin->w_p_cuc && (curwin->w_valid & VALID_VIRTCOL) == 0 && !pum_visible()) { - redraw_later(SOME_VALID); + redraw_later(curwin, SOME_VALID); } // now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise @@ -2023,7 +2026,7 @@ int onepage(Direction dir, long count) } } - redraw_later(VALID); + redraw_later(curwin, VALID); return retval; } @@ -2209,7 +2212,7 @@ void halfpage(bool flag, linenr_T Prenum) check_topfill(curwin, !flag); cursor_correct(); beginline(BL_SOL | BL_FIX); - redraw_later(VALID); + redraw_later(curwin, VALID); } void do_check_cursorbind(void) @@ -2257,7 +2260,7 @@ void do_check_cursorbind(void) } // Correct cursor for multi-byte character. mb_adjust_cursor(); - redraw_later(VALID); + redraw_later(curwin, VALID); // Only scroll when 'scrollbind' hasn't done this. if (!curwin->w_p_scb) { diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 92ca29209e..68ef4cd41e 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -262,11 +262,9 @@ static void parse_msgpack(Channel *channel) call_set_error(channel, buf, ERROR_LOG_LEVEL); } msgpack_unpacked_destroy(&unpacked); - // Bail out from this event loop iteration - return; + } else { + handle_request(channel, &unpacked.data); } - - handle_request(channel, &unpacked.data); } if (result == MSGPACK_UNPACK_NOMEM_ERROR) { diff --git a/src/nvim/normal.c b/src/nvim/normal.c index f291adaffe..b09b99cf35 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -903,6 +903,10 @@ normal_end: msg_nowait = false; + if (finish_op) { + set_reg_var(get_default_register_name()); + } + // Reset finish_op, in case it was set s->c = finish_op; finish_op = false; @@ -3615,7 +3619,7 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff) scrolldown(-y, false); } - redraw_later(VALID); + redraw_later(curwin, VALID); cursor_correct(); curwin->w_redr_status = true; } @@ -4158,7 +4162,7 @@ void scroll_redraw(int up, long count) coladvance(curwin->w_curswant); if (moved) curwin->w_viewport_invalid = true; - redraw_later(VALID); + redraw_later(curwin, VALID); } /* @@ -4256,7 +4260,7 @@ dozet: FALLTHROUGH; case 't': scroll_cursor_top(0, true); - redraw_later(VALID); + redraw_later(curwin, VALID); set_fraction(curwin); break; @@ -4265,7 +4269,7 @@ dozet: FALLTHROUGH; case 'z': scroll_cursor_halfway(true); - redraw_later(VALID); + redraw_later(curwin, VALID); set_fraction(curwin); break; @@ -4286,7 +4290,7 @@ dozet: FALLTHROUGH; case 'b': scroll_cursor_bot(0, true); - redraw_later(VALID); + redraw_later(curwin, VALID); set_fraction(curwin); break; @@ -4333,7 +4337,7 @@ dozet: col = 0; if (curwin->w_leftcol != col) { curwin->w_leftcol = col; - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } } break; @@ -4352,7 +4356,7 @@ dozet: } if (curwin->w_leftcol != col) { curwin->w_leftcol = col; - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } } break; @@ -4705,7 +4709,7 @@ static void nv_clear(cmdarg_T *cap) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { wp->w_s->b_syn_slow = false; } - redraw_later(CLEAR); + redraw_later(curwin, CLEAR); } } @@ -6856,7 +6860,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..9e7d81fc82 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2652,7 +2652,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) xfree(reg->y_array); } if (curwin->w_p_rnu) { - redraw_later(SOME_VALID); // cursor moved to start + redraw_later(curwin, SOME_VALID); // cursor moved to start } if (message) { // Display message about yank? if (yank_type == kMTCharWise && yanklines == 1) { @@ -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 484d9da3a1..fcc051ef1a 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -312,6 +312,9 @@ static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent", static char *(p_fcl_values[]) = { "all", NULL }; static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview", "noinsert", "noselect", NULL }; +#ifdef BACKSLASH_IN_FILENAME +static char *(p_csl_values[]) = { "slash", "backslash", NULL }; +#endif static char *(p_icm_values[]) = { "nosplit", "split", NULL }; static char *(p_scl_values[]) = { "yes", "no", "auto", "auto:1", "auto:2", "auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8", "auto:9", @@ -2563,7 +2566,7 @@ static bool valid_spellfile(const char_u *val) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { for (const char_u *s = val; *s != NUL; s++) { - if (!vim_isfilec(*s) && *s != ',') { + if (!vim_isfilec(*s) && *s != ',' && *s != ' ') { return false; } } @@ -3118,7 +3121,7 @@ ambw_end: } else { if (curwin->w_status_height) { curwin->w_redr_status = true; - redraw_later(VALID); + redraw_later(curwin, VALID); } curbuf->b_help = (curbuf->b_p_bt[0] == 'h'); redraw_titles(); @@ -3188,6 +3191,13 @@ ambw_end: } else { completeopt_was_set(); } +#ifdef BACKSLASH_IN_FILENAME + } else if (gvarp == &p_csl) { // 'completeslash' + if (check_opt_strings(p_csl, p_csl_values, false) != OK + || check_opt_strings(curbuf->b_p_csl, p_csl_values, false) != OK) { + errmsg = e_invarg; + } +#endif } else if (varp == &curwin->w_p_scl) { // 'signcolumn' if (check_opt_strings(*varp, p_scl_values, false) != OK) { @@ -3354,10 +3364,6 @@ ambw_end: if (!parse_winhl_opt(curwin)) { errmsg = e_invarg; } - } else if (varp == &p_rtp) { // 'runtimepath' - if (!nlua_update_package_path()) { - errmsg = (char_u *)N_("E970: Failed to initialize lua interpreter"); - } } else { // Options that are a list of flags. p = NULL; @@ -3741,11 +3747,10 @@ static char_u *set_chars_option(win_T *wp, char_u **varp, bool set) /// Return error message or NULL. char_u *check_stl_option(char_u *s) { - int itemcnt = 0; int groupdepth = 0; static char_u errbuf[80]; - while (*s && itemcnt < STL_MAX_ITEM) { + while (*s) { // Check for valid keys after % sequences while (*s && *s != '%') { s++; @@ -3754,9 +3759,6 @@ char_u *check_stl_option(char_u *s) break; } s++; - if (*s != '%' && *s != ')') { - itemcnt++; - } if (*s == '%' || *s == STL_TRUNCMARK || *s == STL_SEPARATE) { s++; continue; @@ -3798,9 +3800,6 @@ char_u *check_stl_option(char_u *s) } } } - if (itemcnt >= STL_MAX_ITEM) { - return (char_u *)N_("E541: too many items"); - } if (groupdepth != 0) { return (char_u *)N_("E542: unbalanced groups"); } @@ -4692,7 +4691,7 @@ static void check_redraw(uint32_t flags) redraw_curbuf_later(NOT_VALID); } if (flags & P_RWINONLY) { - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } if (doclear) { redraw_all_later(CLEAR); @@ -5706,12 +5705,12 @@ void unset_global_local_option(char *name, void *from) case PV_LCS: clear_string_option(&((win_T *)from)->w_p_lcs); set_chars_option((win_T *)from, &((win_T *)from)->w_p_lcs, true); - redraw_win_later((win_T *)from, NOT_VALID); + redraw_later((win_T *)from, NOT_VALID); break; case PV_FCS: clear_string_option(&((win_T *)from)->w_p_fcs); set_chars_option((win_T *)from, &((win_T *)from)->w_p_fcs, true); - redraw_win_later((win_T *)from, NOT_VALID); + redraw_later((win_T *)from, NOT_VALID); break; } } @@ -5866,6 +5865,9 @@ static char_u *get_varp(vimoption_T *p) case PV_COM: return (char_u *)&(curbuf->b_p_com); case PV_CMS: return (char_u *)&(curbuf->b_p_cms); case PV_CPT: return (char_u *)&(curbuf->b_p_cpt); +# ifdef BACKSLASH_IN_FILENAME + case PV_CSL: return (char_u *)&(curbuf->b_p_csl); +# endif case PV_CFU: return (char_u *)&(curbuf->b_p_cfu); case PV_OFU: return (char_u *)&(curbuf->b_p_ofu); case PV_EOL: return (char_u *)&(curbuf->b_p_eol); @@ -6153,6 +6155,9 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_inf = p_inf; buf->b_p_swf = cmdmod.noswapfile ? false : p_swf; buf->b_p_cpt = vim_strsave(p_cpt); +# ifdef BACKSLASH_IN_FILENAME + buf->b_p_csl = vim_strsave(p_csl); +# endif buf->b_p_cfu = vim_strsave(p_cfu); buf->b_p_ofu = vim_strsave(p_ofu); buf->b_p_tfu = vim_strsave(p_tfu); @@ -6803,7 +6808,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; diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index a09811c8fb..af0ea7f4a2 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 @@ -187,6 +188,7 @@ enum { #define GO_ASELML 'A' // autoselect modeless selection #define GO_BOT 'b' // use bottom scrollbar #define GO_CONDIALOG 'c' // use console dialog +#define GO_DARKTHEME 'd' // use dark theme variant #define GO_TABLINE 'e' // may show tabline #define GO_FORG 'f' // start GUI in foreground #define GO_GREY 'g' // use grey menu items @@ -204,7 +206,7 @@ enum { #define GO_FOOTER 'F' // add footer #define GO_VERTICAL 'v' // arrange dialog buttons vertically #define GO_KEEPWINSIZE 'k' // keep GUI window size -#define GO_ALL "aAbcefFghilmMprTvk" // all possible flags for 'go' +#define GO_ALL "aAbcdefFghilmMprTvk" // all possible flags for 'go' // flags for 'comments' option #define COM_NEST 'n' // comments strings nest @@ -372,6 +374,9 @@ EXTERN long p_columns; // 'columns' EXTERN int p_confirm; // 'confirm' EXTERN int p_cp; // 'compatible' EXTERN char_u *p_cot; // 'completeopt' +# ifdef BACKSLASH_IN_FILENAME +EXTERN char_u *p_csl; // 'completeslash' +# endif EXTERN long p_pb; // 'pumblend' EXTERN long p_ph; // 'pumheight' EXTERN long p_pw; // 'pumwidth' @@ -743,6 +748,7 @@ enum { , BV_CPT , BV_DICT , BV_TSR + , BV_CSL , BV_CFU , BV_DEF , BV_INC diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 02df0ab276..65ef7a4527 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -453,6 +453,15 @@ return { defaults={if_true={vi="menu,preview"}} }, { + full_name='completeslash', abbreviation='csl', + type='string', scope={'buffer'}, + vi_def=true, + vim=true, + varname='p_csl', + enable_if='BACKSLASH_IN_FILENAME', + defaults={if_true={vi=""}} + }, + { full_name='confirm', abbreviation='cf', type='bool', scope={'global'}, vi_def=true, diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index c8ac4218f6..bd5f2b889d 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -16,10 +16,11 @@ #define BASENAMELEN (NAME_MAX - 5) // Use the system path length if it makes sense. -#if defined(PATH_MAX) && (PATH_MAX > 1024) +# define DEFAULT_MAXPATHL 4096 +#if defined(PATH_MAX) && (PATH_MAX > DEFAULT_MAXPATHL) # define MAXPATHL PATH_MAX #else -# define MAXPATHL 1024 +# define MAXPATHL DEFAULT_MAXPATHL #endif // Command-processing buffer. Use large buffers for all platforms. diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 3beada5bc9..ac0c1f6eb1 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) { @@ -786,7 +786,7 @@ static int pum_set_selected(int n, int repeat) // Return cursor to where we were validate_cursor(); - redraw_later(SOME_VALID); + redraw_later(curwin, SOME_VALID); // When the preview window was resized we need to // update the view on the buffer. Only go back to @@ -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 96c903d206..6ca97c3072 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3707,7 +3707,7 @@ static void qf_win_goto(win_T *win, linenr_T lnum) curwin->w_cursor.coladd = 0; curwin->w_curswant = 0; update_topline(); // scroll to show the line - redraw_later(VALID); + redraw_later(curwin, VALID); curwin->w_redr_status = true; // update ruler curwin = old_curwin; curbuf = curwin->w_buffer; @@ -6317,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) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 9f450888a2..36a6ae59f8 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -25,7 +25,7 @@ // // Commands that scroll a window change w_topline and must call // check_cursor() to move the cursor into the visible part of the window, and -// call redraw_later(VALID) to have the window displayed by update_screen() +// call redraw_later(wp, VALID) to have the window displayed by update_screen() // later. // // Commands that change text in the buffer must call changed_bytes() or @@ -37,7 +37,7 @@ // // Commands that change how a window is displayed (e.g., setting 'list') or // invalidate the contents of a window in another way (e.g., change fold -// settings), must call redraw_later(NOT_VALID) to have the whole window +// settings), must call redraw_later(wp, NOT_VALID) to have the whole window // redisplayed by update_screen() later. // // Commands that change how a buffer is displayed (e.g., setting 'tabstop') @@ -45,11 +45,11 @@ // buffer redisplayed by update_screen() later. // // Commands that change highlighting and possibly cause a scroll too must call -// redraw_later(SOME_VALID) to update the whole window but still use scrolling -// to avoid redrawing everything. But the length of displayed lines must not -// change, use NOT_VALID then. +// redraw_later(wp, SOME_VALID) to update the whole window but still use +// scrolling to avoid redrawing everything. But the length of displayed lines +// must not change, use NOT_VALID then. // -// Commands that move the window position must call redraw_later(NOT_VALID). +// Commands that move the window position must call redraw_later(wp, NOT_VALID). // TODO(neovim): should minimize redrawing by scrolling when possible. // // Commands that change everything (e.g., resizing the screen) must call @@ -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_object_to_bool(ret, "provider %s retval", default_true, &err)) { return true; } @@ -195,17 +195,11 @@ static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, return false; } -/* - * Redraw the current window later, with update_screen(type). - * Set must_redraw only if not already set to a higher value. - * e.g. if must_redraw is CLEAR, type NOT_VALID will do nothing. - */ -void redraw_later(int type) -{ - redraw_win_later(curwin, type); -} - -void redraw_win_later(win_T *wp, int type) +/// Redraw a window later, with update_screen(type). +/// +/// Set must_redraw only if not already set to a higher value. +/// e.g. if must_redraw is CLEAR, type NOT_VALID will do nothing. +void redraw_later(win_T *wp, int type) FUNC_ATTR_NONNULL_ALL { if (!exiting && wp->w_redr_type < type) { @@ -223,7 +217,7 @@ void redraw_win_later(win_T *wp, int type) void redraw_all_later(int type) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - redraw_win_later(wp, type); + redraw_later(wp, type); } // This may be needed when switching tabs. if (must_redraw < type) { @@ -234,7 +228,7 @@ void redraw_all_later(int type) void screen_invalidate_highlights(void) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); wp->w_grid.valid = false; } } @@ -251,7 +245,7 @@ void redraw_buf_later(buf_T *buf, int type) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_buffer == buf) { - redraw_win_later(wp, type); + redraw_later(wp, type); } } } @@ -277,7 +271,7 @@ void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline) if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lastline) { wp->w_redraw_bot = lastline; } - redraw_win_later(wp, VALID); + redraw_later(wp, VALID); } } } @@ -305,7 +299,7 @@ redrawWinline( if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) { wp->w_redraw_bot = lnum; } - redraw_win_later(wp, VALID); + redraw_later(wp, VALID); } } @@ -359,7 +353,6 @@ int update_screen(int type) /* Postpone the redrawing when it's not needed and when being called * recursively. */ if (!redrawing() || updating_screen) { - redraw_later(type); /* remember type for next time */ must_redraw = type; if (type > INVERTED_ALL) { curwin->w_lines_valid = 0; // don't use w_lines[].wl_size now @@ -501,6 +494,12 @@ int update_screen(int type) } } + // "start" callback could have changed highlights for global elements + if (win_check_ns_hl(NULL)) { + redraw_cmdline = true; + redraw_tabline = true; + } + if (clear_cmdline) /* going to clear cmdline (done below) */ check_for_delay(FALSE); @@ -1331,6 +1330,8 @@ static void win_update(win_T *wp, Providers *providers) } } + win_check_ns_hl(wp); + for (;; ) { /* stop updating when reached the end of the window (check for _past_ @@ -2185,6 +2186,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // return 'false' or error: skip rest of this window kv_A(*providers, k) = NULL; } + + win_check_ns_hl(wp); } } @@ -4649,8 +4652,8 @@ void status_redraw_all(void) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_status_height) { - wp->w_redr_status = TRUE; - redraw_later(VALID); + wp->w_redr_status = true; + redraw_later(wp, VALID); } } } @@ -4667,7 +4670,7 @@ void status_redraw_buf(buf_T *buf) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_status_height != 0 && wp->w_buffer == buf) { wp->w_redr_status = true; - redraw_later(VALID); + redraw_later(wp, VALID); } } } @@ -5191,8 +5194,8 @@ win_redr_custom ( char_u buf[MAXPATHL]; char_u *stl; char_u *p; - struct stl_hlrec hltab[STL_MAX_ITEM]; - StlClickRecord tabtab[STL_MAX_ITEM]; + stl_hlrec_t *hltab; + StlClickRecord *tabtab; int use_sandbox = false; win_T *ewp; int p_crb_save; @@ -5270,9 +5273,9 @@ win_redr_custom ( /* Make a copy, because the statusline may include a function call that * might change the option value and free the memory. */ stl = vim_strsave(stl); - width = build_stl_str_hl(ewp, buf, sizeof(buf), - stl, use_sandbox, - fillchar, maxwidth, hltab, tabtab); + width = + build_stl_str_hl(ewp, buf, sizeof(buf), stl, use_sandbox, + fillchar, maxwidth, &hltab, &tabtab); xfree(stl); ewp->w_p_crb = p_crb_save; diff --git a/src/nvim/search.c b/src/nvim/search.c index 9458e6333f..b25333c9fa 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -4923,7 +4923,7 @@ search_line: && curwin != curwin_save && win_valid(curwin_save)) { /* Return cursor to where we were */ validate_cursor(); - redraw_later(VALID); + redraw_later(curwin, VALID); win_enter(curwin_save, true); } break; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 1984a357c3..636c71657d 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2258,7 +2258,7 @@ char_u *did_set_spelllang(win_T *wp) theend: xfree(spl_copy); recursive = false; - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); return ret_msg; } @@ -6877,7 +6877,7 @@ void ex_spelldump(exarg_T *eap) if (curbuf->b_ml.ml_line_count > 1) { ml_delete(curbuf->b_ml.ml_line_count, false); } - redraw_later(NOT_VALID); + redraw_later(curwin, NOT_VALID); } // Go through all possible words and: diff --git a/src/nvim/spell_defs.h b/src/nvim/spell_defs.h index 034c580b3e..05667f060e 100644 --- a/src/nvim/spell_defs.h +++ b/src/nvim/spell_defs.h @@ -119,6 +119,7 @@ struct slang_S { bool sl_add; // true if it's a .add file. char_u *sl_fbyts; // case-folded word bytes + long sl_fbyts_len; // length of sl_fbyts idx_T *sl_fidxs; // case-folded word indexes char_u *sl_kbyts; // keep-case word bytes idx_T *sl_kidxs; // keep-case word indexes diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 09d8646c6d..b415a4635b 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -764,20 +764,24 @@ truncerr: } // <LWORDTREE> - res = spell_read_tree(fd, &lp->sl_fbyts, &lp->sl_fidxs, false, 0); - if (res != 0) + res = spell_read_tree(fd, &lp->sl_fbyts, &lp->sl_fbyts_len, + &lp->sl_fidxs, false, 0); + if (res != 0) { goto someerror; + } // <KWORDTREE> - res = spell_read_tree(fd, &lp->sl_kbyts, &lp->sl_kidxs, false, 0); - if (res != 0) + res = spell_read_tree(fd, &lp->sl_kbyts, NULL, &lp->sl_kidxs, false, 0); + if (res != 0) { goto someerror; + } // <PREFIXTREE> - res = spell_read_tree(fd, &lp->sl_pbyts, &lp->sl_pidxs, true, - lp->sl_prefixcnt); - if (res != 0) + res = spell_read_tree(fd, &lp->sl_pbyts, NULL, &lp->sl_pidxs, true, + lp->sl_prefixcnt); + if (res != 0) { goto someerror; + } // For a new file link it in the list of spell files. if (old_lp == NULL && lang != NULL) { @@ -920,8 +924,8 @@ void suggest_load_files(void) // <SUGWORDTREE>: <wordtree> // Read the trie with the soundfolded words. - if (spell_read_tree(fd, &slang->sl_sbyts, &slang->sl_sidxs, - false, 0) != 0) { + if (spell_read_tree(fd, &slang->sl_sbyts, NULL, &slang->sl_sidxs, + false, 0) != 0) { someerror: EMSG2(_("E782: error while reading .sug file: %s"), slang->sl_fname); @@ -1630,10 +1634,12 @@ static int spell_read_tree ( FILE *fd, char_u **bytsp, + long *bytsp_len, idx_T **idxsp, bool prefixtree, // true for the prefix tree int prefixcnt // when "prefixtree" is true: prefix count ) + FUNC_ATTR_NONNULL_ARG(1, 2, 4) { int idx; char_u *bp; @@ -1653,6 +1659,9 @@ spell_read_tree ( // Allocate the byte array. bp = xmalloc(len); *bytsp = bp; + if (bytsp_len != NULL) { + *bytsp_len = len; + } // Allocate the index array. ip = xcalloc(len, sizeof(*ip)); @@ -4850,10 +4859,10 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang) spin->si_blocks_cnt = 0; // Skip over any other NUL bytes (same word with different - // flags). - while (byts[n + 1] == 0) { - ++n; - ++curi[depth]; + // flags). But don't go over the end. + while (n + 1 < slang->sl_fbyts_len && byts[n + 1] == 0) { + n++; + curi[depth]++; } } else { // Normal char, go one level deeper. diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 9a9cc45c6b..ec6accd473 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -73,9 +73,9 @@ struct hl_group { RgbValue sg_rgb_fg; ///< RGB foreground color RgbValue sg_rgb_bg; ///< RGB background color RgbValue sg_rgb_sp; ///< RGB special color - uint8_t *sg_rgb_fg_name; ///< RGB foreground color name - uint8_t *sg_rgb_bg_name; ///< RGB background color name - uint8_t *sg_rgb_sp_name; ///< RGB special color name + char *sg_rgb_fg_name; ///< RGB foreground color name + char *sg_rgb_bg_name; ///< RGB background color name + char *sg_rgb_sp_name; ///< RGB special color name int sg_blend; ///< blend level (0-100 inclusive), -1 if unset }; @@ -3147,7 +3147,7 @@ static void syn_cmd_spell(exarg_T *eap, int syncing) } // assume spell checking changed, force a redraw - redraw_win_later(curwin, NOT_VALID); + redraw_later(curwin, NOT_VALID); } /// Handle ":syntax iskeyword" command. @@ -3187,7 +3187,7 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing) curbuf->b_p_isk = save_isk; } } - redraw_win_later(curwin, NOT_VALID); + redraw_later(curwin, NOT_VALID); } /* @@ -4297,7 +4297,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing) prev_toplvl_grp = curwin->w_s->b_syn_topgrp; curwin->w_s->b_syn_topgrp = sgl_id; if (source ? do_source(eap->arg, false, DOSO_NONE) == FAIL - : source_runtime(eap->arg, DIP_ALL) == FAIL) { + : source_in_path(p_rtp, eap->arg, DIP_ALL) == FAIL) { EMSG2(_(e_notopen), eap->arg); } curwin->w_s->b_syn_topgrp = prev_toplvl_grp; @@ -6899,7 +6899,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) } } } else if (strcmp(key, "GUIFG") == 0) { - char_u **const namep = &HL_TABLE()[idx].sg_rgb_fg_name; + char **namep = &HL_TABLE()[idx].sg_rgb_fg_name; if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) { @@ -6909,8 +6909,8 @@ void do_highlight(const char *line, const bool forceit, const bool init) if (*namep == NULL || STRCMP(*namep, arg) != 0) { xfree(*namep); if (strcmp(arg, "NONE") != 0) { - *namep = (char_u *)xstrdup(arg); - HL_TABLE()[idx].sg_rgb_fg = name_to_color((char_u *)arg); + *namep = xstrdup(arg); + HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg); } else { *namep = NULL; HL_TABLE()[idx].sg_rgb_fg = -1; @@ -6923,7 +6923,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) normal_fg = HL_TABLE()[idx].sg_rgb_fg; } } else if (STRCMP(key, "GUIBG") == 0) { - char_u **const namep = &HL_TABLE()[idx].sg_rgb_bg_name; + char **const namep = &HL_TABLE()[idx].sg_rgb_bg_name; if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) { @@ -6933,8 +6933,8 @@ void do_highlight(const char *line, const bool forceit, const bool init) if (*namep == NULL || STRCMP(*namep, arg) != 0) { xfree(*namep); if (STRCMP(arg, "NONE") != 0) { - *namep = (char_u *)xstrdup(arg); - HL_TABLE()[idx].sg_rgb_bg = name_to_color((char_u *)arg); + *namep = xstrdup(arg); + HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg); } else { *namep = NULL; HL_TABLE()[idx].sg_rgb_bg = -1; @@ -6947,7 +6947,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) normal_bg = HL_TABLE()[idx].sg_rgb_bg; } } else if (strcmp(key, "GUISP") == 0) { - char_u **const namep = &HL_TABLE()[idx].sg_rgb_sp_name; + char **const namep = &HL_TABLE()[idx].sg_rgb_sp_name; if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) { @@ -6957,8 +6957,8 @@ void do_highlight(const char *line, const bool forceit, const bool init) if (*namep == NULL || STRCMP(*namep, arg) != 0) { xfree(*namep); if (strcmp(arg, "NONE") != 0) { - *namep = (char_u *)xstrdup(arg); - HL_TABLE()[idx].sg_rgb_sp = name_to_color((char_u *)arg); + *namep = xstrdup(arg); + HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg); } else { *namep = NULL; HL_TABLE()[idx].sg_rgb_sp = -1; @@ -7152,13 +7152,32 @@ static void highlight_list_one(const int id) msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); } - if (!didh) - highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", ""); + if (!didh) { + highlight_list_arg(id, didh, LIST_STRING, 0, "cleared", ""); + } if (p_verbose > 0) { last_set_msg(sgp->sg_script_ctx); } } +Dictionary get_global_hl_defs(void) +{ + Dictionary rv = ARRAY_DICT_INIT; + for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) { + Dictionary attrs = ARRAY_DICT_INIT; + struct hl_group *h = &HL_TABLE()[i - 1]; + if (h->sg_attr > 0) { + attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true); + } else if (h->sg_link > 0) { + const char *link = (const char *)HL_TABLE()[h->sg_link - 1].sg_name; + PUT(attrs, "link", STRING_OBJ(cstr_to_string(link))); + } + PUT(rv, (const char *)h->sg_name, DICTIONARY_OBJ(attrs)); + } + + return rv; +} + /// Outputs a highlight when doing ":hi MyHighlight" /// /// @param type one of \ref LIST_XXX @@ -7166,15 +7185,15 @@ static void highlight_list_one(const int id) /// @param sarg string used if \p type == LIST_STRING static bool highlight_list_arg( const int id, bool didh, const int type, int iarg, - char_u *const sarg, const char *const name) + char *const sarg, const char *const name) { - char_u buf[100]; + char buf[100]; if (got_int) { return false; } if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) { - char_u *ts = buf; + char *ts = buf; if (type == LIST_INT) { snprintf((char *)buf, sizeof(buf), "%d", iarg - 1); } else if (type == LIST_STRING) { @@ -7191,15 +7210,15 @@ static bool highlight_list_arg( } } - (void)syn_list_header(didh, (int)(vim_strsize(ts) + STRLEN(name) + 1), id, - false); + (void)syn_list_header(didh, (int)(vim_strsize((char_u *)ts) + STRLEN(name) + + 1), id, false); didh = true; if (!got_int) { if (*name != NUL) { MSG_PUTS_ATTR(name, HL_ATTR(HLF_D)); MSG_PUTS_ATTR("=", HL_ATTR(HLF_D)); } - msg_outtrans(ts); + msg_outtrans((char_u *)ts); } } return didh; @@ -7378,7 +7397,7 @@ static void set_hl_attr(int idx) at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1; at_en.hl_blend = sgp->sg_blend; - sgp->sg_attr = hl_get_syn_attr(idx+1, at_en); + sgp->sg_attr = hl_get_syn_attr(0, idx+1, at_en); // a cursor style uses this syn_id, make sure its attribute is updated. if (cursor_mode_uses_syn_id(idx+1)) { @@ -7541,11 +7560,17 @@ int syn_id2attr(int hl_id) struct hl_group *sgp; hl_id = syn_get_final_id(hl_id); - sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ + int attr = ns_get_hl(-1, hl_id, false); + if (attr >= 0) { + return attr; + } + sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one return sgp->sg_attr; } + + /* * Translate a group ID to the final group ID (following links). */ @@ -7562,9 +7587,22 @@ int syn_get_final_id(int hl_id) * Look out for loops! Break after 100 links. */ for (count = 100; --count >= 0; ) { - sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ - if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) + // ACHTUNG: when using "tmp" attribute (no link) the function might be + // called twice. it needs be smart enough to remember attr only to + // syn_id2attr time + int check = ns_get_hl(-1, hl_id, true); + if (check == 0) { + return 0; // how dare! it broke the link! + } else if (check > 0) { + hl_id = check; + continue; + } + + + sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one + if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) { break; + } hl_id = sgp->sg_link; } @@ -8494,7 +8532,7 @@ color_name_table_T color_name_table[] = { /// /// @param[in] name string value to convert to RGB /// return the hex value or -1 if could not find a correct value -RgbValue name_to_color(const char_u *name) +RgbValue name_to_color(const char *name) { if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 81d1ef4c9f..c6b1a0d04c 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2903,7 +2903,7 @@ static int jumpto_tag( && curwin != curwin_save && win_valid(curwin_save)) { /* Return cursor to where we were */ validate_cursor(); - redraw_later(VALID); + redraw_later(curwin, VALID); win_enter(curwin_save, true); } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 52d3eef810..39e2ca6171 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -233,7 +233,7 @@ Terminal *terminal_open(TerminalOptions opts) snprintf(var, sizeof(var), "terminal_color_%d", i); char *name = get_config_string(var); if (name) { - color_val = name_to_color((uint8_t *)name); + color_val = name_to_color(name); xfree(name); if (color_val != -1) { @@ -1060,7 +1060,7 @@ static bool send_mouse_event(Terminal *term, int c) curwin->w_redr_status = true; curwin = save_curwin; curbuf = curwin->w_buffer; - redraw_win_later(mouse_win, NOT_VALID); + redraw_later(mouse_win, NOT_VALID); invalidate_terminal(term, -1, -1); // Only need to exit focus if the scrolled window is the terminal window return mouse_win == curwin; 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_cindent.vim b/src/nvim/testdir/test_cindent.vim index debc9da46d..b6c2d1467e 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -127,4 +127,40 @@ func Test_cindent_case() bwipe! endfunc +func Test_cindent_pragma() + new + setl cindent ts=4 sw=4 + setl cino=Ps + + let code =<< trim [CODE] + { + #pragma omp parallel + { + #pragma omp task + foo(); + # pragma omp taskwait + } + } + [CODE] + + call append(0, code) + normal gg + normal =G + + let expected =<< trim [CODE] + { + #pragma omp parallel + { + #pragma omp task + foo(); + # pragma omp taskwait + } + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +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_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 871143699a..e3c42a4fe3 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -722,7 +722,7 @@ func Test_verbosefile() endfunc func Test_verbose_option() - " See test/functional/ui/cmdline_spec.lua + " See test/functional/legacy/cmdline_spec.lua CheckScreendump let lines =<< trim [SCRIPT] @@ -842,6 +842,25 @@ func Test_buffers_lastused() bwipeout bufc endfunc +func Test_cmdlineclear_tabenter() + " See test/functional/legacy/cmdline_spec.lua + CheckScreendump + + let lines =<< trim [SCRIPT] + call setline(1, range(30)) + [SCRIPT] + + call writefile(lines, 'XtestCmdlineClearTabenter') + let buf = RunVimInTerminal('-S XtestCmdlineClearTabenter', #{rows: 10}) + call term_wait(buf, 50) + " in one tab make the command line higher with CTRL-W - + call term_sendkeys(buf, ":tabnew\<cr>\<C-w>-\<C-w>-gtgt") + call VerifyScreenDump(buf, 'Test_cmdlineclear_tabenter', {}) + + call StopVimInTerminal(buf) + call delete('XtestCmdlineClearTabenter') +endfunc + " test that ";" works to find a match at the start of the first line func Test_zero_line_search() new diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index caebc341e0..abad6983dc 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 @@ -1578,4 +1587,11 @@ func Test_edit_browse() bwipe! endfunc +func Test_read_invalid() + " set encoding=latin1 + " This was not properly checking for going past the end. + call assert_fails('r`=', 'E484') + set encoding=utf-8 +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_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 1339a9f25d..57a0a7aaf4 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,11 +339,94 @@ 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 +" Test for insert path completion with completeslash option +func Test_ins_completeslash() + CheckMSWindows + + call mkdir('Xdir') + + let orig_shellslash = &shellslash + set cpt& + + new + + set noshellslash + + set completeslash= + exe "normal oXd\<C-X>\<C-F>" + call assert_equal('Xdir\', getline('.')) + + set completeslash=backslash + exe "normal oXd\<C-X>\<C-F>" + call assert_equal('Xdir\', getline('.')) + + set completeslash=slash + exe "normal oXd\<C-X>\<C-F>" + call assert_equal('Xdir/', getline('.')) + + set shellslash + + set completeslash= + exe "normal oXd\<C-X>\<C-F>" + call assert_equal('Xdir/', getline('.')) + + set completeslash=backslash + exe "normal oXd\<C-X>\<C-F>" + call assert_equal('Xdir\', getline('.')) + + set completeslash=slash + exe "normal oXd\<C-X>\<C-F>" + call assert_equal('Xdir/', getline('.')) + %bw! + call delete('Xdir', 'rf') + + set noshellslash + set completeslash=slash + call assert_true(stridx(globpath(&rtp, 'syntax/*.vim', 1, 1)[0], '\') != -1) + + let &shellslash = orig_shellslash + set completeslash= +endfunc + +func Test_issue_7021() + CheckMSWindows + + let orig_shellslash = &shellslash + set noshellslash + + set completeslash=slash + call assert_false(expand('~') =~ '/') + + let &shellslash = orig_shellslash + set completeslash= +endfunc + func Test_pum_with_folds_two_tabs() CheckScreendump diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index 31a8b48d37..8e2a987e74 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -199,9 +199,9 @@ func Test_dict_big() try let n = d[1500] catch - let str=substitute(v:exception, '\v(.{14}).*( \d{4}).*', '\1\2', '') + let str = substitute(v:exception, '\v(.{14}).*( "\d{4}").*', '\1\2', '') endtry - call assert_equal('Vim(let):E716: 1500', str) + call assert_equal('Vim(let):E716: "1500"', str) " lookup each items for i in range(1500) diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 7fbf04311d..30239a90c2 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -1,20 +1,16 @@ " Tests for :messages, :echomsg, :echoerr -function Test_messages() +source shared.vim + +func Test_messages() let oldmore = &more try set nomore - " Avoid the "message maintainer" line. - let $LANG = '' - let $LC_ALL = '' - let $LC_MESSAGES = '' - let $LC_COLLATE = '' let arr = map(range(10), '"hello" . v:val') for s in arr echomsg s | redraw endfor - let result = '' " get last two messages redir => result @@ -25,22 +21,17 @@ function Test_messages() " clear messages without last one 1messages clear - redir => result - redraw | messages - redir END - let msg_list = split(result, "\n") + let msg_list = GetMessages() call assert_equal(['hello9'], msg_list) " clear all messages messages clear - redir => result - redraw | messages - redir END - call assert_equal('', result) + let msg_list = GetMessages() + call assert_equal([], msg_list) finally let &more = oldmore endtry -endfunction +endfunc " Patch 7.4.1696 defined the "clearmode()" command for clearing the mode " indicator (e.g., "-- INSERT --") when ":stopinsert" is invoked. Message @@ -74,6 +65,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_mksession.vim b/src/nvim/testdir/test_mksession.vim index 3243edbf55..215065f941 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -470,4 +470,17 @@ func Test_mkvimrc() call delete('Xtestvimrc') endfunc +func Test_scrolloff() + set sessionoptions+=localoptions + setlocal so=1 siso=1 + mksession! Xtest_mks.out + setlocal so=-1 siso=-1 + source Xtest_mks.out + call assert_equal(1, &l:so) + call assert_equal(1, &l:siso) + call delete('Xtest_mks.out') + setlocal so& siso& + set sessionoptions& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 9e8da74db7..10e16f4198 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -267,7 +267,6 @@ func Test_set_errors() call assert_fails('set commentstring=x', 'E537:') call assert_fails('set complete=x', 'E539:') call assert_fails('set statusline=%{', 'E540:') - call assert_fails('set statusline=' . repeat("%p", 81), 'E541:') call assert_fails('set statusline=%(', 'E542:') if has('cursorshape') " This invalid value for 'guicursor' used to cause Vim to crash. diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index cf0af07528..049163890e 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -3602,6 +3602,10 @@ func Test_lhelpgrep_autocmd() au BufEnter * call setqflist([], 'f') augroup END call assert_fails('helpgrep quickfix', 'E925:') + " run the test with a help window already open + help + wincmd w + call assert_fails('helpgrep quickfix', 'E925:') augroup QF_Test au! BufEnter augroup END diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index d20f8d1eef..19a7c6c9d0 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -167,6 +167,75 @@ func Test_set_register() enew! endfunc +func Test_v_register() + enew + call setline(1, 'nothing') + + func s:Put() + let s:register = v:register + exec 'normal! "' .. v:register .. 'P' + endfunc + nnoremap <buffer> <plug>(test) :<c-u>call s:Put()<cr> + nmap <buffer> S <plug>(test) + + let @z = "testz\n" + let @" = "test@\n" + + let s:register = '' + call feedkeys('"_ddS', 'mx') + call assert_equal('test@', getline('.')) " fails before 8.2.0929 + call assert_equal('"', s:register) " fails before 8.2.0929 + + let s:register = '' + call feedkeys('"zS', 'mx') + call assert_equal('z', s:register) + + let s:register = '' + call feedkeys('"zSS', 'mx') + call assert_equal('"', s:register) + + let s:register = '' + call feedkeys('"_S', 'mx') + call assert_equal('_', s:register) + + let s:register = '' + normal "_ddS + call assert_equal('"', s:register) " fails before 8.2.0929 + call assert_equal('test@', getline('.')) " fails before 8.2.0929 + + let s:register = '' + execute 'normal "z:call' "s:Put()\n" + call assert_equal('z', s:register) + call assert_equal('testz', getline('.')) + + " Test operator and omap + let @b = 'testb' + func s:OpFunc(...) + let s:register2 = v:register + endfunc + set opfunc=s:OpFunc + + normal "bg@l + normal S + call assert_equal('"', s:register) " fails before 8.2.0929 + call assert_equal('b', s:register2) + + func s:Motion() + let s:register1 = v:register + normal! l + endfunc + onoremap <buffer> Q :<c-u>call s:Motion()<cr> + + normal "bg@Q + normal S + call assert_equal('"', s:register) + call assert_equal('b', s:register1) + call assert_equal('"', s:register2) + + set opfunc& + bwipe! +endfunc + func Test_ve_blockpaste() new set ve=all diff --git a/src/nvim/testdir/test_ruby.vim b/src/nvim/testdir/test_ruby.vim index 77a42e0a8a..9c74c35049 100644 --- a/src/nvim/testdir/test_ruby.vim +++ b/src/nvim/testdir/test_ruby.vim @@ -1,15 +1,7 @@ " Tests for ruby interface -if !has('ruby') - finish -end - -" Helper function as there is no builtin rubyeval() function similar -" to perleval, luaevel() or pyeval(). -func RubyEval(ruby_expr) - let s = split(execute('ruby print ' . a:ruby_expr), "\n") - return (len(s) == 0) ? '' : s[-1] -endfunc +source check.vim +CheckFeature ruby func Test_ruby_change_buffer() call setline(line('$'), ['1 line 1']) @@ -43,19 +35,19 @@ func Test_rubyfile() call delete(tempfile) endfunc -func Test_set_cursor() +func Test_ruby_set_cursor() " Check that setting the cursor position works. new call setline(1, ['first line', 'second line']) normal gg rubydo $curwin.cursor = [1, 5] call assert_equal([1, 6], [line('.'), col('.')]) - call assert_equal('[1, 5]', RubyEval('$curwin.cursor')) + 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_equal([2, 5], rubyeval('$curwin.cursor')) " call assert_fails('ruby $curwin.cursor = [1]', " \ 'ArgumentError: array length must be 2') @@ -63,34 +55,34 @@ func Test_set_cursor() endfunc " Test buffer.count and buffer.length (number of lines in buffer) -func Test_buffer_count() +func Test_ruby_buffer_count() new call setline(1, ['one', 'two', 'three']) - call assert_equal('3', RubyEval('$curbuf.count')) - call assert_equal('3', RubyEval('$curbuf.length')) + 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() +func Test_ruby_buffer_name() new Xfoo - call assert_equal(expand('%:p'), RubyEval('$curbuf.name')) + call assert_equal(expand('%:p'), rubyeval('$curbuf.name')) bwipe - call assert_equal('', RubyEval('$curbuf.name')) + call assert_equal('', rubyeval('$curbuf.name')) endfunc " Test buffer.number (number of the buffer). -func Test_buffer_number() +func Test_ruby_buffer_number() new - call assert_equal(string(bufnr('%')), RubyEval('$curbuf.number')) + call assert_equal(bufnr('%'), rubyeval('$curbuf.number')) new - call assert_equal(string(bufnr('%')), RubyEval('$curbuf.number')) + call assert_equal(bufnr('%'), rubyeval('$curbuf.number')) %bwipe endfunc " Test buffer.delete({n}) (delete line {n}) -func Test_buffer_delete() +func Test_ruby_buffer_delete() new call setline(1, ['one', 'two', 'three']) ruby $curbuf.delete(2) @@ -104,7 +96,7 @@ func Test_buffer_delete() endfunc " Test buffer.append({str}, str) (append line {str} after line {n}) -func Test_buffer_append() +func Test_ruby_buffer_append() new ruby $curbuf.append(0, 'one') ruby $curbuf.append(1, 'three') @@ -124,11 +116,11 @@ func Test_buffer_append() endfunc " Test buffer.line (get or set the current line) -func Test_buffer_line() +func Test_ruby_buffer_line() new call setline(1, ['one', 'two', 'three']) 2 - call assert_equal('two', RubyEval('$curbuf.line')) + call assert_equal('two', rubyeval('$curbuf.line')) ruby $curbuf.line = 'TWO' call assert_equal(['one', 'TWO', 'three'], getline(1, '$')) @@ -137,20 +129,20 @@ func Test_buffer_line() endfunc " Test buffer.line_number (get current line number) -func Test_buffer_line_number() +func Test_ruby_buffer_line_number() new call setline(1, ['one', 'two', 'three']) 2 - call assert_equal('2', RubyEval('$curbuf.line_number')) + call assert_equal(2, rubyeval('$curbuf.line_number')) bwipe! endfunc -func Test_buffer_get() +func Test_ruby_buffer_get() new call setline(1, ['one', 'two']) - call assert_equal('one', RubyEval('$curbuf[1]')) - call assert_equal('two', RubyEval('$curbuf[2]')) + 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') @@ -160,7 +152,7 @@ func Test_buffer_get() bwipe! endfunc -func Test_buffer_set() +func Test_ruby_buffer_set() new call setline(1, ['one', 'two']) ruby $curbuf[2] = 'TWO' @@ -176,7 +168,7 @@ func Test_buffer_set() endfunc " Test window.width (get or set window height). -func Test_window_height() +func Test_ruby_window_height() new " Test setting window height @@ -184,13 +176,13 @@ func Test_window_height() call assert_equal(2, winheight(0)) " Test getting window height - call assert_equal('2', RubyEval('$curwin.height')) + call assert_equal(2, rubyeval('$curwin.height')) bwipe endfunc " Test window.width (get or set window width). -func Test_window_width() +func Test_ruby_window_width() vnew " Test setting window width @@ -198,13 +190,13 @@ func Test_window_width() call assert_equal(2, winwidth(0)) " Test getting window width - call assert_equal('2', RubyEval('$curwin.width')) + call assert_equal(2, rubyeval('$curwin.width')) bwipe endfunc " Test window.buffer (get buffer object of a window object). -func Test_window_buffer() +func Test_ruby_window_buffer() new Xfoo1 new Xfoo2 ruby $b2 = $curwin.buffer @@ -213,69 +205,69 @@ func Test_window_buffer() 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(string(bufnr('Xfoo1')), RubyEval('$w1.buffer.number')) - call assert_equal(string(bufnr('Xfoo2')), RubyEval('$w2.buffer.number')) + " 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') - " call assert_equal(cw, RubyEval('Vim::Window.current')) +func Test_ruby_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() +func Test_ruby_Vim_window_count() new Xfoo1 new Xfoo2 split - call assert_equal('4', RubyEval('Vim::Window.count')) + call assert_equal(4, rubyeval('Vim::Window.count')) %bwipe - call assert_equal('1', RubyEval('Vim::Window.count')) + call assert_equal(1, rubyeval('Vim::Window.count')) endfunc " Test Vim::Window[n] (get window object of window n) -func Test_Vim_window_get() +func Test_ruby_Vim_window_get() new Xfoo1 new Xfoo2 - call assert_match('Xfoo2$', RubyEval('Vim::Window[0].buffer.name')) + call assert_match('Xfoo2$', rubyeval('Vim::Window[0].buffer.name')) wincmd j - call assert_match('Xfoo1$', RubyEval('Vim::Window[1].buffer.name')) + call assert_match('Xfoo1$', rubyeval('Vim::Window[1].buffer.name')) wincmd j - call assert_equal('', RubyEval('Vim::Window[2].buffer.name')) + 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') - " call assert_equal(cb, RubyEval('Vim::Buffer.current')) +func Test_ruby_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() +func Test_ruby_Vim_buffer_count() new Xfoo1 new Xfoo2 - call assert_equal('3', RubyEval('Vim::Buffer.count')) + call assert_equal(3, rubyeval('Vim::Buffer.count')) %bwipe - call assert_equal('1', RubyEval('Vim::Buffer.count')) + 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() +func Test_ruby_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_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 @@ -283,7 +275,7 @@ endfunc " Test Vim::command({cmd}) (execute a Ex command)) " Test Vim::command({cmd}) -func Test_Vim_command() +func Test_ruby_Vim_command() new call setline(1, ['one', 'two', 'three', 'four']) ruby Vim::command('2,3d') @@ -292,7 +284,7 @@ func Test_Vim_command() endfunc " Test Vim::set_option (set a vim option) -func Test_Vim_set_option() +func Test_ruby_Vim_set_option() call assert_equal(0, &number) ruby Vim::set_option('number') call assert_equal(1, &number) @@ -300,37 +292,39 @@ func Test_Vim_set_option() call assert_equal(0, &number) endfunc -func Test_Vim_evaluate() - call assert_equal('123', RubyEval('Vim::evaluate("123")')) +func Test_ruby_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_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')) + if has('float') + call assert_equal(1.23, rubyeval('Vim::evaluate("1.23")')) + call assert_equal('Float', rubyeval('Vim::evaluate("1.23").class')) + endif - call assert_equal('foo', RubyEval('Vim::evaluate("\"foo\"")')) - call assert_equal('String', RubyEval('Vim::evaluate("\"foo\"").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('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({'1': 2}, rubyeval('Vim::evaluate("{1:2}")')) + call assert_equal('Hash', rubyeval('Vim::evaluate("{1:2}").class')) - call assert_equal('', RubyEval('Vim::evaluate("v:null")')) - call assert_equal('NilClass', RubyEval('Vim::evaluate("v:null").class')) + call assert_equal(v:null, rubyeval('Vim::evaluate("v:null")')) + call assert_equal('NilClass', rubyeval('Vim::evaluate("v:null").class')) - " call assert_equal('', RubyEval('Vim::evaluate("v:none")')) - " call assert_equal('NilClass', RubyEval('Vim::evaluate("v:none").class')) + " call assert_equal(v:null, rubyeval('Vim::evaluate("v:none")')) + " call assert_equal('NilClass', rubyeval('Vim::evaluate("v:none").class')) - call assert_equal('true', RubyEval('Vim::evaluate("v:true")')) - call assert_equal('TrueClass', RubyEval('Vim::evaluate("v:true").class')) - call assert_equal('false', RubyEval('Vim::evaluate("v:false")')) - call assert_equal('FalseClass',RubyEval('Vim::evaluate("v:false").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() +func Test_ruby_Vim_evaluate_list() call setline(line('$'), ['2 line 2']) ruby Vim.command("normal /^2\n") let l = ["abc", "def"] @@ -344,7 +338,7 @@ EOF call assert_equal('abc/def', getline('$')) endfunc -func Test_Vim_evaluate_dict() +func Test_ruby_Vim_evaluate_dict() let d = {'a': 'foo', 'b': 123} redir => l:out ruby d = Vim.evaluate("d"); print d @@ -353,34 +347,47 @@ func Test_Vim_evaluate_dict() endfunc " Test Vim::message({msg}) (display message {msg}) -func Test_Vim_message() +func Test_ruby_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() - ruby print "Hello World!" - let messages = split(execute('message'), "\n") - call assert_equal('Hello World!', messages[-1]) +func Test_ruby_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() +func Test_ruby_p() ruby p 'Just a test' - let messages = split(execute('message'), "\n") + let messages = GetMessages() 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)')) + 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('true', RubyEval('p() == nil')) + call assert_equal(v:true, rubyeval('p() == nil')) - let messages = split(execute('message'), "\n") + let messages = GetMessages() call assert_equal(0, len(messages)) endfunc diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index 7efd181d04..4e38f7ebd8 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -354,6 +354,21 @@ func Test_statusline() delfunc GetNested delfunc GetStatusLine + " Test statusline works with 80+ items + function! StatusLabel() + redrawstatus + return '[label]' + endfunc + let statusline = '%{StatusLabel()}' + for i in range(150) + let statusline .= '%#TabLine' . (i % 2 == 0 ? 'Fill' : 'Sel') . '#' . string(i)[0] + endfor + let &statusline = statusline + redrawstatus + set statusline& + delfunc StatusLabel + + " Check statusline in current and non-current window " with the 'fillchars' option. set fillchars=stl:^,stlnc:=,vert:\|,fold:-,diff:- diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 2ef9bf5a2e..62d7dc8b18 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -316,7 +316,13 @@ static void terminfo_start(UI *ui) #ifdef WIN32 uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW); #else - uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO); + int retry_count = 10; + // A signal may cause uv_tty_set_mode() to fail (e.g., SIGCONT). Retry a + // few times. #12322 + while (uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO) == UV_EINTR + && retry_count > 0) { + retry_count--; + } #endif } else { uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0); @@ -1102,6 +1108,15 @@ static void tui_set_mode(UI *ui, ModeShape mode) static void tui_mode_change(UI *ui, String mode, Integer mode_idx) { TUIData *data = ui->data; +#ifdef UNIX + // If stdin is not a TTY, the LHS of pipe may change the state of the TTY + // after calling uv_tty_set_mode. So, set the mode of the TTY again here. + // #13073 + if (data->is_starting && data->input.in_fd == STDERR_FILENO) { + uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_NORMAL); + uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO); + } +#endif tui_set_mode(ui, (ModeShape)mode_idx); data->is_starting = false; // mode entered, no longer starting data->showing_mode = (ModeShape)mode_idx; diff --git a/src/nvim/types.h b/src/nvim/types.h index a3d87f35ca..17f7e16740 100644 --- a/src/nvim/types.h +++ b/src/nvim/types.h @@ -21,7 +21,7 @@ typedef int handle_T; // absent callback etc. typedef int LuaRef; -typedef uint64_t NS; +typedef handle_T NS; typedef struct expand expand_T; diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index e582d8f859..325ad31eaf 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -26,6 +26,7 @@ #include "nvim/screen.h" #include "nvim/syntax.h" #include "nvim/api/private/helpers.h" +#include "nvim/lua/executor.h" #include "nvim/os/os.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 6c5a6cdb46..a8b8f7aa50 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2455,7 +2455,7 @@ static void u_undo_end( { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_buffer == curbuf && wp->w_p_cole > 0) { - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } } } diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 832703e83d..873bef32d3 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -259,7 +259,6 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() #define PERROR(msg) (void) emsgf("%s: %s", msg, strerror(errno)) #define SHOWCMD_COLS 10 // columns needed by shown command -#define STL_MAX_ITEM 80 // max nr of %<flag> in statusline #include "nvim/path.h" diff --git a/src/nvim/window.c b/src/nvim/window.c index bbc039d151..47b6b7e713 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -606,7 +606,7 @@ win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err) win_config_float(wp, fconfig); wp->w_pos_changed = true; - redraw_win_later(wp, VALID); + redraw_later(wp, VALID); return wp; } @@ -679,7 +679,7 @@ void win_config_float(win_T *wp, FloatConfig fconfig) wp->w_pos_changed = true; if (change_external) { wp->w_hl_needs_update = true; - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } } @@ -764,7 +764,7 @@ static void ui_ext_win_position(win_T *wp) wp->w_grid.focusable = wp->w_float_config.focusable; if (!valid) { wp->w_grid.valid = false; - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } } } else { @@ -1492,8 +1492,8 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // Both windows need redrawing. Update all status lines, in case they // show something related to the window count or position. - redraw_win_later(wp, NOT_VALID); - redraw_win_later(oldwin, NOT_VALID); + redraw_later(wp, NOT_VALID); + redraw_later(oldwin, NOT_VALID); status_redraw_all(); if (need_status) { @@ -1823,8 +1823,8 @@ static void win_exchange(long Prenum) (void)win_comp_pos(); /* recompute window positions */ win_enter(wp, true); - redraw_later(NOT_VALID); - redraw_win_later(wp, NOT_VALID); + redraw_later(curwin, NOT_VALID); + redraw_later(wp, NOT_VALID); } // rotate windows: if upwards true the second window becomes the first one @@ -1996,8 +1996,8 @@ void win_move_after(win_T *win1, win_T *win2) win_append(win2, win1); frame_append(win2->w_frame, win1->w_frame); - (void)win_comp_pos(); /* recompute w_winrow for all windows */ - redraw_later(NOT_VALID); + (void)win_comp_pos(); // recompute w_winrow for all windows + redraw_later(curwin, NOT_VALID); } win_enter(win1, false); @@ -3635,7 +3635,7 @@ void curwin_init(void) void win_init_empty(win_T *wp) { - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); wp->w_lines_valid = 0; wp->w_cursor.lnum = 1; wp->w_curswant = wp->w_cursor.col = 0; @@ -4049,7 +4049,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au prevwin = next_prevwin; last_status(false); // status line may appear or disappear - (void)win_comp_pos(); // recompute w_winrow for all windows + const int row = win_comp_pos(); // recompute w_winrow for all windows diff_need_scrollbind = true; /* The tabpage line may have appeared or disappeared, may need to resize @@ -4060,11 +4060,20 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au clear_cmdline = true; } p_ch = curtab->tp_ch_used; - if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow - )) + + // When cmdheight is changed in a tab page with '<C-w>-', cmdline_row is + // changed but p_ch and tp_ch_used are not changed. Thus we also need to + // check cmdline_row. + if ((row < cmdline_row) && (cmdline_row <= Rows - p_ch)) { + clear_cmdline = true; + } + + if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow)) { shell_new_rows(); - if (curtab->tp_old_Columns != Columns && starting == 0) - shell_new_columns(); /* update window widths */ + } + if (curtab->tp_old_Columns != Columns && starting == 0) { + shell_new_columns(); // update window widths + } lastused_tabpage = old_curtab; @@ -4587,10 +4596,11 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, } maketitle(); - curwin->w_redr_status = TRUE; - redraw_tabline = TRUE; - if (restart_edit) - redraw_later(VALID); /* causes status line redraw */ + curwin->w_redr_status = true; + redraw_tabline = true; + if (restart_edit) { + redraw_later(curwin, VALID); // causes status line redraw + } if (HL_ATTR(HLF_INACTIVE) || (prevwin && prevwin->w_hl_ids[HLF_INACTIVE]) @@ -5080,7 +5090,7 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col) /* position changed, redraw */ wp->w_winrow = *row; wp->w_wincol = *col; - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); wp->w_redr_status = true; wp->w_pos_changed = true; } @@ -5133,7 +5143,7 @@ void win_setheight_win(int height, win_T *win) if (win->w_floating) { win->w_float_config.height = height; win_config_float(win, win->w_float_config); - redraw_win_later(win, NOT_VALID); + redraw_later(win, NOT_VALID); } else { frame_setheight(win->w_frame, height + win->w_status_height); @@ -5336,7 +5346,7 @@ void win_setwidth_win(int width, win_T *wp) if (wp->w_floating) { wp->w_float_config.width = width; win_config_float(wp, wp->w_float_config); - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } else { frame_setwidth(wp->w_frame, width + wp->w_vsep_width); @@ -5869,8 +5879,8 @@ void scroll_to_fraction(win_T *wp, int prev_height) } win_comp_scroll(wp); - redraw_win_later(wp, SOME_VALID); - wp->w_redr_status = TRUE; + redraw_later(wp, SOME_VALID); + wp->w_redr_status = true; invalidate_botline_win(wp); } @@ -5909,7 +5919,7 @@ void win_set_inner_size(win_T *wp) if (!exiting) { scroll_to_fraction(wp, prev_height); } - redraw_win_later(wp, NOT_VALID); // SOME_VALID?? + redraw_later(wp, NOT_VALID); // SOME_VALID?? } if (width != wp->w_width_inner) { @@ -5921,7 +5931,7 @@ void win_set_inner_size(win_T *wp) update_topline(); curs_columns(true); // validate w_wrow } - redraw_win_later(wp, NOT_VALID); + redraw_later(wp, NOT_VALID); } if (wp->w_buffer->terminal) { @@ -6765,7 +6775,7 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, prev->next = m; m->next = cur; - redraw_win_later(wp, rtype); + redraw_later(wp, rtype); return id; fail: @@ -6823,7 +6833,7 @@ int match_delete(win_T *wp, int id, int perr) rtype = VALID; } xfree(cur); - redraw_win_later(wp, rtype); + redraw_later(wp, rtype); return 0; } @@ -6841,7 +6851,7 @@ void clear_matches(win_T *wp) xfree(wp->w_match_head); wp->w_match_head = m; } - redraw_win_later(wp, SOME_VALID); + redraw_later(wp, SOME_VALID); } /* 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/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 9b0668f9e6..3bfae5f3d7 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -7,6 +7,7 @@ local ok = helpers.ok local eq = helpers.eq local matches = helpers.matches local eval = helpers.eval +local exec_lua = helpers.exec_lua local feed = helpers.feed local funcs = helpers.funcs local mkdir = helpers.mkdir @@ -305,6 +306,27 @@ describe('startup', function() '+q' }) eq('[\'+q\'] 1', out) end) + + local function pack_clear(cmd) + clear('--cmd', 'set packpath=test/functional/fixtures', '--cmd', cmd) + end + + + it("handles &packpath during startup", function() + pack_clear [[ let g:x = bar#test() ]] + eq(-3, eval 'g:x') + + pack_clear [[ lua _G.y = require'bar'.doit() ]] + eq(9003, exec_lua [[ return _G.y ]]) + end) + + it("handles :packadd during startup", function() + pack_clear [[ packadd! bonus | let g:x = bonus#secret() ]] + eq('halloj', eval 'g:x') + + pack_clear [[ packadd! bonus | lua _G.y = require'bonus'.launch() ]] + eq('CPE 1704 TKS', exec_lua [[ return _G.y ]]) + end) end) describe('sysinit', function() diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index db0a706319..fa8f7d873f 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -132,7 +132,7 @@ describe('NULL', function() end) describe('dict', function() it('does not crash when indexing NULL dict', function() - eq('\nE716: Key not present in Dictionary: test\nE15: Invalid expression: v:_null_dict.test', + eq('\nE716: Key not present in Dictionary: "test"\nE15: Invalid expression: v:_null_dict.test', redir_exec('echo v:_null_dict.test')) end) null_expr_test('makes extend error out', 'extend(D, {})', 'E742: Cannot change value of extend() argument', 0) diff --git a/test/functional/ex_cmds/packadd_spec.lua b/test/functional/ex_cmds/packadd_spec.lua deleted file mode 100644 index 2b0810cf9b..0000000000 --- a/test/functional/ex_cmds/packadd_spec.lua +++ /dev/null @@ -1,74 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local eq = helpers.eq -local exec_lua = helpers.exec_lua - -describe('packadd', function() - before_each(function() - -- Primarily taken from test/functional/legacy/packadd_spec.lua - clear() - exec_lua [[ - TopDirectory = vim.fn.expand(vim.fn.getcwd() .. '/Xdir_lua') - PlugDirectory = TopDirectory .. '/pack/mine/opt/mytest' - - vim.o.packpath = TopDirectory - - function FindPathsContainingDir(dir) - return vim.fn.filter( - vim.split(package.path, ';'), - function(k, v) - return string.find(v, 'mytest') ~= nil - end - ) - end - ]] - end) - - after_each(function() - exec_lua [[ - vim.fn.delete(TopDirectory, 'rf') - ]] - end) - - it('should immediately update package.path in lua', function() - local count_of_paths = exec_lua [[ - vim.fn.mkdir(PlugDirectory .. '/lua/', 'p') - - local num_paths_before = #FindPathsContainingDir('mytest') - - vim.cmd("packadd mytest") - - local num_paths_after = #FindPathsContainingDir('mytest') - - return { num_paths_before, num_paths_after } - ]] - - eq({0, 2}, count_of_paths) - end) - - it('should immediately update package.path in lua even if lua directory does not exist', function() - local count_of_paths = exec_lua [[ - vim.fn.mkdir(PlugDirectory .. '/plugin/', 'p') - - local num_paths_before = #FindPathsContainingDir('mytest') - - vim.cmd("packadd mytest") - - local num_paths_after = #FindPathsContainingDir('mytest') - - return { num_paths_before, num_paths_after } - ]] - - eq({0, 2}, count_of_paths) - end) - - it('should error for invalid paths', function() - local count_of_paths = exec_lua [[ - local ok, err = pcall(vim.cmd, "packadd asdf") - return ok - ]] - - eq(false, count_of_paths) - end) -end) 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/fixtures/pack/foo/opt/bonus/autoload/bonus.vim b/test/functional/fixtures/pack/foo/opt/bonus/autoload/bonus.vim new file mode 100644 index 0000000000..5ed8b1b887 --- /dev/null +++ b/test/functional/fixtures/pack/foo/opt/bonus/autoload/bonus.vim @@ -0,0 +1,3 @@ +func bonus#secret() + return "halloj" +endfunc diff --git a/test/functional/fixtures/pack/foo/opt/bonus/lua/bonus.lua b/test/functional/fixtures/pack/foo/opt/bonus/lua/bonus.lua new file mode 100644 index 0000000000..52cb0bc118 --- /dev/null +++ b/test/functional/fixtures/pack/foo/opt/bonus/lua/bonus.lua @@ -0,0 +1 @@ +return {launch=function() return "CPE 1704 TKS" end} diff --git a/test/functional/fixtures/pack/foo/start/bar/autoload/bar.vim b/test/functional/fixtures/pack/foo/start/bar/autoload/bar.vim new file mode 100644 index 0000000000..405e7be71c --- /dev/null +++ b/test/functional/fixtures/pack/foo/start/bar/autoload/bar.vim @@ -0,0 +1,3 @@ +func bar#test() + return -3 +endfunc diff --git a/test/functional/fixtures/pack/foo/start/bar/lua/bar.lua b/test/functional/fixtures/pack/foo/start/bar/lua/bar.lua new file mode 100644 index 0000000000..a7e9a61e35 --- /dev/null +++ b/test/functional/fixtures/pack/foo/start/bar/lua/bar.lua @@ -0,0 +1 @@ +return {doit=function() return 9003 end} diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 0bd378d832..d85a6a3cfe 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -751,6 +751,14 @@ module.curbufmeths = module.create_callindex(module.curbuf) module.curwinmeths = module.create_callindex(module.curwin) module.curtabmeths = module.create_callindex(module.curtab) +function module.exec(code) + return module.meths.exec(code, false) +end + +function module.exec_capture(code) + return module.meths.exec(code, true) +end + function module.exec_lua(code, ...) return module.meths.exec_lua(code, {...}) end diff --git a/test/functional/legacy/055_list_and_dict_types_spec.lua b/test/functional/legacy/055_list_and_dict_types_spec.lua index 91ba8bb106..4d71a526c1 100644 --- a/test/functional/legacy/055_list_and_dict_types_spec.lua +++ b/test/functional/legacy/055_list_and_dict_types_spec.lua @@ -229,7 +229,7 @@ describe('list and dictionary types', function() try let n = d[1500] catch - $put =substitute(v:exception, '\v(.{14}).*( \d{4}).*', '\1\2', '') + $put = substitute(v:exception, '\v(.{14}).*( \"\d{4}\").*', '\1\2', '') endtry " Lookup each items. for i in range(1500) @@ -260,7 +260,7 @@ describe('list and dictionary types', function() expect([[ 3000 2900 2001 1600 1501 - Vim(let):E716: 1500 + Vim(let):E716: "1500" NONE 2999 33=999 {'33': 999}]]) diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua new file mode 100644 index 0000000000..9ebe9aeb91 --- /dev/null +++ b/test/functional/legacy/cmdline_spec.lua @@ -0,0 +1,66 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local feed = helpers.feed +local feed_command = helpers.feed_command +local source = helpers.source + +describe('cmdline', function() + before_each(clear) + + it('is cleared when switching tabs', function() + local screen = Screen.new(30, 10) + screen:attach() + feed_command([[call setline(1, range(30))]]) + screen:expect([[ + ^0 | + 1 | + 2 | + 3 | + 4 | + 5 | + 6 | + 7 | + 8 | + :call setline(1, range(30)) | + ]]) + feed([[:tabnew<cr><C-w>-<C-w>-gtgt]]) + screen:expect([[ + + [No Name] [No Name] X| + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + 6 | + 7 | + | + ]]) + end) + + it('prints every executed Ex command if verbose >= 16', function() + local screen = Screen.new(60, 12) + screen:attach() + source([[ + command DoSomething echo 'hello' |set ts=4 |let v = '123' |echo v + call feedkeys("\r", 't') " for the hit-enter prompt + set verbose=20 + ]]) + feed_command('DoSomething') + screen:expect([[ + | + ~ | + ~ | + | + Executing: DoSomething | + Executing: echo 'hello' |set ts=4 |let v = '123' |echo v | + hello | + Executing: set ts=4 |let v = '123' |echo v | + Executing: let v = '123' |echo v | + Executing: echo v | + 123 | + Press ENTER or type command to continue^ | + ]]) + end) +end) diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index 1bccc02847..267c759faf 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -3,7 +3,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local eq = helpers.eq -local neq = helpers.neq local NIL = helpers.NIL local feed = helpers.feed local clear = helpers.clear @@ -13,7 +12,6 @@ local iswin = helpers.iswin local command = helpers.command local write_file = helpers.write_file local redir_exec = helpers.redir_exec -local alter_slashes = helpers.alter_slashes local exec_lua = helpers.exec_lua local screen @@ -285,119 +283,6 @@ describe('debug.debug', function() end) end) -describe('package.path/package.cpath', function() - local sl = alter_slashes - - local function get_new_paths(sufs, runtimepaths) - runtimepaths = runtimepaths or meths.list_runtime_paths() - local new_paths = {} - local sep = package.config:sub(1, 1) - for _, v in ipairs(runtimepaths) do - for _, suf in ipairs(sufs) do - new_paths[#new_paths + 1] = v .. sep .. 'lua' .. suf - end - end - return new_paths - end - local function eval_lua(expr, ...) - return meths.exec_lua('return '..expr, {...}) - end - local function set_path(which, value) - return exec_lua('package[select(1, ...)] = select(2, ...)', which, value) - end - - it('contains directories from &runtimepath on first invocation', function() - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str)) - - local new_cpaths = get_new_paths(iswin() and {'\\?.dll'} or {'/?.so'}) - local new_cpaths_str = table.concat(new_cpaths, ';') - eq(new_cpaths_str, eval_lua('package.cpath'):sub(1, #new_cpaths_str)) - end) - it('puts directories from &runtimepath always at the start', function() - meths.set_option('runtimepath', 'a,b') - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a', 'b'}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str)) - - set_path('path', sl'foo/?.lua;foo/?/init.lua;' .. new_paths_str) - - neq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str)) - - command('set runtimepath+=c') - new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a', 'b', 'c'}) - new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str)) - end) - it('understands uncommon suffixes', function() - set_path('cpath', './?/foo/bar/baz/x.nlua') - meths.set_option('runtimepath', 'a') - local new_paths = get_new_paths({'/?/foo/bar/baz/x.nlua'}, {'a'}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str)) - - set_path('cpath', './yyy?zzz/x') - meths.set_option('runtimepath', 'b') - new_paths = get_new_paths({'/yyy?zzz/x'}, {'b'}) - new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str)) - - set_path('cpath', './yyy?zzz/123?ghi/x') - meths.set_option('runtimepath', 'b') - new_paths = get_new_paths({'/yyy?zzz/123?ghi/x'}, {'b'}) - new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str)) - end) - it('preserves empty items', function() - local many_empty_path = ';;;;;;' - local many_empty_cpath = ';;;;;;./?.luaso' - set_path('path', many_empty_path) - set_path('cpath', many_empty_cpath) - meths.set_option('runtimepath', 'a') - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a'}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str .. ';' .. many_empty_path, eval_lua('package.path')) - local new_cpaths = get_new_paths({'/?.luaso'}, {'a'}) - local new_cpaths_str = table.concat(new_cpaths, ';') - eq(new_cpaths_str .. ';' .. many_empty_cpath, eval_lua('package.cpath')) - end) - it('preserves empty value', function() - set_path('path', '') - meths.set_option('runtimepath', 'a') - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a'}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str .. ';', eval_lua('package.path')) - end) - it('purges out all additions if runtimepath is set to empty', function() - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}) - local new_paths_str = table.concat(new_paths, ';') - local path = eval_lua('package.path') - eq(new_paths_str, path:sub(1, #new_paths_str)) - - local new_cpaths = get_new_paths(iswin() and {'\\?.dll'} or {'/?.so'}) - local new_cpaths_str = table.concat(new_cpaths, ';') - local cpath = eval_lua('package.cpath') - eq(new_cpaths_str, cpath:sub(1, #new_cpaths_str)) - - meths.set_option('runtimepath', '') - eq(path:sub(#new_paths_str + 2, -1), eval_lua('package.path')) - eq(cpath:sub(#new_cpaths_str + 2, -1), eval_lua('package.cpath')) - end) - it('works with paths with escaped commas', function() - meths.set_option('runtimepath', '\\,') - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {','}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str)) - end) - it('ignores paths with semicolons', function() - meths.set_option('runtimepath', 'foo;bar,\\,') - local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {','}) - local new_paths_str = table.concat(new_paths, ';') - eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str)) - end) -end) - describe('os.getenv', function() it('returns nothing for undefined env var', function() eq(NIL, funcs.luaeval('os.getenv("XTEST_1")')) diff --git a/test/functional/lua/treesitter_spec.lua b/test/functional/lua/treesitter_spec.lua index 3526b64395..9eb5c8b1dd 100644 --- a/test/functional/lua/treesitter_spec.lua +++ b/test/functional/lua/treesitter_spec.lua @@ -80,13 +80,6 @@ describe('treesitter API with C parser', function() eq({1,2,1,12}, exec_lua("return {descendant:range()}")) eq("(declaration type: (primitive_type) declarator: (init_declarator declarator: (identifier) value: (number_literal)))", exec_lua("return descendant:sexpr()")) - eq(true, exec_lua("return child == child")) - -- separate lua object, but represents same node - eq(true, exec_lua("return child == root:child(0)")) - eq(false, exec_lua("return child == descendant2")) - eq(false, exec_lua("return child == nil")) - eq(false, exec_lua("return child == tree")) - feed("2G7|ay") exec_lua([[ tree2 = parser:parse() @@ -98,6 +91,21 @@ describe('treesitter API with C parser', function() eq("<node declaration>", exec_lua("return tostring(descendant2)")) eq({1,2,1,13}, exec_lua("return {descendant2:range()}")) + eq(true, exec_lua("return child == child")) + -- separate lua object, but represents same node + eq(true, exec_lua("return child == root:child(0)")) + eq(false, exec_lua("return child == descendant2")) + eq(false, exec_lua("return child == nil")) + eq(false, exec_lua("return child == tree")) + + eq("string", exec_lua("return type(child:id())")) + eq(true, exec_lua("return child:id() == child:id()")) + -- separate lua object, but represents same node + eq(true, exec_lua("return child:id() == root:child(0):id()")) + eq(false, exec_lua("return child:id() == descendant2:id()")) + eq(false, exec_lua("return child:id() == nil")) + eq(false, exec_lua("return child:id() == tree")) + -- orginal tree did not change eq({1,2,1,12}, exec_lua("return {descendant:range()}")) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index f07a2d18a2..73f3fe5d0c 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -270,6 +270,76 @@ 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.callbacks['textDocument/hover'] = function(err, method) + vim.lsp._last_lsp_callback = { err = err; method = method } + end + 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) + local lsp_cb_call = exec_lua("return vim.lsp._last_lsp_callback") + eq("fake-error", lsp_cb_call.err) + eq("textDocument/hover", lsp_cb_call.method) end; on_exit = function(code, signal) eq(0, code, "exit code", fake_lsp_logfile) 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/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 01f0d8a4d7..21c01b3458 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -3,7 +3,6 @@ local Screen = require('test.functional.ui.screen') local clear, feed = helpers.clear, helpers.feed local source = helpers.source local command = helpers.command -local feed_command = helpers.feed_command local function new_screen(opt) local screen = Screen.new(25, 5) @@ -843,34 +842,3 @@ describe('cmdline redraw', function() ]], unchanged=true} end) end) - -describe('cmdline', function() - before_each(function() - clear() - end) - - it('prints every executed Ex command if verbose >= 16', function() - local screen = Screen.new(50, 12) - screen:attach() - source([[ - command DoSomething echo 'hello' |set ts=4 |let v = '123' |echo v - call feedkeys("\r", 't') " for the hit-enter prompt - set verbose=20 - ]]) - feed_command('DoSomething') - screen:expect([[ - | - ~ | - | - Executing: DoSomething | - Executing: echo 'hello' |set ts=4 |let v = '123' || - echo v | - hello | - Executing: set ts=4 |let v = '123' |echo v | - Executing: let v = '123' |echo v | - Executing: echo v | - 123 | - Press ENTER or type command to continue^ | - ]]) - end) -end) diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 304c5aecb1..4182090732 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -5,20 +5,32 @@ local clear = helpers.clear local feed = helpers.feed local insert = helpers.insert local exec_lua = helpers.exec_lua +local exec = helpers.exec local expect_events = helpers.expect_events +local meths = helpers.meths -describe('decorations provider', function() +describe('decorations providers', function() local screen before_each(function() clear() screen = Screen.new(40, 8) screen:attach() - screen:set_default_attr_ids({ - [1] = {bold=true, foreground=Screen.colors.Blue}, - }) + screen:set_default_attr_ids { + [1] = {bold=true, foreground=Screen.colors.Blue}; + [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}; + [3] = {foreground = Screen.colors.Brown}; + [4] = {foreground = Screen.colors.Blue1}; + [5] = {foreground = Screen.colors.Magenta}; + [6] = {bold = true, foreground = Screen.colors.Brown}; + [7] = {background = Screen.colors.Gray90}; + [8] = {bold = true, reverse = true}; + [9] = {reverse = true}; + [10] = {italic = true, background = Screen.colors.Magenta}; + [11] = {foreground = Screen.colors.Red, background = tonumber('0x005028')}; + } end) - local mudholland = [[ + local mulholland = [[ // just to see if there was an accident // on Mulholland Drive try_start(); @@ -28,21 +40,21 @@ describe('decorations provider', function() restore_buffer(&save_buf); ]] local function setup_provider(code) - exec_lua ([[ + return exec_lua ([[ local a = vim.api - test1 = a.nvim_create_namespace "test1" + _G.ns1 = a.nvim_create_namespace "ns1" ]] .. (code or [[ beamtrace = {} - function on_do(kind, ...) + local function on_do(kind, ...) table.insert(beamtrace, {kind, ...}) end ]]) .. [[ - a.nvim_set_decoration_provider( - test1, { + a.nvim_set_decoration_provider(_G.ns1, { on_start = on_do; on_buf = on_do; on_win = on_do; on_line = on_do; on_end = on_do; }) + return _G.ns1 ]]) end @@ -51,8 +63,8 @@ describe('decorations provider', function() expect_events(expected, actual, "beam trace") end - it('leaves a trace', function() - insert(mudholland) + it('leave a trace', function() + insert(mulholland) setup_provider() @@ -99,11 +111,12 @@ describe('decorations provider', function() } end) - it('single provider', function() - insert(mudholland) + it('can have single provider', function() + insert(mulholland) setup_provider [[ local hl = a.nvim_get_hl_id_by_name "ErrorMsg" - function do_it(event, ...) + local test_ns = a.nvim_create_namespace "mulholland" + function on_do(event, ...) if event == "line" then local win, buf, line = ... a.nvim_buf_set_extmark(buf, test_ns, line, line, @@ -114,5 +127,104 @@ describe('decorations provider', function() end end ]] + + screen:expect{grid=[[ + {2:/}/ just to see if there was an accident | + /{2:/} on Mulholland Drive | + tr{2:y}_start(); | + buf{2:r}ef_T save_buf; | + swit{2:c}h_buffer(&save_buf, buf); | + posp {2:=} getmark(mark, false); | + restor{2:e}_buffer(&save_buf);^ | + | + ]]} + end) + + it('can predefine highlights', function() + screen:try_resize(40, 16) + insert(mulholland) + exec [[ + 3 + set ft=c + syntax on + set number cursorline + split + ]] + local ns1 = setup_provider() + + for k,v in pairs { + LineNr = {italic=true, bg="Magenta"}; + Comment = {fg="#FF0000", bg = 80*256+40}; + CursorLine = {link="ErrorMsg"}; + } do meths.set_hl(ns1, k, v) end + + screen:expect{grid=[[ + {3: 1 }{4:// just to see if there was an accid}| + {3: }{4:ent} | + {3: 2 }{4:// on Mulholland Drive} | + {6: 3 }{7:^try_start(); }| + {3: 4 }bufref_T save_buf; | + {3: 5 }switch_buffer(&save_buf, buf); | + {3: 6 }posp = getmark(mark, {5:false}); | + {8:[No Name] [+] }| + {3: 2 }{4:// on Mulholland Drive} | + {6: 3 }{7:try_start(); }| + {3: 4 }bufref_T save_buf; | + {3: 5 }switch_buffer(&save_buf, buf); | + {3: 6 }posp = getmark(mark, {5:false}); | + {3: 7 }restore_buffer(&save_buf); | + {9:[No Name] [+] }| + | + ]]} + + meths.set_hl_ns(ns1) + screen:expect{grid=[[ + {10: 1 }{11:// just to see if there was an accid}| + {10: }{11:ent} | + {10: 2 }{11:// on Mulholland Drive} | + {6: 3 }{2:^try_start(); }| + {10: 4 }bufref_T save_buf; | + {10: 5 }switch_buffer(&save_buf, buf); | + {10: 6 }posp = getmark(mark, {5:false}); | + {8:[No Name] [+] }| + {10: 2 }{11:// on Mulholland Drive} | + {6: 3 }{2:try_start(); }| + {10: 4 }bufref_T save_buf; | + {10: 5 }switch_buffer(&save_buf, buf); | + {10: 6 }posp = getmark(mark, {5:false}); | + {10: 7 }restore_buffer(&save_buf); | + {9:[No Name] [+] }| + | + ]]} + + exec_lua [[ + local a = vim.api + local thewin = a.nvim_get_current_win() + local ns2 = a.nvim_create_namespace 'ns2' + a.nvim_set_decoration_provider (ns2, { + on_win = function (_, win, buf) + a.nvim_set_hl_ns(win == thewin and _G.ns1 or ns2) + end; + }) + ]] + screen:expect{grid=[[ + {10: 1 }{11:// just to see if there was an accid}| + {10: }{11:ent} | + {10: 2 }{11:// on Mulholland Drive} | + {6: 3 }{2:^try_start(); }| + {10: 4 }bufref_T save_buf; | + {10: 5 }switch_buffer(&save_buf, buf); | + {10: 6 }posp = getmark(mark, {5:false}); | + {8:[No Name] [+] }| + {3: 2 }{4:// on Mulholland Drive} | + {6: 3 }{7:try_start(); }| + {3: 4 }bufref_T save_buf; | + {3: 5 }switch_buffer(&save_buf, buf); | + {3: 6 }posp = getmark(mark, {5:false}); | + {3: 7 }restore_buffer(&save_buf); | + {9:[No Name] [+] }| + | + ]]} + end) end) 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/screen.lua b/test/functional/ui/screen.lua index bf979e89f4..8fa9fcc42f 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -1289,7 +1289,7 @@ local function fmt_ext_state(name, state) for k,v in pairs(state) do str = (str.." ["..k.."] = {win = {id = "..v.win.id.."}, topline = " ..v.topline..", botline = "..v.botline..", curline = "..v.curline - ..", curcol = "..v.curcol.."},\n") + ..", curcol = "..v.curcol.."};\n") end return str .. "}" else @@ -1316,7 +1316,7 @@ function Screen:print_snapshot(attrs, ignore) dict = "{"..self:_pprint_attrs(a).."}" end local keyval = (type(i) == "number") and "["..tostring(i).."]" or i - table.insert(attrstrs, " "..keyval.." = "..dict..",") + table.insert(attrstrs, " "..keyval.." = "..dict..";") end attrstr = (", attr_ids={\n"..table.concat(attrstrs, "\n").."\n}") end diff --git a/test/unit/buffer_spec.lua b/test/unit/buffer_spec.lua index bf4e5a0e6d..3692e19379 100644 --- a/test/unit/buffer_spec.lua +++ b/test/unit/buffer_spec.lua @@ -212,7 +212,7 @@ describe('buffer functions', function() describe('build_stl_str_hl', function() local buffer_byte_size = 100 - local STL_MAX_ITEM = 80 + local STL_INITIAL_ITEMS = 20 local output_buffer = '' -- This function builds the statusline @@ -431,31 +431,23 @@ describe('buffer functions', function() 'aaaa%=b%=c%=d%=e%=fg%=hi%=jk%=lmnop%=qrstuv%=wxyz', 'aaaa b c d e fg hi jk lmnop qrstuv wxyz') - -- maximum stl item testing - statusline_test('should handle a much larger amount of = than buffer locations', 20, - ('%='):rep(STL_MAX_ITEM - 1), - ' ') -- Should be fine, because within limit - statusline_test('should handle a much larger amount of = than stl max item', 20, - ('%='):rep(STL_MAX_ITEM + 1), - ' E541') -- Should show the VIM error + -- stl item testing + local tabline = '' + for i= 1, 1000 do + tabline = tabline .. (i % 2 == 0 and '%#TabLineSel#' or '%#TabLineFill#') .. tostring(i % 2) + end + statusline_test('should handle a large amount of any items', 20, + tabline, + '<1010101010101010101') -- Should not show any error + statusline_test('should handle a larger amount of = than stl initial item', 20, + ('%='):rep(STL_INITIAL_ITEMS * 5), + ' ') -- Should not show any error statusline_test('should handle many extra characters', 20, - 'a' .. ('a'):rep(STL_MAX_ITEM * 4), - '<aaaaaaaaaaaaaaaaaaa') -- Does not show the error because there are no items - statusline_test('should handle almost maximum of characters and flags', 20, - 'a' .. ('%=a'):rep(STL_MAX_ITEM - 1), - 'a<aaaaaaaaaaaaaaaaaa') -- Should not show the VIM error - statusline_test('should handle many extra characters and flags', 20, - 'a' .. ('%=a'):rep(STL_MAX_ITEM), - 'a<aaaaaaaaaaaaa E541') -- Should show the VIM error + 'a' .. ('a'):rep(STL_INITIAL_ITEMS * 5), + '<aaaaaaaaaaaaaaaaaaa') -- Does not show any error statusline_test('should handle many extra characters and flags', 20, - 'a' .. ('%=a'):rep(STL_MAX_ITEM * 2), - 'a<aaaaaaaaaaaaa E541') -- Should show the VIM error - statusline_test('should handle many extra characters and flags with truncation', 20, - 'aaa%<' .. ('%=a'):rep(STL_MAX_ITEM), - 'aaa<aaaaaaaaaaa E541') -- Should show the VIM error - statusline_test('should handle many characters and flags before and after truncation', 20, - 'a%=a%=a%<' .. ('%=a'):rep(STL_MAX_ITEM), - 'aaa<aaaaaaaaaaa E541') -- Should show the VIM error + 'a' .. ('%=a'):rep(STL_INITIAL_ITEMS * 2), + 'a<aaaaaaaaaaaaaaaaaa') -- Should not show any error -- multi-byte testing diff --git a/third-party/cmake/GetBinaryDeps.cmake b/third-party/cmake/GetBinaryDeps.cmake index f262ae7159..982bf62265 100644 --- a/third-party/cmake/GetBinaryDeps.cmake +++ b/third-party/cmake/GetBinaryDeps.cmake @@ -39,7 +39,6 @@ function(GetBinaryDep) -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake CONFIGURE_COMMAND "" BUILD_IN_SOURCE 1 - CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/bin COMMAND "${_gettool_INSTALL_COMMAND}") |