diff options
50 files changed, 741 insertions, 90 deletions
diff --git a/.github/actions/cache/action.yml b/.github/actions/cache/action.yml index 9ad14b8c27..e7ffdd66b6 100644 --- a/.github/actions/cache/action.yml +++ b/.github/actions/cache/action.yml @@ -10,6 +10,10 @@ runs: run: echo "CACHE_KEY=$CACHE_KEY-${{ join(matrix.*, '-') }}" >> $GITHUB_ENV shell: bash + - id: image + run: echo "version=$ImageVersion" >> $GITHUB_OUTPUT + shell: bash + # Avoid using '**/CMakeLists.txt' (or any pattern starting with '**/') even # if it makes the expression below simpler. hashFiles() has a timer that # will fail the job if it times out, which can happen if there are too many @@ -17,6 +21,6 @@ runs: - uses: actions/cache@v3 with: path: .deps - key: ${{ env.CACHE_KEY }}-${{ hashFiles('cmake**', + key: ${{ env.CACHE_KEY }}-${{ steps.image.outputs.version }}-${{ hashFiles('cmake**', '.github/workflows/test.yml', 'CMakeLists.txt', 'runtime/CMakeLists.txt', 'src/nvim/**/CMakeLists.txt') }} diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 8c44db1867..748a8ba3bb 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -1487,7 +1487,7 @@ nvim_set_keymap({mode}, {lhs}, {rhs}, {*opts}) *nvim_set_keymap()* Parameters: ~ • {mode} Mode short-name (map command prefix: "n", "i", "v", "x", …) or "!" for |:map!|, or empty string for |:map|. "ia", "ca" or - "!a" for abbreviation in insert mode, cmdline mode, or both, + "!a" for abbreviation in Insert mode, Cmdline mode, or both, respectively • {lhs} Left-hand-side |{lhs}| of the mapping. • {rhs} Right-hand-side |{rhs}| of the mapping. diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 94a529930e..b64938ee9e 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -774,6 +774,9 @@ OptionSet After setting an option (except during the option. Similarly |v:option_oldglobal| is only set when |:set| or |:setglobal| was used. + This does not set |<abuf>|, you could use + |bufnr()|. + Note that when setting a |global-local| string option with |:set|, then |v:option_old| is the old global value. However, for all other kinds diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index bdd9f2fd3a..1fea6ad715 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1233,7 +1233,7 @@ clearmatches([{win}]) *clearmatches()* Can also be used as a |method|: > GetWin()->clearmatches() < -col({expr} [, {winid}) *col()* +col({expr} [, {winid}]) *col()* The result is a Number, which is the byte index of the column position given with {expr}. The accepted positions are: . the cursor position @@ -1296,7 +1296,7 @@ complete({startcol}, {matches}) *complete()* *E785* Example: > inoremap <F5> <C-R>=ListMonths()<CR> - func! ListMonths() + func ListMonths() call complete(col('.'), ['January', 'February', 'March', \ 'April', 'May', 'June', 'July', 'August', 'September', \ 'October', 'November', 'December']) @@ -1540,7 +1540,7 @@ cursor({list}) This is like the return value of |getpos()| or |getcurpos()|, but without the first item. - To position the cursor using the character count, use + To position the cursor using {col} as the character count, use |setcursorcharpos()|. Does not change the jumplist. @@ -3637,7 +3637,7 @@ getscriptinfo([{opts}]) *getscriptinfo()* name Vim script file name. sid Script ID |<SID>|. variables A dictionary with the script-local variables. - Present only when the a particular script is + Present only when a particular script is specified using the "sid" item in {opts}. Note that this is a copy, the value of script-local variables cannot be changed using @@ -5626,7 +5626,7 @@ mkdir({name} [, {flags} [, {prot}]]) < and "subdir" already exists then "subdir/tmp" will be scheduled for deletion, like with: > defer delete('subdir/tmp', 'rf') - +< If {prot} is given it is used to set the protection bits of the new directory. The default is 0o755 (rwxr-xr-x: r/w for the user, readable for others). Use 0o700 to make it diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index c43d1caa0e..a8f07dd6c5 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -902,9 +902,10 @@ Note: these are typed literally, they are not special keys! write. *E495* *:<abuf>* *<abuf>* <abuf> When executing autocommands, is replaced with the currently - effective buffer number (for ":r file" and ":so file" it is - the current buffer, the file being read/sourced is not in a - buffer). *E496* + effective buffer number. It is not set for all events, + also see |bufnr()|. For ":r file" and ":so file" it is the + current buffer, the file being read/sourced is not in a + buffer. *E496* *:<amatch>* *<amatch>* <amatch> When executing autocommands, is replaced with the match for which this autocommand was executed. *E497* diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index aa53244dc8..5cee668b39 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2799,7 +2799,7 @@ text... let mylist = [1, 2, 3] lockvar 0 mylist let mylist[0] = 77 " OK - call add(mylist, 4] " OK + call add(mylist, 4) " OK let mylist = [7, 8, 9] " Error! < *E743* For unlimited depth use [!] and omit [depth]. diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 7248d03196..27ef38743b 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -197,6 +197,7 @@ specification. These LSP requests/notifications are defined by default: textDocument/formatting textDocument/hover textDocument/implementation* + textDocument/inlayHint textDocument/publishDiagnostics textDocument/rangeFormatting textDocument/references @@ -208,6 +209,7 @@ specification. These LSP requests/notifications are defined by default: window/showDocument window/showMessageRequest workspace/applyEdit + workspace/inlayHint/refresh workspace/symbol * NOTE: These are sometimes not implemented by servers. diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index babd9b801c..44682d40e5 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -2563,7 +2563,7 @@ set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()* -- Map to a Lua function: vim.keymap.set('n', 'lhs', function() print("real lua function") end) -- Map to multiple modes: - vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer=true }) + vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer = true }) -- Buffer-local mapping: vim.keymap.set('n', '<leader>w', "<cmd>w<cr>", { silent = true, buffer = 5 }) -- Expr mapping: @@ -2697,8 +2697,8 @@ find({names}, {opts}) *vim.fs.find()* directories joinpath({...}) *vim.fs.joinpath()* - Concatenate directories and/or file into a single path with normalization - (e.g., `"foo/"` and `"bar"` get joined to `"foo/bar"`) + Concatenate directories and/or file paths into a single path with + normalization (e.g., `"foo/"` and `"bar"` get joined to `"foo/bar"`) Parameters: ~ • {...} (string) @@ -2999,6 +2999,8 @@ pipeline depend on the type passed to this function: • Non-list tables pass both the key and value of each element • Function iterators pass all of the values returned by their respective function +• Tables with a metatable implementing __call are treated as function + iterators Examples: >lua @@ -3032,6 +3034,12 @@ Examples: >lua end) -- true + local rb = vim.ringbuf(3) + rb:push("a") + rb:push("b") + vim.iter(rb):totable() + -- { "a", "b" } + < In addition to the |vim.iter()| function, the |vim.iter| module provides diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index ad7901b962..9613092ba9 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -349,7 +349,7 @@ Note: - The command is not echo'ed, no need for <silent>. - The {rhs} is not subject to abbreviations nor to other mappings, even if the mapping is recursive. -- In Visual mode you can use `line('v')` and `col('v')` to get one end of the +- In Visual mode you can use `line('v')` and `col('v')` to get one end of the Visual area, the cursor is at the other end. *E1255* *E1136* diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index a901c4f13c..ac9e60637a 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -82,8 +82,10 @@ The following new APIs or features were added. • |vim.system()| for running system commands. -• |nvim_set_keymap()| now supports abbreviations. +• |nvim_set_keymap()| and |nvim_del_keymap()| now support abbreviations. +• Added |lsp-handler| for inlay hints: `textDocument/inlayHint` and + `workspace/inlayHint/refresh` ============================================================================== CHANGED FEATURES *news-changed* diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index b98c96ec87..aa37161d34 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -392,8 +392,8 @@ If there is no g:termdebug_config you can use: >vim let g:termdebug_map_K = 0 < *termdebug_disasm_window* -If you want the Asm window shown by default, set the flag to 1. -the "disasm_window_height" entry can be used to set the window height: >vim +If you want the Asm window shown by default, set the "disasm_window" flag to +1. The "disasm_window_height" entry can be used to set the window height: >vim let g:termdebug_config['disasm_window'] = 1 let g:termdebug_config['disasm_window_height'] = 15 If there is no g:termdebug_config you can use: >vim diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 7729ec5d38..0f53288608 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1361,7 +1361,7 @@ A jump table for the options with a short description can be found at |Q_op|. The screen column can be an absolute number, or a number preceded with '+' or '-', which is added to or subtracted from 'textwidth'. > - :set cc=+1 " highlight column after 'textwidth' + :set cc=+1 " highlight column after 'textwidth' :set cc=+1,+2,+3 " highlight three columns after 'textwidth' :hi ColorColumn ctermbg=lightgrey guibg=lightgrey < @@ -5386,12 +5386,12 @@ A jump table for the options with a short description can be found at |Q_op|. local to buffer Number of spaces to use for each step of (auto)indent. Used for |'cindent'|, |>>|, |<<|, etc. - When zero the 'ts' value will be used. Use the |shiftwidth()| + When zero the 'tabstop' value will be used. Use the |shiftwidth()| function to get the effective shiftwidth value. *'shortmess'* *'shm'* 'shortmess' 'shm' string (default "filnxtToOCF") - global + global *E1336* This option helps to avoid all the |hit-enter| prompts caused by file messages, for example with CTRL-G, and to avoid some other messages. It is a list of flags: @@ -5662,6 +5662,8 @@ A jump table for the options with a short description can be found at |Q_op|. line in the window wraps part of it may not be visible, as if it is above the window. "<<<" is displayed at the start of the first line, highlighted with |hl-NonText|. + You may also want to add "lastline" to the 'display' option to show as + much of the last line as possible. NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y and scrolling with the mouse. @@ -6313,13 +6315,25 @@ A jump table for the options with a short description can be found at |Q_op|. (or 3 or whatever you prefer) and use 'noexpandtab'. Then Vim will use a mix of tabs and spaces, but typing <Tab> and <BS> will behave like a tab appears every 4 (or 3) characters. - 2. Set 'tabstop' and 'shiftwidth' to whatever you prefer and use + This is the recommended way, the file will look the same with other + tools and when listing it in a terminal. + 2. Set 'softtabstop' and 'shiftwidth' to whatever you prefer and use + 'expandtab'. This way you will always insert spaces. The + formatting will never be messed up when 'tabstop' is changed (leave + it at 8 just in case). The file will be a bit larger. + You do need to check if no Tabs exist in the file. You can get rid + of them by first setting 'expandtab' and using `%retab!`, making + sure the value of 'tabstop' is set correctly. + 3. Set 'tabstop' and 'shiftwidth' to whatever you prefer and use 'expandtab'. This way you will always insert spaces. The formatting will never be messed up when 'tabstop' is changed. - 3. Set 'tabstop' and 'shiftwidth' to whatever you prefer and use a + You do need to check if no Tabs exist in the file, just like in the + item just above. + 4. Set 'tabstop' and 'shiftwidth' to whatever you prefer and use a |modeline| to set these values when editing the file again. Only - works when using Vim to edit the file. - 4. Always set 'tabstop' and 'shiftwidth' to the same value, and + works when using Vim to edit the file, other tools assume a tabstop + is worth 8 spaces. + 5. Always set 'tabstop' and 'shiftwidth' to the same value, and 'noexpandtab'. This should then work (for initial indents only) for any tabstop setting that people use. It might be nice to have tabs after the first non-blank inserted as spaces if you do this diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index 071b062957..558cc75d65 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -260,8 +260,8 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. 'runtimepath'. If the filetype detection was already enabled (this - is usually done with a "syntax enable" or "filetype - on" command in your |init.vim|, or automatically during + is usually done with a `syntax enable` or `filetype on` + command in your |vimrc|, or automatically during |initialization|), and the package was found in "pack/*/opt/{name}", this command will also look for "{name}/ftdetect/*.vim" files. diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt index 75e4767743..29e4a7b0aa 100644 --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -111,7 +111,7 @@ zuG Undo |zW| and |zG|, remove the word from the internal list, like with |zW|. *:spellra* *:spellrare* -:[count]spellr[are] {word} +:[count]spellra[re] {word} Add {word} as a rare word to 'spellfile', similar to |zw|. Without count the first name is used, with a count of two the second entry, etc. @@ -124,7 +124,7 @@ zuG Undo |zW| and |zG|, remove the word from the internal nnoremap z/ :exe ':spellrare! ' .. expand('<cWORD>')<CR> < |:spellundo|, |zuw|, or |zuW| can be used to undo this. -:spellr[rare]! {word} Add {word} as a rare word to the internal word +:spellra[re]! {word} Add {word} as a rare word to the internal word list, similar to |zW|. :[count]spellu[ndo] {word} *:spellu* *:spellundo* diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index b3a30b20e8..2c7b7ed61b 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -426,9 +426,11 @@ accordingly, proceeding as follows: 2. Process the arguments The options and file names from the command that start Vim are - inspected. Buffers are created for all files (but not loaded yet). + inspected. The |-V| argument can be used to display or log what happens next, useful for debugging the initializations. + The |--cmd| arguments are executed. + Buffers are created for all files (but not loaded yet). 3. Start a server (unless |--listen| was given) and set |v:servername|. diff --git a/runtime/doc/userfunc.txt b/runtime/doc/userfunc.txt index db0127df95..8b6462911d 100644 --- a/runtime/doc/userfunc.txt +++ b/runtime/doc/userfunc.txt @@ -44,6 +44,13 @@ functions. unless "!" is given. {name} may be a |Dictionary| |Funcref| entry: > :function dict.init +< Note that {name} is not an expression, you cannot use + a variable that is a function reference. You can use + this dirty trick to list the function referred to with + variable "Funcref": > + let g:MyFuncref = Funcref + func g:MyFuncref + unlet g:MyFuncref :fu[nction] /{pattern} List functions with a name matching {pattern}. Example that lists all functions ending with "File": > @@ -72,8 +79,7 @@ See |:verbose-cmd| for more information. name has a colon in the name, e.g. for "foo:bar()". Before that patch no error was given). - {name} can also be a |Dictionary| entry that is a - |Funcref|: > + {name} may be a |Dictionary| |Funcref| entry: > :function dict.init(arg) < "dict" must be an existing dictionary. The entry "init" is added if it didn't exist yet. Otherwise [!] diff --git a/runtime/ftplugin/corn.vim b/runtime/ftplugin/corn.vim new file mode 100644 index 0000000000..2259442229 --- /dev/null +++ b/runtime/ftplugin/corn.vim @@ -0,0 +1,18 @@ +" Vim filetype plugin +" Language: Corn +" Original Author: Jake Stanger (mail@jstanger.dev) +" License: MIT +" Last Change: 2023 May 28 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setlocal formatoptions-=t + +" Set comment (formatting) related options. +setlocal commentstring=//\ %s comments=:// + +" Let Vim know how to disable the plug-in. +let b:undo_ftplugin = 'setlocal commentstring< comments< formatoptions<' diff --git a/runtime/ftplugin/fennel.vim b/runtime/ftplugin/fennel.vim index 2e502699c5..93cf366726 100644 --- a/runtime/ftplugin/fennel.vim +++ b/runtime/ftplugin/fennel.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: Fennel " Maintainer: Gregory Anders <greg[NOSPAM]@gpanders.com> -" Last Update: 2022 Apr 20 +" Last Update: 2023 Jun 9 if exists('b:did_ftplugin') finish @@ -13,6 +13,6 @@ setlocal comments=:;;,:; setlocal formatoptions-=t setlocal suffixesadd=.fnl setlocal lisp -setlocal lispwords=accumulate,collect,do,doto,each,eval-compiler,fn,for,icollect,lambda,let,macro,macros,match,match-try,when,while,with-open +setlocal lispwords=accumulate,case,case-try,collect,do,doto,each,eval-compiler,faccumulate,fcollect,fn,for,icollect,lambda,let,macro,macros,match,match-try,when,while,with-open let b:undo_ftplugin = 'setlocal commentstring< comments< formatoptions< suffixesadd< lisp< lispwords<' diff --git a/runtime/ftplugin/urlshortcut.vim b/runtime/ftplugin/urlshortcut.vim new file mode 100644 index 0000000000..ebe08ac1d8 --- /dev/null +++ b/runtime/ftplugin/urlshortcut.vim @@ -0,0 +1,20 @@ +" Vim filetype plugin file +" Language: MS Windows URL shortcut file +" Maintainer: ObserverOfTime <chronobserver@disroot.org> +" Latest Revision: 2023-06-04 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let s:cpo_save = &cpoptions +set cpoptions&vim + +let b:undo_ftplugin = "setl com< cms< fo<" + +setlocal comments=:; commentstring=;\ %s +setlocal formatoptions-=t formatoptions+=croql + +let &cpoptions = s:cpo_save +unlet s:cpo_save diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index d46b0fbf32..dc396b8f65 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -932,7 +932,7 @@ function vim._cs_remote(rcid, server_addr, connect_error, args) or subcmd == 'tab-wait' or subcmd == 'tab-wait-silent' then - return { errmsg = 'E5600: Wait commands not yet implemented in nvim' } + return { errmsg = 'E5600: Wait commands not yet implemented in Nvim' } elseif subcmd == 'tab-silent' then f_tab = true f_silent = true @@ -946,8 +946,9 @@ function vim._cs_remote(rcid, server_addr, connect_error, args) if rcid == 0 then return { errmsg = connection_failure_errmsg('Send expression failed.') } end - print(vim.fn.rpcrequest(rcid, 'nvim_eval', args[2])) - return { should_exit = true, tabbed = false } + local expr = 'string(' .. args[2] .. ')' + local res = vim.fn.rpcrequest(rcid, 'nvim_eval', expr) + return { result = res, should_exit = true, tabbed = false } elseif subcmd ~= '' then return { errmsg = 'Unknown option argument: ' .. args[1] } end diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 18c47ea2f4..fc8871f593 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1033,8 +1033,8 @@ local extension = { swift = 'swift', svh = 'systemverilog', sv = 'systemverilog', - cmm = 't32', - t32 = 't32', + cmm = 'trace32', + t32 = 'trace32', td = 'tablegen', tak = 'tak', tal = 'tal', diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 5e63fb6237..c89147dbd2 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -72,7 +72,7 @@ function M.basename(file) return file:match('[/\\]$') and '' or (file:match('[^\\/]*$'):gsub('\\', '/')) end ---- Concatenate directories and/or file into a single path with normalization +--- Concatenate directories and/or file paths into a single path with normalization --- (e.g., `"foo/"` and `"bar"` get joined to `"foo/bar"`) --- ---@param ... string diff --git a/runtime/lua/vim/iter.lua b/runtime/lua/vim/iter.lua index 0e98d0437e..204d22b9be 100644 --- a/runtime/lua/vim/iter.lua +++ b/runtime/lua/vim/iter.lua @@ -14,6 +14,8 @@ --- - Non-list tables pass both the key and value of each element --- - Function iterators pass all of the values returned by their respective --- function +--- - Tables with a metatable implementing __call are treated as function +--- iterators --- --- Examples: --- <pre>lua @@ -46,6 +48,12 @@ --- return k == 'z' --- end) --- -- true +--- +--- local rb = vim.ringbuf(3) +--- rb:push("a") +--- rb:push("b") +--- vim.iter(rb):totable() +--- -- { "a", "b" } --- </pre> --- --- In addition to the |vim.iter()| function, the |vim.iter| module provides @@ -889,6 +897,17 @@ end function Iter.new(src, ...) local it = {} if type(src) == 'table' then + local mt = getmetatable(src) + if mt and type(mt.__call) == 'function' then + ---@private + function it.next() + return src() + end + + setmetatable(it, Iter) + return it + end + local t = {} -- Check if source table can be treated like a list (indices are consecutive integers diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua index 911b584c14..0d7fc3b1ec 100644 --- a/runtime/lua/vim/keymap.lua +++ b/runtime/lua/vim/keymap.lua @@ -6,7 +6,7 @@ local keymap = {} --- -- Map to a Lua function: --- vim.keymap.set('n', 'lhs', function() print("real lua function") end) --- -- Map to multiple modes: ---- vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer=true }) +--- vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer = true }) --- -- Buffer-local mapping: --- vim.keymap.set('n', '<leader>w', "<cmd>w<cr>", { silent = true, buffer = 5 }) --- -- Expr mapping: diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 6ddbfc6df7..6f9a6c460b 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -17,6 +17,7 @@ local if_nil = vim.F.if_nil local lsp = { protocol = protocol, + _inlay_hint = require('vim.lsp._inlay_hint'), handlers = default_handlers, @@ -60,6 +61,8 @@ lsp._request_name_to_capability = { ['textDocument/documentHighlight'] = { 'documentHighlightProvider' }, ['textDocument/semanticTokens/full'] = { 'semanticTokensProvider' }, ['textDocument/semanticTokens/full/delta'] = { 'semanticTokensProvider' }, + ['textDocument/inlayHint'] = { 'inlayHintProvider' }, + ['inlayHint/resolve'] = { 'inlayHintProvider', 'resolveProvider' }, } -- TODO improve handling of scratch buffers with LSP attached. @@ -933,7 +936,7 @@ function lsp.status() end local message = table.concat(messages, ', ') if percentage then - return string.format('%03d: %s', percentage, message) + return string.format('%3d%%: %s', percentage, message) end return message end @@ -1498,16 +1501,20 @@ function lsp.start_client(config) end -- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state changetracking.flush(client, bufnr) + local version = util.buf_versions[bufnr] bufnr = resolve_bufnr(bufnr) if log.debug() then log.debug(log_prefix, 'client.request', client_id, method, params, handler, bufnr) end local success, request_id = rpc.request(method, params, function(err, result) - handler( - err, - result, - { method = method, client_id = client_id, bufnr = bufnr, params = params } - ) + local context = { + method = method, + client_id = client_id, + bufnr = bufnr, + params = params, + version = version, + } + handler(err, result, context) end, function(request_id) local request = client.requests[request_id] request.type = 'complete' diff --git a/runtime/lua/vim/lsp/_inlay_hint.lua b/runtime/lua/vim/lsp/_inlay_hint.lua new file mode 100644 index 0000000000..aa6ec9aca8 --- /dev/null +++ b/runtime/lua/vim/lsp/_inlay_hint.lua @@ -0,0 +1,217 @@ +local util = require('vim.lsp.util') +local log = require('vim.lsp.log') +local api = vim.api +local M = {} + +---@class lsp._inlay_hint.bufstate +---@field version integer +---@field client_hint table<integer, table<integer, lsp.InlayHint[]>> client_id -> (lnum -> hints) + +---@type table<integer, lsp._inlay_hint.bufstate> +local hint_cache_by_buf = setmetatable({}, { + __index = function(t, b) + local key = b > 0 and b or api.nvim_get_current_buf() + return rawget(t, key) + end, +}) + +local namespace = api.nvim_create_namespace('vim_lsp_inlayhint') + +M.__explicit_buffers = {} + +--- |lsp-handler| for the method `textDocument/inlayHint` +--- Store hints for a specific buffer and client +--- Resolves unresolved hints +---@private +function M.on_inlayhint(err, result, ctx, _) + if err then + local _ = log.error() and log.error('inlayhint', err) + return + end + local bufnr = ctx.bufnr + if util.buf_versions[bufnr] ~= ctx.version then + return + end + local client_id = ctx.client_id + if not result then + return + end + local bufstate = hint_cache_by_buf[bufnr] + if not bufstate then + bufstate = { + client_hint = vim.defaulttable(), + version = ctx.version, + } + hint_cache_by_buf[bufnr] = bufstate + api.nvim_buf_attach(bufnr, false, { + on_detach = function(_, b) + api.nvim_buf_clear_namespace(b, namespace, 0, -1) + hint_cache_by_buf[b] = nil + end, + on_reload = function(_, b) + api.nvim_buf_clear_namespace(b, namespace, 0, -1) + hint_cache_by_buf[b] = nil + end, + }) + end + local hints_by_client = bufstate.client_hint + local client = vim.lsp.get_client_by_id(client_id) + + local new_hints_by_lnum = vim.defaulttable() + local num_unprocessed = #result + if num_unprocessed == 0 then + hints_by_client[client_id] = {} + bufstate.version = ctx.version + api.nvim__buf_redraw_range(bufnr, 0, -1) + return + end + + local lines = api.nvim_buf_get_lines(bufnr, 0, -1, false) + ---@private + local function pos_to_byte(position) + local col = position.character + if col > 0 then + local line = lines[position.line + 1] or '' + local ok, convert_result + ok, convert_result = pcall(util._str_byteindex_enc, line, col, client.offset_encoding) + if ok then + return convert_result + end + return math.min(#line, col) + end + return col + end + + for _, hint in ipairs(result) do + local lnum = hint.position.line + hint.position.character = pos_to_byte(hint.position) + table.insert(new_hints_by_lnum[lnum], hint) + end + + hints_by_client[client_id] = new_hints_by_lnum + bufstate.version = ctx.version + api.nvim__buf_redraw_range(bufnr, 0, -1) +end + +---@private +local function resolve_bufnr(bufnr) + return bufnr == 0 and api.nvim_get_current_buf() or bufnr +end + +--- Refresh inlay hints for a buffer +--- +--- It is recommended to trigger this using an autocmd or via keymap. +---@param opts (nil|table) Optional arguments +--- - bufnr (integer, default: 0): Buffer whose hints to refresh +--- - only_visible (boolean, default: false): Whether to only refresh hints for the visible regions of the buffer +--- +--- Example: +--- <pre>vim +--- autocmd BufEnter,InsertLeave,BufWritePost <buffer> lua vim.lsp._inlay_hint.refresh() +--- </pre> +--- +---@private +function M.refresh(opts) + opts = opts or {} + local bufnr = opts.bufnr or 0 + local only_visible = opts.only_visible or false + bufnr = resolve_bufnr(bufnr) + M.__explicit_buffers[bufnr] = true + local buffer_windows = {} + for _, winid in ipairs(api.nvim_list_wins()) do + if api.nvim_win_get_buf(winid) == bufnr then + table.insert(buffer_windows, winid) + end + end + for _, window in ipairs(buffer_windows) do + local first = vim.fn.line('w0', window) + local last = vim.fn.line('w$', window) + local params = { + textDocument = util.make_text_document_params(bufnr), + range = { + start = { line = first - 1, character = 0 }, + ['end'] = { line = last, character = 0 }, + }, + } + vim.lsp.buf_request(bufnr, 'textDocument/inlayHint', params) + end + if not only_visible then + local params = { + textDocument = util.make_text_document_params(bufnr), + range = { + start = { line = 0, character = 0 }, + ['end'] = { line = api.nvim_buf_line_count(bufnr), character = 0 }, + }, + } + vim.lsp.buf_request(bufnr, 'textDocument/inlayHint', params) + end +end + +--- Clear inlay hints +--- +---@param client_id integer|nil filter by client_id. All clients if nil +---@param bufnr integer|nil filter by buffer. All buffers if nil +---@private +function M.clear(client_id, bufnr) + local buffers = bufnr and { resolve_bufnr(bufnr) } or vim.tbl_keys(hint_cache_by_buf) + for _, iter_bufnr in ipairs(buffers) do + M.__explicit_buffers[iter_bufnr] = false + local bufstate = hint_cache_by_buf[iter_bufnr] + local client_lens = (bufstate or {}).client_hint or {} + local client_ids = client_id and { client_id } or vim.tbl_keys(client_lens) + for _, iter_client_id in ipairs(client_ids) do + if bufstate then + bufstate.client_hint[iter_client_id] = {} + end + end + api.nvim_buf_clear_namespace(iter_bufnr, namespace, 0, -1) + end + vim.cmd('redraw!') +end + +api.nvim_set_decoration_provider(namespace, { + on_win = function(_, _, bufnr, topline, botline) + local bufstate = hint_cache_by_buf[bufnr] + if not bufstate then + return + end + + if bufstate.version ~= util.buf_versions[bufnr] then + return + end + local hints_by_client = bufstate.client_hint + api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) + + for lnum = topline, botline do + for _, hints_by_lnum in pairs(hints_by_client) do + local line_hints = hints_by_lnum[lnum] or {} + for _, hint in pairs(line_hints) do + local text = '' + if type(hint.label) == 'string' then + text = hint.label + else + for _, part in ipairs(hint.label) do + text = text .. part.value + end + end + if hint.paddingLeft then + text = ' ' .. text + end + if hint.paddingRight then + text = text .. ' ' + end + api.nvim_buf_set_extmark(bufnr, namespace, lnum, hint.position.character, { + virt_text_pos = 'inline', + ephemeral = false, + virt_text = { + { text, 'LspInlayHint' }, + }, + hl_mode = 'combine', + }) + end + end + end + end, +}) + +return M diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 19338ae8f0..44a9a58aca 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -219,6 +219,10 @@ M['textDocument/codeLens'] = function(...) return require('vim.lsp.codelens').on_codelens(...) end +M['textDocument/inlayHint'] = function(...) + return require('vim.lsp._inlay_hint').on_inlayhint(...) +end + --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references M['textDocument/references'] = function(_, result, ctx, config) if not result or vim.tbl_isempty(result) then @@ -612,6 +616,28 @@ M['window/showDocument'] = function(_, result, ctx, _) return { success = success or false } end +---@see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_inlayHint_refresh +M['workspace/inlayHint/refresh'] = function(err, _, ctx) + local inlay_hint = require('vim.lsp._inlay_hint') + if not inlay_hint.__explicit_buffers[ctx.bufnr] then + return vim.NIL + end + if err then + return vim.NIL + end + + for _, bufnr in ipairs(vim.lsp.get_buffers_by_client_id(ctx.client_id)) do + for _, winid in ipairs(api.nvim_list_wins()) do + if api.nvim_win_get_buf(winid) == bufnr then + inlay_hint.refresh({ bufnr = bufnr }) + break + end + end + end + + return vim.NIL +end + -- Add boilerplate error validation and logging for all of these. for k, fn in pairs(M) do M[k] = function(err, result, ctx, config) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 172d43e483..b3a7903420 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -641,6 +641,12 @@ function protocol.make_client_capabilities() }, }, textDocument = { + inlayHint = { + dynamicRegistration = false, + resolveSupport = { + properties = {}, + }, + }, semanticTokens = { dynamicRegistration = false, tokenTypes = { @@ -853,6 +859,9 @@ function protocol.make_client_capabilities() dynamicRegistration = true, relativePatternSupport = true, }, + inlayHint = { + refreshSupport = true, + }, }, experimental = nil, window = { diff --git a/runtime/lua/vim/lsp/types.lua b/runtime/lua/vim/lsp/types.lua index ef85a0d10f..108aeeb922 100644 --- a/runtime/lua/vim/lsp/types.lua +++ b/runtime/lua/vim/lsp/types.lua @@ -69,3 +69,28 @@ --- @field method string --- @alias lsp.UnregistrationParams {unregisterations: lsp.Unregistration[]} + +---@class lsp.Location +---@field uri string +---@field range lsp.Range + +---@class lsp.MarkupContent +---@field kind string +---@field value string + +---@class lsp.InlayHintLabelPart +---@field value string +---@field tooltip? string | lsp.MarkupContent +---@field location? lsp.Location + +---@class lsp.TextEdit +---@field range lsp.Range +---@field newText string + +---@class lsp.InlayHint +---@field position lsp.Position +---@field label string | lsp.InlayHintLabelPart[] +---@field kind? integer +---@field textEdits? lsp.TextEdit[] +---@field paddingLeft? boolean +---@field paddingRight? boolean diff --git a/runtime/syntax/meson.vim b/runtime/syntax/meson.vim index 0af0d776f8..4eaf696322 100644 --- a/runtime/syntax/meson.vim +++ b/runtime/syntax/meson.vim @@ -3,7 +3,7 @@ " License: VIM License " Maintainer: Nirbheek Chauhan <nirbheek.chauhan@gmail.com> " Liam Beguin <liambeguin@gmail.com> -" Last Change: 2021 Aug 16 +" Last Change: 2023 May 27 " Credits: Zvezdan Petkovic <zpetkovic@acm.org> " Neil Schemenauer <nas@meson.ca> " Dmitry Vasiliev @@ -68,6 +68,7 @@ syn keyword mesonBuiltin \ add_global_link_arguments \ add_languages \ add_project_arguments + \ add_project_dependencies \ add_project_link_arguments \ add_test_setup \ alias_target @@ -99,6 +100,7 @@ syn keyword mesonBuiltin \ install_headers \ install_man \ install_subdir + \ install_symlink \ install_emptydir \ is_disabler \ is_variable @@ -115,6 +117,7 @@ syn keyword mesonBuiltin \ shared_library \ shared_module \ static_library + \ structured_sources \ subdir \ subdir_done \ subproject @@ -125,6 +128,7 @@ syn keyword mesonBuiltin \ vcs_tag \ warning \ range + \ debug if exists("meson_space_error_highlight") " trailing whitespace @@ -146,7 +150,7 @@ hi def link mesonEscape Special hi def link mesonNumber Number hi def link mesonBuiltin Function hi def link mesonBoolean Boolean -if exists("meson_space_error_higlight") +if exists("meson_space_error_highlight") hi def link mesonSpaceError Error endif diff --git a/runtime/syntax/structurizr.vim b/runtime/syntax/structurizr.vim index ab9e4ee609..363ee70438 100644 --- a/runtime/syntax/structurizr.vim +++ b/runtime/syntax/structurizr.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Structurizr DSL " Maintainer: Bastian Venthur <venthur@debian.org> -" Last Change: 2022-02-15 +" Last Change: 2022-05-22 " Remark: For a language reference, see " https://github.com/structurizr/dsl @@ -26,6 +26,7 @@ syn keyword skeyword configuration syn keyword skeyword container syn keyword skeyword containerinstance syn keyword skeyword custom +syn keyword skeyword default syn keyword skeyword deployment syn keyword skeyword deploymentenvironment syn keyword skeyword deploymentgroup @@ -40,6 +41,7 @@ syn keyword skeyword group syn keyword skeyword healthcheck syn keyword skeyword include syn keyword skeyword infrastructurenode +syn keyword skeyword instances syn keyword skeyword model syn keyword skeyword person syn keyword skeyword perspectives @@ -54,6 +56,7 @@ syn keyword skeyword tags syn keyword skeyword technology syn keyword skeyword terminology syn keyword skeyword theme +syn keyword skeyword themes syn keyword skeyword title syn keyword skeyword url syn keyword skeyword users diff --git a/runtime/syntax/swayconfig.vim b/runtime/syntax/swayconfig.vim index 996b8f596c..6b36210252 100644 --- a/runtime/syntax/swayconfig.vim +++ b/runtime/syntax/swayconfig.vim @@ -2,9 +2,9 @@ " Language: sway window manager config " Original Author: James Eapen <james.eapen@vai.org> " Maintainer: James Eapen <james.eapen@vai.org> -" Version: 0.1.6 -" Reference version (jamespeapen/swayconfig.vim): 0.11.6 -" Last Change: 2022 Aug 08 +" Version: 0.2.1 +" Reference version (jamespeapen/swayconfig.vim): 0.12.1 +" Last Change: 2023 Mar 20 " References: " http://i3wm.org/docs/userguide.html#configuring @@ -58,6 +58,10 @@ syn match swayConfigClientColor /^\s*client.\w\+\s\+.*$/ contains=i3ConfigClient syn keyword swayConfigInputKeyword input contained syn match swayConfigInput /^\s*input\s\+.*$/ contains=swayConfigInputKeyword +" Seat config +syn keyword swayConfigSeatKeyword seat contained +syn match swayConfigSeat /^\s*seat\s\+.*$/ contains=swayConfigSeatKeyword + " set display outputs syn match swayConfigOutput /^\s*output\s\+.*$/ contains=i3ConfigOutput @@ -66,6 +70,10 @@ syn keyword swayConfigFocusKeyword focus contained syn keyword swayConfigFocusType output contained syn match swayConfigFocus /^\s*focus\soutput\s.*$/ contains=swayConfigFocusKeyword,swayConfigFocusType +" mouse warping +syn keyword swayConfigMouseWarpingType container contained +syn match swayConfigMouseWarping /^\s*mouse_warping\s\+\(output\|container\|none\)\s\?$/ contains=i3ConfigMouseWarpingKeyword,i3ConfigMouseWarpingType,swayConfigMouseWarpingType + " focus follows mouse syn clear i3ConfigFocusFollowsMouseType syn clear i3ConfigFocusFollowsMouse @@ -80,7 +88,7 @@ syn match swayConfigXwaylandModifier /^\s*xwayland\s\+\(enable\|disable\|force\) " Group mode/bar syn clear i3ConfigBlock -syn region swayConfigBlock start=+.*s\?{$+ end=+^}$+ contains=i3ConfigBlockKeyword,i3ConfigString,i3ConfigBind,i3ConfigInitializeKeyword,i3ConfigComment,i3ConfigFont,i3ConfigFocusWrappingType,i3ConfigColor,i3ConfigVariable,swayConfigInputKeyword,i3ConfigOutput transparent keepend extend +syn region swayConfigBlock start=+.*s\?{$+ end=+^}$+ contains=i3ConfigBlockKeyword,i3ConfigString,i3ConfigBind,i3ConfigInitializeKeyword,i3ConfigComment,i3ConfigFont,i3ConfigFocusWrappingType,i3ConfigColor,i3ConfigVariable,swayConfigInputKeyword,swayConfigSeatKeyword,i3ConfigOutput transparent keepend extend "hi def link swayConfigError Error hi def link i3ConfigFloating Error @@ -89,6 +97,8 @@ hi def link swayConfigFloatingMouseAction Type hi def link swayConfigFocusKeyword Type hi def link swayConfigSmartBorderKeyword Type hi def link swayConfigInputKeyword Type +hi def link swayConfigSeatKeyword Type +hi def link swayConfigMouseWarpingType Type hi def link swayConfigFocusFollowsMouseType Type hi def link swayConfigBindGestureCommand Identifier hi def link swayConfigBindGestureDirection Constant diff --git a/runtime/syntax/urlshortcut.vim b/runtime/syntax/urlshortcut.vim new file mode 100644 index 0000000000..f6cc3835a2 --- /dev/null +++ b/runtime/syntax/urlshortcut.vim @@ -0,0 +1,14 @@ +" Vim syntax file +" Language: MS Windows URL shortcut file +" Maintainer: ObserverOfTime <chronobserver@disroot.org> +" LastChange: 2023-06-04 + +" Quit when a syntax file was already loaded. +if exists("b:current_syntax") + finish +endif + +" Just use the dosini syntax for now +runtime! syntax/dosini.vim + +let b:current_syntax = "urlshortcut" diff --git a/runtime/syntax/xpm.vim b/runtime/syntax/xpm.vim index 77d82403e9..b094092b73 100644 --- a/runtime/syntax/xpm.vim +++ b/runtime/syntax/xpm.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: X Pixmap " Maintainer: Ronald Schild <rs@scutum.de> -" Last Change: 2023 May 11 +" Last Change: 2023 May 24 " Version: 5.4n.2 " Jemma Nelson added termguicolors support " Dominique Pellé fixed spelling support @@ -42,7 +42,7 @@ function s:CreateSyntax() abort let values = split(s[1 : -2]) " Values string invalid, bail out - if len(values) != 4 + if len(values) != 4 && len(values) != 6 && len(values) != 7 return endif @@ -100,8 +100,8 @@ function s:CreateSyntax() abort endif " escape meta characters in patterns - let s = escape(s, '/\*^$.~[] ') - let chars = escape(chars, '/\*^$.~[] ') + let s = escape(s, '/\*^$.~[]') + let chars = escape(chars, '/\*^$.~[]') " now create syntax items " highlight the color string as normal string (no pixel string) diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index b40f8526ea..2d4c8b2234 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -223,6 +223,7 @@ CONFIG = { 'log.lua', 'rpc.lua', 'protocol.lua', + 'inlay_hint.lua' ], 'files': [ 'runtime/lua/vim/lsp', diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 4bbbf644a8..62b4fd9764 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1420,7 +1420,7 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode) /// @param channel_id /// @param mode Mode short-name (map command prefix: "n", "i", "v", "x", …) /// or "!" for |:map!|, or empty string for |:map|. -/// "ia", "ca" or "!a" for abbreviation in insert mode, cmdline mode, or both, respectively +/// "ia", "ca" or "!a" for abbreviation in Insert mode, Cmdline mode, or both, respectively /// @param lhs Left-hand-side |{lhs}| of the mapping. /// @param rhs Right-hand-side |{rhs}| of the mapping. /// @param opts Optional parameters map: Accepts all |:map-arguments| as keys except |<buffer>|, diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index ff14ae0e41..280c1d6994 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -92,7 +92,7 @@ typedef struct { int fromcol; ///< start of inverting int tocol; ///< end of inverting - long vcol_sbr; ///< virtual column after showbreak + colnr_T vcol_sbr; ///< virtual column after showbreak bool need_showbreak; ///< overlong line, skipping first x chars int char_attr; ///< attributes for next character @@ -832,6 +832,12 @@ static void handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv) wlv->need_showbreak = false; } wlv->vcol_sbr = wlv->vcol + mb_charlen(sbr); + + // Correct start of highlighted area for 'showbreak'. + if (wlv->fromcol >= wlv->vcol && wlv->fromcol < wlv->vcol_sbr) { + wlv->fromcol = wlv->vcol_sbr; + } + // Correct end of highlighted area for 'showbreak', // required when 'linebreak' is also set. if (wlv->tocol == wlv->vcol) { diff --git a/src/nvim/grid.c b/src/nvim/grid.c index f2ceb2ac24..11cd691f22 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -540,7 +540,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle size_t skip = 0; if (wp->w_p_nu && wp->w_p_rnu) { // do not overwrite the line number, change "123 text" to - // "123>>>xt". + // "123<<<xt". while (skip < max_off_from && ascii_isdigit(*linebuf_char[off])) { off++; skip++; diff --git a/src/nvim/help.c b/src/nvim/help.c index 46310cb681..8c7c19e7c3 100644 --- a/src/nvim/help.c +++ b/src/nvim/help.c @@ -458,7 +458,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep // Replace "^x" by "CTRL-X". Don't do this for "^_" to make // ":help i_^_CTRL-D" work. // Insert '-' before and after "CTRL-X" when applicable. - if (*s < ' ' + if ((uint8_t)(*s) < ' ' || (*s == '^' && s[1] && (ASCII_ISALPHA(s[1]) || vim_strchr("?@[\\]^", (uint8_t)s[1]) != NULL))) { if (d > IObuff && d[-1] != '_' && d[-1] != '\\') { diff --git a/src/nvim/main.c b/src/nvim/main.c index 88eedf80d3..6a02aa3dae 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -953,7 +953,7 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr, TriState tabbed = kNone; for (size_t i = 0; i < rvobj.data.dictionary.size; i++) { - if (strcmp(rvobj.data.dictionary.items[i].key.data, "errmsg") == 0) { + if (strequal(rvobj.data.dictionary.items[i].key.data, "errmsg")) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) { os_errmsg("vim._cs_remote returned an unexpected type for 'errmsg'\n"); os_exit(2); @@ -961,13 +961,20 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr, os_errmsg(rvobj.data.dictionary.items[i].value.data.string.data); os_errmsg("\n"); os_exit(2); - } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "tabbed") == 0) { + } else if (strequal(rvobj.data.dictionary.items[i].key.data, "result")) { + if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) { + os_errmsg("vim._cs_remote returned an unexpected type for 'result'\n"); + os_exit(2); + } + os_msg(rvobj.data.dictionary.items[i].value.data.string.data); + os_msg("\n"); + } else if (strequal(rvobj.data.dictionary.items[i].key.data, "tabbed")) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { os_errmsg("vim._cs_remote returned an unexpected type for 'tabbed'\n"); os_exit(2); } tabbed = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; - } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) { + } else if (strequal(rvobj.data.dictionary.items[i].key.data, "should_exit")) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { os_errmsg("vim._cs_remote returned an unexpected type for 'should_exit'\n"); os_exit(2); diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 18331cc95d..4fe669631f 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1762,7 +1762,7 @@ static tagmatch_status_T findtags_parse_line(findtags_state_T *st, tagptrs_T *ta if (st->state == TS_BINARY) { int tagcmp; // Simplistic check for unsorted tags file. - int i = (int)tagpp->tagname[0]; + int i = (uint8_t)tagpp->tagname[0]; if (margs->sortic) { i = TOUPPER_ASC(tagpp->tagname[0]); } diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index e56dfd483a..953402ada3 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -400,6 +400,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function() -- maparg(), which does not accept "!" (though it returns "!" in its output -- if getting a mapping set with |:map!|). local function normalize_mapmode(mode, generate_expected) + if mode:sub(-1) == 'a' then + mode = mode:sub(1, -2) + end if not generate_expected and mode == '!' then -- Cannot retrieve mapmode-ic mappings with "!", but can with "i" or "c". mode = 'i' @@ -435,7 +438,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() -- Gets a maparg() dict from Nvim, if one exists. local function get_mapargs(mode, lhs) - local mapargs = funcs.maparg(lhs, normalize_mapmode(mode), false, true) + local mapargs = funcs.maparg(lhs, normalize_mapmode(mode), mode:sub(-1) == 'a', true) -- drop "lhsraw" and "lhsrawalt" which are hard to check mapargs.lhsraw = nil mapargs.lhsrawalt = nil @@ -744,7 +747,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() end) -- Perform exhaustive tests of basic functionality - local mapmodes = {'n', 'v', 'x', 's', 'o', '!', 'i', 'l', 'c', 't', ''} + local mapmodes = {'n', 'v', 'x', 's', 'o', '!', 'i', 'l', 'c', 't', '', 'ia', 'ca', '!a'} for _, mapmode in ipairs(mapmodes) do it('can set/unset normal mappings in mapmode '..mapmode, function() meths.set_keymap(mapmode, 'lhs', 'rhs', {}) @@ -773,11 +776,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function() -- remove some map arguments that are harder to test, or were already tested optnames = {'nowait', 'silent', 'expr', 'noremap'} for _, mapmode in ipairs(mapmodes) do - local printable_mode = normalize_mapmode(mapmode) - -- Test with single mappings for _, maparg in ipairs(optnames) do - it('can set/unset '..printable_mode..'-mappings with maparg: '..maparg, + it('can set/unset '..mapmode..'-mappings with maparg: '..maparg, function() meths.set_keymap(mapmode, 'lhs', 'rhs', {[maparg] = true}) eq(generate_mapargs(mapmode, 'lhs', 'rhs', {[maparg] = true}), @@ -785,7 +786,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() meths.del_keymap(mapmode, 'lhs') eq({}, get_mapargs(mapmode, 'lhs')) end) - it ('can set/unset '..printable_mode..'-mode mappings with maparg '.. + it ('can set/unset '..mapmode..'-mode mappings with maparg '.. maparg..', whose value is false', function() meths.set_keymap(mapmode, 'lhs', 'rhs', {[maparg] = false}) eq(generate_mapargs(mapmode, 'lhs', 'rhs'), @@ -798,7 +799,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() -- Test with triplets of mappings, one of which is false for i = 1, (#optnames - 2) do local opt1, opt2, opt3 = optnames[i], optnames[i + 1], optnames[i + 2] - it('can set/unset '..printable_mode..'-mode mappings with mapargs '.. + it('can set/unset '..mapmode..'-mode mappings with mapargs '.. opt1..', '..opt2..', '..opt3, function() local opts = {[opt1] = true, [opt2] = false, [opt3] = true} meths.set_keymap(mapmode, 'lhs', 'rhs', opts) diff --git a/test/functional/core/remote_spec.lua b/test/functional/core/remote_spec.lua index f74bb65553..23aab400df 100644 --- a/test/functional/core/remote_spec.lua +++ b/test/functional/core/remote_spec.lua @@ -8,6 +8,8 @@ local exec_lua = helpers.exec_lua local expect = helpers.expect local funcs = helpers.funcs local insert = helpers.insert +local is_os = helpers.is_os +local nvim_prog = helpers.nvim_prog local new_argv = helpers.new_argv local neq = helpers.neq local set_session = helpers.set_session @@ -38,10 +40,11 @@ describe('Remote', function() server:close() end) + -- Run a `nvim --remote*` command and return { stdout, stderr } of the process local function run_remote(...) set_session(server) local addr = funcs.serverlist()[1] - local client_argv = new_argv({args={'--server', addr, ...}}) + local client_argv = { nvim_prog, '--clean', '--headless', '--server', addr, ... } -- Create an nvim instance just to run the remote-invoking nvim. We want -- to wait for the remote instance to exit and calling jobwait blocks @@ -50,32 +53,43 @@ describe('Remote', function() local client_starter = spawn(new_argv(), false, nil, true) set_session(client_starter) -- Call jobstart() and jobwait() in the same RPC request to reduce flakiness. - eq({ 0 }, exec_lua([[return vim.fn.jobwait({ vim.fn.jobstart(...) })]], client_argv)) + eq({ 0 }, exec_lua([[return vim.fn.jobwait({ vim.fn.jobstart(..., { + stdout_buffered = true, + stderr_buffered = true, + on_stdout = function(_, data, _) + _G.Remote_stdout = table.concat(data, '\n') + end, + on_stderr = function(_, data, _) + _G.Remote_stderr = table.concat(data, '\n') + end, + }) })]], client_argv)) + local res = exec_lua([[return { _G.Remote_stdout, _G.Remote_stderr }]]) client_starter:close() set_session(server) + return res end it('edit a single file', function() - run_remote('--remote', fname) + eq({ '', '' }, run_remote('--remote', fname)) expect(contents) eq(2, #funcs.getbufinfo()) end) it('tab edit a single file with a non-changed buffer', function() - run_remote('--remote-tab', fname) + eq({ '', '' }, run_remote('--remote-tab', fname)) expect(contents) eq(1, #funcs.gettabinfo()) end) it('tab edit a single file with a changed buffer', function() insert('hello') - run_remote('--remote-tab', fname) + eq({ '', '' }, run_remote('--remote-tab', fname)) expect(contents) eq(2, #funcs.gettabinfo()) end) it('edit multiple files', function() - run_remote('--remote', fname, other_fname) + eq({ '', '' }, run_remote('--remote', fname, other_fname)) expect(contents) command('next') expect(other_contents) @@ -83,7 +97,7 @@ describe('Remote', function() end) it('send keys', function() - run_remote('--remote-send', ':edit '..fname..'<CR><C-W>v') + eq({ '', '' }, run_remote('--remote-send', ':edit '..fname..'<CR><C-W>v')) expect(contents) eq(2, #funcs.getwininfo()) -- Only a single buffer as we're using edit and not drop like --remote does @@ -91,7 +105,9 @@ describe('Remote', function() end) it('evaluate expressions', function() - run_remote('--remote-expr', 'setline(1, "Yo")') + local linesep = is_os('win') and '\r\n' or '\n' + eq({ "function('get')" .. linesep, '' }, run_remote('--remote-expr', 'function("get")')) + eq({ '0' .. linesep, '' }, run_remote('--remote-expr', 'setline(1, "Yo")')) expect('Yo') end) end) @@ -113,7 +129,7 @@ describe('Remote', function() eq(nil, string.find(exec_capture('messages'), 'E247:')) end) - pending('exits with error on', function() + describe('exits with error on', function() local function run_and_check_exit_code(...) local bogus_argv = new_argv(...) diff --git a/test/functional/ex_cmds/help_spec.lua b/test/functional/ex_cmds/help_spec.lua index 0ec7249218..aca0cbbaa6 100644 --- a/test/functional/ex_cmds/help_spec.lua +++ b/test/functional/ex_cmds/help_spec.lua @@ -4,6 +4,10 @@ local clear = helpers.clear local command = helpers.command local eq = helpers.eq local funcs = helpers.funcs +local meths = helpers.meths +local mkdir = helpers.mkdir +local rmdir = helpers.rmdir +local write_file = helpers.write_file describe(':help', function() before_each(clear) @@ -25,4 +29,17 @@ describe(':help', function() -- Before #9773, Nvim would crash on quitting the help window. eq(1002, funcs.win_getid()) end) + + it('multibyte help tags work #23975', function() + mkdir('Xhelptags') + finally(function() + rmdir('Xhelptags') + end) + mkdir('Xhelptags/doc') + write_file('Xhelptags/doc/Xhelptags.txt', '*…*') + command('helptags Xhelptags/doc') + command('set rtp+=Xhelptags') + command('help …') + eq('*…*', meths.get_current_line()) + end) end) diff --git a/test/functional/legacy/051_highlight_spec.lua b/test/functional/legacy/highlight_spec.lua index d3f2897493..0a130f1607 100644 --- a/test/functional/legacy/051_highlight_spec.lua +++ b/test/functional/legacy/highlight_spec.lua @@ -1,5 +1,3 @@ --- Tests for ":highlight". - local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers')(after_each) local clear, feed = helpers.clear, helpers.feed @@ -8,10 +6,11 @@ local eq = helpers.eq local poke_eventloop = helpers.poke_eventloop local exc_exec = helpers.exc_exec local feed_command = helpers.feed_command +local exec = helpers.exec -describe(':highlight', function() - setup(clear) +before_each(clear) +describe(':highlight', function() it('is working', function() local screen = Screen.new(35, 10) screen:attach() @@ -94,3 +93,30 @@ describe(':highlight', function() Group3 xxx cleared]]) end) end) + +describe('Visual selection highlight', function() + -- oldtest: Test_visual_sbr() + it("when 'showbreak' is set", function() + local screen = Screen.new(60, 6) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {background = Screen.colors.LightGrey}, -- Visual + [2] = {bold = true}, -- ModeMsg + }) + screen:attach() + exec([[ + set showbreak=> + call setline(1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.') + exe "normal! z1\<CR>" + ]]) + feed('v$') + screen:expect([[ + {0:>}{1:n, no sea takimata sanctus est Lorem ipsum dolor sit amet.}^ | + | + | + | + | + {2:-- VISUAL --} | + ]]) + end) +end) diff --git a/test/functional/lua/iter_spec.lua b/test/functional/lua/iter_spec.lua index 6e1ecc2f7e..3b603c9911 100644 --- a/test/functional/lua/iter_spec.lua +++ b/test/functional/lua/iter_spec.lua @@ -4,6 +4,15 @@ local matches = helpers.matches local pcall_err = helpers.pcall_err describe('vim.iter', function() + it('new() on iterable class instance', function() + local rb = vim.ringbuf(3) + rb:push("a") + rb:push("b") + + local it = vim.iter(rb) + eq({"a", "b"}, it:totable()) + end) + it('filter()', function() local function odd(v) return v % 2 ~= 0 diff --git a/test/functional/plugin/lsp/inlay_hint_spec.lua b/test/functional/plugin/lsp/inlay_hint_spec.lua new file mode 100644 index 0000000000..b134095c4f --- /dev/null +++ b/test/functional/plugin/lsp/inlay_hint_spec.lua @@ -0,0 +1,132 @@ +local helpers = require('test.functional.helpers')(after_each) +local lsp_helpers = require('test.functional.plugin.lsp.helpers') +local Screen = require('test.functional.ui.screen') + +local dedent = helpers.dedent +local exec_lua = helpers.exec_lua +local insert = helpers.insert + +local clear_notrace = lsp_helpers.clear_notrace +local create_server_definition = lsp_helpers.create_server_definition + +before_each(function() + clear_notrace() +end) + +after_each(function() + exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })") +end) + +describe('inlay hints', function() + local screen + before_each(function() + screen = Screen.new(50, 9) + screen:attach() + end) + + describe('general', function() + local text = dedent([[ + auto add(int a, int b) { return a + b; } + + int main() { + int x = 1; + int y = 2; + return add(x,y); + } + }]]) + + + local response = [==[ + [ + {"kind":1,"paddingLeft":false,"label":"-> int","position":{"character":22,"line":0},"paddingRight":false}, + {"kind":2,"paddingLeft":false,"label":"a:","position":{"character":15,"line":5},"paddingRight":true}, + {"kind":2,"paddingLeft":false,"label":"b:","position":{"character":17,"line":5},"paddingRight":true} + ] + ]==] + + + before_each(function() + exec_lua(create_server_definition) + exec_lua([[ + local response = ... + server = _create_server({ + capabilities = { + inlayHintProvider = true, + }, + handlers = { + ['textDocument/inlayHint'] = function() + return vim.json.decode(response) + end, + } + }) + ]], response) + end) + + it( + 'inlay hints are applied when vim.lsp._inlay_hint.refresh() is called', + function() + exec_lua([[ + bufnr = vim.api.nvim_get_current_buf() + vim.api.nvim_win_set_buf(0, bufnr) + client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + ]]) + + insert(text) + exec_lua([[vim.lsp._inlay_hint.refresh({bufnr = bufnr})]]) + screen:expect({ + grid = [[ + auto add(int a, int b)-> int { return a + b; } | + | + int main() { | + int x = 1; | + int y = 2; | + return add(a: x,b: y); | + } | + ^} | + | +]] + }) + end) + + it( + 'inlay hints are cleared when vim.lsp._inlay_hint.clear() is called', + function() + exec_lua([[ + bufnr = vim.api.nvim_get_current_buf() + vim.api.nvim_win_set_buf(0, bufnr) + client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + ]]) + + insert(text) + exec_lua([[vim.lsp._inlay_hint.refresh({bufnr = bufnr})]]) + screen:expect({ + grid = [[ + auto add(int a, int b)-> int { return a + b; } | + | + int main() { | + int x = 1; | + int y = 2; | + return add(a: x,b: y); | + } | + ^} | + | +]] + }) + exec_lua([[vim.lsp._inlay_hint.clear()]]) + screen:expect({ + grid = [[ + auto add(int a, int b) { return a + b; } | + | + int main() { | + int x = 1; | + int y = 2; | + return add(x,y); | + } | + ^} | + | +]], + unchanged = true + }) + end) + end) +end) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 85138417ff..2f4a703c74 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -3230,9 +3230,10 @@ describe('LSP', function() eq(0, signal, "exit signal") end; on_handler = function(err, result, ctx) - -- Don't compare & assert params, they're not relevant for the testcase + -- Don't compare & assert params and version, they're not relevant for the testcase -- This allows us to be lazy and avoid declaring them ctx.params = nil + ctx.version = nil eq(table.remove(test.expected_handlers), {err, result, ctx}, "expected handler") if ctx.method == 'start' then @@ -3314,6 +3315,7 @@ describe('LSP', function() end, on_handler = function(err, result, ctx) ctx.params = nil -- don't compare in assert + ctx.version = nil eq(table.remove(expected_handlers), { err, result, ctx }) if ctx.method == 'start' then exec_lua([[ diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim index 779de51785..67d44099d8 100644 --- a/test/old/testdir/test_filetype.vim +++ b/test/old/testdir/test_filetype.vim @@ -593,7 +593,7 @@ let s:filename_checks = { \ 'sysctl': ['/etc/sysctl.conf', '/etc/sysctl.d/file.conf', 'any/etc/sysctl.conf', 'any/etc/sysctl.d/file.conf'], \ 'systemd': ['any/systemd/file.automount', 'any/systemd/file.dnssd', 'any/systemd/file.link', 'any/systemd/file.mount', 'any/systemd/file.netdev', 'any/systemd/file.network', 'any/systemd/file.nspawn', 'any/systemd/file.path', 'any/systemd/file.service', 'any/systemd/file.slice', 'any/systemd/file.socket', 'any/systemd/file.swap', 'any/systemd/file.target', 'any/systemd/file.timer', '/etc/systemd/some.conf.d/file.conf', '/etc/systemd/system/some.d/file.conf', '/etc/systemd/system/some.d/.#file', '/etc/systemd/system/.#otherfile', '/home/user/.config/systemd/user/some.d/mine.conf', '/home/user/.config/systemd/user/some.d/.#file', '/home/user/.config/systemd/user/.#otherfile', '/.config/systemd/user/.#', '/.config/systemd/user/.#-file', '/.config/systemd/user/file.d/.#', '/.config/systemd/user/file.d/.#-file', '/.config/systemd/user/file.d/file.conf', '/etc/systemd/file.conf.d/file.conf', '/etc/systemd/system/.#', '/etc/systemd/system/.#-file', '/etc/systemd/system/file.d/.#', '/etc/systemd/system/file.d/.#-file', '/etc/systemd/system/file.d/file.conf', '/systemd/file.automount', '/systemd/file.dnssd', '/systemd/file.link', '/systemd/file.mount', '/systemd/file.netdev', '/systemd/file.network', '/systemd/file.nspawn', '/systemd/file.path', '/systemd/file.service', '/systemd/file.slice', '/systemd/file.socket', '/systemd/file.swap', '/systemd/file.target', '/systemd/file.timer', 'any/.config/systemd/user/.#', 'any/.config/systemd/user/.#-file', 'any/.config/systemd/user/file.d/.#', 'any/.config/systemd/user/file.d/.#-file', 'any/.config/systemd/user/file.d/file.conf', 'any/etc/systemd/file.conf.d/file.conf', 'any/etc/systemd/system/.#', 'any/etc/systemd/system/.#-file', 'any/etc/systemd/system/file.d/.#', 'any/etc/systemd/system/file.d/.#-file', 'any/etc/systemd/system/file.d/file.conf'], \ 'systemverilog': ['file.sv', 'file.svh'], - \ 't32': ['file.cmm', 'file.t32'], + \ 'trace32': ['file.cmm', 'file.t32'], \ 'tags': ['tags'], \ 'tak': ['file.tak'], \ 'tal': ['file.tal'], diff --git a/test/old/testdir/test_highlight.vim b/test/old/testdir/test_highlight.vim index 8a102f2e65..8ed03732c2 100644 --- a/test/old/testdir/test_highlight.vim +++ b/test/old/testdir/test_highlight.vim @@ -698,7 +698,7 @@ func Test_colorcolumn_sbr() let lines =<< trim END call setline(1, 'The quick brown fox jumped over the lazy dogs') END - call writefile(lines, 'Xtest_colorcolumn_srb') + call writefile(lines, 'Xtest_colorcolumn_srb', 'D') let buf = RunVimInTerminal('-S Xtest_colorcolumn_srb', {'rows': 10,'columns': 40}) call term_sendkeys(buf, ":set co=40 showbreak=+++>\\ cc=40,41,43\<CR>") call TermWait(buf) @@ -706,7 +706,26 @@ func Test_colorcolumn_sbr() " clean up call StopVimInTerminal(buf) - call delete('Xtest_colorcolumn_srb') +endfunc + +func Test_visual_sbr() + CheckScreendump + + " check Visual highlight when 'showbreak' is set + let lines =<< trim END + set showbreak=> + call setline(1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.') + exe "normal! z1\<CR>" + END + call writefile(lines, 'Xtest_visual_sbr', 'D') + let buf = RunVimInTerminal('-S Xtest_visual_sbr', {'rows': 6,'columns': 60}) + + call term_sendkeys(buf, "v$") + call VerifyScreenDump(buf, 'Test_visual_sbr_1', {}) + + " clean up + call term_sendkeys(buf, "\<Esc>") + call StopVimInTerminal(buf) endfunc " This test must come before the Test_cursorline test, as it appears this |