diff options
121 files changed, 2594 insertions, 2002 deletions
diff --git a/cmake.deps/deps.txt b/cmake.deps/deps.txt index d98ff6ea97..1623d0ff57 100644 --- a/cmake.deps/deps.txt +++ b/cmake.deps/deps.txt @@ -56,7 +56,7 @@ TREESITTER_SHA256 61a21f5d83cfe256472bfa941123a6941fb45073784ee7ec0bc32fdd52f7a4 WASMTIME_URL https://github.com/bytecodealliance/wasmtime/archive/v25.0.2.tar.gz WASMTIME_SHA256 6d1c17c756b83f29f629963228e5fa208ba9d6578421ba2cd07132b6a120accb -UNCRUSTIFY_URL https://github.com/uncrustify/uncrustify/archive/uncrustify-0.79.0.tar.gz -UNCRUSTIFY_SHA256 e7afaeabf636b7f0ce4e3e9747b95f7bd939613a8db49579755dddf44fedca5f +UNCRUSTIFY_URL https://github.com/uncrustify/uncrustify/archive/uncrustify-0.80.1.tar.gz +UNCRUSTIFY_SHA256 0e2616ec2f78e12816388c513f7060072ff7942b42f1175eb28b24cb75aaec48 LUA_DEV_DEPS_URL https://github.com/neovim/deps/raw/5a1f71cceb24990a0b15fd9a472a5f549f019248/opt/lua-dev-deps.tar.gz LUA_DEV_DEPS_SHA256 27db2495f5eddc7fc191701ec9b291486853530c6125609d3197d03481e8d5a2 diff --git a/runtime/compiler/cppcheck.vim b/runtime/compiler/cppcheck.vim index 4df12d1714..033613c091 100644 --- a/runtime/compiler/cppcheck.vim +++ b/runtime/compiler/cppcheck.vim @@ -1,7 +1,7 @@ " vim compiler file " Compiler: cppcheck (C++ static checker) " Maintainer: Vincent B. (twinside@free.fr) -" Last Change: 2024 Nov 08 by @Konfekt +" Last Change: 2024 Nov 19 by @Konfekt if exists("current_compiler") | finish | endif let current_compiler = "cppcheck" @@ -25,7 +25,7 @@ let &l:makeprg = 'cppcheck --quiet' \ (filereadable('compile_commands.json') ? '--project=compile_commands.json' : \ (!empty(glob('*'..s:slash..'compile_commands.json', 1, 1)) ? '--project='..glob('*'..s:slash..'compile_commands.json', 1, 1)[0] : \ (empty(&path) ? '' : '-I')..join(map(filter(split(&path, ','), 'isdirectory(v:val)'),'shellescape(v:val)'), ' -I'))))) -exe 'CompilerSet makeprg='..escape(&l:makeprg, ' "') +exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"') CompilerSet errorformat= \%f:%l:%c:\ %tarning:\ %m, diff --git a/runtime/compiler/groff.vim b/runtime/compiler/groff.vim index 640146d6a1..3e9ae0488f 100644 --- a/runtime/compiler/groff.vim +++ b/runtime/compiler/groff.vim @@ -1,7 +1,7 @@ " Vim compiler file " Compiler: Groff " Maintainer: Konfekt -" Last Change: 2024 Sep 8 +" Last Change: 2024 Nov 19 " " Expects output file extension, say `:make html` or `:make pdf`. " Supported devices as of Sept 2024 are: (x)html, pdf, ps, dvi, lj4, lbp ... @@ -30,7 +30,7 @@ execute 'CompilerSet makeprg=groff'..escape( \ ' '..s:groff_compiler_lang().. \ ' -K'..get(b:, 'groff_compiler_encoding', get(g:, 'groff_compiler_encoding', 'utf8')).. \ ' '..get(b:, 'groff_compiler_args', get(g:, 'groff_compiler_args', '')).. - \ ' -mom -T$* -- %:S > %:r:S.$*', ' ') + \ ' -mom -T$* -- %:S > %:r:S.$*', ' \|"') " From Gavin Freeborn's https://github.com/Gavinok/vim-troff under Vim License " https://github.com/Gavinok/vim-troff/blob/91017b1423caa80aba541c997909a4f810edd275/compiler/troff.vim#L39 CompilerSet errorformat=%o:<standard\ input>\ (%f):%l:%m, diff --git a/runtime/compiler/mypy.vim b/runtime/compiler/mypy.vim index 891488626a..907b98b777 100644 --- a/runtime/compiler/mypy.vim +++ b/runtime/compiler/mypy.vim @@ -1,7 +1,7 @@ " Vim compiler file " Compiler: Mypy (Python static checker) " Maintainer: @Konfekt -" Last Change: 2024 Nov 07 +" Last Change: 2024 Nov 19 if exists("current_compiler") | finish | endif let current_compiler = "mypy" @@ -12,7 +12,7 @@ set cpo&vim " CompilerSet makeprg=mypy let &l:makeprg = 'mypy --show-column-numbers ' \ ..get(b:, 'mypy_makeprg_params', get(g:, 'mypy_makeprg_params', '--strict --ignore-missing-imports')) -exe 'CompilerSet makeprg='..escape(&l:makeprg, ' "') +exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"') CompilerSet errorformat=%f:%l:%c:\ %t%*[^:]:\ %m let &cpo = s:cpo_save diff --git a/runtime/compiler/pandoc.vim b/runtime/compiler/pandoc.vim index 6c15e104c3..5d90a518c9 100644 --- a/runtime/compiler/pandoc.vim +++ b/runtime/compiler/pandoc.vim @@ -1,7 +1,7 @@ " Vim compiler file " Compiler: Pandoc " Maintainer: Konfekt -" Last Change: 2024 Sep 8 +" Last Change: 2024 Nov 19 " " Expects output file extension, say `:make html` or `:make pdf`. " Passes additional arguments to pandoc, say `:make html --self-contained`. @@ -56,7 +56,7 @@ execute 'CompilerSet makeprg=pandoc'..escape( \ ' '..s:PandocLang().. \ ' --from='..s:PandocFiletype(&filetype).. \ ' '..get(b:, 'pandoc_compiler_args', get(g:, 'pandoc_compiler_args', '')).. - \ ' --output %:r:S.$* -- %:S', ' ') + \ ' --output %:r:S.$* -- %:S', ' \|"') CompilerSet errorformat=\"%f\",\ line\ %l:\ %m let &cpo = s:keepcpo diff --git a/runtime/compiler/powershell.vim b/runtime/compiler/powershell.vim index 821fea4085..3d37d7c847 100644 --- a/runtime/compiler/powershell.vim +++ b/runtime/compiler/powershell.vim @@ -3,8 +3,9 @@ " URL: https://github.com/PProvost/vim-ps1 " Contributors: Enno Nagel " Last Change: 2024 Mar 29 -" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) -" 2024 Apr 05 by The Vim Project (avoid leaving behind g:makeprg) +" 2024 Apr 03 by the Vim Project (removed :CompilerSet definition) +" 2024 Apr 05 by the Vim Project (avoid leaving behind g:makeprg) +" 2024 Nov 19 by the Vim Project (properly escape makeprg setting) if exists("current_compiler") finish @@ -49,7 +50,7 @@ let s:makeprg = g:ps1_makeprg_cmd .. ' %:p:S' " + CategoryInfo : ObjectNotFound: (Write-Ouput:String) [], CommandNotFoundException " + FullyQualifiedErrorId : CommandNotFoundException -execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' ') +execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' \|"') " Showing error in context with underlining. CompilerSet errorformat=%+G+%m diff --git a/runtime/compiler/pylint.vim b/runtime/compiler/pylint.vim index 4c9c23e125..96abf315ab 100644 --- a/runtime/compiler/pylint.vim +++ b/runtime/compiler/pylint.vim @@ -2,6 +2,7 @@ " Compiler: Pylint for Python " Maintainer: Daniel Moch <daniel@danielmoch.com> " Last Change: 2024 Nov 07 by The Vim Project (added params variable) +" 2024 Nov 19 by the Vim Project (properly escape makeprg setting) if exists("current_compiler") | finish | endif let current_compiler = "pylint" @@ -13,7 +14,7 @@ set cpo&vim let &l:makeprg = 'pylint ' . \ '--output-format=text --msg-template="{path}:{line}:{column}:{C}: [{symbol}] {msg}" --reports=no ' . \ get(b:, "pylint_makeprg_params", get(g:, "pylint_makeprg_params", '--jobs=0')) -exe 'CompilerSet makeprg='..escape(&l:makeprg, ' "') +exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"') CompilerSet errorformat=%A%f:%l:%c:%t:\ %m,%A%f:%l:\ %m,%A%f:(%l):\ %m,%-Z%p^%.%#,%-G%.%# let &cpo = s:cpo_save diff --git a/runtime/compiler/ruff.vim b/runtime/compiler/ruff.vim index 11a69740d8..318f4fe5cb 100644 --- a/runtime/compiler/ruff.vim +++ b/runtime/compiler/ruff.vim @@ -2,6 +2,7 @@ " Compiler: Ruff (Python linter) " Maintainer: @pbnj-dragon " Last Change: 2024 Nov 07 +" 2024 Nov 19 by the Vim Project (properly escape makeprg setting) if exists("current_compiler") | finish | endif let current_compiler = "ruff" @@ -12,7 +13,7 @@ set cpo&vim " CompilerSet makeprg=ruff let &l:makeprg= 'ruff check --output-format=concise ' \ ..get(b:, 'ruff_makeprg_params', get(g:, 'ruff_makeprg_params', '--preview')) -exe 'CompilerSet makeprg='..escape(&l:makeprg, ' "') +exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"') CompilerSet errorformat=%f:%l:%c:\ %m,%f:%l:\ %m,%f:%l:%c\ -\ %m,%f: let &cpo = s:cpo_save diff --git a/runtime/compiler/tex.vim b/runtime/compiler/tex.vim index 282b3a0588..bc1623729a 100644 --- a/runtime/compiler/tex.vim +++ b/runtime/compiler/tex.vim @@ -3,8 +3,9 @@ " Maintainer: Artem Chuprina <ran@ran.pp.ru> " Contributors: Enno Nagel " Last Change: 2024 Mar 29 -" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) -" 2024 Apr 05 by The Vim Project (avoid leaving behind g:makeprg) +" 2024 Apr 03 by the Vim Project (removed :CompilerSet definition) +" 2024 Apr 05 by the Vim Project (avoid leaving behind g:makeprg) +" 2024 Nov 19 by the Vim Project (properly escape makeprg setting) if exists("current_compiler") finish @@ -27,7 +28,7 @@ if exists('b:tex_ignore_makefile') || exists('g:tex_ignore_makefile') || let current_compiler = "latex" endif let s:makeprg=current_compiler .. ' -interaction=nonstopmode' - execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' ') + execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' \|"') else let current_compiler = 'make' endif diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index c9b84f5238..c5dabeb551 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -658,8 +658,8 @@ nvim_echo({chunks}, {history}, {opts}) *nvim_echo()* Parameters: ~ • {chunks} A list of `[text, hl_group]` arrays, each representing a - text chunk with specified highlight. `hl_group` element can - be omitted for no highlight. + text chunk with specified highlight group name or ID. + `hl_group` element can be omitted for no highlight. • {history} if true, add to |message-history|. • {opts} Optional parameters. • verbose: Message is printed as a result of 'verbose' @@ -2672,8 +2672,10 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts}) • id : id of the extmark to edit. • end_row : ending line of the mark, 0-based inclusive. • end_col : ending col of the mark, 0-based exclusive. - • hl_group : name of the highlight group used to highlight - this mark. + • hl_group : highlight group used for the text range. This + and below highlight groups can be supplied either as a + string or as an integer, the latter of which can be + obtained using |nvim_get_hl_id_by_name()|. • hl_eol : when true, for a multiline highlight covering the EOL of a line, continue the highlight for the rest of the screen line (just like for diff and cursorline highlight). @@ -2682,9 +2684,7 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts}) with specified highlight. `highlight` element can either be a single highlight group, or an array of multiple highlight groups that will be stacked (highest priority - last). A highlight group can be supplied either as a - string or as an integer, the latter which can be obtained - using |nvim_get_hl_id_by_name()|. + last). • virt_text_pos : position of virtual text. Possible values: • "eol": right after eol character (default). • "overlay": display over the specified column, without @@ -2750,15 +2750,14 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts}) buffer or end of the line respectively. Defaults to true. • sign_text: string of length 1-2 used to display in the sign column. - • sign_hl_group: name of the highlight group used to - highlight the sign column text. - • number_hl_group: name of the highlight group used to - highlight the number column. - • line_hl_group: name of the highlight group used to - highlight the whole line. - • cursorline_hl_group: name of the highlight group used to - highlight the sign column text when the cursor is on the - same line as the mark and 'cursorline' is enabled. + • sign_hl_group: highlight group used for the sign column + text. + • number_hl_group: highlight group used for the number + column. + • line_hl_group: highlight group used for the whole line. + • cursorline_hl_group: highlight group used for the sign + column text when the cursor is on the same line as the + mark and 'cursorline' is enabled. • conceal: string which should be either empty or a single character. Enable concealing similar to |:syn-conceal|. When a character is supplied it is used as |:syn-cchar|. @@ -3196,7 +3195,7 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* • focusable: Enable focus by user actions (wincmds, mouse events). Defaults to true. Non-focusable windows can be entered by |nvim_set_current_win()|, or, when the `mouse` - field is set to true, by mouse events. + field is set to true, by mouse events. See |focusable|. • mouse: Specify how this window interacts with mouse events. Defaults to `focusable` value. • If false, mouse events pass through this window. diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index 5e809ad26c..c6ca5e5ce9 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -51,11 +51,19 @@ LSP • vim.lsp.buf_request_all The `error` key has been renamed to `err` inside the result parameter of the handler. • *vim.lsp.with()* Pass configuration to equivalent - functions in `vim.lsp.buf.*'. + functions in `vim.lsp.buf.*`. • |vim.lsp.handlers| No longer support client to server response handlers. Only server to client requests/notification handlers are supported. • *vim.lsp.handlers.signature_help()* Use |vim.lsp.buf.signature_help()| instead. +• `client.request()` Use |Client:request()| instead. +• `client.request_sync()` Use |Client:request_sync()| instead. +• `client.notify()` Use |Client:notify()| instead. +• `client.cancel_request()` Use |Client:cancel_request()| instead. +• `client.stop()` Use |Client:stop()| instead. +• `client.is_stopped()` Use |Client:is_stopped()| instead. +• `client.supports_method()` Use |Client:supports_method()| instead. +• `client.on_attach()` Use |Client:on_attach()| instead. ------------------------------------------------------------------------------ DEPRECATED IN 0.10 *deprecated-0.10* diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 87df1e06d6..3cb3e590f4 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -338,13 +338,16 @@ Highlight groups that are meant to be used by |vim.lsp.buf.document_highlight()| You can see more about the differences in types here: https://microsoft.github.io/language-server-protocol/specification#textDocument_documentHighlight - *hl-LspReferenceText* + *hl-LspReferenceText* LspReferenceText used for highlighting "text" references - *hl-LspReferenceRead* + *hl-LspReferenceRead* LspReferenceRead used for highlighting "read" references - *hl-LspReferenceWrite* + *hl-LspReferenceWrite* LspReferenceWrite used for highlighting "write" references - *hl-LspInlayHint* + *hl-LspReferenceTarget* +LspReferenceTarget used for highlighting reference targets (e.g. in a + hover range) + *hl-LspInlayHint* LspInlayHint used for highlighting inlay hints @@ -969,54 +972,24 @@ Lua module: vim.lsp.client *lsp-client* • {capabilities} (`lsp.ClientCapabilities`) The capabilities provided by the client (editor or tool) • {dynamic_capabilities} (`lsp.DynamicCapabilities`) - • {request} (`fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?`) - Sends a request to the server. This is a thin - wrapper around {client.rpc.request} with some - additional checking. If {handler} is not - specified and if there's no respective global - handler, then an error will occur. Returns: - {status}, {client_id}?. {status} is a boolean - indicating if the notification was successful. - If it is `false`, then it will always be - `false` (the client has shutdown). If {status} - is `true`, the function returns {request_id} - as the second result. You can use this with - `client.cancel_request(request_id)` to cancel - the request. - • {request_sync} (`fun(method: string, params: table?, timeout_ms: integer?, bufnr: integer): {err: lsp.ResponseError?, result:any}?, string?`) - err # a dict - • {notify} (`fun(method: string, params: table?): boolean`) - Sends a notification to an LSP server. - Returns: a boolean to indicate if the - notification was successful. If it is false, - then it will always be false (the client has - shutdown). - • {cancel_request} (`fun(id: integer): boolean`) Cancels a - request with a given request id. Returns: same - as `notify()`. - • {stop} (`fun(force?: boolean)`) Stops a client, - optionally with force. By default, it will - just ask the server to shutdown without force. - If you request to stop a client which has - previously been requested to shutdown, it will - automatically escalate and force shutdown. - • {on_attach} (`fun(bufnr: integer)`) Runs the on_attach - function from the client's config if it was - defined. Useful for buffer-local setup. - • {supports_method} (`fun(method: string, opts?: {bufnr: integer?}): boolean`) - Checks if a client supports a given method. - Always returns true for unknown off-spec - methods. {opts} is a optional - `{bufnr?: integer}` table. Some language - server capabilities can be file specific. - • {is_stopped} (`fun(): boolean`) Checks whether a client is - stopped. Returns: true if the client is fully - stopped. + • {request} (`fun(self: vim.lsp.Client, method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?`) + See |Client:request()|. + • {request_sync} (`fun(self: vim.lsp.Client, method: string, params: table, timeout_ms: integer?, bufnr: integer): {err: lsp.ResponseError?, result:any}?, string?`) + See |Client:request_sync()|. + • {notify} (`fun(self: vim.lsp.Client, method: string, params: table?): boolean`) + See |Client:notify()|. + • {cancel_request} (`fun(self: vim.lsp.Client, id: integer): boolean`) + See |Client:cancel_request()|. + • {stop} (`fun(self: vim.lsp.Client, force: boolean?)`) + See |Client:stop()|. + • {is_stopped} (`fun(self: vim.lsp.Client): boolean`) See + |Client:is_stopped()|. • {exec_cmd} (`fun(self: vim.lsp.Client, command: lsp.Command, context: {bufnr?: integer}?, handler: lsp.Handler?)`) - Execute a lsp command, either via client - command function (if available) or via - workspace/executeCommand (if supported by the - server) + See |Client:exec_cmd()|. + • {on_attach} (`fun(self: vim.lsp.Client, bufnr: integer)`) + See |Client:on_attach()|. + • {supports_method} (`fun(self: vim.lsp.Client, method: string, bufnr: integer?)`) + See |Client:supports_method()|. *vim.lsp.Client.Progress* Extends: |vim.Ringbuf| @@ -1087,7 +1060,8 @@ Lua module: vim.lsp.client *lsp-client* • {name}? (`string`, default: client-id) Name in log messages. • {get_language_id}? (`fun(bufnr: integer, filetype: string): string`) - Language ID as string. Defaults to the filetype. + Language ID as string. Defaults to the buffer + filetype. • {offset_encoding}? (`'utf-8'|'utf-16'|'utf-32'`) The encoding that the LSP server expects. Client does not verify this is correct. @@ -1146,6 +1120,18 @@ Lua module: vim.lsp.client *lsp-client* on initialization. +Client:cancel_request({id}) *Client:cancel_request()* + Cancels a request with a given request id. + + Parameters: ~ + • {id} (`integer`) id of request to cancel + + Return: ~ + (`boolean`) status indicating if the notification was successful. + + See also: ~ + • |Client:notify()| + Client:exec_cmd({command}, {context}, {handler}) *Client:exec_cmd()* Execute a lsp command, either via client command function (if available) or via workspace/executeCommand (if supported by the server) @@ -1155,6 +1141,95 @@ Client:exec_cmd({command}, {context}, {handler}) *Client:exec_cmd()* • {context} (`{bufnr?: integer}?`) • {handler} (`lsp.Handler?`) only called if a server command +Client:is_stopped() *Client:is_stopped()* + Checks whether a client is stopped. + + Return: ~ + (`boolean`) true if client is stopped or in the process of being + stopped; false otherwise + +Client:notify({method}, {params}) *Client:notify()* + Sends a notification to an LSP server. + + Parameters: ~ + • {method} (`string`) LSP method name. + • {params} (`table?`) LSP request params. + + Return: ~ + (`boolean`) status indicating if the notification was successful. If + it is false, then the client has shutdown. + +Client:on_attach({bufnr}) *Client:on_attach()* + Runs the on_attach function from the client's config if it was defined. + Useful for buffer-local setup. + + Parameters: ~ + • {bufnr} (`integer`) Buffer number + + *Client:request()* +Client:request({method}, {params}, {handler}, {bufnr}) + Sends a request to the server. + + This is a thin wrapper around {client.rpc.request} with some additional + checks for capabilities and handler availability. + + Parameters: ~ + • {method} (`string`) LSP method name. + • {params} (`table?`) LSP request params. + • {handler} (`lsp.Handler?`) Response |lsp-handler| for this method. + • {bufnr} (`integer?`) Buffer handle. 0 for current (default). + + Return (multiple): ~ + (`boolean`) status indicates whether the request was successful. If it + is `false`, then it will always be `false` (the client has shutdown). + (`integer?`) request_id Can be used with |Client:cancel_request()|. + `nil` is request failed. + + See also: ~ + • |vim.lsp.buf_request_all()| + + *Client:request_sync()* +Client:request_sync({method}, {params}, {timeout_ms}, {bufnr}) + Sends a request to the server and synchronously waits for the response. + + This is a wrapper around |Client:request()| + + Parameters: ~ + • {method} (`string`) LSP method name. + • {params} (`table`) LSP request params. + • {timeout_ms} (`integer?`) Maximum time in milliseconds to wait for a + result. Defaults to 1000 + • {bufnr} (`integer`) Buffer handle (0 for current). + + Return (multiple): ~ + (`{err: lsp.ResponseError?, result:any}?`) `result` and `err` from the + |lsp-handler|. `nil` is the request was unsuccessful + (`string?`) err On timeout, cancel or error, where `err` is a string + describing the failure reason. + + See also: ~ + • |vim.lsp.buf_request_sync()| + +Client:stop({force}) *Client:stop()* + Stops a client, optionally with force. + + By default, it will just request the server to shutdown without force. If + you request to stop a client which has previously been requested to + shutdown, it will automatically escalate and force shutdown. + + Parameters: ~ + • {force} (`boolean?`) + +Client:supports_method({method}, {bufnr}) *Client:supports_method()* + Checks if a client supports a given method. Always returns true for + unknown off-spec methods. + + Note: Some language server capabilities can be file specific. + + Parameters: ~ + • {method} (`string`) + • {bufnr} (`integer?`) + ============================================================================== Lua module: vim.lsp.buf *lsp-buf* @@ -1335,6 +1410,14 @@ hover({config}) *vim.lsp.buf.hover()* mappings are available as usual, except that "q" dismisses the window. You can scroll the contents the same as you would any other buffer. + Note: to disable hover highlights, add the following to your config: >lua + vim.api.nvim_create_autocmd('ColorScheme', { + callback = function() + vim.api.nvim_set_hl(0, 'LspReferenceTarget', {}) + end, + }) +< + Parameters: ~ • {config} (`vim.lsp.buf.hover.Opts?`) See |vim.lsp.buf.hover.Opts|. @@ -1587,12 +1670,12 @@ get({filter}) *vim.lsp.inlay_hint.get()* local hint = vim.lsp.inlay_hint.get({ bufnr = 0 })[1] -- 0 for current buffer local client = vim.lsp.get_client_by_id(hint.client_id) - local resp = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0) + local resp = client:request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0) local resolved_hint = assert(resp and resp.result, resp.err) vim.lsp.util.apply_text_edits(resolved_hint.textEdits, 0, client.encoding) location = resolved_hint.label[1].location - client.request('textDocument/hover', { + client:request('textDocument/hover', { textDocument = { uri = location.uri }, position = location.range.start, }) @@ -1942,12 +2025,10 @@ make_given_range_params({start_pos}, {end_pos}, {bufnr}, {offset_encoding}) selection. • {bufnr} (`integer?`) buffer handle or 0 for current, defaults to current - • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) defaults to - `offset_encoding` of first client of `bufnr` + • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'`) Return: ~ - (`table`) { textDocument = { uri = `current_file_uri` }, range = { - start = `start_position`, end = `end_position` } } + (`{ textDocument: { uri: lsp.DocumentUri }, range: lsp.Range }`) *vim.lsp.util.make_position_params()* make_position_params({window}, {offset_encoding}) @@ -1957,9 +2038,7 @@ make_position_params({window}, {offset_encoding}) Parameters: ~ • {window} (`integer?`) window handle or 0 for current, defaults to current - • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) defaults to - `offset_encoding` of first client of buffer of - `window` + • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'`) Return: ~ (`lsp.TextDocumentPositionParams`) @@ -1977,13 +2056,10 @@ make_range_params({window}, {offset_encoding}) Parameters: ~ • {window} (`integer?`) window handle or 0 for current, defaults to current - • {offset_encoding} (`"utf-8"|"utf-16"|"utf-32"?`) defaults to - `offset_encoding` of first client of buffer of - `window` + • {offset_encoding} (`"utf-8"|"utf-16"|"utf-32"`) Return: ~ - (`table`) { textDocument = { uri = `current_file_uri` }, range = { - start = `current_position`, end = `current_position` } } + (`{ textDocument: { uri: lsp.DocumentUri }, range: lsp.Range }`) *vim.lsp.util.make_text_document_params()* make_text_document_params({bufnr}) @@ -2149,7 +2225,7 @@ should_log({level}) *vim.lsp.log.should_log()* • {level} (`integer`) log level Return: ~ - (`bool`) true if would log, false if not + (`boolean`) true if would log, false if not ============================================================================== diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 243c907180..4d4a51872a 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1937,12 +1937,10 @@ vim.show_pos({bufnr}, {row}, {col}, {filter}) *vim.show_pos()* *vim.Ringbuf* Fields: ~ - • {clear} (`fun()`) Clear all items - • {push} (`fun(item: T)`) Adds an item, overriding the oldest item if - the buffer is full. - • {pop} (`fun(): T?`) Removes and returns the first unread item - • {peek} (`fun(): T?`) Returns the first unread item without removing - it + • {clear} (`fun()`) See |Ringbuf:clear()|. + • {push} (`fun(item: T)`) See |Ringbuf:push()|. + • {pop} (`fun(): T?`) See |Ringbuf:pop()|. + • {peek} (`fun(): T?`) See |Ringbuf:peek()|. Ringbuf:clear() *Ringbuf:clear()* diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index c8d0df5c96..c0d0f7b1bc 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -99,6 +99,9 @@ LSP Instead use: >lua vim.diagnostic.config(config, vim.lsp.diagnostic.get_namespace(client_id)) < +• |vim.lsp.util.make_position_params()|, |vim.lsp.util.make_range_params()| + and |vim.lsp.util.make_given_range_params()| now require the `offset_encoding` + parameter. LUA @@ -220,12 +223,15 @@ LSP • |vim.lsp.buf.signature_help()| can now cycle through different signatures using `<C-s>` and also support multiple clients. • The client now supports `'utf-8'` and `'utf-32'` position encodings. +• |vim.lsp.buf.hover()| now highlights hover ranges using the + |hl-LspReferenceTarget| highlight group. +• Functions in |vim.lsp.Client| can now be called as methods. LUA • |vim.fs.rm()| can delete files and directories. • |vim.validate()| now has a new signature which uses less tables, - is more peformant and easier to read. + is more performant and easier to read. • |vim.str_byteindex()| and |vim.str_utfindex()| gained overload signatures supporting two new parameters, `encoding` and `strict_indexing`. @@ -322,6 +328,11 @@ These existing features changed their behavior. current window, and it no longer throws |E444| when there is only one window on the screen. Global variable `vim.g.pager` is removed. +• Default 'titlestring' is now implemented with 'statusline' "%" format items. + This means the default, empty value is essentially an alias to: + `%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim`. This is only an + implementation simplification, not a behavior change. + ============================================================================== REMOVED FEATURES *news-removed* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index c972a05c4d..64ad2d2956 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6590,6 +6590,10 @@ A jump table for the options with a short description can be found at |Q_op|. expanded according to the rules used for 'statusline'. If it contains an invalid '%' format, the value is used as-is and no error or warning will be given when the value is set. + + The default behaviour is equivalent to: >vim + set titlestring=%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim +< This option cannot be set in a modeline when 'modelineexpr' is off. Example: >vim diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index 09d1369d46..04dd854637 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -1,4 +1,4 @@ -*pi_netrw.txt* For Vim version 8.2. Last change: 2020 Aug 15 +*pi_netrw.txt* Nvim ------------------------------------------------ NETRW REFERENCE MANUAL by Charles E. Campbell diff --git a/runtime/doc/pi_tar.txt b/runtime/doc/pi_tar.txt index c8570044e5..96b26d92e7 100644 --- a/runtime/doc/pi_tar.txt +++ b/runtime/doc/pi_tar.txt @@ -1,4 +1,4 @@ -*pi_tar.txt* For Vim version 8.2. Last change: 2020 Jan 07 +*pi_tar.txt* Nvim +====================+ | Tar File Interface | diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index e439e8fccb..5fc6429f7a 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -1605,8 +1605,7 @@ LanguageTree:register_cbs({cbs}, {recursive}) • {cbs} (`table<TSCallbackNameOn,function>`) An |nvim_buf_attach()|-like table argument with the following handlers: - • `on_bytes` : see |nvim_buf_attach()|, but this will be - called after the parsers callback. + • `on_bytes` : see |nvim_buf_attach()|. • `on_changedtree` : a callback that will be called every time the tree has syntactical changes. It will be passed two arguments: a table of the ranges (as node diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index 4ee8121034..55eba484bc 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -795,11 +795,16 @@ must handle. "echo" |:echo| message "echomsg" |:echomsg| message "echoerr" |:echoerr| message + "list_cmd" List output for various commands (|:ls|, |:set|, …) "lua_error" Error in |:lua| code + "lua_print" |print()| from |:lua| code "rpc_error" Error response from |rpcrequest()| + "number_prompt" Number input prompt (|inputlist()|, |z=|, …) "return_prompt" |press-enter| prompt after a multiple messages "quickfix" Quickfix navigation message + "search_cmd" Entered search command "search_count" Search count message ("S" flag of 'shortmess') + "wildlist" 'wildmode' "list" message "wmsg" Warning ("search hit BOTTOM", |W10|, …) New kinds may be added in the future; clients should treat unknown kinds as the empty kind. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 8fa94a2601..adc866af6b 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -89,7 +89,6 @@ Defaults *nvim-defaults* - 'undodir' defaults to ~/.local/state/nvim/undo// (|xdg|), auto-created - 'viewoptions' includes "unix,slash", excludes "options" - 'viminfo' includes "!" -- 'wildmenu' is enabled - 'wildoptions' defaults to "pum,tagfile" - |editorconfig| plugin is enabled, .editorconfig settings are applied. @@ -414,7 +413,7 @@ TUI: - 'term' reflects the terminal type derived from |$TERM| and other environment checks. For debugging only; not reliable during startup. >vim :echo &term -- "builtin_x" means one of the |builtin-terms| was chosen, because the expected +- "builtin_x" means one of the |builtin-terms| was chosen, because the expected terminfo file was not found on the system. - Nvim will use 256-colour capability on Linux virtual terminals. Vim uses only 8 colours plus bright foreground on Linux VTs. @@ -616,6 +615,11 @@ Autocommands: - |TermResponse| is fired for any OSC sequence received from the terminal, instead of the Primary Device Attributes response. |v:termresponse| +Options: +- 'titlestring' uses printf-style '%' items (see: 'statusline') to implement + the default behaviour. The implementation is equivalent to setting + 'titlestring' to `%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim`. + ============================================================================== Missing features *nvim-missing* diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 5729dd0874..24aa7b1b11 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -69,7 +69,8 @@ If a window is focusable, it is part of the "navigation stack", that is, editor commands such as :windo, |CTRL-W|, etc., will consider the window as one that can be made the "current window". A non-focusable window will be skipped by such commands (though it can be explicitly focused by -|nvim_set_current_win()|). +|nvim_set_current_win()|). Non-focusable windows are not listed by |:tabs|, and +are not counted by the default 'tabline'. Windows (especially floating windows) can have many other |api-win_config| properties such as "hide" and "fixed" which also affect behavior. diff --git a/runtime/ftplugin/checkhealth.vim b/runtime/ftplugin/checkhealth.vim index 62a1970b4a..cc53d723c2 100644 --- a/runtime/ftplugin/checkhealth.vim +++ b/runtime/ftplugin/checkhealth.vim @@ -1,6 +1,5 @@ " Vim filetype plugin " Language: Nvim :checkhealth buffer -" Last Change: 2022 Nov 10 if exists("b:did_ftplugin") finish @@ -8,11 +7,11 @@ endif runtime! ftplugin/help.vim -setlocal wrap breakindent linebreak +setlocal wrap breakindent linebreak nolist let &l:iskeyword='!-~,^*,^|,^",192-255' if exists("b:undo_ftplugin") - let b:undo_ftplugin .= "|setl wrap< bri< lbr< kp< isk<" + let b:undo_ftplugin .= "|setl wrap< bri< lbr< kp< isk< list<" else - let b:undo_ftplugin = "setl wrap< bri< lbr< kp< isk<" + let b:undo_ftplugin = "setl wrap< bri< lbr< kp< isk< list<" endif diff --git a/runtime/ftplugin/karel.vim b/runtime/ftplugin/karel.vim new file mode 100644 index 0000000000..8ccc2b32ce --- /dev/null +++ b/runtime/ftplugin/karel.vim @@ -0,0 +1,16 @@ +" Vim filetype plugin file +" Language: KAREL +" Last Change: 2024-11-18 +" Maintainer: Kirill Morozov <kirill@robotix.pro> +" Credits: Patrick Meiser-Knosowski for the initial implementation. + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +setlocal comments=:-- +setlocal commentstring=--\ %s +setlocal suffixesadd+=.kl,.KL + +let b:undo_ftplugin = "setlocal com< cms< sua<" diff --git a/runtime/ftplugin/mss.vim b/runtime/ftplugin/mss.vim new file mode 100644 index 0000000000..de2f8791ec --- /dev/null +++ b/runtime/ftplugin/mss.vim @@ -0,0 +1,16 @@ +" Vim filetype plugin file +" Language: Vivado mss file +" Last Change: 2024 Oct 22 +" Document: https://docs.amd.com/r/2020.2-English/ug1400-vitis-embedded/Microprocessor-Software-Specification-MSS +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +setlocal comments=b:#,fb:- +setlocal commentstring=#\ %s + +let b:match_words = '\<BEGIN\>:\<END\>' +let b:undo_ftplugin = "setl com< cms< | unlet b:match_words" diff --git a/runtime/ftplugin/opencl.vim b/runtime/ftplugin/opencl.vim new file mode 100644 index 0000000000..e8570fbe95 --- /dev/null +++ b/runtime/ftplugin/opencl.vim @@ -0,0 +1,12 @@ +" Vim filetype plugin file +" Language: OpenCL +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Last Change: 2024 Nov 19 + +if exists("b:did_ftplugin") | finish | endif +let b:did_ftplugin = 1 + +setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,:// +setlocal commentstring=/*\ %s\ */ define& include& + +let b:undo_ftplugin = "setl commentstring< comments<" diff --git a/runtime/ftplugin/query.lua b/runtime/ftplugin/query.lua index 32d615c65c..711ee35775 100644 --- a/runtime/ftplugin/query.lua +++ b/runtime/ftplugin/query.lua @@ -1,6 +1,5 @@ -- Neovim filetype plugin file -- Language: Treesitter query --- Last Change: 2024 Jul 03 if vim.b.did_ftplugin == 1 then return diff --git a/runtime/indent/query.lua b/runtime/indent/query.lua index c5b4f1f03d..17b8e9d2ac 100644 --- a/runtime/indent/query.lua +++ b/runtime/indent/query.lua @@ -1,6 +1,5 @@ -- Neovim indent file -- Language: Treesitter query --- Last Change: 2024 Jul 03 -- it's a lisp! vim.cmd([[runtime! indent/lisp.vim]]) diff --git a/runtime/lua/vim/_buf.lua b/runtime/lua/vim/_buf.lua new file mode 100644 index 0000000000..0631c96f77 --- /dev/null +++ b/runtime/lua/vim/_buf.lua @@ -0,0 +1,23 @@ +local M = {} + +--- Adds one or more blank lines above or below the cursor. +-- TODO: move to _defaults.lua once it is possible to assign a Lua function to options #25672 +--- @param above? boolean Place blank line(s) above the cursor +local function add_blank(above) + local offset = above and 1 or 0 + local repeated = vim.fn['repeat']({ '' }, vim.v.count1) + local linenr = vim.api.nvim_win_get_cursor(0)[1] + vim.api.nvim_buf_set_lines(0, linenr - offset, linenr - offset, true, repeated) +end + +-- TODO: move to _defaults.lua once it is possible to assign a Lua function to options #25672 +function M.space_above() + add_blank(true) +end + +-- TODO: move to _defaults.lua once it is possible to assign a Lua function to options #25672 +function M.space_below() + add_blank() +end + +return M diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 25afd83145..06f6ed6829 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -366,16 +366,16 @@ do -- Add empty lines vim.keymap.set('n', '[<Space>', function() - local repeated = vim.fn['repeat']({ '' }, vim.v.count1) - local linenr = vim.api.nvim_win_get_cursor(0)[1] - vim.api.nvim_buf_set_lines(0, linenr - 1, linenr - 1, true, repeated) - end, { desc = 'Add empty line above cursor' }) + -- TODO: update once it is possible to assign a Lua function to options #25672 + vim.go.operatorfunc = "v:lua.require'vim._buf'.space_above" + return 'g@l' + end, { expr = true, desc = 'Add empty line above cursor' }) vim.keymap.set('n', ']<Space>', function() - local repeated = vim.fn['repeat']({ '' }, vim.v.count1) - local linenr = vim.api.nvim_win_get_cursor(0)[1] - vim.api.nvim_buf_set_lines(0, linenr, linenr, true, repeated) - end, { desc = 'Add empty line below cursor' }) + -- TODO: update once it is possible to assign a Lua function to options #25672 + vim.go.operatorfunc = "v:lua.require'vim._buf'.space_below" + return 'g@l' + end, { expr = true, desc = 'Add empty line below cursor' }) end end diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index b4a1e0fc15..44f17b3f85 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -1151,21 +1151,16 @@ end --- @param ... any --- @return any # given arguments. function vim.print(...) - if vim.in_fast_event() then - print(...) - return ... - end - + local msg = {} for i = 1, select('#', ...) do local o = select(i, ...) if type(o) == 'string' then - vim.api.nvim_out_write(o) + table.insert(msg, o) else - vim.api.nvim_out_write(vim.inspect(o, { newline = '\n', indent = ' ' })) + table.insert(msg, vim.inspect(o, { newline = '\n', indent = ' ' })) end - vim.api.nvim_out_write('\n') end - + print(table.concat(msg, '\n')) return ... end diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index c28af7bbff..3c9b9d4f44 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -592,8 +592,9 @@ function vim.api.nvim_buf_line_count(buffer) end --- - id : id of the extmark to edit. --- - end_row : ending line of the mark, 0-based inclusive. --- - end_col : ending col of the mark, 0-based exclusive. ---- - hl_group : name of the highlight group used to highlight ---- this mark. +--- - hl_group : highlight group used for the text range. This and below +--- highlight groups can be supplied either as a string or as an integer, +--- the latter of which can be obtained using `nvim_get_hl_id_by_name()`. --- - hl_eol : when true, for a multiline highlight covering the --- EOL of a line, continue the highlight for the rest --- of the screen line (just like for diff and @@ -603,9 +604,7 @@ function vim.api.nvim_buf_line_count(buffer) end --- text chunk with specified highlight. `highlight` element --- can either be a single highlight group, or an array of --- multiple highlight groups that will be stacked ---- (highest priority last). A highlight group can be supplied ---- either as a string or as an integer, the latter which ---- can be obtained using `nvim_get_hl_id_by_name()`. +--- (highest priority last). --- - virt_text_pos : position of virtual text. Possible values: --- - "eol": right after eol character (default). --- - "overlay": display over the specified column, without @@ -676,15 +675,12 @@ function vim.api.nvim_buf_line_count(buffer) end --- buffer or end of the line respectively. Defaults to true. --- - sign_text: string of length 1-2 used to display in the --- sign column. ---- - sign_hl_group: name of the highlight group used to ---- highlight the sign column text. ---- - number_hl_group: name of the highlight group used to ---- highlight the number column. ---- - line_hl_group: name of the highlight group used to ---- highlight the whole line. ---- - cursorline_hl_group: name of the highlight group used to ---- highlight the sign column text when the cursor is on ---- the same line as the mark and 'cursorline' is enabled. +--- - sign_hl_group: highlight group used for the sign column text. +--- - number_hl_group: highlight group used for the number column. +--- - line_hl_group: highlight group used for the whole line. +--- - cursorline_hl_group: highlight group used for the sign +--- column text when the cursor is on the same line as the +--- mark and 'cursorline' is enabled. --- - conceal: string which should be either empty or a single --- character. Enable concealing similar to `:syn-conceal`. --- When a character is supplied it is used as `:syn-cchar`. @@ -1106,8 +1102,8 @@ function vim.api.nvim_del_var(name) end --- Echo a message. --- --- @param chunks any[] A list of `[text, hl_group]` arrays, each representing a ---- text chunk with specified highlight. `hl_group` element ---- can be omitted for no highlight. +--- text chunk with specified highlight group name or ID. +--- `hl_group` element can be omitted for no highlight. --- @param history boolean if true, add to `message-history`. --- @param opts vim.api.keyset.echo_opts Optional parameters. --- - verbose: Message is printed as a result of 'verbose' option. @@ -1768,7 +1764,7 @@ function vim.api.nvim_open_term(buffer, opts) end --- - focusable: Enable focus by user actions (wincmds, mouse events). --- Defaults to true. Non-focusable windows can be entered by --- `nvim_set_current_win()`, or, when the `mouse` field is set to true, ---- by mouse events. +--- by mouse events. See `focusable`. --- - mouse: Specify how this window interacts with mouse events. --- Defaults to `focusable` value. --- - If false, mouse events pass through this window. diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index cb783720ac..e485009ca2 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -7122,6 +7122,13 @@ vim.go.titleold = vim.o.titleold --- expanded according to the rules used for 'statusline'. If it contains --- an invalid '%' format, the value is used as-is and no error or warning --- will be given when the value is set. +--- +--- The default behaviour is equivalent to: +--- +--- ```vim +--- set titlestring=%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim +--- ``` +--- --- This option cannot be set in a modeline when 'modelineexpr' is off. --- --- Example: diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 3b3c4481f2..4383c0983e 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -275,6 +275,9 @@ local extension = { mdh = 'c', epro = 'c', qc = 'c', + c3 = 'c3', + c3i = 'c3', + c3t = 'c3', cabal = 'cabal', cairo = 'cairo', capnp = 'capnp', @@ -300,6 +303,7 @@ local extension = { cho = 'chordpro', chordpro = 'chordpro', ck = 'chuck', + cl = detect.cl, eni = 'cl', icl = 'clean', cljx = 'clojure', @@ -653,6 +657,8 @@ local extension = { jsp = 'jsp', jl = 'julia', just = 'just', + kl = 'karel', + KL = 'karel', kdl = 'kdl', kv = 'kivy', kix = 'kix', @@ -686,7 +692,6 @@ local extension = { ily = 'lilypond', liquid = 'liquid', liq = 'liquidsoap', - cl = 'lisp', L = 'lisp', lisp = 'lisp', el = 'lisp', @@ -792,6 +797,7 @@ local extension = { mof = 'msidl', odl = 'msidl', msql = 'msql', + mss = 'mss', mu = 'mupad', mush = 'mush', mustache = 'mustache', @@ -2211,6 +2217,7 @@ local pattern = { ['/screengrab/.*%.conf$'] = 'dosini', ['^${GNUPGHOME}/gpg%.conf$'] = 'gpg', ['/boot/grub/grub%.conf$'] = 'grub', + ['/hypr/.*%.conf$'] = 'hyprlang', ['^lilo%.conf'] = starsetf('lilo'), ['^named.*%.conf$'] = 'named', ['^rndc.*%.conf$'] = 'named', diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 98b001bd51..81b94c69db 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -181,6 +181,16 @@ function M.changelog(_, bufnr) end --- @type vim.filetype.mapfn +function M.cl(_, bufnr) + local lines = table.concat(getlines(bufnr, 1, 4)) + if lines:match('/%*') then + return 'opencl' + else + return 'lisp' + end +end + +--- @type vim.filetype.mapfn function M.class(_, bufnr) -- Check if not a Java class (starts with '\xca\xfe\xba\xbe') if not getline(bufnr, 1):find('^\202\254\186\190') then diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 42a0ccc3d4..c032d25cb1 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -3,7 +3,6 @@ local validate = vim.validate local lsp = vim._defer_require('vim.lsp', { _changetracking = ..., --- @module 'vim.lsp._changetracking' - _dynamic = ..., --- @module 'vim.lsp._dynamic' _snippet_grammar = ..., --- @module 'vim.lsp._snippet_grammar' _tagfunc = ..., --- @module 'vim.lsp._tagfunc' _watchfiles = ..., --- @module 'vim.lsp._watchfiles' @@ -31,6 +30,13 @@ local changetracking = lsp._changetracking ---@nodoc lsp.rpc_response_error = lsp.rpc.rpc_response_error +lsp._resolve_to_request = { + [ms.codeAction_resolve] = ms.textDocument_codeAction, + [ms.codeLens_resolve] = ms.textDocument_codeLens, + [ms.documentLink_resolve] = ms.textDocument_documentLink, + [ms.inlayHint_resolve] = ms.textDocument_inlayHint, +} + -- maps request name to the required server_capability in the client. lsp._request_name_to_capability = { [ms.callHierarchy_incomingCalls] = { 'callHierarchyProvider' }, @@ -343,17 +349,17 @@ end ---@param bufnr integer function lsp._set_defaults(client, bufnr) if - client.supports_method(ms.textDocument_definition) and is_empty_or_default(bufnr, 'tagfunc') + client:supports_method(ms.textDocument_definition) and is_empty_or_default(bufnr, 'tagfunc') then vim.bo[bufnr].tagfunc = 'v:lua.vim.lsp.tagfunc' end if - client.supports_method(ms.textDocument_completion) and is_empty_or_default(bufnr, 'omnifunc') + client:supports_method(ms.textDocument_completion) and is_empty_or_default(bufnr, 'omnifunc') then vim.bo[bufnr].omnifunc = 'v:lua.vim.lsp.omnifunc' end if - client.supports_method(ms.textDocument_rangeFormatting) + client:supports_method(ms.textDocument_rangeFormatting) and is_empty_or_default(bufnr, 'formatprg') and is_empty_or_default(bufnr, 'formatexpr') then @@ -361,14 +367,14 @@ function lsp._set_defaults(client, bufnr) end vim._with({ buf = bufnr }, function() if - client.supports_method(ms.textDocument_hover) + client:supports_method(ms.textDocument_hover) and is_empty_or_default(bufnr, 'keywordprg') and vim.fn.maparg('K', 'n', false, false) == '' then vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr, desc = 'vim.lsp.buf.hover()' }) end end) - if client.supports_method(ms.textDocument_diagnostic) then + if client:supports_method(ms.textDocument_diagnostic) then lsp.diagnostic._enable(bufnr) end end @@ -479,12 +485,12 @@ local function text_document_did_save_handler(bufnr) local name = api.nvim_buf_get_name(bufnr) local old_name = changetracking._get_and_set_name(client, bufnr, name) if old_name and name ~= old_name then - client.notify(ms.textDocument_didClose, { + client:notify(ms.textDocument_didClose, { textDocument = { uri = vim.uri_from_fname(old_name), }, }) - client.notify(ms.textDocument_didOpen, { + client:notify(ms.textDocument_didOpen, { textDocument = { version = 0, uri = uri, @@ -500,7 +506,7 @@ local function text_document_did_save_handler(bufnr) if type(save_capability) == 'table' and save_capability.includeText then included_text = text(bufnr) end - client.notify(ms.textDocument_didSave, { + client:notify(ms.textDocument_didSave, { textDocument = { uri = uri, }, @@ -521,10 +527,10 @@ local function buf_detach_client(bufnr, client) changetracking.reset_buf(client, bufnr) - if client.supports_method(ms.textDocument_didClose) then + if client:supports_method(ms.textDocument_didClose) then local uri = vim.uri_from_bufnr(bufnr) local params = { textDocument = { uri = uri } } - client.notify(ms.textDocument_didClose, params) + client:notify(ms.textDocument_didClose, params) end client.attached_buffers[bufnr] = nil @@ -558,12 +564,12 @@ local function buf_attach(bufnr) }, reason = protocol.TextDocumentSaveReason.Manual, ---@type integer } - if client.supports_method(ms.textDocument_willSave) then - client.notify(ms.textDocument_willSave, params) + if client:supports_method(ms.textDocument_willSave) then + client:notify(ms.textDocument_willSave, params) end - if client.supports_method(ms.textDocument_willSaveWaitUntil) then + if client:supports_method(ms.textDocument_willSaveWaitUntil) then local result, err = - client.request_sync(ms.textDocument_willSaveWaitUntil, params, 1000, ctx.buf) + client:request_sync(ms.textDocument_willSaveWaitUntil, params, 1000, ctx.buf) if result and result.result then util.apply_text_edits(result.result, ctx.buf, client.offset_encoding) elseif err then @@ -597,8 +603,8 @@ local function buf_attach(bufnr) local params = { textDocument = { uri = uri } } for _, client in ipairs(clients) do changetracking.reset_buf(client, bufnr) - if client.supports_method(ms.textDocument_didClose) then - client.notify(ms.textDocument_didClose, params) + if client:supports_method(ms.textDocument_didClose) then + client:notify(ms.textDocument_didClose, params) end end for _, client in ipairs(clients) do @@ -656,7 +662,7 @@ function lsp.buf_attach_client(bufnr, client_id) -- Send didOpen for the client if it is initialized. If it isn't initialized -- then it will send didOpen on initialize. if client.initialized then - client:_on_attach(bufnr) + client:on_attach(bufnr) end return true end @@ -734,13 +740,13 @@ function lsp.stop_client(client_id, force) for _, id in ipairs(ids) do if type(id) == 'table' then if id.stop then - id.stop(force) + id:stop(force) end else --- @cast id -vim.lsp.Client local client = all_clients[id] if client then - client.stop(force) + client:stop(force) end end end @@ -784,7 +790,7 @@ function lsp.get_clients(filter) and (filter.id == nil or client.id == filter.id) and (filter.bufnr == nil or client.attached_buffers[bufnr]) and (filter.name == nil or client.name == filter.name) - and (filter.method == nil or client.supports_method(filter.method, { bufnr = filter.bufnr })) + and (filter.method == nil or client:supports_method(filter.method, filter.bufnr)) and (filter._uninitialized or client.initialized) then clients[#clients + 1] = client @@ -806,7 +812,7 @@ api.nvim_create_autocmd('VimLeavePre', { local active_clients = lsp.get_clients() log.info('exit_handler', active_clients) for _, client in pairs(all_clients) do - client.stop() + client:stop() end local timeouts = {} --- @type table<integer,integer> @@ -841,7 +847,7 @@ api.nvim_create_autocmd('VimLeavePre', { if not vim.wait(max_timeout, check_clients_closed, poll_time) then for client_id, client in pairs(active_clients) do if timeouts[client_id] ~= nil then - client.stop(true) + client:stop(true) end end end @@ -877,11 +883,11 @@ function lsp.buf_request(bufnr, method, params, handler, on_unsupported) local clients = lsp.get_clients({ bufnr = bufnr }) local client_request_ids = {} --- @type table<integer,integer> for _, client in ipairs(clients) do - if client.supports_method(method, { bufnr = bufnr }) then + if client:supports_method(method, bufnr) then method_supported = true local cparams = type(params) == 'function' and params(client, bufnr) or params --[[@as table?]] - local request_success, request_id = client.request(method, cparams, handler, bufnr) + local request_success, request_id = client:request(method, cparams, handler, 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 @@ -904,7 +910,7 @@ function lsp.buf_request(bufnr, method, params, handler, on_unsupported) local function _cancel_all_requests() for client_id, request_id in pairs(client_request_ids) do local client = all_clients[client_id] - client.cancel_request(request_id) + client:cancel_request(request_id) end end @@ -1043,7 +1049,7 @@ function lsp.formatexpr(opts) end local bufnr = api.nvim_get_current_buf() for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do - if client.supports_method(ms.textDocument_rangeFormatting) then + if client:supports_method(ms.textDocument_rangeFormatting) then local params = util.make_formatting_params() local end_line = vim.fn.getline(end_lnum) --[[@as string]] local end_col = vim.str_utfindex(end_line, client.offset_encoding) @@ -1059,7 +1065,7 @@ function lsp.formatexpr(opts) }, } local response = - client.request_sync(ms.textDocument_rangeFormatting, params, timeout_ms, bufnr) + client:request_sync(ms.textDocument_rangeFormatting, params, timeout_ms, bufnr) if response and response.result then lsp.util.apply_text_edits(response.result, bufnr, client.offset_encoding) return 0 diff --git a/runtime/lua/vim/lsp/_changetracking.lua b/runtime/lua/vim/lsp/_changetracking.lua index b2be53269f..8588502697 100644 --- a/runtime/lua/vim/lsp/_changetracking.lua +++ b/runtime/lua/vim/lsp/_changetracking.lua @@ -40,7 +40,7 @@ local M = {} --- @class vim.lsp.CTGroupState --- @field buffers table<integer,vim.lsp.CTBufferState> --- @field debounce integer debounce duration in ms ---- @field clients table<integer, table> clients using this state. {client_id, client} +--- @field clients table<integer, vim.lsp.Client> clients using this state. {client_id, client} ---@param group vim.lsp.CTGroup ---@return string @@ -273,8 +273,8 @@ local function send_changes(bufnr, sync_kind, state, buf_state) end local uri = vim.uri_from_bufnr(bufnr) for _, client in pairs(state.clients) do - if not client.is_stopped() and vim.lsp.buf_is_attached(bufnr, client.id) then - client.notify(protocol.Methods.textDocument_didChange, { + if not client:is_stopped() and vim.lsp.buf_is_attached(bufnr, client.id) then + client:notify(protocol.Methods.textDocument_didChange, { textDocument = { uri = uri, version = util.buf_versions[bufnr], diff --git a/runtime/lua/vim/lsp/_dynamic.lua b/runtime/lua/vim/lsp/_dynamic.lua deleted file mode 100644 index 27113c0e74..0000000000 --- a/runtime/lua/vim/lsp/_dynamic.lua +++ /dev/null @@ -1,110 +0,0 @@ -local glob = vim.glob - ---- @class lsp.DynamicCapabilities ---- @field capabilities table<string, lsp.Registration[]> ---- @field client_id number -local M = {} - ---- @param client_id number ---- @return lsp.DynamicCapabilities -function M.new(client_id) - return setmetatable({ - capabilities = {}, - client_id = client_id, - }, { __index = M }) -end - -function M:supports_registration(method) - local client = vim.lsp.get_client_by_id(self.client_id) - if not client then - return false - end - local capability = vim.tbl_get(client.capabilities, unpack(vim.split(method, '/'))) - return type(capability) == 'table' and capability.dynamicRegistration -end - ---- @param registrations lsp.Registration[] -function M:register(registrations) - -- remove duplicates - self:unregister(registrations) - for _, reg in ipairs(registrations) do - local method = reg.method - if not self.capabilities[method] then - self.capabilities[method] = {} - end - table.insert(self.capabilities[method], reg) - end -end - ---- @param unregisterations lsp.Unregistration[] -function M:unregister(unregisterations) - for _, unreg in ipairs(unregisterations) do - local method = unreg.method - if not self.capabilities[method] then - return - end - local id = unreg.id - for i, reg in ipairs(self.capabilities[method]) do - if reg.id == id then - table.remove(self.capabilities[method], i) - break - end - end - end -end - ---- @param method string ---- @param opts? {bufnr: integer?} ---- @return lsp.Registration? (table|nil) the registration if found -function M:get(method, opts) - opts = opts or {} - opts.bufnr = opts.bufnr or vim.api.nvim_get_current_buf() - for _, reg in ipairs(self.capabilities[method] or {}) do - if not reg.registerOptions then - return reg - end - local documentSelector = reg.registerOptions.documentSelector - if not documentSelector then - return reg - end - if self:match(opts.bufnr, documentSelector) then - return reg - end - end -end - ---- @param method string ---- @param opts? {bufnr: integer?} -function M:supports(method, opts) - return self:get(method, opts) ~= nil -end - ---- @param bufnr number ---- @param documentSelector lsp.DocumentSelector ---- @private -function M:match(bufnr, documentSelector) - local client = vim.lsp.get_client_by_id(self.client_id) - if not client then - return false - end - local language = client.get_language_id(bufnr, vim.bo[bufnr].filetype) - local uri = vim.uri_from_bufnr(bufnr) - local fname = vim.uri_to_fname(uri) - for _, filter in ipairs(documentSelector) do - local matches = true - if filter.language and language ~= filter.language then - matches = false - end - if matches and filter.scheme and not vim.startswith(uri, filter.scheme .. ':') then - matches = false - end - if matches and filter.pattern and not glob.to_lpeg(filter.pattern):match(fname) then - matches = false - end - if matches then - return true - end - end -end - -return M diff --git a/runtime/lua/vim/lsp/_tagfunc.lua b/runtime/lua/vim/lsp/_tagfunc.lua index f75d43f373..f6ffc63824 100644 --- a/runtime/lua/vim/lsp/_tagfunc.lua +++ b/runtime/lua/vim/lsp/_tagfunc.lua @@ -59,7 +59,7 @@ local function query_definition(pattern) remaining = remaining - 1 end local params = util.make_position_params(win, client.offset_encoding) - client.request(ms.textDocument_definition, params, on_response, bufnr) + client:request(ms.textDocument_definition, params, on_response, bufnr) end vim.wait(1000, function() return remaining == 0 diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua index 98e9818bcd..248969885c 100644 --- a/runtime/lua/vim/lsp/_watchfiles.lua +++ b/runtime/lua/vim/lsp/_watchfiles.lua @@ -44,9 +44,8 @@ M._poll_exclude_pattern = glob.to_lpeg('**/.git/{objects,subtree-cache}/**') --- Registers the workspace/didChangeWatchedFiles capability dynamically. --- ---@param reg lsp.Registration LSP Registration object. ----@param ctx lsp.HandlerContext Context from the |lsp-handler|. -function M.register(reg, ctx) - local client_id = ctx.client_id +---@param client_id integer Client ID. +function M.register(reg, client_id) local client = assert(vim.lsp.get_client_by_id(client_id), 'Client must be running') -- Ill-behaved servers may not honor the client capability and try to register -- anyway, so ignore requests when the user has opted out of the feature. @@ -117,7 +116,7 @@ function M.register(reg, ctx) local params = { changes = change_queues[client_id], } - client.notify(ms.workspace_didChangeWatchedFiles, params) + client:notify(ms.workspace_didChangeWatchedFiles, params) queue_timers[client_id] = nil change_queues[client_id] = nil change_cache[client_id] = nil @@ -155,9 +154,8 @@ end --- Unregisters the workspace/didChangeWatchedFiles capability dynamically. --- ---@param unreg lsp.Unregistration LSP Unregistration object. ----@param ctx lsp.HandlerContext Context from the |lsp-handler|. -function M.unregister(unreg, ctx) - local client_id = ctx.client_id +---@param client_id integer Client ID. +function M.unregister(unreg, client_id) local client_cancels = cancels[client_id] local reg_cancels = client_cancels[unreg.id] while #reg_cancels > 0 do diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 6d7597c5ff..10c0dbefdc 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -20,6 +20,8 @@ local function client_positional_params(params) end end +local hover_ns = api.nvim_create_namespace('vim_lsp_hover_range') + --- @class vim.lsp.buf.hover.Opts : vim.lsp.util.open_floating_preview.Opts --- @field silent? boolean @@ -30,13 +32,24 @@ end --- In the floating window, all commands and mappings are available as usual, --- except that "q" dismisses the window. --- You can scroll the contents the same as you would any other buffer. +--- +--- Note: to disable hover highlights, add the following to your config: +--- +--- ```lua +--- vim.api.nvim_create_autocmd('ColorScheme', { +--- callback = function() +--- vim.api.nvim_set_hl(0, 'LspReferenceTarget', {}) +--- end, +--- }) +--- ``` --- @param config? vim.lsp.buf.hover.Opts function M.hover(config) config = config or {} config.focus_id = ms.textDocument_hover lsp.buf_request_all(0, ms.textDocument_hover, client_positional_params(), function(results, ctx) - if api.nvim_get_current_buf() ~= ctx.bufnr then + local bufnr = assert(ctx.bufnr) + if api.nvim_get_current_buf() ~= bufnr then -- Ignore result since buffer changed. This happens for slow language servers. return end @@ -67,9 +80,10 @@ function M.hover(config) local format = 'markdown' for client_id, result in pairs(results1) do + local client = assert(lsp.get_client_by_id(client_id)) if nresults > 1 then -- Show client name if there are multiple clients - contents[#contents + 1] = string.format('# %s', lsp.get_client_by_id(client_id).name) + contents[#contents + 1] = string.format('# %s', client.name) end if type(result.contents) == 'table' and result.contents.kind == 'plaintext' then if #results1 == 1 then @@ -87,6 +101,22 @@ function M.hover(config) else vim.list_extend(contents, util.convert_input_to_markdown_lines(result.contents)) end + local range = result.range + if range then + local start = range.start + local end_ = range['end'] + local start_idx = util._get_line_byte_from_position(bufnr, start, client.offset_encoding) + local end_idx = util._get_line_byte_from_position(bufnr, end_, client.offset_encoding) + + vim.hl.range( + bufnr, + hover_ns, + 'LspReferenceTarget', + { start.line, start_idx }, + { end_.line, end_idx }, + { priority = vim.hl.priorities.user } + ) + end contents[#contents + 1] = '---' end @@ -100,7 +130,16 @@ function M.hover(config) return end - lsp.util.open_floating_preview(contents, format, config) + local _, winid = lsp.util.open_floating_preview(contents, format, config) + + api.nvim_create_autocmd('WinClosed', { + pattern = tostring(winid), + once = true, + callback = function() + api.nvim_buf_clear_namespace(bufnr, hover_ns, 0, -1) + return true + end, + }) end) end @@ -193,7 +232,7 @@ local function get_locations(method, opts) end for _, client in ipairs(clients) do local params = util.make_position_params(win, client.offset_encoding) - client.request(method, params, function(_, result) + client:request(method, params, function(_, result) on_response(_, result, client) end) end @@ -529,12 +568,14 @@ function M.format(opts) end if opts.async then + --- @param idx integer + --- @param client vim.lsp.Client local function do_format(idx, client) if not client then return end local params = set_range(client, util.make_formatting_params(opts.formatting_options)) - client.request(method, params, function(...) + client:request(method, params, function(...) local handler = client.handlers[method] or lsp.handlers[method] handler(...) do_format(next(clients, idx)) @@ -545,7 +586,7 @@ function M.format(opts) local timeout_ms = opts.timeout_ms or 1000 for _, client in pairs(clients) do local params = set_range(client, util.make_formatting_params(opts.formatting_options)) - local result, err = client.request_sync(method, params, timeout_ms, bufnr) + local result, err = client:request_sync(method, params, timeout_ms, bufnr) if result and result.result then util.apply_text_edits(result.result, bufnr, client.offset_encoding) elseif err then @@ -609,6 +650,8 @@ function M.rename(new_name, opts) )[1] end + --- @param idx integer + --- @param client? vim.lsp.Client local function try_use_client(idx, client) if not client then return @@ -620,15 +663,15 @@ function M.rename(new_name, opts) params.newName = name local handler = client.handlers[ms.textDocument_rename] or lsp.handlers[ms.textDocument_rename] - client.request(ms.textDocument_rename, params, function(...) + client:request(ms.textDocument_rename, params, function(...) handler(...) try_use_client(next(clients, idx)) end, bufnr) end - if client.supports_method(ms.textDocument_prepareRename) then + if client:supports_method(ms.textDocument_prepareRename) then local params = util.make_position_params(win, client.offset_encoding) - client.request(ms.textDocument_prepareRename, params, function(err, result) + client:request(ms.textDocument_prepareRename, params, function(err, result) if err or result == nil then if next(clients, idx) then try_use_client(next(clients, idx)) @@ -667,7 +710,7 @@ function M.rename(new_name, opts) end, bufnr) else assert( - client.supports_method(ms.textDocument_rename), + client:supports_method(ms.textDocument_rename), 'Client must support textDocument/rename' ) if new_name then @@ -742,7 +785,7 @@ function M.references(context, opts) params.context = context or { includeDeclaration = true, } - client.request(ms.textDocument_references, params, function(_, result) + client:request(ms.textDocument_references, params, function(_, result) local items = util.locations_to_items(result or {}, client.offset_encoding) vim.list_extend(all_items, items) remaining = remaining - 1 @@ -774,7 +817,7 @@ local function request_with_id(client_id, method, params, handler, bufnr) ) return end - client.request(method, params, handler, bufnr) + client:request(method, params, handler, bufnr) end --- @param item lsp.TypeHierarchyItem|lsp.CallHierarchyItem @@ -841,7 +884,7 @@ local function hierarchy(method) for _, client in ipairs(clients) do local params = util.make_position_params(win, client.offset_encoding) --- @param result lsp.CallHierarchyItem[]|lsp.TypeHierarchyItem[]? - client.request(prepare_method, params, function(err, result, ctx) + client:request(prepare_method, params, function(err, result, ctx) if err then vim.notify(err.message, vim.log.levels.WARN) elseif result then @@ -1092,13 +1135,8 @@ local function on_code_action_results(results, opts) local action = choice.action local bufnr = assert(choice.ctx.bufnr, 'Must have buffer number') - local reg = client.dynamic_capabilities:get(ms.textDocument_codeAction, { bufnr = bufnr }) - - local supports_resolve = vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider') - or client.supports_method(ms.codeAction_resolve) - - if not action.edit and client and supports_resolve then - client.request(ms.codeAction_resolve, action, function(err, resolved_action) + if not action.edit and client:supports_method(ms.codeAction_resolve) then + client:request(ms.codeAction_resolve, action, function(err, resolved_action) if err then if action.command then apply_action(action, client, choice.ctx) @@ -1219,7 +1257,7 @@ function M.code_action(opts) }) end - client.request(ms.textDocument_codeAction, params, on_result, bufnr) + client:request(ms.textDocument_codeAction, params, on_result, bufnr) end end diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 2718f40c96..3b79da99ea 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -91,7 +91,7 @@ local validate = vim.validate --- (default: client-id) --- @field name? string --- ---- Language ID as string. Defaults to the filetype. +--- Language ID as string. Defaults to the buffer filetype. --- @field get_language_id? fun(bufnr: integer, filetype: string): string --- --- The encoding that the LSP server expects. Client does not verify this is correct. @@ -216,72 +216,31 @@ local validate = vim.validate --- --- The capabilities provided by the client (editor or tool) --- @field capabilities lsp.ClientCapabilities +--- @field private registrations table<string,lsp.Registration[]> --- @field dynamic_capabilities lsp.DynamicCapabilities --- ---- Sends a request to the server. ---- This is a thin wrapper around {client.rpc.request} with some additional ---- checking. ---- If {handler} is not specified and if there's no respective global ---- handler, then an error will occur. ---- Returns: {status}, {client_id}?. {status} is a boolean indicating if ---- the notification was successful. If it is `false`, then it will always ---- be `false` (the client has shutdown). ---- If {status} is `true`, the function returns {request_id} as the second ---- result. You can use this with `client.cancel_request(request_id)` to cancel ---- the request. ---- @field request fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer? ---- ---- Sends a request to the server and synchronously waits for the response. ---- This is a wrapper around {client.request} ---- Returns: { err=err, result=result }, a dict, where `err` and `result` ---- come from the |lsp-handler|. On timeout, cancel or error, returns `(nil, ---- err)` where `err` is a string describing the failure reason. If the request ---- was unsuccessful returns `nil`. ---- @field request_sync fun(method: string, params: table?, timeout_ms: integer?, bufnr: integer): {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dict ---- ---- Sends a notification to an LSP server. ---- Returns: a boolean to indicate if the notification was successful. If ---- it is false, then it will always be false (the client has shutdown). ---- @field notify fun(method: string, params: table?): boolean ---- ---- Cancels a request with a given request id. ---- Returns: same as `notify()`. ---- @field cancel_request fun(id: integer): boolean ---- ---- Stops a client, optionally with force. ---- By default, it will just ask the server to shutdown without force. ---- If you request to stop a client which has previously been requested to ---- shutdown, it will automatically escalate and force shutdown. ---- @field stop fun(force?: boolean) ---- ---- Runs the on_attach function from the client's config if it was defined. ---- Useful for buffer-local setup. ---- @field on_attach fun(bufnr: integer) ---- --- @field private _before_init_cb? vim.lsp.client.before_init_cb --- @field private _on_attach_cbs vim.lsp.client.on_attach_cb[] --- @field private _on_init_cbs vim.lsp.client.on_init_cb[] --- @field private _on_exit_cbs vim.lsp.client.on_exit_cb[] --- @field private _on_error_cb? fun(code: integer, err: string) ---- ---- Checks if a client supports a given method. ---- Always returns true for unknown off-spec methods. ---- {opts} is a optional `{bufnr?: integer}` table. ---- Some language server capabilities can be file specific. ---- @field supports_method fun(method: string, opts?: {bufnr: integer?}): boolean ---- ---- Checks whether a client is stopped. ---- Returns: true if the client is fully stopped. ---- @field is_stopped fun(): boolean local Client = {} Client.__index = Client ---- @param cls table ---- @param meth any ---- @return function -local function method_wrapper(cls, meth) - return function(...) - return meth(cls, ...) +--- @param obj table<string,any> +--- @param cls table<string,function> +--- @param name string +local function method_wrapper(obj, cls, name) + local meth = assert(cls[name]) + obj[name] = function(...) + local arg = select(1, ...) + if arg and getmetatable(arg) == cls then + -- First argument is self, call meth directly + return meth(...) + end + vim.deprecate('client.' .. name, 'client:' .. name, '0.13') + -- First argument is not self, insert it + return meth(obj, ...) end end @@ -403,18 +362,16 @@ local function get_name(id, config) return tostring(id) end ---- @param workspace_folders lsp.WorkspaceFolder[]? ---- @param root_dir string? +--- @param workspace_folders string|lsp.WorkspaceFolder[]? --- @return lsp.WorkspaceFolder[]? -local function get_workspace_folders(workspace_folders, root_dir) - if workspace_folders then +local function get_workspace_folders(workspace_folders) + if type(workspace_folders) == 'table' then return workspace_folders - end - if root_dir then + elseif type(workspace_folders) == 'string' then return { { - uri = vim.uri_from_fname(root_dir), - name = root_dir, + uri = vim.uri_from_fname(workspace_folders), + name = workspace_folders, }, } end @@ -451,13 +408,13 @@ function Client.create(config) requests = {}, attached_buffers = {}, server_capabilities = {}, - dynamic_capabilities = lsp._dynamic.new(id), + registrations = {}, commands = config.commands or {}, settings = config.settings or {}, flags = config.flags or {}, get_language_id = config.get_language_id or default_get_language_id, capabilities = config.capabilities or lsp.protocol.make_client_capabilities(), - workspace_folders = get_workspace_folders(config.workspace_folders, config.root_dir), + workspace_folders = get_workspace_folders(config.workspace_folders or config.root_dir), root_dir = config.root_dir, _before_init_cb = config.before_init, _on_init_cbs = ensure_list(config.on_init), @@ -478,24 +435,45 @@ function Client.create(config) messages = { name = name, messages = {}, progress = {}, status = {} }, } - self.request = method_wrapper(self, Client._request) - self.request_sync = method_wrapper(self, Client._request_sync) - self.notify = method_wrapper(self, Client._notify) - self.cancel_request = method_wrapper(self, Client._cancel_request) - self.stop = method_wrapper(self, Client._stop) - self.is_stopped = method_wrapper(self, Client._is_stopped) - self.on_attach = method_wrapper(self, Client._on_attach) - self.supports_method = method_wrapper(self, Client._supports_method) + --- @class lsp.DynamicCapabilities + --- @nodoc + self.dynamic_capabilities = { + capabilities = self.registrations, + client_id = id, + register = function(_, registrations) + return self:_register_dynamic(registrations) + end, + unregister = function(_, unregistrations) + return self:_unregister_dynamic(unregistrations) + end, + get = function(_, method, opts) + return self:_get_registration(method, opts and opts.bufnr) + end, + supports_registration = function(_, method) + return self:_supports_registration(method) + end, + supports = function(_, method, opts) + return self:_get_registration(method, opts and opts.bufnr) ~= nil + end, + } --- @type table<string|integer, string> title of unfinished progress sequences by token self.progress.pending = {} --- @type vim.lsp.rpc.Dispatchers local dispatchers = { - notification = method_wrapper(self, Client._notification), - server_request = method_wrapper(self, Client._server_request), - on_error = method_wrapper(self, Client._on_error), - on_exit = method_wrapper(self, Client._on_exit), + notification = function(...) + return self:_notification(...) + end, + server_request = function(...) + return self:_server_request(...) + end, + on_error = function(...) + return self:_on_error(...) + end, + on_exit = function(...) + return self:_on_exit(...) + end, } -- Start the RPC client. @@ -512,6 +490,15 @@ function Client.create(config) setmetatable(self, Client) + method_wrapper(self, Client, 'request') + method_wrapper(self, Client, 'request_sync') + method_wrapper(self, Client, 'notify') + method_wrapper(self, Client, 'cancel_request') + method_wrapper(self, Client, 'stop') + method_wrapper(self, Client, 'is_stopped') + method_wrapper(self, Client, 'on_attach') + method_wrapper(self, Client, 'supports_method') + return self end @@ -595,7 +582,7 @@ function Client:initialize() end if next(self.settings) then - self:_notify(ms.workspace_didChangeConfiguration, { settings = self.settings }) + self:notify(ms.workspace_didChangeConfiguration, { settings = self.settings }) end -- If server is being restarted, make sure to re-attach to any previously attached buffers. @@ -607,7 +594,7 @@ function Client:initialize() for buf in pairs(reattach_bufs) do -- The buffer may have been detached in the on_init callback. if self.attached_buffers[buf] then - self:_on_attach(buf) + self:on_attach(buf) end end @@ -624,14 +611,14 @@ end --- Returns the default handler if the user hasn't set a custom one. --- --- @param method (string) LSP method name ---- @return lsp.Handler|nil handler for the given method, if defined, or the default from |vim.lsp.handlers| +--- @return lsp.Handler? handler for the given method, if defined, or the default from |vim.lsp.handlers| function Client:_resolve_handler(method) return self.handlers[method] or lsp.handlers[method] end --- Returns the buffer number for the given {bufnr}. --- ---- @param bufnr (integer|nil) Buffer number to resolve. Defaults to current buffer +--- @param bufnr integer? Buffer number to resolve. Defaults to current buffer --- @return integer bufnr local function resolve_bufnr(bufnr) validate('bufnr', bufnr, 'number', true) @@ -641,7 +628,6 @@ local function resolve_bufnr(bufnr) return bufnr end ---- @private --- Sends a request to the server. --- --- This is a thin wrapper around {client.rpc.request} with some additional @@ -650,15 +636,14 @@ end --- @param method string LSP method name. --- @param params? table LSP request params. --- @param handler? lsp.Handler Response |lsp-handler| for this method. ---- @param bufnr integer Buffer handle (0 for current). ---- @return boolean status, integer? request_id {status} is a bool indicating ---- whether the request was successful. If it is `false`, then it will ---- always be `false` (the client has shutdown). If it was ---- successful, then it will return {request_id} as the ---- second result. You can use this with `client.cancel_request(request_id)` +--- @param bufnr? integer Buffer handle. 0 for current (default). +--- @return boolean status indicates whether the request was successful. +--- If it is `false`, then it will always be `false` (the client has shutdown). +--- @return integer? request_id Can be used with |Client:cancel_request()|. +--- `nil` is request failed. --- to cancel the-request. --- @see |vim.lsp.buf_request_all()| -function Client:_request(method, params, handler, bufnr) +function Client:request(method, params, handler, bufnr) if not handler then handler = assert( self:_resolve_handler(method), @@ -667,8 +652,8 @@ function Client:_request(method, params, handler, bufnr) end -- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state changetracking.flush(self, bufnr) - local version = lsp.util.buf_versions[bufnr] bufnr = resolve_bufnr(bufnr) + local version = lsp.util.buf_versions[bufnr] log.debug(self._log_prefix, 'client.request', self.id, method, params, handler, bufnr) local success, request_id = self.rpc.request(method, params, function(err, result) local context = { @@ -722,29 +707,27 @@ local function err_message(...) end end ---- @private --- Sends a request to the server and synchronously waits for the response. --- ---- This is a wrapper around {client.request} +--- This is a wrapper around |Client:request()| --- ---- @param method (string) LSP method name. ---- @param params (table) LSP request params. ---- @param timeout_ms (integer|nil) Maximum time in milliseconds to wait for +--- @param method string LSP method name. +--- @param params table LSP request params. +--- @param timeout_ms integer? Maximum time in milliseconds to wait for --- a result. Defaults to 1000 ---- @param bufnr (integer) Buffer handle (0 for current). ---- @return {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dict, where ---- `err` and `result` come from the |lsp-handler|. ---- On timeout, cancel or error, returns `(nil, err)` where `err` is a ---- string describing the failure reason. If the request was unsuccessful ---- returns `nil`. +--- @param bufnr integer Buffer handle (0 for current). +--- @return {err: lsp.ResponseError?, result:any}? `result` and `err` from the |lsp-handler|. +--- `nil` is the request was unsuccessful +--- @return string? err On timeout, cancel or error, where `err` is a +--- string describing the failure reason. --- @see |vim.lsp.buf_request_sync()| -function Client:_request_sync(method, params, timeout_ms, bufnr) +function Client:request_sync(method, params, timeout_ms, bufnr) local request_result = nil local function _sync_handler(err, result) request_result = { err = err, result = result } end - local success, request_id = self:_request(method, params, _sync_handler, bufnr) + local success, request_id = self:request(method, params, _sync_handler, bufnr) if not success then return nil end @@ -755,22 +738,20 @@ function Client:_request_sync(method, params, timeout_ms, bufnr) if not wait_result then if request_id then - self:_cancel_request(request_id) + self:cancel_request(request_id) end return nil, wait_result_reason[reason] end return request_result end ---- @package --- Sends a notification to an LSP server. --- --- @param method string LSP method name. ---- @param params table|nil LSP request params. ---- @return boolean status true if the notification was successful. ---- If it is false, then it will always be false ---- (the client has shutdown). -function Client:_notify(method, params) +--- @param params table? LSP request params. +--- @return boolean status indicating if the notification was successful. +--- If it is false, then the client has shutdown. +function Client:notify(method, params) if method ~= ms.textDocument_didChange then changetracking.flush(self) end @@ -793,13 +774,12 @@ function Client:_notify(method, params) return client_active end ---- @private --- Cancels a request with a given request id. --- ---- @param id (integer) id of request to cancel ---- @return boolean status true if notification was successful. false otherwise ---- @see |vim.lsp.client.notify()| -function Client:_cancel_request(id) +--- @param id integer id of request to cancel +--- @return boolean status indicating if the notification was successful. +--- @see |Client:notify()| +function Client:cancel_request(id) validate('id', id, 'number') local request = self.requests[id] if request and request.type == 'pending' then @@ -813,15 +793,14 @@ function Client:_cancel_request(id) return self.rpc.notify(ms.dollar_cancelRequest, { id = id }) end ---- @private --- Stops a client, optionally with force. --- ---- By default, it will just ask the - server to shutdown without force. If +--- By default, it will just request the server to shutdown without force. If --- you request to stop a client which has previously been requested to --- shutdown, it will automatically escalate and force shutdown. --- ---- @param force boolean|nil -function Client:_stop(force) +--- @param force? boolean +function Client:stop(force) local rpc = self.rpc if rpc.is_closing() then @@ -846,12 +825,110 @@ function Client:_stop(force) end) end +--- Get options for a method that is registered dynamically. +--- @param method string +function Client:_supports_registration(method) + local capability = vim.tbl_get(self.capabilities, unpack(vim.split(method, '/'))) + return type(capability) == 'table' and capability.dynamicRegistration +end + +--- @private +--- @param registrations lsp.Registration[] +function Client:_register_dynamic(registrations) + -- remove duplicates + self:_unregister_dynamic(registrations) + for _, reg in ipairs(registrations) do + local method = reg.method + if not self.registrations[method] then + self.registrations[method] = {} + end + table.insert(self.registrations[method], reg) + end +end + +--- @param registrations lsp.Registration[] +function Client:_register(registrations) + self:_register_dynamic(registrations) + + local unsupported = {} --- @type string[] + + for _, reg in ipairs(registrations) do + local method = reg.method + if method == ms.workspace_didChangeWatchedFiles then + vim.lsp._watchfiles.register(reg, self.id) + elseif not self:_supports_registration(method) then + unsupported[#unsupported + 1] = method + end + end + + if #unsupported > 0 then + local warning_tpl = 'The language server %s triggers a registerCapability ' + .. 'handler for %s despite dynamicRegistration set to false. ' + .. 'Report upstream, this warning is harmless' + log.warn(string.format(warning_tpl, self.name, table.concat(unsupported, ', '))) + end +end + +--- @private +--- @param unregistrations lsp.Unregistration[] +function Client:_unregister_dynamic(unregistrations) + for _, unreg in ipairs(unregistrations) do + local sreg = self.registrations[unreg.method] + -- Unegister dynamic capability + for i, reg in ipairs(sreg or {}) do + if reg.id == unreg.id then + table.remove(sreg, i) + break + end + end + end +end + +--- @param unregistrations lsp.Unregistration[] +function Client:_unregister(unregistrations) + self:_unregister_dynamic(unregistrations) + for _, unreg in ipairs(unregistrations) do + if unreg.method == ms.workspace_didChangeWatchedFiles then + vim.lsp._watchfiles.unregister(unreg, self.id) + end + end +end + --- @private +function Client:_get_language_id(bufnr) + return self.get_language_id(bufnr, vim.bo[bufnr].filetype) +end + +--- @param method string +--- @param bufnr? integer +--- @return lsp.Registration? +function Client:_get_registration(method, bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + for _, reg in ipairs(self.registrations[method] or {}) do + if not reg.registerOptions or not reg.registerOptions.documentSelector then + return reg + end + local documentSelector = reg.registerOptions.documentSelector + local language = self:_get_language_id(bufnr) + local uri = vim.uri_from_bufnr(bufnr) + local fname = vim.uri_to_fname(uri) + for _, filter in ipairs(documentSelector) do + if + not (filter.language and language ~= filter.language) + and not (filter.scheme and not vim.startswith(uri, filter.scheme .. ':')) + and not (filter.pattern and not vim.glob.to_lpeg(filter.pattern):match(fname)) + then + return reg + end + end + end +end + --- Checks whether a client is stopped. --- --- @return boolean # true if client is stopped or in the process of being --- stopped; false otherwise -function Client:_is_stopped() +function Client:is_stopped() return self.rpc.is_closing() end @@ -893,7 +970,7 @@ function Client:exec_cmd(command, context, handler) command = cmdname, arguments = command.arguments, } - self.request(ms.workspace_executeCommand, params, handler, context.bufnr) + self:request(ms.workspace_executeCommand, params, handler, context.bufnr) end --- Default handler for the 'textDocument/didOpen' LSP notification. @@ -901,19 +978,18 @@ end --- @param bufnr integer Number of the buffer, or 0 for current function Client:_text_document_did_open_handler(bufnr) changetracking.init(self, bufnr) - if not self.supports_method(ms.textDocument_didOpen) then + if not self:supports_method(ms.textDocument_didOpen) then return end if not api.nvim_buf_is_loaded(bufnr) then return end - local filetype = vim.bo[bufnr].filetype - self.notify(ms.textDocument_didOpen, { + self:notify(ms.textDocument_didOpen, { textDocument = { version = lsp.util.buf_versions[bufnr], uri = vim.uri_from_bufnr(bufnr), - languageId = self.get_language_id(bufnr, filetype), + languageId = self:_get_language_id(bufnr), text = lsp._buf_get_full_text(bufnr), }, }) @@ -930,8 +1006,9 @@ function Client:_text_document_did_open_handler(bufnr) end --- Runs the on_attach function from the client's config if it was defined. +--- Useful for buffer-local setup. --- @param bufnr integer Buffer number -function Client:_on_attach(bufnr) +function Client:on_attach(bufnr) self:_text_document_did_open_handler(bufnr) lsp._set_defaults(self, bufnr) @@ -966,10 +1043,18 @@ function Client:write_error(code, err) err_message(self._log_prefix, ': Error ', client_error, ': ', vim.inspect(err)) end ---- @private +--- Checks if a client supports a given method. +--- Always returns true for unknown off-spec methods. +--- +--- Note: Some language server capabilities can be file specific. --- @param method string ---- @param opts? {bufnr: integer?} -function Client:_supports_method(method, opts) +--- @param bufnr? integer +function Client:supports_method(method, bufnr) + -- Deprecated form + if type(bufnr) == 'table' then + --- @diagnostic disable-next-line:no-unknown + bufnr = bufnr.bufnr + end 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 @@ -978,12 +1063,37 @@ function Client:_supports_method(method, opts) if vim.tbl_get(self.server_capabilities, unpack(required_capability)) then return true end - if self.dynamic_capabilities:supports_registration(method) then - return self.dynamic_capabilities:supports(method, opts) + + local rmethod = lsp._resolve_to_request[method] + if rmethod then + if self:_supports_registration(rmethod) then + local reg = self:_get_registration(rmethod, bufnr) + return vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider') or false + end + else + if self:_supports_registration(method) then + return self:_get_registration(method, bufnr) ~= nil + end end return false end +--- Get options for a method that is registered dynamically. +--- @param method string +--- @param bufnr? integer +--- @return lsp.LSPAny? +function Client:_get_registration_options(method, bufnr) + if not self:_supports_registration(method) then + return + end + + local reg = self:_get_registration(method, bufnr) + + if reg then + return reg.registerOptions + end +end + --- @private --- Handles a notification sent by an LSP server by invoking the --- corresponding handler. @@ -1061,9 +1171,9 @@ function Client:_add_workspace_folder(dir) end end - local wf = assert(get_workspace_folders(nil, dir)) + local wf = assert(get_workspace_folders(dir)) - self:_notify(ms.workspace_didChangeWorkspaceFolders, { + self:notify(ms.workspace_didChangeWorkspaceFolders, { event = { added = wf, removed = {} }, }) @@ -1076,9 +1186,9 @@ end --- Remove a directory to the workspace folders. --- @param dir string? function Client:_remove_workspace_folder(dir) - local wf = assert(get_workspace_folders(nil, dir)) + local wf = assert(get_workspace_folders(dir)) - self:_notify(ms.workspace_didChangeWorkspaceFolders, { + self:notify(ms.workspace_didChangeWorkspaceFolders, { event = { added = {}, removed = wf }, }) diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index fdbdda695a..a11f84d6c6 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -231,7 +231,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback) countdown() else assert(client) - client.request(ms.codeLens_resolve, lens, function(_, result) + client:request(ms.codeLens_resolve, lens, function(_, result) if api.nvim_buf_is_loaded(bufnr) and result and result.command then lens.command = result.command -- Eager display to have some sort of incremental feedback diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index 92bc110a97..0f388a88fd 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -404,7 +404,7 @@ local function request(clients, bufnr, win, callback) for _, client in pairs(clients) do local client_id = client.id local params = lsp.util.make_position_params(win, client.offset_encoding) - local ok, request_id = client.request(ms.textDocument_completion, params, function(err, result) + local ok, request_id = client:request(ms.textDocument_completion, params, function(err, result) responses[client_id] = { err = err, result = result } remaining_requests = remaining_requests - 1 if remaining_requests == 0 then @@ -421,7 +421,7 @@ local function request(clients, bufnr, win, callback) for client_id, request_id in pairs(request_ids) do local client = lsp.get_client_by_id(client_id) if client then - client.cancel_request(request_id) + client:cancel_request(request_id) end end end @@ -582,7 +582,7 @@ local function on_complete_done() local changedtick = vim.b[bufnr].changedtick --- @param result lsp.CompletionItem - client.request(ms.completionItem_resolve, completion_item, function(err, result) + client:request(ms.completionItem_resolve, completion_item, function(err, result) if changedtick ~= vim.b[bufnr].changedtick then return end diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 2b7aefe0e1..5c28d88b38 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -122,46 +122,19 @@ end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability --- @param params lsp.RegistrationParams RSC[ms.client_registerCapability] = function(_, params, ctx) - local client_id = ctx.client_id - local client = assert(vim.lsp.get_client_by_id(client_id)) - - client.dynamic_capabilities:register(params.registrations) - for bufnr, _ in pairs(client.attached_buffers) do + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) + client:_register(params.registrations) + for bufnr in pairs(client.attached_buffers) do vim.lsp._set_defaults(client, bufnr) end - - ---@type string[] - local unsupported = {} - for _, reg in ipairs(params.registrations) do - if reg.method == ms.workspace_didChangeWatchedFiles then - vim.lsp._watchfiles.register(reg, ctx) - elseif not client.dynamic_capabilities:supports_registration(reg.method) then - unsupported[#unsupported + 1] = reg.method - end - end - if #unsupported > 0 then - local warning_tpl = 'The language server %s triggers a registerCapability ' - .. 'handler for %s despite dynamicRegistration set to false. ' - .. 'Report upstream, this warning is harmless' - local client_name = client and client.name or string.format('id=%d', client_id) - local warning = string.format(warning_tpl, client_name, table.concat(unsupported, ', ')) - log.warn(warning) - end return vim.NIL end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_unregisterCapability --- @param params lsp.UnregistrationParams RSC[ms.client_unregisterCapability] = function(_, params, ctx) - local client_id = ctx.client_id - local client = assert(vim.lsp.get_client_by_id(client_id)) - client.dynamic_capabilities:unregister(params.unregisterations) - - for _, unreg in ipairs(params.unregisterations) do - if unreg.method == ms.workspace_didChangeWatchedFiles then - vim.lsp._watchfiles.unregister(unreg, ctx) - end - end + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) + client:_unregister(params.unregisterations) return vim.NIL end @@ -173,8 +146,7 @@ RSC[ms.workspace_applyEdit] = function(_, params, ctx) 'workspace/applyEdit must be called with `ApplyWorkspaceEditParams`. Server is violating the specification' ) -- TODO(ashkan) Do something more with label? - local client_id = ctx.client_id - local client = assert(vim.lsp.get_client_by_id(client_id)) + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) if params.label then print('Workspace edit', params.label) end @@ -196,12 +168,11 @@ end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration --- @param params lsp.ConfigurationParams RSC[ms.workspace_configuration] = function(_, params, ctx) - local client_id = ctx.client_id - local client = vim.lsp.get_client_by_id(client_id) + local client = vim.lsp.get_client_by_id(ctx.client_id) if not client then err_message( 'LSP[', - client_id, + ctx.client_id, '] client has shut down after sending a workspace/configuration request' ) return @@ -229,10 +200,9 @@ end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_workspaceFolders RSC[ms.workspace_workspaceFolders] = function(_, _, ctx) - local client_id = ctx.client_id - local client = vim.lsp.get_client_by_id(client_id) + local client = vim.lsp.get_client_by_id(ctx.client_id) if not client then - err_message('LSP[id=', client_id, '] client has shut down after sending the message') + err_message('LSP[id=', ctx.client_id, '] client has shut down after sending the message') return end return client.workspace_folders or vim.NIL diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index ca7bc3b022..f1a58de621 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -65,7 +65,7 @@ function M.on_inlayhint(err, result, ctx) if num_unprocessed == 0 then client_hints[client_id] = {} bufstate.version = ctx.version - api.nvim__redraw({ buf = bufnr, valid = true }) + api.nvim__redraw({ buf = bufnr, valid = true, flush = false }) return end @@ -81,7 +81,7 @@ function M.on_inlayhint(err, result, ctx) client_hints[client_id] = new_lnum_hints bufstate.version = ctx.version - api.nvim__redraw({ buf = bufnr, valid = true }) + api.nvim__redraw({ buf = bufnr, valid = true, flush = false }) end --- |lsp-handler| for the method `workspace/inlayHint/refresh` @@ -122,12 +122,12 @@ end --- local hint = vim.lsp.inlay_hint.get({ bufnr = 0 })[1] -- 0 for current buffer --- --- local client = vim.lsp.get_client_by_id(hint.client_id) ---- local resp = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0) +--- local resp = client:request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0) --- local resolved_hint = assert(resp and resp.result, resp.err) --- vim.lsp.util.apply_text_edits(resolved_hint.textEdits, 0, client.encoding) --- --- location = resolved_hint.label[1].location ---- client.request('textDocument/hover', { +--- client:request('textDocument/hover', { --- textDocument = { uri = location.uri }, --- position = location.range.start, --- }) @@ -215,7 +215,7 @@ local function clear(bufnr) end end api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) - api.nvim__redraw({ buf = bufnr, valid = true }) + api.nvim__redraw({ buf = bufnr, valid = true, flush = false }) end --- Disable inlay hints for a buffer diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 4f177b47fd..ec78dd3dc5 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -32,12 +32,12 @@ local function notify(msg, level) end end -local logfilename = vim.fs.joinpath(vim.fn.stdpath('log'), 'lsp.log') +local logfilename = vim.fs.joinpath(vim.fn.stdpath('log') --[[@as string]], 'lsp.log') -- TODO: Ideally the directory should be created in open_logfile(), right -- before opening the log file, but open_logfile() can be called from libuv -- callbacks, where using fn.mkdir() is not allowed. -vim.fn.mkdir(vim.fn.stdpath('log'), 'p') +vim.fn.mkdir(vim.fn.stdpath('log') --[[@as string]], 'p') --- Returns the log filename. ---@return string log filename @@ -82,6 +82,7 @@ end for level, levelnr in pairs(log_levels) do -- Also export the log level on the root object. + ---@diagnostic disable-next-line: no-unknown log[level] = levelnr -- Add a reverse lookup. @@ -93,7 +94,7 @@ end --- @return fun(...:any): boolean? local function create_logger(level, levelnr) return function(...) - if levelnr < current_log_level then + if not log.should_log(levelnr) then return false end local argc = select('#', ...) @@ -169,7 +170,7 @@ end --- Checks whether the level is sufficient for logging. ---@param level integer log level ----@return bool : true if would log, false if not +---@return boolean : true if would log, false if not function log.should_log(level) return level >= current_log_level end diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 215e5f41aa..01421fea29 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -273,7 +273,7 @@ function STHighlighter:send_request() if client and current_result.version ~= version and active_request.version ~= version then -- cancel stale in-flight request if active_request.request_id then - client.cancel_request(active_request.request_id) + client:cancel_request(active_request.request_id) active_request = {} state.active_request = active_request end @@ -288,7 +288,7 @@ function STHighlighter:send_request() method = method .. '/delta' params.previousResultId = current_result.result_id end - local success, request_id = client.request(method, params, function(err, response, ctx) + local success, request_id = client:request(method, params, function(err, response, ctx) -- look client up again using ctx.client_id instead of using a captured -- client object local c = vim.lsp.get_client_by_id(ctx.client_id) @@ -519,7 +519,7 @@ function STHighlighter:reset() if state.active_request.request_id then local client = vim.lsp.get_client_by_id(client_id) assert(client) - client.cancel_request(state.active_request.request_id) + client:cancel_request(state.active_request.request_id) state.active_request = {} end end @@ -547,7 +547,7 @@ function STHighlighter:mark_dirty(client_id) if state.active_request.request_id then local client = vim.lsp.get_client_by_id(client_id) assert(client) - client.cancel_request(state.active_request.request_id) + client:cancel_request(state.active_request.request_id) state.active_request = {} end end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 6eab0f3da4..cfa8a194d9 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1848,12 +1848,11 @@ function M.try_trim_markdown_code_blocks(lines) end ---@param window integer?: window handle or 0 for current, defaults to current ----@param offset_encoding? 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of buffer of `window` +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32' local function make_position_param(window, offset_encoding) window = window or 0 local buf = api.nvim_win_get_buf(window) local row, col = unpack(api.nvim_win_get_cursor(window)) - offset_encoding = offset_encoding or M._get_offset_encoding(buf) row = row - 1 local line = api.nvim_buf_get_lines(buf, row, row + 1, true)[1] if not line then @@ -1868,13 +1867,19 @@ end --- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. --- ---@param window integer?: window handle or 0 for current, defaults to current ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of buffer of `window` +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32' ---@return lsp.TextDocumentPositionParams ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams function M.make_position_params(window, offset_encoding) window = window or 0 local buf = api.nvim_win_get_buf(window) - offset_encoding = offset_encoding or M._get_offset_encoding(buf) + if offset_encoding == nil then + vim.notify_once( + 'warning: offset_encoding is required, using the offset_encoding from the first client', + vim.log.levels.WARN + ) + offset_encoding = M._get_offset_encoding(buf) + end return { textDocument = M.make_text_document_params(buf), position = make_position_param(window, offset_encoding), @@ -1882,6 +1887,7 @@ function M.make_position_params(window, offset_encoding) end --- Utility function for getting the encoding of the first LSP client on the given buffer. +---@deprecated ---@param bufnr integer buffer handle or 0 for current, defaults to current ---@return string encoding first client if there is one, nil otherwise function M._get_offset_encoding(bufnr) @@ -1904,7 +1910,7 @@ function M._get_offset_encoding(bufnr) offset_encoding = this_offset_encoding elseif offset_encoding ~= this_offset_encoding then vim.notify_once( - 'warning: multiple different client offset_encodings detected for buffer, this is not supported yet', + 'warning: multiple different client offset_encodings detected for buffer, vim.lsp.util._get_offset_encoding() uses the offset_encoding from the first client', vim.log.levels.WARN ) end @@ -1919,12 +1925,17 @@ end --- `textDocument/rangeFormatting`. --- ---@param window integer? window handle or 0 for current, defaults to current ----@param offset_encoding "utf-8"|"utf-16"|"utf-32"? defaults to `offset_encoding` of first client of buffer of `window` ----@return table { textDocument = { uri = `current_file_uri` }, range = { start = ----`current_position`, end = `current_position` } } +---@param offset_encoding "utf-8"|"utf-16"|"utf-32" +---@return { textDocument: { uri: lsp.DocumentUri }, range: lsp.Range } function M.make_range_params(window, offset_encoding) local buf = api.nvim_win_get_buf(window or 0) - offset_encoding = offset_encoding or M._get_offset_encoding(buf) + if offset_encoding == nil then + vim.notify_once( + 'warning: offset_encoding is required, using the offset_encoding from the first client', + vim.log.levels.WARN + ) + offset_encoding = M._get_offset_encoding(buf) + end local position = make_position_param(window, offset_encoding) return { textDocument = M.make_text_document_params(buf), @@ -1940,15 +1951,20 @@ end ---@param end_pos [integer,integer]? {row,col} mark-indexed position. --- Defaults to the end of the last visual selection. ---@param bufnr integer? buffer handle or 0 for current, defaults to current ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of `bufnr` ----@return table { textDocument = { uri = `current_file_uri` }, range = { start = ----`start_position`, end = `end_position` } } +---@param offset_encoding 'utf-8'|'utf-16'|'utf-32' +---@return { textDocument: { uri: lsp.DocumentUri }, range: lsp.Range } function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) validate('start_pos', start_pos, 'table', true) validate('end_pos', end_pos, 'table', true) validate('offset_encoding', offset_encoding, 'string', true) bufnr = bufnr or api.nvim_get_current_buf() - offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) + if offset_encoding == nil then + vim.notify_once( + 'warning: offset_encoding is required, using the offset_encoding from the first client', + vim.log.levels.WARN + ) + offset_encoding = M._get_offset_encoding(bufnr) + end --- @type [integer, integer] local A = { unpack(start_pos or api.nvim_buf_get_mark(bufnr, '<')) } --- @type [integer, integer] @@ -2122,7 +2138,7 @@ function M._refresh(method, opts) local first = vim.fn.line('w0', window) local last = vim.fn.line('w$', window) for _, client in ipairs(clients) do - client.request(method, { + client:request(method, { textDocument = textDocument, range = make_line_range_params(bufnr, first - 1, last - 1, client.offset_encoding), }, nil, bufnr) @@ -2131,7 +2147,7 @@ function M._refresh(method, opts) end else for _, client in ipairs(clients) do - client.request(method, { + client:request(method, { textDocument = textDocument, range = make_line_range_params( bufnr, diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 7bdcdc774a..8ce8652f7d 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -93,9 +93,6 @@ function TSHighlighter.new(tree, opts) opts = opts or {} ---@type { queries: table<string,string> } self.tree = tree tree:register_cbs({ - on_bytes = function(...) - self:on_bytes(...) - end, on_detach = function() self:on_detach() end, @@ -215,13 +212,6 @@ function TSHighlighter:for_each_highlight_state(fn) end ---@package ----@param start_row integer ----@param new_end integer -function TSHighlighter:on_bytes(_, _, start_row, _, _, _, _, _, new_end) - api.nvim__redraw({ buf = self.bufnr, range = { start_row, start_row + new_end + 1 } }) -end - ----@package function TSHighlighter:on_detach() self:destroy() end @@ -230,7 +220,7 @@ end ---@param changes Range6[] function TSHighlighter:on_changedtree(changes) for _, ch in ipairs(changes) do - api.nvim__redraw({ buf = self.bufnr, range = { ch[1], ch[4] + 1 } }) + api.nvim__redraw({ buf = self.bufnr, range = { ch[1], ch[4] + 1 }, flush = false }) end end diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index fd68c2b910..4b42164dc8 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -1037,7 +1037,7 @@ end --- Registers callbacks for the [LanguageTree]. ---@param cbs table<TSCallbackNameOn,function> An [nvim_buf_attach()]-like table argument with the following handlers: ---- - `on_bytes` : see [nvim_buf_attach()], but this will be called _after_ the parsers callback. +--- - `on_bytes` : see [nvim_buf_attach()]. --- - `on_changedtree` : a callback that will be called every time the tree has syntactical changes. --- It will be passed two arguments: a table of the ranges (as node ranges) that --- changed and the changed tree. diff --git a/runtime/syntax/checkhealth.vim b/runtime/syntax/checkhealth.vim index a4f6e016cb..14c80640ba 100644 --- a/runtime/syntax/checkhealth.vim +++ b/runtime/syntax/checkhealth.vim @@ -1,6 +1,5 @@ " Vim syntax file " Language: Nvim :checkhealth buffer -" Last Change: 2022 Nov 10 if exists("b:current_syntax") finish diff --git a/runtime/syntax/hyprlang.vim b/runtime/syntax/hyprlang.vim new file mode 100644 index 0000000000..f36c58c646 --- /dev/null +++ b/runtime/syntax/hyprlang.vim @@ -0,0 +1,58 @@ +" Vim syntax file +" Language: hyprlang +" Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Last Change: 2024 nov 15 + +if exists("b:current_syntax") + finish +endif +let b:current_syntax = "hyprlang" + +syn case ignore + +syn match hyprCommand '^\s*\zs\S\+\ze\s*=' contains=hyprVariable +syn match hyprValue '=\s*\zs.\+\ze$' contains=hyprNumber,hyprFloat,hyprBoolean,hyprString,hyprColor,hyprModifier,hyprVariable,hyprComment + +syn match hyprVariable '\$\w\+' contained + +" Category +syn region hyprCategory matchgroup=hyprCategoryD start='^\s*\k\+\s*{' end='^\s*}' contains=hyprCommand,hyprValue,hyprComment,hyprCategory,hyprCategoryD + +" Variables Types +syn match hyprNumber '\%[-+]\<\d\+\>\%[%]' contained +syn match hyprFloat '\%[-+]\<\d\+\.\d\+\>\%[%]' contained +syn match hyprString '["\'].*["\']' contained +syn match hyprColor 'rgb(\(\w\|\d\)\{6})' contained +syn match hyprColor 'rgba(\(\w\|\d\)\{8})' contained +syn match hyprColor '0x\(\w\|\d\)\{8}' contained +syn keyword hyprBoolean true false yes no on off contained + +" Super Shift Alt Ctrl Control +syn keyword hyprModifier contained + \ super supershift superalt superctrl supercontrol + \ super_shift super_alt super_ctrl super_control + \ shift shiftsuper shiftalt shiftctrl shiftcontrol + \ shift_super shift_alt shift_ctrl shift_control + \ alt altsuper altshift altctrl altcontrol + \ alt_super alt_shift alt_ctrl alt_control + \ ctrl ctrlsuper ctrlshift ctrlalt ctrlcontrol + \ ctrl_super ctrl_shift ctrl_alt ctrl_control + \ control controlsuper controlshift controlalt controlctrl + \ control_super control_shift control_alt control_ctrl + +" Comments +syn match hyprComment '#.*$' + +" Link to default groups +hi def link hyprVariable Identifier +hi def link hyprCategoryD Special +hi def link hyprComment Comment +hi def link hyprNumber Constant +hi def link hyprModifier Constant +hi def link hyprFloat hyprNumber +hi def link hyprBoolean Boolean +hi def link hyprString String +hi def link hyprColor Structure +hi def link hyprCommand Keyword + +" vim: ts=8 sts=2 sw=2 et diff --git a/runtime/syntax/karel.vim b/runtime/syntax/karel.vim new file mode 100644 index 0000000000..85c78529e6 --- /dev/null +++ b/runtime/syntax/karel.vim @@ -0,0 +1,112 @@ +" Vim syntax file +" Language: KAREL +" Last Change: 2024-11-17 +" Maintainer: Kirill Morozov <kirill@robotix.pro> +" Credits: Jay Strybis for the initial implementation and Patrick Knosowski +" for a couple of fixes. + +if exists("b:current_syntax") + finish +endif + +" KAREL is case-insensitive +syntax case ignore + +" Identifiers +syn match karelIdentifier /[a-zA-Z0-9_]\+/ +hi def link karelIdentifier Identifier + +" Constants +syn keyword karelConstant CR +syn region karelString start="'" end="'" +syn match karelReal /\d\+\.\d\+/ +syn match karelInteger /\d\+/ +syn keyword karelBoolean true false +hi def link karelConstant Constant +hi def link karelString String +hi def link karelInteger Number +hi def link karelReal Float +hi def link karelBoolean Boolean + +" Directives +syn match karelDirective /%[a-zA-Z]\+/ +hi def link karelDirective PreProc + +" Operators +syn keyword karelOperator AND OR NOT DIV MOD +syn match karelOperator /[\+\-\*\/\<\=\>\:\#\@]/ +syn match karelOperator /<=/ +syn match karelOperator />=/ +syn match karelOperator /<>/ +syn match karelOperator />=</ +hi def link karelOperator Operator + +" Types +syn keyword karelType ARRAY BOOLEAN BYTE CONFIG DISP_DAT_T FILE INTEGER JOINTPOS PATH POSITION QUEUE_TYPE REAL SHORT STD_PTH_NODE STRING VECTOR XYZWPR XYZWPREXT +syn keyword karelStructure STRUCTURE ENDSTRUCTURE +hi def link karelType Type +hi def link karelStructure Typedef + +syn keyword karelAction NOABORT NOMESSAGE NOPAUSE PAUSE PULSE RESUME STOP UNHOLD UNPAUSE +syn match karelAction /SIGNAL EVENT/ +syn match karelAction /SIGNAL SEMAPHORE/ +hi def link karelAction Keyword + +syn keyword karelFunction ABS ACOS APPROACH ARRAY_LEN ASIN ATAN2 ATTACH BYNAME BYTES_LEFT CHR COS CURJPOS CURPOS CURR_PROG EXP +syn keyword karelFunction FRAME GET_FILE_POS GET_JPOS_REG GET_JPOS_TPE GET_PORT_ATR GET_POS_REG GET_POS_TPE GET_USEC_TIM INDEX +syn keyword karelFunction IN_RANGE INV IO_STATUS J_IN_RANGE JOINT2POS LN MIRROR MOTION_CTL NODE_SIZE ORD ORIENT PATH_LEN POS POS2JOINT +syn keyword karelFunction ROUND SEMA_COUNT SIN SQRT STR_LEN SUB_STR TAN TRUNC UNINIT +hi def link karelFunction Function + +syn keyword karelClause EVAL FROM IN WHEN WITH +hi def link karelClause Keyword + +syn keyword karelConditional IF THEN ELSE ENDIF SELECT ENDSELECT CASE +hi def link karelConditional Conditional + +syn keyword karelRepeat WHILE DO ENDWHILE FOR +hi def link karelRepeat Repeat + +syn keyword karelProcedure ABORT_TASK ACT_SCREEN ACT_TBL ADD_BYNAMEPC ADD_DICT ADD_INTPC ADD_REALPC ADD_STRINGPC APPEND_NODE APPEND_QUEUE +syn keyword karelProcedure ATT_WINDOW_D ATT_WINDOW_S AVL_POS_NUM +syn keyword karelProcedure BYTES_AHEAD +syn keyword karelProcedure CALL_PROG CALL_PROGLIN CHECK_DICT CHECK_EPOS CHECK_NAME CLEAR CLEAR_SEMA CLOSE_TEP CLR_IO_STAT CLR_PORT_SIM CLR_POS_REG +syn keyword karelProcedure CNC_DYN_DISB CNC_DYN_DISE CNC_DYN_DISI CNC_DYN_DISP CNC_DYN_DISR CNC_DYN_DISS CNCL_STP_MTN CNV_CNF_STRG CNV_CONF_STR CNV_INT_STR CNV_JPOS_REL CNV_REAL_STR CNV_REL_JPOS CNV_STR_CONF CNV_STR_INT CNV_STR_REAL CNV_STR_TIME CNV_TIME_STR +syn keyword karelProcedure COMPARE_FILE CONT_TASK COPY_FILE COPY_PATH COPY_QUEUE COPY_TPE CREATE_TPE CREATE_VAR +syn keyword karelProcedure DAQ_CHECKP DAQ_REGPIPE DAQ_START DAQ_STOP DAQ_UNREG DAQ_WRITE DEF_SCREEN DEF_WINDOW +syn keyword karelProcedure DELETE_FILE DELETE_NODE DELETE_QUEUE DEL_INST_TPE DET_WINDOW DISCTRL_ALPH DISCTRL_FORM DISCTRL_LIST DISCTRL_PLMN DISCTRL_SBMN DISCTRL_TBL DISMOUNT_DEV DOSFILE_INF +syn keyword karelProcedure ERR_DATA FILE_LIST FORCE_SPMENU FORMAT_DEV GET_ATTR_PRG GET_PORT_ASG GET_PORT_CMT GET_PORT_MOD GET_PORT_SIM GET_PORT_VAL GET_POS_FRM GET_POS_TYP GET_PREG_CMT GET_QUEUE +syn keyword karelProcedure GET_REG GET_REG_CMT GET_SREG_CMT GET_STR_REG GET_TIME GET_TPE_CMT GET_TPE_PRM GET_TSK_INFO GET_USEC_SUB GET_VAR +syn keyword karelProcedure INI_DYN_DISB INI_DYN_DISE INI_DYN_DISI INI_DYN_DISP INI_DYN_DISR INI_DYN_DISS INIT_QUEUE INIT_TBL INSERT_NODE INSERT_QUEUE IO_MOD_TYPE +syn keyword karelProcedure KCL KCL_NO_WAIT KCL_STATUS LOAD LOAD_STATUS LOCK_GROUP MODIFY_QUEUE MOUNT_DEV MOVE_FILE MSG_CONNECT MSG_DISO MSG_PING +syn keyword karelProcedure OPEN_TPE PAUSE_TASK PEND_SEMA PIPE_CONFIG POP_KEY_RD POS_REG_TYPE POST_ERR POST_ERR_L POST_SEMA PRINT_FILE PROG_BACKUP PROG_CLEAR PROG_RESTORE PROG_LIST +syn keyword karelProcedure PURGE_DEV PUSH_KEY_RD READ_DICT READ_DICT_V READ_KB REMOVE_DICT RENAME_FILE RENAME_VAR RENAME_VARS RESET RUN_TASK SAVE SAVE_DRAM SELECT_TPE SEND_DATAPC SEND_EVENTPC SET_ATTR_PRG SET_CURSOR SET_EPOS_REG SET_EPOS_TPE +syn keyword karelProcedure SET_FILE_ATR SET_FILE_POS SET_INT_REG SET_JPOS_REG SET_JPOS_TPE SET_LANG SET_PERCH SET_PORT_ASG SET_PORT_ATR SET_PORT_CMT SET_PORT_MOD SET_PORT_SIM SET_PORT_VAL SET_POS_REG SET_POS_TPE SET_PREG_CMT SET_REAL_REG SET_REG CMT SET_SREG_CMT SET_STR_REG SET_TIME SET_TPE_CMT SET_TRNS_TPE SET_TSK_ATTR SET_TSK_NAME SET_VAR +syn keyword karelProcedure TRANSLATE UNLOCK_GROUP UNPOS V_CAM_CALIB V_GET_OFFSET V_GET_PASSFL V_GET_QUEUE V_INIT_QUEUE V_RALC_QUEUE V_RUN_FIND V_SET_REF V_START_VTRK V_STOP_VTRK VAR_INFO VAR_LIST VOL_SPACE VREG_FND_POS VREG_OFFSET +syn keyword karelProcedure WRITE_DICT WRITE_DICT_V XML_ADDTAG XML_GETDATA XML_REMTAG XML_SCAN XML_SETVAR +hi def link karelProcedure Function + +syn keyword karelStatement ABORT CONDITION ENDCONDITION CONTINUE DELAY ERROR EVENT FOR ENDFOR HOLD READ RELEASE REPEAT RETURN SEMAPHORE UNTIL USING ENDUSING WRITE +syn match karelStatement /CANCEL FILE/ +syn match karelStatement /CLOSE FILE/ +syn match karelStatement /CLOSE HAND/ +syn match karelStatement /CONNECT TIMER/ +syn match karelStatement /DISABLE CONDITION/ +syn match karelStatement /DISCONNECT TIMER/ +syn match karelStatement /ENABLE CONDITION/ +syn match karelStatement /GO TO/ +syn match karelStatement /OPEN FILE/ +syn match karelStatement /OPEN HAND/ +syn match karelStatement /PURGE CONDITION/ +syn match karelStatement /RELAX HAND/ +syn match karelStatement /WAIT FOR/ +hi def link karelStatement Statement + +syn keyword karelKeyword BEGIN CONST END PROGRAM ROUTINE STRUCT TYPE VAR +hi def link karelKeyword Keyword + +" Comments +syn region karelComment start="--" end="$" +hi def link karelComment Comment + +let b:current_syntax = "karel" diff --git a/runtime/syntax/mss.vim b/runtime/syntax/mss.vim new file mode 100644 index 0000000000..de95d1d2ad --- /dev/null +++ b/runtime/syntax/mss.vim @@ -0,0 +1,23 @@ +" Vim syntax file +" Language: Vivado mss file +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2024 Oct 22 +" Document: https://docs.amd.com/r/2020.2-English/ug1400-vitis-embedded/Microprocessor-Software-Specification-MSS +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> + +if exists("b:current_syntax") + finish +endif + +syn case ignore +syn match mssComment "#.*$" contains=@Spell +syn keyword mssKeyword BEGIN END PARAMETER +syn keyword mssType OS PROCESSOR DRIVER LIBRARY +syn keyword mssConstant VERSION PROC_INSTANCE HW_INSTANCE OS_NAME OS_VER DRIVER_NAME DRIVER_VER LIBRARY_NAME LIBRARY_VER STDIN STDOUT XMDSTUB_PERIPHERAL ARCHIVER COMPILER COMPILER_FLAGS EXTRA_COMPILER_FLAGS + +hi def link mssComment Comment +hi def link mssKeyword Keyword +hi def link mssType Type +hi def link mssConstant Constant + +let b:current_syntax = "mss" diff --git a/runtime/syntax/opencl.vim b/runtime/syntax/opencl.vim new file mode 100644 index 0000000000..c237aa30f9 --- /dev/null +++ b/runtime/syntax/opencl.vim @@ -0,0 +1,13 @@ +" Vim syntax file +" Language: OpenCL +" Last Change: 2024 Nov 19 +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> + +if exists("b:current_syntax") + finish +endif + +" TODO: support openCL specific keywords +runtime! syntax/c.vim + +let current_syntax = "opencl" diff --git a/runtime/syntax/query.lua b/runtime/syntax/query.lua index 2dfe29f69b..0de08b4dfb 100644 --- a/runtime/syntax/query.lua +++ b/runtime/syntax/query.lua @@ -1,6 +1,5 @@ -- Neovim syntax file -- Language: Treesitter query --- Last Change: 2024 Jul 03 -- it's a lisp! vim.cmd([[runtime! syntax/lisp.vim]]) diff --git a/scripts/gen_eval_files.lua b/scripts/gen_eval_files.lua index edf95043c5..a9431ae2e5 100755 --- a/scripts/gen_eval_files.lua +++ b/scripts/gen_eval_files.lua @@ -617,8 +617,8 @@ local function render_option_meta(_f, opt, write) end for _, s in pairs { - { 'wo', 'window' }, - { 'bo', 'buffer' }, + { 'wo', 'win' }, + { 'bo', 'buf' }, { 'go', 'global' }, } do local id, scope = s[1], s[2] @@ -661,8 +661,8 @@ end local function scope_to_doc(s) local m = { global = 'global', - buffer = 'local to buffer', - window = 'local to window', + buf = 'local to buffer', + win = 'local to window', tab = 'local to tab page', } @@ -717,7 +717,7 @@ local function get_option_meta() local optinfo = vim.api.nvim_get_all_options_info() local ret = {} --- @type table<string,vim.option_meta> for _, o in ipairs(opts) do - local is_window_option = #o.scope == 1 and o.scope[1] == 'window' + local is_window_option = #o.scope == 1 and o.scope[1] == 'win' local is_option_hidden = o.immutable and not o.varname and not is_window_option if not is_option_hidden and o.desc then if o.full_name == 'cmdheight' then diff --git a/scripts/gen_vimdoc.lua b/scripts/gen_vimdoc.lua index 9cd5b598da..1125021bdc 100755 --- a/scripts/gen_vimdoc.lua +++ b/scripts/gen_vimdoc.lua @@ -538,7 +538,8 @@ end --- @param generics? table<string,string> --- @param classes? table<string,nvim.luacats.parser.class> --- @param exclude_types? true -local function render_fields_or_params(xs, generics, classes, exclude_types) +--- @param cfg nvim.gen_vimdoc.Config +local function render_fields_or_params(xs, generics, classes, exclude_types, cfg) local ret = {} --- @type string[] xs = vim.tbl_filter(should_render_field_or_param, xs) @@ -558,7 +559,9 @@ local function render_fields_or_params(xs, generics, classes, exclude_types) p.desc = pdesc inline_type(p, classes) - local nm, ty, desc = p.name, p.type, p.desc + local nm, ty = p.name, p.type + + local desc = p.classvar and string.format('See |%s|.', cfg.fn_helptag_fmt(p)) or p.desc local fnm = p.kind == 'operator' and fmt('op(%s)', nm) or fmt_field_name(nm) local pnm = fmt(' • %-' .. indent .. 's', fnm) @@ -591,7 +594,8 @@ end --- @param class nvim.luacats.parser.class --- @param classes table<string,nvim.luacats.parser.class> -local function render_class(class, classes) +--- @param cfg nvim.gen_vimdoc.Config +local function render_class(class, classes, cfg) if class.access or class.nodoc or class.inlinedoc then return end @@ -610,7 +614,7 @@ local function render_class(class, classes) table.insert(ret, md_to_vimdoc(class.desc, INDENTATION, INDENTATION, TEXT_WIDTH)) end - local fields_txt = render_fields_or_params(class.fields, nil, classes) + local fields_txt = render_fields_or_params(class.fields, nil, classes, nil, cfg) if not fields_txt:match('^%s*$') then table.insert(ret, '\n Fields: ~\n') table.insert(ret, fields_txt) @@ -621,11 +625,12 @@ local function render_class(class, classes) end --- @param classes table<string,nvim.luacats.parser.class> -local function render_classes(classes) +--- @param cfg nvim.gen_vimdoc.Config +local function render_classes(classes, cfg) local ret = {} --- @type string[] for _, class in vim.spairs(classes) do - ret[#ret + 1] = render_class(class, classes) + ret[#ret + 1] = render_class(class, classes, cfg) end return table.concat(ret) @@ -656,10 +661,6 @@ local function render_fun_header(fun, cfg) local proto = fun.table and nm or nm .. '(' .. table.concat(args, ', ') .. ')' - if not cfg.fn_helptag_fmt then - cfg.fn_helptag_fmt = fn_helptag_fmt_common - end - local tag = '*' .. cfg.fn_helptag_fmt(fun) .. '*' if #proto + #tag > TEXT_WIDTH - 8 then @@ -774,7 +775,8 @@ local function render_fun(fun, classes, cfg) end if fun.params and #fun.params > 0 then - local param_txt = render_fields_or_params(fun.params, fun.generics, classes, cfg.exclude_types) + local param_txt = + render_fields_or_params(fun.params, fun.generics, classes, cfg.exclude_types, cfg) if not param_txt:match('^%s*$') then table.insert(ret, '\n Parameters: ~\n') ret[#ret + 1] = param_txt @@ -957,6 +959,7 @@ end --- @param cfg nvim.gen_vimdoc.Config local function gen_target(cfg) + cfg.fn_helptag_fmt = cfg.fn_helptag_fmt or fn_helptag_fmt_common print('Target:', cfg.filename) local sections = {} --- @type table<string,nvim.gen_vimdoc.Section> @@ -987,7 +990,7 @@ local function gen_target(cfg) print(' Processing file:', f) local funs_txt = render_funs(funs, all_classes, cfg) if next(classes) then - local classes_txt = render_classes(classes) + local classes_txt = render_classes(classes, cfg) if vim.trim(classes_txt) ~= '' then funs_txt = classes_txt .. '\n' .. funs_txt end diff --git a/scripts/luacats_parser.lua b/scripts/luacats_parser.lua index 9a763e4d7b..8a50077aa8 100644 --- a/scripts/luacats_parser.lua +++ b/scripts/luacats_parser.lua @@ -1,9 +1,6 @@ local luacats_grammar = require('scripts.luacats_grammar') ---- @class nvim.luacats.parser.param ---- @field name string ---- @field type string ---- @field desc string +--- @class nvim.luacats.parser.param : nvim.luacats.Param --- @class nvim.luacats.parser.return --- @field name string @@ -41,21 +38,14 @@ local luacats_grammar = require('scripts.luacats_grammar') --- @field notes? nvim.luacats.parser.note[] --- @field see? nvim.luacats.parser.note[] ---- @class nvim.luacats.parser.field ---- @field name string ---- @field type string ---- @field desc string ---- @field access? 'private'|'package'|'protected' +--- @class nvim.luacats.parser.field : nvim.luacats.Field +--- @field classvar? string --- @field nodoc? true ---- @class nvim.luacats.parser.class ---- @field kind 'class' ---- @field parent? string ---- @field name string ---- @field desc string +--- @class nvim.luacats.parser.class : nvim.luacats.Class +--- @field desc? string --- @field nodoc? true --- @field inlinedoc? true ---- @field access? 'private'|'package'|'protected' --- @field fields nvim.luacats.parser.field[] --- @field notes? string[] @@ -332,7 +322,10 @@ local function process_lua_line(line, state, classes, classvars, has_indent) end -- Add method as the field to the class - table.insert(classes[class].fields, fun2field(cur_obj)) + local cls = classes[class] + local field = fun2field(cur_obj) + field.classvar = cur_obj.classvar + table.insert(cls.fields, field) return end diff --git a/src/clint.py b/src/clint.py index b57bbe354b..8044607098 100755 --- a/src/clint.py +++ b/src/clint.py @@ -897,7 +897,7 @@ def CheckIncludes(filename, lines, error): if (not name.endswith('.h.generated.h') and not name.endswith('/defs.h') and not name.endswith('_defs.h') and - not name.endswith('h.inline.generated.h') and + not name.endswith('.h.inline.generated.h') and not name.endswith('_defs.generated.h') and not name.endswith('_enum.generated.h')): error(filename, i, 'build/include_defs', 5, @@ -2206,12 +2206,6 @@ def ProcessFileData(filename, file_extension, lines, error, error = RecordedError - if file_extension == 'h': - CheckForHeaderGuard(filename, lines, error) - CheckIncludes(filename, lines, error) - if filename.endswith('/defs.h') or filename.endswith('_defs.h'): - CheckNonSymbols(filename, lines, error) - RemoveMultiLineComments(filename, lines, error) clean_lines = CleansedLines(lines, init_lines) for line in range(clean_lines.NumLines()): @@ -2219,6 +2213,12 @@ def ProcessFileData(filename, file_extension, lines, error, nesting_state, error, extra_check_functions) + if file_extension == 'h': + CheckForHeaderGuard(filename, lines, error) + CheckIncludes(filename, lines, error) + if filename.endswith('/defs.h') or filename.endswith('_defs.h'): + CheckNonSymbols(filename, lines, error) + # We check here rather than inside ProcessLine so that we see raw # lines rather than "cleaned" lines. CheckForBadCharacters(filename, lines, error) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 1b6827322d..38b54082c3 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -308,7 +308,6 @@ set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.lua) set(GENERATOR_HASHY ${GENERATOR_DIR}/hashy.lua) set(GENERATOR_PRELOAD ${GENERATOR_DIR}/preload.lua) set(HEADER_GENERATOR ${GENERATOR_DIR}/gen_declarations.lua) -set(OPTIONS_ENUM_GENERATOR ${GENERATOR_DIR}/gen_options_enum.lua) set(OPTIONS_GENERATOR ${GENERATOR_DIR}/gen_options.lua) # GENERATED_DIR and GENERATED_INCLUDES_DIR @@ -687,16 +686,11 @@ add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} DEPENDS ${LUA_GEN_DEPS} ${EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua ) -add_custom_command(OUTPUT ${GENERATED_OPTIONS} - COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS} +add_custom_command(OUTPUT ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} + COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua ) -add_custom_command(OUTPUT ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} - COMMAND ${LUA_GEN} ${OPTIONS_ENUM_GENERATOR} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} - DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_ENUM_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua -) - # NVIM_GENERATED_FOR_SOURCES and NVIM_GENERATED_FOR_HEADERS must be mutually exclusive. foreach(hfile ${NVIM_GENERATED_FOR_HEADERS}) list(FIND NVIM_GENERATED_FOR_SOURCES ${hfile} hfile_idx) diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index b3ba832fec..b38a7d4173 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -533,7 +533,7 @@ void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err) FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(11) { - set_option_to(channel_id, NULL, kOptReqGlobal, name, value, err); + set_option_to(channel_id, NULL, kOptScopeGlobal, name, value, err); } /// Gets the global value of an option. @@ -546,7 +546,7 @@ Object nvim_get_option(String name, Error *err) FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(11) { - return get_option_from(NULL, kOptReqGlobal, name, err); + return get_option_from(NULL, kOptScopeGlobal, name, err); } /// Gets a buffer option value @@ -566,7 +566,7 @@ Object nvim_buf_get_option(Buffer buffer, String name, Error *err) return (Object)OBJECT_INIT; } - return get_option_from(buf, kOptReqBuf, name, err); + return get_option_from(buf, kOptScopeBuf, name, err); } /// Sets a buffer option value. Passing `nil` as value deletes the option (only @@ -588,7 +588,7 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object return; } - set_option_to(channel_id, buf, kOptReqBuf, name, value, err); + set_option_to(channel_id, buf, kOptScopeBuf, name, value, err); } /// Gets a window option value @@ -608,7 +608,7 @@ Object nvim_win_get_option(Window window, String name, Error *err) return (Object)OBJECT_INIT; } - return get_option_from(win, kOptReqWin, name, err); + return get_option_from(win, kOptScopeWin, name, err); } /// Sets a window option value. Passing `nil` as value deletes the option (only @@ -630,48 +630,18 @@ void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object return; } - set_option_to(channel_id, win, kOptReqWin, name, value, err); -} - -/// Check if option has a value in the requested scope. -/// -/// @param opt_idx Option index in options[] table. -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// -/// @return true if option has a value in the requested scope, false otherwise. -static bool option_has_scope(OptIndex opt_idx, OptReqScope req_scope) -{ - if (opt_idx == kOptInvalid) { - return false; - } - - vimoption_T *opt = get_option(opt_idx); - - // TTY option. - if (is_tty_option(opt->fullname)) { - return req_scope == kOptReqGlobal; - } - - switch (req_scope) { - case kOptReqGlobal: - return opt->var != VAR_WIN; - case kOptReqBuf: - return opt->indir & PV_BUF; - case kOptReqWin: - return opt->indir & PV_WIN; - } - UNREACHABLE; + set_option_to(channel_id, win, kOptScopeWin, name, value, err); } /// Gets the value of a global or local (buffer, window) option. /// /// @param[in] from Pointer to buffer or window for local option value. -/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param req_scope Requested option scope. See OptScope in option.h. /// @param name The option name. /// @param[out] err Details of an error that may have occurred. /// /// @return the option value. -static Object get_option_from(void *from, OptReqScope req_scope, String name, Error *err) +static Object get_option_from(void *from, OptScope req_scope, String name, Error *err) { VALIDATE_S(name.size > 0, "option name", "<empty>", { return (Object)OBJECT_INIT; @@ -681,7 +651,7 @@ static Object get_option_from(void *from, OptReqScope req_scope, String name, Er OptVal value = NIL_OPTVAL; if (option_has_scope(opt_idx, req_scope)) { - value = get_option_value_for(opt_idx, req_scope == kOptReqGlobal ? OPT_GLOBAL : OPT_LOCAL, + value = get_option_value_for(opt_idx, req_scope == kOptScopeGlobal ? OPT_GLOBAL : OPT_LOCAL, req_scope, from, err); if (ERROR_SET(err)) { return (Object)OBJECT_INIT; @@ -698,11 +668,11 @@ static Object get_option_from(void *from, OptReqScope req_scope, String name, Er /// Sets the value of a global or local (buffer, window) option. /// /// @param[in] to Pointer to buffer or window for local option value. -/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param req_scope Requested option scope. See OptScope in option.h. /// @param name The option name. /// @param value New option value. /// @param[out] err Details of an error that may have occurred. -static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope, String name, +static void set_option_to(uint64_t channel_id, void *to, OptScope req_scope, String name, Object value, Error *err) { VALIDATE_S(name.size > 0, "option name", "<empty>", { @@ -725,12 +695,12 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope, return; }); - int attrs = get_option_attrs(opt_idx); // For global-win-local options -> setlocal // For win-local options -> setglobal and setlocal (opt_flags == 0) - const int opt_flags = (req_scope == kOptReqWin && !(attrs & SOPT_GLOBAL)) - ? 0 - : (req_scope == kOptReqGlobal) ? OPT_GLOBAL : OPT_LOCAL; + const int opt_flags + = (req_scope == kOptScopeWin && !option_has_scope(opt_idx, kOptScopeGlobal)) + ? 0 + : ((req_scope == kOptScopeGlobal) ? OPT_GLOBAL : OPT_LOCAL); WITH_SCRIPT_CONTEXT(channel_id, { set_option_value_for(name.data, opt_idx, optval, opt_flags, req_scope, to, err); diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 7786c30624..c94b8df9ea 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -381,8 +381,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// - id : id of the extmark to edit. /// - end_row : ending line of the mark, 0-based inclusive. /// - end_col : ending col of the mark, 0-based exclusive. -/// - hl_group : name of the highlight group used to highlight -/// this mark. +/// - hl_group : highlight group used for the text range. This and below +/// highlight groups can be supplied either as a string or as an integer, +/// the latter of which can be obtained using |nvim_get_hl_id_by_name()|. /// - hl_eol : when true, for a multiline highlight covering the /// EOL of a line, continue the highlight for the rest /// of the screen line (just like for diff and @@ -392,9 +393,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// text chunk with specified highlight. `highlight` element /// can either be a single highlight group, or an array of /// multiple highlight groups that will be stacked -/// (highest priority last). A highlight group can be supplied -/// either as a string or as an integer, the latter which -/// can be obtained using |nvim_get_hl_id_by_name()|. +/// (highest priority last). /// - virt_text_pos : position of virtual text. Possible values: /// - "eol": right after eol character (default). /// - "overlay": display over the specified column, without @@ -465,15 +464,12 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// buffer or end of the line respectively. Defaults to true. /// - sign_text: string of length 1-2 used to display in the /// sign column. -/// - sign_hl_group: name of the highlight group used to -/// highlight the sign column text. -/// - number_hl_group: name of the highlight group used to -/// highlight the number column. -/// - line_hl_group: name of the highlight group used to -/// highlight the whole line. -/// - cursorline_hl_group: name of the highlight group used to -/// highlight the sign column text when the cursor is on -/// the same line as the mark and 'cursorline' is enabled. +/// - sign_hl_group: highlight group used for the sign column text. +/// - number_hl_group: highlight group used for the number column. +/// - line_hl_group: highlight group used for the whole line. +/// - cursorline_hl_group: highlight group used for the sign +/// column text when the cursor is on the same line as the +/// mark and 'cursorline' is enabled. /// - conceal: string which should be either empty or a single /// character. Enable concealing similar to |:syn-conceal|. /// When a character is supplied it is used as |:syn-cchar|. diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 96866d80ba..3289daeb6f 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -23,8 +23,8 @@ #endif static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *opt_idxp, - int *scope, OptReqScope *req_scope, void **from, - char **filetype, Error *err) + int *scope, OptScope *req_scope, void **from, char **filetype, + Error *err) { #define HAS_KEY_X(d, v) HAS_KEY(d, option, v) if (HAS_KEY_X(opts, scope)) { @@ -39,14 +39,14 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex * } } - *req_scope = kOptReqGlobal; + *req_scope = kOptScopeGlobal; if (filetype != NULL && HAS_KEY_X(opts, filetype)) { *filetype = opts->filetype.data; } if (HAS_KEY_X(opts, win)) { - *req_scope = kOptReqWin; + *req_scope = kOptScopeWin; *from = find_window_by_handle(opts->win, err); if (ERROR_SET(err)) { return FAIL; @@ -59,7 +59,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex * return FAIL; }); *scope = OPT_LOCAL; - *req_scope = kOptReqBuf; + *req_scope = kOptScopeBuf; *from = find_buffer_by_handle(opts->buf, err); if (ERROR_SET(err)) { return FAIL; @@ -78,18 +78,17 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex * }); *opt_idxp = find_option(name); - int flags = get_option_attrs(*opt_idxp); - if (flags == 0) { + if (*opt_idxp == kOptInvalid) { // unknown option api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name); - } else if (*req_scope == kOptReqBuf || *req_scope == kOptReqWin) { + } else if (*req_scope == kOptScopeBuf || *req_scope == kOptScopeWin) { // if 'buf' or 'win' is passed, make sure the option supports it - int req_flags = *req_scope == kOptReqBuf ? SOPT_BUF : SOPT_WIN; - if (!(flags & req_flags)) { - char *tgt = *req_scope & kOptReqBuf ? "buf" : "win"; - char *global = flags & SOPT_GLOBAL ? "global " : ""; - char *req = flags & SOPT_BUF ? "buffer-local " - : flags & SOPT_WIN ? "window-local " : ""; + if (!option_has_scope(*opt_idxp, *req_scope)) { + char *tgt = *req_scope == kOptScopeBuf ? "buf" : "win"; + char *global = option_has_scope(*opt_idxp, kOptScopeGlobal) ? "global " : ""; + char *req = option_has_scope(*opt_idxp, kOptScopeBuf) + ? "buffer-local " + : (option_has_scope(*opt_idxp, kOptScopeWin) ? "window-local " : ""); api_set_error(err, kErrorTypeValidation, "'%s' cannot be passed for %s%soption '%s'", tgt, global, req, name); @@ -153,7 +152,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) { OptIndex opt_idx = 0; int scope = 0; - OptReqScope req_scope = kOptReqGlobal; + OptScope req_scope = kOptScopeGlobal; void *from = NULL; char *filetype = NULL; @@ -166,6 +165,14 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) buf_T *ftbuf = do_ft_buf(filetype, &aco, err); if (ERROR_SET(err)) { + if (ftbuf != NULL) { + // restore curwin/curbuf and a few other things + aucmd_restbuf(&aco); + + assert(curbuf != ftbuf); // safety check + wipe_buffer(ftbuf, false); + } + return (Object)OBJECT_INIT; } @@ -218,7 +225,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( { OptIndex opt_idx = 0; int scope = 0; - OptReqScope req_scope = kOptReqGlobal; + OptScope req_scope = kOptScopeGlobal; void *to = NULL; if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &to, NULL, err)) { return; @@ -230,9 +237,8 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( // - option is global or local to window (global-local) // // Then force scope to local since we don't want to change the global option - if (req_scope == kOptReqWin && scope == 0) { - int flags = get_option_attrs(opt_idx); - if (flags & SOPT_GLOBAL) { + if (req_scope == kOptScopeWin && scope == 0) { + if (option_has_scope(opt_idx, kOptScopeGlobal)) { scope = OPT_LOCAL; } } @@ -305,15 +311,15 @@ Dict nvim_get_option_info2(String name, Dict(option) *opts, Arena *arena, Error { OptIndex opt_idx = 0; int scope = 0; - OptReqScope req_scope = kOptReqGlobal; + OptScope req_scope = kOptScopeGlobal; void *from = NULL; if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, NULL, err)) { return (Dict)ARRAY_DICT_INIT; } - buf_T *buf = (req_scope == kOptReqBuf) ? (buf_T *)from : curbuf; - win_T *win = (req_scope == kOptReqWin) ? (win_T *)from : curwin; + buf_T *buf = (req_scope == kOptScopeBuf) ? (buf_T *)from : curbuf; + win_T *win = (req_scope == kOptScopeWin) ? (win_T *)from : curwin; return get_vimoption(name, scope, buf, win, arena, err); } diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index d21caf7ed0..8ddaecc58e 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -771,7 +771,7 @@ int object_to_hl_id(Object obj, const char *what, Error *err) int id = (int)obj.data.integer; return (1 <= id && id <= highlight_num_groups()) ? id : 0; } else { - api_set_error(err, kErrorTypeValidation, "Invalid highlight: %s", what); + api_set_error(err, kErrorTypeValidation, "Invalid hl_group: %s", what); return 0; } } @@ -809,28 +809,20 @@ HlMessage parse_hl_msg(Array chunks, Error *err) { HlMessage hl_msg = KV_INITIAL_VALUE; for (size_t i = 0; i < chunks.size; i++) { - if (chunks.items[i].type != kObjectTypeArray) { - api_set_error(err, kErrorTypeValidation, "Chunk is not an array"); + VALIDATE_T("chunk", kObjectTypeArray, chunks.items[i].type, { goto free_exit; - } + }); Array chunk = chunks.items[i].data.array; - if (chunk.size == 0 || chunk.size > 2 - || chunk.items[0].type != kObjectTypeString - || (chunk.size == 2 && chunk.items[1].type != kObjectTypeString)) { - api_set_error(err, kErrorTypeValidation, - "Chunk is not an array with one or two strings"); + VALIDATE((chunk.size > 0 && chunk.size <= 2 && chunk.items[0].type == kObjectTypeString), + "%s", "Invalid chunk: expected Array with 1 or 2 Strings", { goto free_exit; - } + }); String str = copy_string(chunk.items[0].data.string, NULL); int hl_id = 0; if (chunk.size == 2) { - String hl = chunk.items[1].data.string; - if (hl.size > 0) { - // TODO(bfredl): use object_to_hl_id and allow integer - hl_id = syn_check_group(hl.data, hl.size); - } + hl_id = object_to_hl_id(chunk.items[1], "text highlight", err); } kv_push(hl_msg, ((HlMessageChunk){ .text = str, .hl_id = hl_id })); } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index e6ec88c5e8..83f9aa573d 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -779,8 +779,8 @@ void nvim_set_vvar(String name, Object value, Error *err) /// Echo a message. /// /// @param chunks A list of `[text, hl_group]` arrays, each representing a -/// text chunk with specified highlight. `hl_group` element -/// can be omitted for no highlight. +/// text chunk with specified highlight group name or ID. +/// `hl_group` element can be omitted for no highlight. /// @param history if true, add to |message-history|. /// @param opts Optional parameters. /// - verbose: Message is printed as a result of 'verbose' option. @@ -1004,10 +1004,10 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err) buf_copy_options(buf, BCO_ENTER | BCO_NOHELP); if (scratch) { - set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0, kOptReqBuf, - buf); - set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0, kOptReqBuf, - buf); + set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0, + kOptScopeBuf, buf); + set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0, + kOptScopeBuf, buf); assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already buf->b_p_swf = false; buf->b_p_ml = false; @@ -2398,14 +2398,22 @@ void nvim__redraw(Dict(redraw) *opts, Error *err) redraw_buf_range_later(rbuf, first, last); } - bool flush = opts->flush; + // Redraw later types require update_screen() so call implicitly unless set to false. + if (HAS_KEY(opts, redraw, valid) || HAS_KEY(opts, redraw, range)) { + opts->flush = HAS_KEY(opts, redraw, flush) ? opts->flush : true; + } + + // When explicitly set to false and only "redraw later" types are present, + // don't call ui_flush() either. + bool flush_ui = opts->flush; if (opts->tabline) { // Flush later in case tabline was just hidden or shown for the first time. if (redraw_tabline && firstwin->w_lines_valid == 0) { - flush = true; + opts->flush = true; } else { draw_tabline(); } + flush_ui = true; } bool save_lz = p_lz; @@ -2416,31 +2424,35 @@ void nvim__redraw(Dict(redraw) *opts, Error *err) if (win == NULL) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (buf == NULL || wp->w_buffer == buf) { - redraw_status(wp, opts, &flush); + redraw_status(wp, opts, &opts->flush); } } } else { - redraw_status(win, opts, &flush); + redraw_status(win, opts, &opts->flush); } + flush_ui = true; } win_T *cwin = win ? win : curwin; // Allow moving cursor to recently opened window and make sure it is drawn #28868. if (opts->cursor && (!cwin->w_grid.target || !cwin->w_grid.target->valid)) { - flush = true; + opts->flush = true; } // Redraw pending screen updates when explicitly requested or when determined // that it is necessary to properly draw other requested components. - if (flush && !cmdpreview) { + if (opts->flush && !cmdpreview) { update_screen(); } if (opts->cursor) { setcursor_mayforce(cwin, true); + flush_ui = true; } - ui_flush(); + if (flush_ui) { + ui_flush(); + } RedrawingDisabled = save_rd; p_lz = save_lz; diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 16811e0cd9..6f5a9a90c0 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -130,7 +130,7 @@ /// - focusable: Enable focus by user actions (wincmds, mouse events). /// Defaults to true. Non-focusable windows can be entered by /// |nvim_set_current_win()|, or, when the `mouse` field is set to true, -/// by mouse events. +/// by mouse events. See |focusable|. /// - mouse: Specify how this window interacts with mouse events. /// Defaults to `focusable` value. /// - If false, mouse events pass through this window. diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index abcce0dfe8..1908516e85 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2819,6 +2819,7 @@ void buflist_list(exarg_T *eap) garray_T buflist; buf_T **buflist_data = NULL; + msg_ext_set_kind("list_cmd"); if (vim_strchr(eap->arg, 't')) { ga_init(&buflist, sizeof(buf_T *), 50); for (buf = firstbuf; buf != NULL; buf = buf->b_next) { @@ -3341,96 +3342,11 @@ void maketitle(void) title_str = p_titlestring; } } else { - // Format: "fname + (path) (1 of 2) - VIM". - -#define SPACE_FOR_FNAME (sizeof(buf) - 100) -#define SPACE_FOR_DIR (sizeof(buf) - 20) -#define SPACE_FOR_ARGNR (sizeof(buf) - 10) // At least room for " - Nvim". - char *buf_p = buf; - if (curbuf->b_fname == NULL) { - const size_t size = xstrlcpy(buf_p, _("[No Name]"), - SPACE_FOR_FNAME + 1); - buf_p += MIN(size, SPACE_FOR_FNAME); - } else { - buf_p += transstr_buf(path_tail(curbuf->b_fname), -1, buf_p, SPACE_FOR_FNAME + 1, true); - } - - switch (bufIsChanged(curbuf) - | (curbuf->b_p_ro << 1) - | (!MODIFIABLE(curbuf) << 2)) { - case 0: - break; - case 1: - buf_p = strappend(buf_p, " +"); break; - case 2: - buf_p = strappend(buf_p, " ="); break; - case 3: - buf_p = strappend(buf_p, " =+"); break; - case 4: - case 6: - buf_p = strappend(buf_p, " -"); break; - case 5: - case 7: - buf_p = strappend(buf_p, " -+"); break; - default: - abort(); - } - - if (curbuf->b_fname != NULL) { - // Get path of file, replace home dir with ~. - *buf_p++ = ' '; - *buf_p++ = '('; - home_replace(curbuf, curbuf->b_ffname, buf_p, - (SPACE_FOR_DIR - (size_t)(buf_p - buf)), true); -#ifdef BACKSLASH_IN_FILENAME - // Avoid "c:/name" to be reduced to "c". - if (isalpha((uint8_t)(*buf_p)) && *(buf_p + 1) == ':') { - buf_p += 2; - } -#endif - // Remove the file name. - char *p = path_tail_with_sep(buf_p); - if (p == buf_p) { - // Must be a help buffer. - xstrlcpy(buf_p, _("help"), SPACE_FOR_DIR - (size_t)(buf_p - buf)); - } else { - *p = NUL; - } - - // Translate unprintable chars and concatenate. Keep some - // room for the server name. When there is no room (very long - // file name) use (...). - if ((size_t)(buf_p - buf) < SPACE_FOR_DIR) { - char *const tbuf = transstr(buf_p, true); - const size_t free_space = SPACE_FOR_DIR - (size_t)(buf_p - buf) + 1; - const size_t dir_len = xstrlcpy(buf_p, tbuf, free_space); - buf_p += MIN(dir_len, free_space - 1); - xfree(tbuf); - } else { - const size_t free_space = SPACE_FOR_ARGNR - (size_t)(buf_p - buf) + 1; - const size_t dots_len = xstrlcpy(buf_p, "...", free_space); - buf_p += MIN(dots_len, free_space - 1); - } - *buf_p++ = ')'; - *buf_p = NUL; - } else { - *buf_p = NUL; - } - - append_arg_number(curwin, buf_p, (int)(SPACE_FOR_ARGNR - (size_t)(buf_p - buf))); - - xstrlcat(buf_p, " - Nvim", (sizeof(buf) - (size_t)(buf_p - buf))); - - if (maxlen > 0) { - // Make it shorter by removing a bit in the middle. - if (vim_strsize(buf) > maxlen) { - trunc_string(buf, buf, maxlen, sizeof(buf)); - } - } + // Format: "fname + (path) (1 of 2) - Nvim". + char *default_titlestring = "%t%( %M%)%( (%{expand(\"%:~:h\")})%)%a - Nvim"; + build_stl_str_hl(curwin, buf, sizeof(buf), default_titlestring, + kOptTitlestring, 0, 0, maxlen, NULL, NULL, NULL, NULL); title_str = buf; -#undef SPACE_FOR_FNAME -#undef SPACE_FOR_DIR -#undef SPACE_FOR_ARGNR } } bool mustset = value_change(title_str, &lasttitle); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index e6bd63f4f8..d33734ccfe 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -206,7 +206,7 @@ typedef struct { OptInt wo_winbl; #define w_p_winbl w_onebuf_opt.wo_winbl // 'winblend' - LastSet wo_script_ctx[WV_COUNT]; // SCTXs for window-local options + LastSet wo_script_ctx[kWinOptCount]; // SCTXs for window-local options #define w_p_script_ctx w_onebuf_opt.wo_script_ctx } winopt_T; @@ -512,7 +512,7 @@ struct file_buffer { // or contents of the file being edited. bool b_p_initialized; // set when options initialized - LastSet b_p_script_ctx[BV_COUNT]; // SCTXs for buffer-local options + LastSet b_p_script_ctx[kBufOptCount]; // SCTXs for buffer-local options int b_p_ai; ///< 'autoindent' int b_p_ai_nopaste; ///< b_p_ai saved for paste mode diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 700d554821..9b1193b4e0 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -1084,6 +1084,7 @@ int showmatches(expand_T *xp, bool wildmenu) ui_flush(); cmdline_row = msg_row; msg_didany = false; // lines_left will be set again + msg_ext_set_kind("wildlist"); msg_start(); // prepare for paging } diff --git a/src/nvim/diff.c b/src/nvim/diff.c index a690c70875..f1dd08f0e6 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1390,8 +1390,8 @@ void diff_win_options(win_T *wp, bool addbuf) } wp->w_p_fdm_save = xstrdup(wp->w_p_fdm); } - set_option_direct_for(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("diff"), OPT_LOCAL, 0, kOptReqWin, - wp); + set_option_direct_for(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("diff"), OPT_LOCAL, 0, + kOptScopeWin, wp); if (!wp->w_p_diff) { wp->w_p_fen_save = wp->w_p_fen; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9acbc05fdf..35f0bde871 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -922,13 +922,12 @@ int eval_expr_typval(const typval_T *expr, bool want_func, typval_T *argv, int a { if (expr->v_type == VAR_PARTIAL) { return eval_expr_partial(expr, argv, argc, rettv); - } else if (expr->v_type == VAR_FUNC || want_func) { + } + if (expr->v_type == VAR_FUNC || want_func) { return eval_expr_func(expr, argv, argc, rettv); - } else { - return eval_expr_string(expr, rettv); } - return OK; + return eval_expr_string(expr, rettv); } /// Like eval_to_bool() but using a typval_T instead of a string. @@ -1368,7 +1367,7 @@ int eval_foldexpr(win_T *wp, int *cp) const bool use_sandbox = was_set_insecurely(wp, kOptFoldexpr, OPT_LOCAL); char *arg = skipwhite(wp->w_p_fde); - current_sctx = wp->w_p_script_ctx[WV_FDE].script_ctx; + current_sctx = wp->w_p_script_ctx[kWinOptFoldexpr].script_ctx; emsg_off++; if (use_sandbox) { @@ -1982,7 +1981,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, bool copy, const bool // handle +=, -=, *=, /=, %= and .= di = NULL; - if (eval_variable(lp->ll_name, (int)strlen(lp->ll_name), + if (eval_variable(lp->ll_name, (int)lp->ll_name_len, &tv, &di, true, false) == OK) { if ((di == NULL || (!var_check_ro(di->di_flags, lp->ll_name, TV_CSTRING) @@ -6857,11 +6856,11 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex char *temp_result = eval_to_string(expr_start + 1, false, false); if (temp_result != NULL) { - retval = xmalloc(strlen(temp_result) + (size_t)(expr_start - in_start) - + (size_t)(in_end - expr_end) + 1); - STRCPY(retval, in_start); - strcat(retval, temp_result); - strcat(retval, expr_end + 1); + size_t retvalsize = (size_t)(expr_start - in_start) + + strlen(temp_result) + + (size_t)(in_end - expr_end) + 1; + retval = xmalloc(retvalsize); + vim_snprintf(retval, retvalsize, "%s%s%s", in_start, temp_result, expr_end + 1); } xfree(temp_result); @@ -7881,7 +7880,7 @@ void ex_echo(exarg_T *eap) char *tofree = encode_tv2echo(&rettv, NULL); if (*tofree != NUL) { msg_ext_set_kind("echo"); - msg_multiline(tofree, echo_hl_id, true, false, &need_clear); + msg_multiline(cstr_as_string(tofree), echo_hl_id, true, false, &need_clear); } xfree(tofree); } @@ -8350,9 +8349,10 @@ repeat: char *const sub = xmemdupz(s, (size_t)(p - s)); char *const str = xmemdupz(*fnamep, *fnamelen); *usedlen = (size_t)(p + 1 - src); - s = do_string_sub(str, pat, sub, NULL, flags); + size_t slen; + s = do_string_sub(str, *fnamelen, pat, sub, NULL, flags, &slen); *fnamep = s; - *fnamelen = strlen(s); + *fnamelen = slen; xfree(*bufp); *bufp = s; didit = true; @@ -8391,12 +8391,14 @@ repeat: /// When "sub" is NULL "expr" is used, must be a VAR_FUNC or VAR_PARTIAL. /// "flags" can be "g" to do a global substitute. /// +/// @param ret_len length of returned buffer +/// /// @return an allocated string, NULL for error. -char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char *flags) +char *do_string_sub(char *str, size_t len, char *pat, char *sub, typval_T *expr, const char *flags, + size_t *ret_len) { regmatch_T regmatch; garray_T ga; - char *zero_width = NULL; // Make 'cpoptions' empty, so that the 'l' flag doesn't work here char *save_cpo = p_cpo; @@ -8404,14 +8406,15 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char ga_init(&ga, 1, 200); - int do_all = (flags[0] == 'g'); - regmatch.rm_ic = p_ic; regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { - int sublen; char *tail = str; - char *end = str + strlen(str); + char *end = str + len; + bool do_all = (flags[0] == 'g'); + int sublen; + char *zero_width = NULL; + while (vim_regexec_nl(®match, str, (colnr_T)(tail - str))) { // Skip empty match except for first match. if (regmatch.startp[0] == regmatch.endp[0]) { @@ -8458,12 +8461,17 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char if (ga.ga_data != NULL) { STRCPY((char *)ga.ga_data + ga.ga_len, tail); + ga.ga_len += (int)(end - tail); } vim_regfree(regmatch.regprog); } - char *ret = xstrdup(ga.ga_data == NULL ? str : ga.ga_data); + if (ga.ga_data != NULL) { + str = ga.ga_data; + len = (size_t)ga.ga_len; + } + char *ret = xstrnsave(str, len); ga_clear(&ga); if (p_cpo == empty_string_option) { p_cpo = save_cpo; @@ -8477,6 +8485,10 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char free_string_option(save_cpo); } + if (ret_len != NULL) { + *ret_len = len; + } + return ret; } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 4828d67428..f700e732a9 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3540,6 +3540,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } + msg_ext_set_kind("list_cmd"); msg_start(); msg_row = Rows - 1; // for when 'cmdheight' > 1 lines_left = Rows; // avoid more prompt @@ -7848,8 +7849,8 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) || flg == NULL) { rettv->vval.v_string = NULL; } else { - rettv->vval.v_string = do_string_sub((char *)str, (char *)pat, - (char *)sub, expr, (char *)flg); + rettv->vval.v_string = do_string_sub((char *)str, strlen(str), (char *)pat, + (char *)sub, expr, (char *)flg, NULL); } } diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 35ad00f373..3ecb446cd6 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1403,6 +1403,7 @@ static void list_one_var(dictitem_T *v, const char *prefix, int *first) static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t name_len, const VarType type, const char *string, int *first) { + msg_ext_set_kind("list_cmd"); // don't use msg() to avoid overwriting "v:statusmsg" msg_start(); msg_puts(prefix); diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index e937961b44..8cccf08e11 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -189,7 +189,7 @@ void do_ascii(exarg_T *eap) transchar(c), buf1, buf2, cval, cval, cval); } - msg_multiline(IObuff, 0, true, false, &need_clear); + msg_multiline(cstr_as_string(IObuff), 0, true, false, &need_clear); off += (size_t)utf_ptr2len(data); // needed for overlong ascii? } @@ -224,7 +224,7 @@ void do_ascii(exarg_T *eap) c, c, c); } - msg_multiline(IObuff, 0, true, false, &need_clear); + msg_multiline(cstr_as_string(IObuff), 0, true, false, &need_clear); off += (size_t)utf_ptr2len(data + off); // needed for overlong ascii? } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index e8b9470391..9968f32de1 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -980,8 +980,6 @@ void handle_did_throw(void) force_abort = true; } - msg_ext_set_kind("emsg"); // kind=emsg for :throw, exceptions. #9993 - if (messages != NULL) { do { msglist_T *next = messages->next; @@ -5507,6 +5505,8 @@ static void ex_tabs(exarg_T *eap) FOR_ALL_WINDOWS_IN_TAB(wp, tp) { if (got_int) { break; + } else if (!wp->w_config.focusable) { + continue; } msg_putchar('\n'); diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index aeaf448a05..d183978d2d 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1648,7 +1648,7 @@ static char *eval_includeexpr(const char *const ptr, const size_t len) { const sctx_T save_sctx = current_sctx; set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t)len); - current_sctx = curbuf->b_p_script_ctx[BV_INEX].script_ctx; + current_sctx = curbuf->b_p_script_ctx[kBufOptIncludeexpr].script_ctx; char *res = eval_to_string_safe(curbuf->b_p_inex, was_set_insecurely(curwin, kOptIncludeexpr, OPT_LOCAL), diff --git a/src/nvim/fold.c b/src/nvim/fold.c index e7231c31ab..c9699cb161 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -1731,7 +1731,7 @@ char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo curwin = wp; curbuf = wp->w_buffer; - current_sctx = wp->w_p_script_ctx[WV_FDT].script_ctx; + current_sctx = wp->w_p_script_ctx[kWinOptFoldtext].script_ctx; emsg_off++; // handle exceptions, but don't display errors diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua index 92349b5298..02f3ac3257 100644 --- a/src/nvim/generators/gen_options.lua +++ b/src/nvim/generators/gen_options.lua @@ -1,6 +1,10 @@ local options_file = arg[1] +local options_enum_file = arg[2] +local options_map_file = arg[3] local opt_fd = assert(io.open(options_file, 'w')) +local opt_enum_fd = assert(io.open(options_enum_file, 'w')) +local opt_map_fd = assert(io.open(options_map_file, 'w')) local w = function(s) if s:match('^ %.') then @@ -10,10 +14,129 @@ local w = function(s) end end +--- @param s string +local function enum_w(s) + opt_enum_fd:write(s .. '\n') +end + +--- @param s string +local function map_w(s) + opt_map_fd:write(s .. '\n') +end + --- @module 'nvim.options' local options = require('options') +local options_meta = options.options local cstr = options.cstr +local valid_scopes = options.valid_scopes + +--- Options for each scope. +--- @type table<string, vim.option_meta[]> +local scope_options = {} +for _, scope in ipairs(valid_scopes) do + scope_options[scope] = {} +end + +--- @param s string +--- @return string +local lowercase_to_titlecase = function(s) + return s:sub(1, 1):upper() .. s:sub(2) +end + +-- Generate options enum file +enum_w('// IWYU pragma: private, include "nvim/option_defs.h"') +enum_w('') + +--- Map of option name to option index +--- @type table<string, string> +local option_index = {} + +-- Generate option index enum and populate the `option_index` and `scope_option` dicts. +enum_w('typedef enum {') +enum_w(' kOptInvalid = -1,') + +for i, o in ipairs(options_meta) do + local enum_val_name = 'kOpt' .. lowercase_to_titlecase(o.full_name) + enum_w((' %s = %u,'):format(enum_val_name, i - 1)) + + option_index[o.full_name] = enum_val_name + + if o.abbreviation then + option_index[o.abbreviation] = enum_val_name + end + + if o.alias then + o.alias = type(o.alias) == 'string' and { o.alias } or o.alias + + for _, v in ipairs(o.alias) do + option_index[v] = enum_val_name + end + end + + for _, scope in ipairs(o.scope) do + table.insert(scope_options[scope], o) + end +end + +enum_w(' // Option count') +enum_w('#define kOptCount ' .. tostring(#options_meta)) +enum_w('} OptIndex;') + +--- @param scope string +--- @param option_name string +--- @return string +local get_scope_option = function(scope, option_name) + return ('k%sOpt%s'):format(lowercase_to_titlecase(scope), lowercase_to_titlecase(option_name)) +end + +-- Generate option index enum for each scope +for _, scope in ipairs(valid_scopes) do + enum_w('') + + local scope_name = lowercase_to_titlecase(scope) + enum_w('typedef enum {') + enum_w((' %s = -1,'):format(get_scope_option(scope, 'Invalid'))) + + for idx, option in ipairs(scope_options[scope]) do + enum_w((' %s = %u,'):format(get_scope_option(scope, option.full_name), idx - 1)) + end + + enum_w((' // %s option count'):format(scope_name)) + enum_w(('#define %s %d'):format(get_scope_option(scope, 'Count'), #scope_options[scope])) + enum_w(('} %sOptIndex;'):format(scope_name)) +end + +-- Generate reverse lookup from option scope index to option index for each scope. +for _, scope in ipairs(valid_scopes) do + enum_w('') + enum_w(('EXTERN const OptIndex %s_opt_idx[] INIT( = {'):format(scope)) + for _, option in ipairs(scope_options[scope]) do + local idx = option_index[option.full_name] + enum_w((' [%s] = %s,'):format(get_scope_option(scope, option.full_name), idx)) + end + enum_w('});') +end + +opt_enum_fd:close() + +-- Generate option index map. +local hashy = require('generators.hashy') +local neworder, hashfun = hashy.hashy_hash('find_option', vim.tbl_keys(option_index), function(idx) + return ('option_hash_elems[%s].name'):format(idx) +end) + +map_w('static const struct { const char *name; OptIndex opt_idx; } option_hash_elems[] = {') + +for _, name in ipairs(neworder) do + assert(option_index[name] ~= nil) + map_w((' { .name = "%s", .opt_idx = %s },'):format(name, option_index[name])) +end + +map_w('};\n') +map_w('static ' .. hashfun) + +opt_map_fd:close() local redraw_flags = { ui_option = 'kOptFlagUIOption', @@ -35,12 +158,6 @@ local list_flags = { flagscomma = 'kOptFlagComma|kOptFlagFlagList', } ---- @param s string ---- @return string -local lowercase_to_titlecase = function(s) - return s:sub(1, 1):upper() .. s:sub(2) -end - --- @param o vim.option_meta --- @return string local function get_flags(o) @@ -95,6 +212,12 @@ local function opt_type_enum(opt_type) return ('kOptValType%s'):format(lowercase_to_titlecase(opt_type)) end +--- @param scope vim.option_scope +--- @return string +local function opt_scope_enum(scope) + return ('kOptScope%s'):format(lowercase_to_titlecase(scope)) +end + --- @param o vim.option_meta --- @return string local function get_type_flags(o) @@ -110,6 +233,35 @@ local function get_type_flags(o) return type_flags end +--- @param o vim.option_meta +--- @return string +local function get_scope_flags(o) + local scope_flags = '0' + + for _, scope in ipairs(o.scope) do + scope_flags = ('%s | (1 << %s)'):format(scope_flags, opt_scope_enum(scope)) + end + + return scope_flags +end + +--- @param o vim.option_meta +--- @return string +local function get_scope_idx(o) + --- @type string[] + local strs = {} + + for _, scope in pairs(valid_scopes) do + local has_scope = vim.tbl_contains(o.scope, scope) + strs[#strs + 1] = (' [%s] = %s'):format( + opt_scope_enum(scope), + get_scope_option(scope, has_scope and o.full_name or 'Invalid') + ) + end + + return ('{\n%s\n }'):format(table.concat(strs, ',\n')) +end + --- @param c string|string[] --- @param base_string? string --- @return string @@ -166,7 +318,6 @@ end --- @param d vim.option_value|function --- @param n string --- @return string - local get_defaults = function(d, n) if d == nil then error("option '" .. n .. "' should have a default value") @@ -174,9 +325,6 @@ local get_defaults = function(d, n) return get_opt_val(d) end ---- @type [string,string][] -local defines = {} - --- @param i integer --- @param o vim.option_meta local function dump_option(i, o) @@ -187,42 +335,28 @@ local function dump_option(i, o) end w(' .flags=' .. get_flags(o)) w(' .type_flags=' .. get_type_flags(o)) + w(' .scope_flags=' .. get_scope_flags(o)) + w(' .scope_idx=' .. get_scope_idx(o)) if o.enable_if then w(get_cond(o.enable_if)) end - if o.varname then - w(' .var=&' .. o.varname) - elseif o.immutable then - -- Immutable options can directly point to the default value. - w((' .var=&options[%u].def_val.data'):format(i - 1)) - elseif #o.scope == 1 and o.scope[1] == 'window' then - w(' .var=VAR_WIN') + local is_window_local = #o.scope == 1 and o.scope[1] == 'win' + + if not is_window_local then + if o.varname then + w(' .var=&' .. o.varname) + elseif o.immutable then + -- Immutable options can directly point to the default value. + w((' .var=&options[%u].def_val.data'):format(i - 1)) + else + -- Option must be immutable or have a variable. + assert(false) + end else - -- Option must be immutable or have a variable. - assert(false) + w(' .var=NULL') end w(' .immutable=' .. (o.immutable and 'true' or 'false')) - if #o.scope == 1 and o.scope[1] == 'global' then - w(' .indir=PV_NONE') - else - assert(#o.scope == 1 or #o.scope == 2) - assert(#o.scope == 1 or o.scope[1] == 'global') - local min_scope = o.scope[#o.scope] - local varname = o.pv_name or o.varname or ('p_' .. (o.abbreviation or o.full_name)) - local pv_name = ( - 'OPT_' - .. min_scope:sub(1, 3):upper() - .. '(' - .. (min_scope:sub(1, 1):upper() .. 'V_' .. varname:sub(3):upper()) - .. ')' - ) - if #o.scope == 2 then - pv_name = 'OPT_BOTH(' .. pv_name .. ')' - end - table.insert(defines, { 'PV_' .. varname:sub(3):upper(), pv_name }) - w(' .indir=' .. pv_name) - end if o.cb then w(' .opt_did_set_cb=' .. o.cb) end @@ -235,7 +369,6 @@ local function dump_option(i, o) w((' .var=&options[%u].def_val.data'):format(i - 1)) -- Option is always immutable on the false branch of `enable_if`. w(' .immutable=true') - w(' .indir=PV_NONE') w('#endif') end if o.defaults then @@ -256,6 +389,7 @@ local function dump_option(i, o) w(' },') end +-- Generate options[] array. w([[ #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" @@ -274,8 +408,3 @@ for i, o in ipairs(options.options) do dump_option(i, o) end w('};') -w('') - -for _, v in ipairs(defines) do - w('#define ' .. v[1] .. ' ' .. v[2]) -end diff --git a/src/nvim/generators/gen_options_enum.lua b/src/nvim/generators/gen_options_enum.lua deleted file mode 100644 index d1419137d3..0000000000 --- a/src/nvim/generators/gen_options_enum.lua +++ /dev/null @@ -1,129 +0,0 @@ --- Generates option index enum and map of option name to option index. --- Handles option full name, short name and aliases. --- Also generates BV_ and WV_ enum constants. - -local options_enum_file = arg[1] -local options_map_file = arg[2] -local options_enum_fd = assert(io.open(options_enum_file, 'w')) -local options_map_fd = assert(io.open(options_map_file, 'w')) - ---- @param s string -local function enum_w(s) - options_enum_fd:write(s .. '\n') -end - ---- @param s string -local function map_w(s) - options_map_fd:write(s .. '\n') -end - -enum_w('// IWYU pragma: private, include "nvim/option_defs.h"') -enum_w('') - ---- @param s string ---- @return string -local lowercase_to_titlecase = function(s) - return s:sub(1, 1):upper() .. s:sub(2) -end - ---- @type vim.option_meta[] -local options = require('options').options - --- Generate BV_ enum constants. -enum_w('/// "indir" values for buffer-local options.') -enum_w('/// These need to be defined globally, so that the BV_COUNT can be used with') -enum_w('/// b_p_script_stx[].') -enum_w('enum {') - -local bv_val = 0 - -for _, o in ipairs(options) do - assert(#o.scope == 1 or #o.scope == 2) - assert(#o.scope == 1 or o.scope[1] == 'global') - local min_scope = o.scope[#o.scope] - if min_scope == 'buffer' then - local varname = o.pv_name or o.varname or ('p_' .. (o.abbreviation or o.full_name)) - local bv_name = 'BV_' .. varname:sub(3):upper() - enum_w((' %s = %u,'):format(bv_name, bv_val)) - bv_val = bv_val + 1 - end -end - -enum_w((' BV_COUNT = %u, ///< must be the last one'):format(bv_val)) -enum_w('};') -enum_w('') - --- Generate WV_ enum constants. -enum_w('/// "indir" values for window-local options.') -enum_w('/// These need to be defined globally, so that the WV_COUNT can be used in the') -enum_w('/// window structure.') -enum_w('enum {') - -local wv_val = 0 - -for _, o in ipairs(options) do - assert(#o.scope == 1 or #o.scope == 2) - assert(#o.scope == 1 or o.scope[1] == 'global') - local min_scope = o.scope[#o.scope] - if min_scope == 'window' then - local varname = o.pv_name or o.varname or ('p_' .. (o.abbreviation or o.full_name)) - local wv_name = 'WV_' .. varname:sub(3):upper() - enum_w((' %s = %u,'):format(wv_name, wv_val)) - wv_val = wv_val + 1 - end -end - -enum_w((' WV_COUNT = %u, ///< must be the last one'):format(wv_val)) -enum_w('};') -enum_w('') - ---- @type { [string]: string } -local option_index = {} - --- Generate option index enum and populate the `option_index` dict. -enum_w('typedef enum {') -enum_w(' kOptInvalid = -1,') - -for i, o in ipairs(options) do - local enum_val_name = 'kOpt' .. lowercase_to_titlecase(o.full_name) - enum_w((' %s = %u,'):format(enum_val_name, i - 1)) - - option_index[o.full_name] = enum_val_name - - if o.abbreviation then - option_index[o.abbreviation] = enum_val_name - end - - if o.alias then - o.alias = type(o.alias) == 'string' and { o.alias } or o.alias - - for _, v in ipairs(o.alias) do - option_index[v] = enum_val_name - end - end -end - -enum_w(' // Option count, used when iterating through options') -enum_w('#define kOptIndexCount ' .. tostring(#options)) -enum_w('} OptIndex;') -enum_w('') - -options_enum_fd:close() - ---- Generate option index map. -local hashy = require('generators.hashy') -local neworder, hashfun = hashy.hashy_hash('find_option', vim.tbl_keys(option_index), function(idx) - return ('option_hash_elems[%s].name'):format(idx) -end) - -map_w('static const struct { const char *name; OptIndex opt_idx; } option_hash_elems[] = {') - -for _, name in ipairs(neworder) do - assert(option_index[name] ~= nil) - map_w((' { .name = "%s", .opt_idx = %s },'):format(name, option_index[name])) -end - -map_w('};\n') -map_w('static ' .. hashfun) - -options_map_fd:close() diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 65641f120f..cc1b833c3c 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -215,6 +215,7 @@ static const char *highlight_init_both[] = { "default link LspReferenceRead LspReferenceText", "default link LspReferenceText Visual", "default link LspReferenceWrite LspReferenceText", + "default link LspReferenceTarget LspReferenceText", "default link LspSignatureActiveParameter Visual", "default link SnippetTabstop Visual", @@ -302,7 +303,7 @@ static const char *highlight_init_both[] = { "default link @tag.builtin Special", // :help - // Higlight "===" and "---" heading delimiters specially. + // Highlight "===" and "---" heading delimiters specially. "default @markup.heading.1.delimiter.vimdoc guibg=bg guifg=bg guisp=fg gui=underdouble,nocombine ctermbg=NONE ctermfg=NONE cterm=underdouble,nocombine", "default @markup.heading.2.delimiter.vimdoc guibg=bg guifg=bg guisp=fg gui=underline,nocombine ctermbg=NONE ctermfg=NONE cterm=underline,nocombine", @@ -1000,6 +1001,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) { // If no argument, list current highlighting. if (!init && ends_excmd((uint8_t)(*line))) { + msg_ext_set_kind("list_cmd"); for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) { // TODO(brammool): only call when the group has attributes set highlight_list_one(i); @@ -1037,6 +1039,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) if (id == 0) { semsg(_(e_highlight_group_name_not_found_str), line); } else { + msg_ext_set_kind("list_cmd"); highlight_list_one(id); } return; diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 58215f738c..e487728901 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -1182,7 +1182,7 @@ int get_expr_indent(void) sandbox++; } textlock++; - current_sctx = curbuf->b_p_script_ctx[BV_INDE].script_ctx; + current_sctx = curbuf->b_p_script_ctx[kBufOptIndentexpr].script_ctx; // Need to make a copy, the 'indentexpr' option could be changed while // evaluating it. diff --git a/src/nvim/input.c b/src/nvim/input.c index 3d3240c59f..0c1a8af45f 100644 --- a/src/nvim/input.c +++ b/src/nvim/input.c @@ -223,6 +223,7 @@ int get_number(int colon, bool *mouse_used) /// the line number. int prompt_for_number(bool *mouse_used) { + msg_ext_set_kind("number_prompt"); // When using ":silent" assume that <CR> was entered. if (mouse_used != NULL) { msg_puts(_("Type number and <Enter> or click with the mouse " diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 15f70fb725..c4fa8b0fff 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -954,41 +954,10 @@ static void nlua_common_free_all_mem(lua_State *lstate) static void nlua_print_event(void **argv) { - char *str = argv[0]; - const size_t len = (size_t)(intptr_t)argv[1] - 1; // exclude final NUL - - for (size_t i = 0; i < len;) { - if (got_int) { - break; - } - const size_t start = i; - while (i < len) { - switch (str[i]) { - case NUL: - str[i] = NL; - i++; - continue; - case NL: - // TODO(bfredl): use proper multiline msg? Probably should implement - // print() in lua in terms of nvim_message(), when it is available. - str[i] = NUL; - i++; - break; - default: - i++; - continue; - } - break; - } - msg(str + start, 0); - if (msg_silent == 0) { - msg_didout = true; // Make blank lines work properly - } - } - if (len && str[len - 1] == NUL) { // Last was newline - msg("", 0); - } - xfree(str); + HlMessage msg = KV_INITIAL_VALUE; + HlMessageChunk chunk = { { .data = argv[0], .size = (size_t)(intptr_t)argv[1] - 1 }, 0 }; + kv_push(msg, chunk); + msg_multihl(msg, "lua_print", true); } /// Print as a Vim message diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 1a6b2c3581..1896f042f2 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -581,6 +581,9 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, const bool has_lhs = (args->lhs[0] != NUL); const bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop; const bool do_print = !has_lhs || (maptype != MAPTYPE_UNMAP && !has_rhs); + if (do_print) { + msg_ext_set_kind("list_cmd"); + } // check for :unmap without argument if (maptype == MAPTYPE_UNMAP && !has_lhs) { diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 65f718f925..b5a8588edd 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -1119,7 +1119,7 @@ int utf_char2bytes(const int c, char *const buf) /// stateful algorithm to determine grapheme clusters. Still available /// to support some legacy code which hasn't been refactored yet. /// -/// To check if a char would combine with a preceeding space, use +/// To check if a char would combine with a preceding space, use /// utf_iscomposing_first() instead. /// /// Based on code from Markus Kuhn. diff --git a/src/nvim/message.c b/src/nvim/message.c index e8f20916b8..c927a2546c 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -249,35 +249,33 @@ bool msg(const char *s, const int hl_id) return msg_hl_keep(s, hl_id, false, false); } -/// Similar to msg_outtrans, but support newlines and tabs. -void msg_multiline(const char *s, int hl_id, bool check_int, bool hist, bool *need_clear) +/// Similar to msg_outtrans_len, but support newlines and tabs. +void msg_multiline(String str, int hl_id, bool check_int, bool hist, bool *need_clear) FUNC_ATTR_NONNULL_ALL { - const char *next_spec = s; - - while (next_spec != NULL) { + const char *s = str.data; + const char *chunk = s; + while ((size_t)(s - str.data) < str.size) { if (check_int && got_int) { return; } - next_spec = strpbrk(s, "\t\n\r"); - - if (next_spec != NULL) { - // Printing all char that are before the char found by strpbrk - msg_outtrans_len(s, (int)(next_spec - s), hl_id, hist); + if (*s == '\n' || *s == TAB || *s == '\r') { + // Print all chars before the delimiter + msg_outtrans_len(chunk, (int)(s - chunk), hl_id, hist); - if (*next_spec != TAB && *need_clear) { + if (*s != TAB && *need_clear) { msg_clr_eos(); *need_clear = false; } - msg_putchar_hl((uint8_t)(*next_spec), hl_id); - s = next_spec + 1; + msg_putchar_hl((uint8_t)(*s), hl_id); + chunk = s + 1; } + s++; } - // Print the rest of the message. We know there is no special - // character because strpbrk returned NULL - if (*s != NUL) { - msg_outtrans(s, hl_id, hist); + // Print the rest of the message + if (*chunk != NUL) { + msg_outtrans_len(chunk, (int)(str.size - (size_t)(chunk - str.data)), hl_id, hist); } } @@ -290,7 +288,7 @@ void msg_multihl(HlMessage hl_msg, const char *kind, bool history) msg_ext_set_kind(kind); for (uint32_t i = 0; i < kv_size(hl_msg); i++) { HlMessageChunk chunk = kv_A(hl_msg, i); - msg_multiline(chunk.text.data, chunk.hl_id, true, false, &need_clear); + msg_multiline(chunk.text, chunk.hl_id, true, false, &need_clear); } if (history && kv_size(hl_msg)) { add_msg_hist_multihl(NULL, 0, 0, true, hl_msg); @@ -349,7 +347,7 @@ bool msg_hl_keep(const char *s, int hl_id, bool keep, bool multiline) bool need_clear = true; if (multiline) { - msg_multiline(s, hl_id, false, false, &need_clear); + msg_multiline(cstr_as_string(s), hl_id, false, false, &need_clear); } else { msg_outtrans(s, hl_id, false); } @@ -752,6 +750,10 @@ bool emsg_multiline(const char *s, bool multiline) msg_scroll = true; msg_source(hl_id); + if (msg_ext_kind == NULL) { + msg_ext_set_kind("emsg"); + } + // Display the error message itself. msg_nowait = false; // Wait for this msg. return msg_hl_keep(s, hl_id, false, multiline); @@ -2689,12 +2691,13 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) // primitive way to compute the current column if (*s == '\r' || *s == '\n') { msg_col = 0; + msg_didout = false; } else { msg_col += cw; + msg_didout = true; } s += len; } - msg_didout = true; // assume that line is not empty } /// Show the more-prompt and handle the user response. diff --git a/src/nvim/move.c b/src/nvim/move.c index 6324466dcc..b298592683 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -150,25 +150,20 @@ static void redraw_for_cursorline(win_T *wp) } } -/// Redraw when w_virtcol changes and +/// Redraw when 'concealcursor' is active, or when w_virtcol changes and: /// - 'cursorcolumn' is set, or /// - 'cursorlineopt' contains "screenline", or -/// - 'concealcursor' is active, or /// - Visual mode is active. static void redraw_for_cursorcolumn(win_T *wp) FUNC_ATTR_NONNULL_ALL { - if (wp->w_valid & VALID_VIRTCOL) { - return; - } - // If the cursor moves horizontally when 'concealcursor' is active, then the // current line needs to be redrawn to calculate the correct cursor position. if (wp->w_p_cole > 0 && conceal_cursor_line(wp)) { redrawWinline(wp, wp->w_cursor.lnum); } - if (pum_visible()) { + if ((wp->w_valid & VALID_VIRTCOL) || pum_visible()) { return; } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index d2716bf236..55aa385b33 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -5249,6 +5249,12 @@ void nv_g_home_m_cmd(cmdarg_T *cap) curwin->w_valid &= ~VALID_WCOL; } curwin->w_set_curswant = true; + if (hasAnyFolding(curwin)) { + validate_cheight(curwin); + if (curwin->w_cline_folded) { + update_curswant_force(); + } + } adjust_skipcol(); } diff --git a/src/nvim/option.c b/src/nvim/option.c index efd52f9233..1cfe4cd08b 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -228,7 +228,7 @@ static void set_init_default_backupskip(void) #endif { p = vim_getenv(names[i]); - plen = 0; // will be calcuated below + plen = 0; // will be calculated below } if (p != NULL && *p != NUL) { bool has_trailing_path_sep = false; @@ -303,7 +303,7 @@ static void set_init_default_cdpath(void) /// them. static void set_init_expand_env(void) { - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { vimoption_T *opt = &options[opt_idx]; if (opt->flags & kOptFlagNoDefExp) { continue; @@ -435,7 +435,7 @@ void set_init_1(bool clean_arg) static OptVal get_option_default(const OptIndex opt_idx, int opt_flags) { vimoption_T *opt = &options[opt_idx]; - bool is_global_local_option = opt->indir & PV_BOTH; + bool is_global_local_option = option_is_global_local(opt_idx); #ifdef UNIX if (opt_idx == kOptModeline && getuid() == ROOT_UID) { @@ -461,7 +461,7 @@ static OptVal get_option_default(const OptIndex opt_idx, int opt_flags) /// This ensures that we don't need to always check if the option default is allocated or not. static void alloc_options_default(void) { - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { options[opt_idx].def_val = optval_copy(options[opt_idx].def_val); } } @@ -500,7 +500,7 @@ static void set_option_default(const OptIndex opt_idx, int opt_flags) /// @param opt_flags Option flags. static void set_options_default(int opt_flags) { - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { if (!(options[opt_idx].flags & kOptFlagNoDefault)) { set_option_default(opt_idx, opt_flags); } @@ -564,16 +564,16 @@ static char *find_dup_item(char *origval, const char *newval, const size_t newva /// Free all options. void free_all_options(void) { - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { bool hidden = is_option_hidden(opt_idx); - if (options[opt_idx].indir == PV_NONE || hidden) { + if (option_is_global_only(opt_idx) || hidden) { // global option: free value and default value. // hidden option: free default value only. if (!hidden) { optval_free(optval_from_varp(opt_idx, options[opt_idx].var)); } - } else if (options[opt_idx].var != VAR_WIN) { + } else if (!option_is_window_local(opt_idx)) { // buffer-local option: free global value. optval_free(optval_from_varp(opt_idx, options[opt_idx].var)); } @@ -977,12 +977,12 @@ static int validate_opt_idx(win_T *win, OptIndex opt_idx, int opt_flags, uint32_ // Skip all options that are not window-local (used when showing // an already loaded buffer in a window). - if ((opt_flags & OPT_WINONLY) && (opt_idx == kOptInvalid || options[opt_idx].var != VAR_WIN)) { + if ((opt_flags & OPT_WINONLY) && (opt_idx == kOptInvalid || !option_is_window_local(opt_idx))) { return FAIL; } // Skip all options that are window-local (used for :vimgrep). - if ((opt_flags & OPT_NOWIN) && opt_idx != kOptInvalid && options[opt_idx].var == VAR_WIN) { + if ((opt_flags & OPT_NOWIN) && opt_idx != kOptInvalid && option_is_window_local(opt_idx)) { return FAIL; } @@ -999,10 +999,7 @@ static int validate_opt_idx(win_T *win, OptIndex opt_idx, int opt_flags, uint32_ // In diff mode some options are overruled. This avoids that // 'foldmethod' becomes "marker" instead of "diff" and that // "wrap" gets set. - if (win->w_p_diff - && opt_idx != kOptInvalid // shut up coverity warning - && (options[opt_idx].indir == PV_FDM - || options[opt_idx].indir == PV_WRAP)) { + if (win->w_p_diff && (opt_idx == kOptFoldmethod || opt_idx == kOptWrap)) { return FAIL; } } @@ -1099,7 +1096,7 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr vimoption_T *opt = &options[opt_idx]; char *arg = *argp; // When setting the local value of a global option, the old value may be the global value. - const bool oldval_is_global = ((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL); + const bool oldval_is_global = option_is_global_local(opt_idx) && (opt_flags & OPT_LOCAL); OptVal oldval = optval_from_varp(opt_idx, oldval_is_global ? get_varp(opt) : varp); OptVal newval = NIL_OPTVAL; @@ -1279,16 +1276,17 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * gotocmdline(true); // cursor at status line *did_show = true; // remember that we did a line } + msg_ext_set_kind("list_cmd"); showoneopt(&options[opt_idx], opt_flags); if (p_verbose > 0) { // Mention where the option was last set. if (varp == options[opt_idx].var) { option_last_set_msg(options[opt_idx].last_set); - } else if ((int)options[opt_idx].indir & PV_WIN) { - option_last_set_msg(curwin->w_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]); - } else if ((int)options[opt_idx].indir & PV_BUF) { - option_last_set_msg(curbuf->b_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]); + } else if (option_has_scope(opt_idx, kOptScopeWin)) { + option_last_set_msg(curwin->w_p_script_ctx[option_scope_idx(opt_idx, kOptScopeWin)]); + } else if (option_has_scope(opt_idx, kOptScopeBuf)) { + option_last_set_msg(curbuf->b_p_script_ctx[option_scope_idx(opt_idx, kOptScopeBuf)]); } } @@ -1643,7 +1641,7 @@ static void didset_options2(void) /// Check for string options that are NULL (normally only termcap options). void check_options(void) { - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { if ((option_has_type(opt_idx, kOptValTypeString)) && options[opt_idx].var != NULL) { check_string_option((char **)get_varp(&(options[opt_idx]))); } @@ -1673,24 +1671,25 @@ uint32_t *insecure_flag(win_T *const wp, OptIndex opt_idx, int opt_flags) { if (opt_flags & OPT_LOCAL) { assert(wp != NULL); - switch ((int)options[opt_idx].indir) { - case PV_STL: + switch (opt_idx) { + case kOptStatusline: return &wp->w_p_stl_flags; - case PV_WBR: + case kOptWinbar: return &wp->w_p_wbr_flags; - case PV_FDE: + case kOptFoldexpr: return &wp->w_p_fde_flags; - case PV_FDT: + case kOptFoldtext: return &wp->w_p_fdt_flags; - case PV_INDE: + case kOptIndentexpr: return &wp->w_buffer->b_p_inde_flags; - case PV_FEX: + case kOptFormatexpr: return &wp->w_buffer->b_p_fex_flags; - case PV_INEX: + case kOptIncludeexpr: return &wp->w_buffer->b_p_inex_flags; + default: + break; } } - // Nothing special, return global flags field. return &options[opt_idx].flags; } @@ -1804,7 +1803,6 @@ sctx_T *get_option_sctx(OptIndex opt_idx) void set_option_sctx(OptIndex opt_idx, int opt_flags, sctx_T script_ctx) { bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; - int indir = (int)options[opt_idx].indir; nlua_set_sctx(&script_ctx); LastSet last_set = { .script_ctx = script_ctx, @@ -1818,17 +1816,17 @@ void set_option_sctx(OptIndex opt_idx, int opt_flags, sctx_T script_ctx) // Remember where the option was set. For local options need to do that // in the buffer or window structure. - if (both || (opt_flags & OPT_GLOBAL) || (indir & (PV_BUF|PV_WIN)) == 0) { + if (both || (opt_flags & OPT_GLOBAL) || option_is_global_only(opt_idx)) { options[opt_idx].last_set = last_set; } if (both || (opt_flags & OPT_LOCAL)) { - if (indir & PV_BUF) { - curbuf->b_p_script_ctx[indir & PV_MASK] = last_set; - } else if (indir & PV_WIN) { - curwin->w_p_script_ctx[indir & PV_MASK] = last_set; + if (option_has_scope(opt_idx, kOptScopeBuf)) { + curbuf->b_p_script_ctx[option_scope_idx(opt_idx, kOptScopeBuf)] = last_set; + } else if ((option_has_scope(opt_idx, kOptScopeWin))) { + curwin->w_p_script_ctx[option_scope_idx(opt_idx, kOptScopeWin)] = last_set; if (both) { // also setting the "all buffers" value - curwin->w_allbuf_opt.wo_script_ctx[indir & PV_MASK] = last_set; + curwin->w_allbuf_opt.wo_script_ctx[option_scope_idx(opt_idx, kOptScopeWin)] = last_set; } } } @@ -3267,7 +3265,7 @@ OptVal object_as_optval(Object o, bool *error) /// Get an allocated string containing a list of valid types for an option. /// For options with a singular type, it returns the name of the type. For options with multiple /// possible types, it returns a slash separated list of types. For example, if an option can be a -/// number, boolean or string, the function returns "Number/Boolean/String" +/// number, boolean or string, the function returns "number/boolean/string" static char *option_get_valid_types(OptIndex opt_idx) { StringBuilder str = KV_INITIAL_VALUE; @@ -3299,14 +3297,73 @@ static char *option_get_valid_types(OptIndex opt_idx) bool is_option_hidden(OptIndex opt_idx) { // Hidden options are always immutable and point to their default value - return opt_idx == kOptInvalid - ? false - : (options[opt_idx].immutable && options[opt_idx].var == &options[opt_idx].def_val.data); + return opt_idx != kOptInvalid && options[opt_idx].immutable + && options[opt_idx].var == &options[opt_idx].def_val.data; +} + +/// Check if option is multitype (supports multiple types). +static bool option_is_multitype(OptIndex opt_idx) +{ + const OptTypeFlags type_flags = get_option(opt_idx)->type_flags; + assert(type_flags != 0); + return !is_power_of_two(type_flags); +} + +/// Check if option supports a specific type. +bool option_has_type(OptIndex opt_idx, OptValType type) +{ + // Ensure that type flags variable can hold all types. + STATIC_ASSERT(kOptValTypeSize <= sizeof(OptTypeFlags) * 8, + "Option type_flags cannot fit all option types"); + // Ensure that the type is valid before accessing type_flags. + assert(type > kOptValTypeNil && type < kOptValTypeSize); + // Bitshift 1 by the value of type to get the type's corresponding flag, and check if it's set in + // the type_flags bit field. + return get_option(opt_idx)->type_flags & (1 << type); } +/// Check if option supports a specific scope. +bool option_has_scope(OptIndex opt_idx, OptScope scope) +{ + // Ensure that scope flags variable can hold all scopes. + STATIC_ASSERT(kOptScopeSize <= sizeof(OptScopeFlags) * 8, + "Option scope_flags cannot fit all option scopes"); + // Ensure that the scope is valid before accessing scope_flags. + assert(scope >= kOptScopeGlobal && scope < kOptScopeSize); + // Bitshift 1 by the value of scope to get the scope's corresponding flag, and check if it's set + // in the scope_flags bit field. + return get_option(opt_idx)->scope_flags & (1 << scope); +} + +/// Check if option is global-local. static inline bool option_is_global_local(OptIndex opt_idx) { - return opt_idx == kOptInvalid ? false : (options[opt_idx].indir & PV_BOTH); + // Global-local options have at least two types, so their type flag cannot be a power of two. + return opt_idx != kOptInvalid && !is_power_of_two(options[opt_idx].scope_flags); +} + +/// Check if option only supports global scope. +static inline bool option_is_global_only(OptIndex opt_idx) +{ + // For an option to be global-only, it has to only have a single scope, which means the scope + // flags must be a power of two, and it must have the global scope. + return opt_idx != kOptInvalid && is_power_of_two(options[opt_idx].scope_flags) + && option_has_scope(opt_idx, kOptScopeGlobal); +} + +/// Check if option only supports window scope. +static inline bool option_is_window_local(OptIndex opt_idx) +{ + // For an option to be window-local it has to only have a single scope, which means the scope + // flags must be a power of two, and it must have the window scope. + return opt_idx != kOptInvalid && is_power_of_two(options[opt_idx].scope_flags) + && option_has_scope(opt_idx, kOptScopeWin); +} + +/// Get option index for scope. +ssize_t option_scope_idx(OptIndex opt_idx, OptScope scope) +{ + return options[opt_idx].scope_idx[scope]; } /// Get option flags. @@ -3357,7 +3414,7 @@ static OptVal get_option_unset_value(OptIndex opt_idx) vimoption_T *opt = &options[opt_idx]; // For global-local options, use the unset value of the local value. - if (opt->indir & PV_BOTH) { + if (option_is_global_local(opt_idx)) { // String global-local options always use an empty string for the unset value. if (option_has_type(opt_idx, kOptValTypeString)) { return STATIC_CSTR_AS_OPTVAL(""); @@ -3389,7 +3446,7 @@ static bool is_option_local_value_unset(OptIndex opt_idx) vimoption_T *opt = get_option(opt_idx); // Local value of option that isn't global-local is always considered set. - if (!((int)opt->indir & PV_BOTH)) { + if (!option_is_global_local(opt_idx)) { return false; } @@ -3748,10 +3805,10 @@ void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set /// @param set_sid Script ID. Special values: /// 0: Use current script ID. /// SID_NONE: Don't set script ID. -/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param req_scope Requested option scope. See OptScope in option.h. /// @param[in] from Target buffer/window. void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid, - OptReqScope req_scope, void *const from) + OptScope req_scope, void *const from) { buf_T *save_curbuf = curbuf; win_T *save_curwin = curwin; @@ -3760,15 +3817,15 @@ void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T // side-effects when setting an option directly. Just change the values of curbuf and curwin if // needed, no need to properly switch the window / buffer. switch (req_scope) { - case kOptReqGlobal: + case kOptScopeGlobal: break; - case kOptReqBuf: - curbuf = (buf_T *)from; - break; - case kOptReqWin: + case kOptScopeWin: curwin = (win_T *)from; curbuf = curwin->w_buffer; break; + case kOptScopeBuf: + curbuf = (buf_T *)from; + break; } set_option_direct(opt_idx, value, opt_flags, set_sid); @@ -3855,16 +3912,17 @@ void set_option_value_give_err(const OptIndex opt_idx, OptVal value, int opt_fla /// Switch current context to get/set option value for window/buffer. /// /// @param[out] ctx Current context. switchwin_T for window and aco_save_T for buffer. -/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param req_scope Requested option scope. See OptScope in option.h. /// @param[in] from Target buffer/window. /// @param[out] err Error message, if any. /// /// @return true if context was switched, false otherwise. -static bool switch_option_context(void *const ctx, OptReqScope req_scope, void *const from, - Error *err) +static bool switch_option_context(void *const ctx, OptScope req_scope, void *const from, Error *err) { switch (req_scope) { - case kOptReqWin: { + case kOptScopeGlobal: + return false; + case kOptScopeWin: { win_T *const win = (win_T *)from; switchwin_T *const switchwin = (switchwin_T *)ctx; @@ -3884,7 +3942,7 @@ static bool switch_option_context(void *const ctx, OptReqScope req_scope, void * } return true; } - case kOptReqBuf: { + case kOptScopeBuf: { buf_T *const buf = (buf_T *)from; aco_save_T *const aco = (aco_save_T *)ctx; @@ -3894,76 +3952,44 @@ static bool switch_option_context(void *const ctx, OptReqScope req_scope, void * aucmd_prepbuf(aco, buf); return true; } - case kOptReqGlobal: - return false; } UNREACHABLE; } /// Restore context after getting/setting option for window/buffer. See switch_option_context() for /// params. -static void restore_option_context(void *const ctx, OptReqScope req_scope) +static void restore_option_context(void *const ctx, OptScope req_scope) { switch (req_scope) { - case kOptReqWin: + case kOptScopeGlobal: + break; + case kOptScopeWin: restore_win_noblock((switchwin_T *)ctx, true); break; - case kOptReqBuf: + case kOptScopeBuf: aucmd_restbuf((aco_save_T *)ctx); break; - case kOptReqGlobal: - break; } } -/// Get attributes for an option. -/// -/// @param opt_idx Option index in options[] table. -/// -/// @return Option attributes. -/// 0 for hidden or unknown option. -/// See SOPT_* in option_defs.h for other flags. -int get_option_attrs(OptIndex opt_idx) -{ - if (opt_idx == kOptInvalid) { - return 0; - } - - vimoption_T *opt = get_option(opt_idx); - - int attrs = 0; - - if (opt->indir == PV_NONE || (opt->indir & PV_BOTH)) { - attrs |= SOPT_GLOBAL; - } - if (opt->indir & PV_WIN) { - attrs |= SOPT_WIN; - } else if (opt->indir & PV_BUF) { - attrs |= SOPT_BUF; - } - - assert(attrs != 0); - return attrs; -} - /// Get option value for buffer / window. /// /// @param opt_idx Option index in options[] table. /// @param[out] flagsp Set to the option flags (see OptFlags) (if not NULL). /// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination). /// @param[out] hidden Whether option is hidden. -/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param req_scope Requested option scope. See OptScope in option.h. /// @param[in] from Target buffer/window. /// @param[out] err Error message, if any. /// /// @return Option value. Must be freed by caller. -OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptReqScope req_scope, - void *const from, Error *err) +OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptScope req_scope, void *const from, + Error *err) { switchwin_T switchwin; aco_save_T aco; - void *ctx = req_scope == kOptReqWin ? (void *)&switchwin - : (req_scope == kOptReqBuf ? (void *)&aco : NULL); + void *ctx = req_scope == kOptScopeWin ? (void *)&switchwin + : (req_scope == kOptScopeBuf ? (void *)&aco : NULL); bool switched = switch_option_context(ctx, req_scope, from, err); if (ERROR_SET(err)) { @@ -3985,17 +4011,17 @@ OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptReqScope req_s /// @param opt_idx Option index in options[] table. /// @param[in] value Option value. /// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both). -/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param req_scope Requested option scope. See OptScope in option.h. /// @param[in] from Target buffer/window. /// @param[out] err Error message, if any. void set_option_value_for(const char *name, OptIndex opt_idx, OptVal value, const int opt_flags, - const OptReqScope req_scope, void *const from, Error *err) + const OptScope req_scope, void *const from, Error *err) FUNC_ATTR_NONNULL_ARG(1) { switchwin_T switchwin; aco_save_T aco; - void *ctx = req_scope == kOptReqWin ? (void *)&switchwin - : (req_scope == kOptReqBuf ? (void *)&aco : NULL); + void *ctx = req_scope == kOptScopeWin ? (void *)&switchwin + : (req_scope == kOptScopeBuf ? (void *)&aco : NULL); bool switched = switch_option_context(ctx, req_scope, from, err); if (ERROR_SET(err)) { @@ -4023,6 +4049,7 @@ static void showoptions(bool all, int opt_flags) vimoption_T **items = xmalloc(sizeof(vimoption_T *) * OPTION_COUNT); + msg_ext_set_kind("list_cmd"); // Highlight title if (opt_flags & OPT_GLOBAL) { msg_puts_title(_("\n--- Global option values ---")); @@ -4040,7 +4067,7 @@ static void showoptions(bool all, int opt_flags) // collect the items in items[] int item_count = 0; vimoption_T *opt; - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { opt = &options[opt_idx]; // apply :filter /pat/ if (message_filtered(opt->fullname)) { @@ -4049,7 +4076,7 @@ static void showoptions(bool all, int opt_flags) void *varp = NULL; if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) != 0) { - if (opt->indir != PV_NONE) { + if (!option_is_global_only(opt_idx)) { varp = get_varp_scope(opt, opt_flags); } } else { @@ -4124,7 +4151,7 @@ static int optval_default(OptIndex opt_idx, void *varp) /// Send update to UIs with values of UI relevant options void ui_refresh_options(void) { - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { uint32_t flags = options[opt_idx].flags; if (!(flags & kOptFlagUIOption)) { continue; @@ -4204,13 +4231,13 @@ int makeset(FILE *fd, int opt_flags, int local_only) // kOptFlagPriMkrc flag and once without. for (int pri = 1; pri >= 0; pri--) { vimoption_T *opt; - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { opt = &options[opt_idx]; if (!(opt->flags & kOptFlagNoMkrc) && ((pri == 1) == ((opt->flags & kOptFlagPriMkrc) != 0))) { // skip global option when only doing locals - if (opt->indir == PV_NONE && !(opt_flags & OPT_GLOBAL)) { + if (option_is_global_only(opt_idx) && !(opt_flags & OPT_GLOBAL)) { continue; } @@ -4237,21 +4264,19 @@ int makeset(FILE *fd, int opt_flags, int local_only) int round = 2; void *varp_local = NULL; // fresh value - if (opt->indir != PV_NONE) { - if (opt->var == VAR_WIN) { - // skip window-local option when only doing globals - if (!(opt_flags & OPT_LOCAL)) { - continue; - } - // When fresh value of window-local option is not at the - // default, need to write it too. - if (!(opt_flags & OPT_GLOBAL) && !local_only) { - void *varp_fresh = get_varp_scope(opt, OPT_GLOBAL); // local value - if (!optval_default(opt_idx, varp_fresh)) { - round = 1; - varp_local = varp; - varp = varp_fresh; - } + if (option_is_window_local(opt_idx)) { + // skip window-local option when only doing globals + if (!(opt_flags & OPT_LOCAL)) { + continue; + } + // When fresh value of window-local option is not at the + // default, need to write it too. + if (!(opt_flags & OPT_GLOBAL) && !local_only) { + void *varp_fresh = get_varp_scope(opt, OPT_GLOBAL); // local value + if (!optval_default(opt_idx, varp_fresh)) { + round = 1; + varp_local = varp; + varp = varp_fresh; } } } @@ -4267,9 +4292,9 @@ int makeset(FILE *fd, int opt_flags, int local_only) } bool do_endif = false; - // Don't set 'syntax' and 'filetype' again if the value is - // already right, avoids reloading the syntax file. - if (opt->indir == PV_SYN || opt->indir == PV_FT) { + // Don't set 'syntax' and 'filetype' again if the value is already right, avoids reloading + // the syntax file. + if (opt_idx == kOptSyntax || opt_idx == kOptFiletype) { if (fprintf(fd, "if &%s != '%s'", opt->fullname, *(char **)(varp)) < 0 || put_eol(fd) < 0) { @@ -4325,7 +4350,7 @@ static int put_set(FILE *fd, char *cmd, OptIndex opt_idx, void *varp) char *name = opt->fullname; uint64_t flags = opt->flags; - if ((opt->indir & PV_BOTH) && varp != opt->var + if (option_is_global_local(opt_idx) && varp != opt->var && optval_equal(value, get_option_unset_value(opt_idx))) { // Processing unset local value of global-local option. Do nothing. return OK; @@ -4430,76 +4455,80 @@ static int put_set(FILE *fd, char *cmd, OptIndex opt_idx, void *varp) void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win) { - if ((scope & OPT_GLOBAL) && p->indir != PV_NONE) { - if (p->var == VAR_WIN) { + OptIndex opt_idx = get_opt_idx(p); + + if ((scope & OPT_GLOBAL) && !option_is_global_only(opt_idx)) { + if (option_is_window_local(opt_idx)) { return GLOBAL_WO(get_varp_from(p, buf, win)); } return p->var; } - if ((scope & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) { - switch ((int)p->indir) { - case PV_FP: + + if ((scope & OPT_LOCAL) && option_is_global_local(opt_idx)) { + switch (opt_idx) { + case kOptFormatprg: return &(buf->b_p_fp); - case PV_FFU: + case kOptFindfunc: return &(buf->b_p_ffu); - case PV_EFM: + case kOptErrorformat: return &(buf->b_p_efm); - case PV_GP: + case kOptGrepprg: return &(buf->b_p_gp); - case PV_MP: + case kOptMakeprg: return &(buf->b_p_mp); - case PV_EP: + case kOptEqualprg: return &(buf->b_p_ep); - case PV_KP: + case kOptKeywordprg: return &(buf->b_p_kp); - case PV_PATH: + case kOptPath: return &(buf->b_p_path); - case PV_AR: + case kOptAutoread: return &(buf->b_p_ar); - case PV_TAGS: + case kOptTags: return &(buf->b_p_tags); - case PV_TC: + case kOptTagcase: return &(buf->b_p_tc); - case PV_SISO: + case kOptSidescrolloff: return &(win->w_p_siso); - case PV_SO: + case kOptScrolloff: return &(win->w_p_so); - case PV_DEF: + case kOptDefine: return &(buf->b_p_def); - case PV_INC: + case kOptInclude: return &(buf->b_p_inc); - case PV_COT: + case kOptCompleteopt: return &(buf->b_p_cot); - case PV_DICT: + case kOptDictionary: return &(buf->b_p_dict); - case PV_TSR: + case kOptThesaurus: return &(buf->b_p_tsr); - case PV_TSRFU: + case kOptThesaurusfunc: return &(buf->b_p_tsrfu); - case PV_TFU: + case kOptTagfunc: return &(buf->b_p_tfu); - case PV_SBR: + case kOptShowbreak: return &(win->w_p_sbr); - case PV_STL: + case kOptStatusline: return &(win->w_p_stl); - case PV_WBR: + case kOptWinbar: return &(win->w_p_wbr); - case PV_UL: + case kOptUndolevels: return &(buf->b_p_ul); - case PV_LW: + case kOptLispwords: return &(buf->b_p_lw); - case PV_BKC: + case kOptBackupcopy: return &(buf->b_p_bkc); - case PV_MENC: + case kOptMakeencoding: return &(buf->b_p_menc); - case PV_FCS: + case kOptFillchars: return &(win->w_p_fcs); - case PV_LCS: + case kOptListchars: return &(win->w_p_lcs); - case PV_VE: + case kOptVirtualedit: return &(win->w_p_ve); + default: + abort(); } - return NULL; // "cannot happen" } return get_varp_from(p, buf, win); } @@ -4521,291 +4550,290 @@ void *get_option_varp_scope_from(OptIndex opt_idx, int scope, buf_T *buf, win_T void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) { - // hidden options always use the same var pointer - if (is_option_hidden(get_opt_idx(p))) { - return p->var; - } + OptIndex opt_idx = get_opt_idx(p); - switch ((int)p->indir) { - case PV_NONE: + // Hidden options and global-only options always use the same var pointer + if (is_option_hidden(opt_idx) || option_is_global_only(opt_idx)) { return p->var; + } + switch (opt_idx) { // global option with local value: use local value if it's been set - case PV_EP: + case kOptEqualprg: return *buf->b_p_ep != NUL ? &buf->b_p_ep : p->var; - case PV_KP: + case kOptKeywordprg: return *buf->b_p_kp != NUL ? &buf->b_p_kp : p->var; - case PV_PATH: + case kOptPath: return *buf->b_p_path != NUL ? &(buf->b_p_path) : p->var; - case PV_AR: + case kOptAutoread: return buf->b_p_ar >= 0 ? &(buf->b_p_ar) : p->var; - case PV_TAGS: + case kOptTags: return *buf->b_p_tags != NUL ? &(buf->b_p_tags) : p->var; - case PV_TC: + case kOptTagcase: return *buf->b_p_tc != NUL ? &(buf->b_p_tc) : p->var; - case PV_SISO: + case kOptSidescrolloff: return win->w_p_siso >= 0 ? &(win->w_p_siso) : p->var; - case PV_SO: + case kOptScrolloff: return win->w_p_so >= 0 ? &(win->w_p_so) : p->var; - case PV_BKC: + case kOptBackupcopy: return *buf->b_p_bkc != NUL ? &(buf->b_p_bkc) : p->var; - case PV_DEF: + case kOptDefine: return *buf->b_p_def != NUL ? &(buf->b_p_def) : p->var; - case PV_INC: + case kOptInclude: return *buf->b_p_inc != NUL ? &(buf->b_p_inc) : p->var; - case PV_COT: + case kOptCompleteopt: return *buf->b_p_cot != NUL ? &(buf->b_p_cot) : p->var; - case PV_DICT: + case kOptDictionary: return *buf->b_p_dict != NUL ? &(buf->b_p_dict) : p->var; - case PV_TSR: + case kOptThesaurus: return *buf->b_p_tsr != NUL ? &(buf->b_p_tsr) : p->var; - case PV_TSRFU: + case kOptThesaurusfunc: return *buf->b_p_tsrfu != NUL ? &(buf->b_p_tsrfu) : p->var; - case PV_FP: + case kOptFormatprg: return *buf->b_p_fp != NUL ? &(buf->b_p_fp) : p->var; - case PV_FFU: + case kOptFindfunc: return *buf->b_p_ffu != NUL ? &(buf->b_p_ffu) : p->var; - case PV_EFM: + case kOptErrorformat: return *buf->b_p_efm != NUL ? &(buf->b_p_efm) : p->var; - case PV_GP: + case kOptGrepprg: return *buf->b_p_gp != NUL ? &(buf->b_p_gp) : p->var; - case PV_MP: + case kOptMakeprg: return *buf->b_p_mp != NUL ? &(buf->b_p_mp) : p->var; - case PV_SBR: + case kOptShowbreak: return *win->w_p_sbr != NUL ? &(win->w_p_sbr) : p->var; - case PV_STL: + case kOptStatusline: return *win->w_p_stl != NUL ? &(win->w_p_stl) : p->var; - case PV_WBR: + case kOptWinbar: return *win->w_p_wbr != NUL ? &(win->w_p_wbr) : p->var; - case PV_UL: + case kOptUndolevels: return buf->b_p_ul != NO_LOCAL_UNDOLEVEL ? &(buf->b_p_ul) : p->var; - case PV_LW: + case kOptLispwords: return *buf->b_p_lw != NUL ? &(buf->b_p_lw) : p->var; - case PV_MENC: + case kOptMakeencoding: return *buf->b_p_menc != NUL ? &(buf->b_p_menc) : p->var; - case PV_FCS: + case kOptFillchars: return *win->w_p_fcs != NUL ? &(win->w_p_fcs) : p->var; - case PV_LCS: + case kOptListchars: return *win->w_p_lcs != NUL ? &(win->w_p_lcs) : p->var; - case PV_VE: + case kOptVirtualedit: return *win->w_p_ve != NUL ? &win->w_p_ve : p->var; - case PV_ARAB: + case kOptArabic: return &(win->w_p_arab); - case PV_LIST: + case kOptList: return &(win->w_p_list); - case PV_SPELL: + case kOptSpell: return &(win->w_p_spell); - case PV_CUC: + case kOptCursorcolumn: return &(win->w_p_cuc); - case PV_CUL: + case kOptCursorline: return &(win->w_p_cul); - case PV_CULOPT: + case kOptCursorlineopt: return &(win->w_p_culopt); - case PV_CC: + case kOptColorcolumn: return &(win->w_p_cc); - case PV_DIFF: + case kOptDiff: return &(win->w_p_diff); - case PV_FDC: + case kOptFoldcolumn: return &(win->w_p_fdc); - case PV_FEN: + case kOptFoldenable: return &(win->w_p_fen); - case PV_FDI: + case kOptFoldignore: return &(win->w_p_fdi); - case PV_FDL: + case kOptFoldlevel: return &(win->w_p_fdl); - case PV_FDM: + case kOptFoldmethod: return &(win->w_p_fdm); - case PV_FML: + case kOptFoldminlines: return &(win->w_p_fml); - case PV_FDN: + case kOptFoldnestmax: return &(win->w_p_fdn); - case PV_FDE: + case kOptFoldexpr: return &(win->w_p_fde); - case PV_FDT: + case kOptFoldtext: return &(win->w_p_fdt); - case PV_FMR: + case kOptFoldmarker: return &(win->w_p_fmr); - case PV_NU: + case kOptNumber: return &(win->w_p_nu); - case PV_RNU: + case kOptRelativenumber: return &(win->w_p_rnu); - case PV_NUW: + case kOptNumberwidth: return &(win->w_p_nuw); - case PV_WFB: + case kOptWinfixbuf: return &(win->w_p_wfb); - case PV_WFH: + case kOptWinfixheight: return &(win->w_p_wfh); - case PV_WFW: + case kOptWinfixwidth: return &(win->w_p_wfw); - case PV_PVW: + case kOptPreviewwindow: return &(win->w_p_pvw); - case PV_RL: + case kOptRightleft: return &(win->w_p_rl); - case PV_RLC: + case kOptRightleftcmd: return &(win->w_p_rlc); - case PV_SCROLL: + case kOptScroll: return &(win->w_p_scr); - case PV_SMS: + case kOptSmoothscroll: return &(win->w_p_sms); - case PV_WRAP: + case kOptWrap: return &(win->w_p_wrap); - case PV_LBR: + case kOptLinebreak: return &(win->w_p_lbr); - case PV_BRI: + case kOptBreakindent: return &(win->w_p_bri); - case PV_BRIOPT: + case kOptBreakindentopt: return &(win->w_p_briopt); - case PV_SCBIND: + case kOptScrollbind: return &(win->w_p_scb); - case PV_CRBIND: + case kOptCursorbind: return &(win->w_p_crb); - case PV_COCU: + case kOptConcealcursor: return &(win->w_p_cocu); - case PV_COLE: + case kOptConceallevel: return &(win->w_p_cole); - case PV_AI: + case kOptAutoindent: return &(buf->b_p_ai); - case PV_BIN: + case kOptBinary: return &(buf->b_p_bin); - case PV_BOMB: + case kOptBomb: return &(buf->b_p_bomb); - case PV_BH: + case kOptBufhidden: return &(buf->b_p_bh); - case PV_BT: + case kOptBuftype: return &(buf->b_p_bt); - case PV_BL: + case kOptBuflisted: return &(buf->b_p_bl); - case PV_CHANNEL: + case kOptChannel: return &(buf->b_p_channel); - case PV_CI: + case kOptCopyindent: return &(buf->b_p_ci); - case PV_CIN: + case kOptCindent: return &(buf->b_p_cin); - case PV_CINK: + case kOptCinkeys: return &(buf->b_p_cink); - case PV_CINO: + case kOptCinoptions: return &(buf->b_p_cino); - case PV_CINSD: + case kOptCinscopedecls: return &(buf->b_p_cinsd); - case PV_CINW: + case kOptCinwords: return &(buf->b_p_cinw); - case PV_COM: + case kOptComments: return &(buf->b_p_com); - case PV_CMS: + case kOptCommentstring: return &(buf->b_p_cms); - case PV_CPT: + case kOptComplete: return &(buf->b_p_cpt); #ifdef BACKSLASH_IN_FILENAME - case PV_CSL: + case kOptCompleteslash: return &(buf->b_p_csl); #endif - case PV_CFU: + case kOptCompletefunc: return &(buf->b_p_cfu); - case PV_OFU: + case kOptOmnifunc: return &(buf->b_p_ofu); - case PV_EOF: + case kOptEndoffile: return &(buf->b_p_eof); - case PV_EOL: + case kOptEndofline: return &(buf->b_p_eol); - case PV_FIXEOL: + case kOptFixendofline: return &(buf->b_p_fixeol); - case PV_ET: + case kOptExpandtab: return &(buf->b_p_et); - case PV_FENC: + case kOptFileencoding: return &(buf->b_p_fenc); - case PV_FF: + case kOptFileformat: return &(buf->b_p_ff); - case PV_FT: + case kOptFiletype: return &(buf->b_p_ft); - case PV_FO: + case kOptFormatoptions: return &(buf->b_p_fo); - case PV_FLP: + case kOptFormatlistpat: return &(buf->b_p_flp); - case PV_IMI: + case kOptIminsert: return &(buf->b_p_iminsert); - case PV_IMS: + case kOptImsearch: return &(buf->b_p_imsearch); - case PV_INF: + case kOptInfercase: return &(buf->b_p_inf); - case PV_ISK: + case kOptIskeyword: return &(buf->b_p_isk); - case PV_INEX: + case kOptIncludeexpr: return &(buf->b_p_inex); - case PV_INDE: + case kOptIndentexpr: return &(buf->b_p_inde); - case PV_INDK: + case kOptIndentkeys: return &(buf->b_p_indk); - case PV_FEX: + case kOptFormatexpr: return &(buf->b_p_fex); - case PV_LISP: + case kOptLisp: return &(buf->b_p_lisp); - case PV_LOP: + case kOptLispoptions: return &(buf->b_p_lop); - case PV_ML: + case kOptModeline: return &(buf->b_p_ml); - case PV_MPS: + case kOptMatchpairs: return &(buf->b_p_mps); - case PV_MA: + case kOptModifiable: return &(buf->b_p_ma); - case PV_MOD: + case kOptModified: return &(buf->b_changed); - case PV_NF: + case kOptNrformats: return &(buf->b_p_nf); - case PV_PI: + case kOptPreserveindent: return &(buf->b_p_pi); - case PV_QE: + case kOptQuoteescape: return &(buf->b_p_qe); - case PV_RO: + case kOptReadonly: return &(buf->b_p_ro); - case PV_SCBK: + case kOptScrollback: return &(buf->b_p_scbk); - case PV_SI: + case kOptSmartindent: return &(buf->b_p_si); - case PV_STS: + case kOptSofttabstop: return &(buf->b_p_sts); - case PV_SUA: + case kOptSuffixesadd: return &(buf->b_p_sua); - case PV_SWF: + case kOptSwapfile: return &(buf->b_p_swf); - case PV_SMC: + case kOptSynmaxcol: return &(buf->b_p_smc); - case PV_SYN: + case kOptSyntax: return &(buf->b_p_syn); - case PV_SPC: + case kOptSpellcapcheck: return &(win->w_s->b_p_spc); - case PV_SPF: + case kOptSpellfile: return &(win->w_s->b_p_spf); - case PV_SPL: + case kOptSpelllang: return &(win->w_s->b_p_spl); - case PV_SPO: + case kOptSpelloptions: return &(win->w_s->b_p_spo); - case PV_SW: + case kOptShiftwidth: return &(buf->b_p_sw); - case PV_TFU: + case kOptTagfunc: return &(buf->b_p_tfu); - case PV_TS: + case kOptTabstop: return &(buf->b_p_ts); - case PV_TW: + case kOptTextwidth: return &(buf->b_p_tw); - case PV_UDF: + case kOptUndofile: return &(buf->b_p_udf); - case PV_WM: + case kOptWrapmargin: return &(buf->b_p_wm); - case PV_VSTS: + case kOptVarsofttabstop: return &(buf->b_p_vsts); - case PV_VTS: + case kOptVartabstop: return &(buf->b_p_vts); - case PV_KMAP: + case kOptKeymap: return &(buf->b_p_keymap); - case PV_SCL: + case kOptSigncolumn: return &(win->w_p_scl); - case PV_WINHL: + case kOptWinhighlight: return &(win->w_p_winhl); - case PV_WINBL: + case kOptWinblend: return &(win->w_p_winbl); - case PV_STC: + case kOptStatuscolumn: return &(win->w_p_stc); default: iemsg(_("E356: get_varp ERROR")); @@ -5008,26 +5036,8 @@ void didset_window_options(win_T *wp, bool valid_cursor) wp->w_grid_alloc.blending = wp->w_p_winbl > 0; } -/// Index into the options table for a buffer-local option enum. -static OptIndex buf_opt_idx[BV_COUNT]; #define COPY_OPT_SCTX(buf, bv) buf->b_p_script_ctx[bv] = options[buf_opt_idx[bv]].last_set -/// Initialize buf_opt_idx[] if not done already. -static void init_buf_opt_idx(void) -{ - static bool did_init_buf_opt_idx = false; - - if (did_init_buf_opt_idx) { - return; - } - did_init_buf_opt_idx = true; - for (OptIndex i = 0; i < kOptIndexCount; i++) { - if (options[i].indir & PV_BUF) { - buf_opt_idx[options[i].indir & PV_MASK] = i; - } - } -} - /// Copy global option values to local options for one buffer. /// Used when creating a new buffer and sometimes when entering a buffer. /// flags: @@ -5065,7 +5075,6 @@ void buf_copy_options(buf_T *buf, int flags) if (should_copy || (flags & BCO_ALWAYS)) { CLEAR_FIELD(buf->b_p_script_ctx); - init_buf_opt_idx(); // Don't copy the options specific to a help buffer when // BCO_NOHELP is given or the options were initialized already // (jumping back to a help file with CTRL-T or CTRL-O) @@ -5101,61 +5110,61 @@ void buf_copy_options(buf_T *buf, int flags) } buf->b_p_ai = p_ai; - COPY_OPT_SCTX(buf, BV_AI); + COPY_OPT_SCTX(buf, kBufOptAutoindent); buf->b_p_ai_nopaste = p_ai_nopaste; buf->b_p_sw = p_sw; - COPY_OPT_SCTX(buf, BV_SW); + COPY_OPT_SCTX(buf, kBufOptShiftwidth); buf->b_p_scbk = p_scbk; - COPY_OPT_SCTX(buf, BV_SCBK); + COPY_OPT_SCTX(buf, kBufOptScrollback); buf->b_p_tw = p_tw; - COPY_OPT_SCTX(buf, BV_TW); + COPY_OPT_SCTX(buf, kBufOptTextwidth); buf->b_p_tw_nopaste = p_tw_nopaste; buf->b_p_tw_nobin = p_tw_nobin; buf->b_p_wm = p_wm; - COPY_OPT_SCTX(buf, BV_WM); + COPY_OPT_SCTX(buf, kBufOptWrapmargin); buf->b_p_wm_nopaste = p_wm_nopaste; buf->b_p_wm_nobin = p_wm_nobin; buf->b_p_bin = p_bin; - COPY_OPT_SCTX(buf, BV_BIN); + COPY_OPT_SCTX(buf, kBufOptBinary); buf->b_p_bomb = p_bomb; - COPY_OPT_SCTX(buf, BV_BOMB); + COPY_OPT_SCTX(buf, kBufOptBomb); buf->b_p_et = p_et; - COPY_OPT_SCTX(buf, BV_ET); + COPY_OPT_SCTX(buf, kBufOptExpandtab); buf->b_p_fixeol = p_fixeol; - COPY_OPT_SCTX(buf, BV_FIXEOL); + COPY_OPT_SCTX(buf, kBufOptFixendofline); buf->b_p_et_nobin = p_et_nobin; buf->b_p_et_nopaste = p_et_nopaste; buf->b_p_ml = p_ml; - COPY_OPT_SCTX(buf, BV_ML); + COPY_OPT_SCTX(buf, kBufOptModeline); buf->b_p_ml_nobin = p_ml_nobin; buf->b_p_inf = p_inf; - COPY_OPT_SCTX(buf, BV_INF); + COPY_OPT_SCTX(buf, kBufOptInfercase); if (cmdmod.cmod_flags & CMOD_NOSWAPFILE) { buf->b_p_swf = false; } else { buf->b_p_swf = p_swf; - COPY_OPT_SCTX(buf, BV_SWF); + COPY_OPT_SCTX(buf, kBufOptSwapfile); } buf->b_p_cpt = xstrdup(p_cpt); - COPY_OPT_SCTX(buf, BV_CPT); + COPY_OPT_SCTX(buf, kBufOptComplete); #ifdef BACKSLASH_IN_FILENAME buf->b_p_csl = xstrdup(p_csl); - COPY_OPT_SCTX(buf, BV_CSL); + COPY_OPT_SCTX(buf, kBufOptCompleteslash); #endif buf->b_p_cfu = xstrdup(p_cfu); - COPY_OPT_SCTX(buf, BV_CFU); + COPY_OPT_SCTX(buf, kBufOptCompletefunc); set_buflocal_cfu_callback(buf); buf->b_p_ofu = xstrdup(p_ofu); - COPY_OPT_SCTX(buf, BV_OFU); + COPY_OPT_SCTX(buf, kBufOptOmnifunc); set_buflocal_ofu_callback(buf); buf->b_p_tfu = xstrdup(p_tfu); - COPY_OPT_SCTX(buf, BV_TFU); + COPY_OPT_SCTX(buf, kBufOptTagfunc); set_buflocal_tfu_callback(buf); buf->b_p_sts = p_sts; - COPY_OPT_SCTX(buf, BV_STS); + COPY_OPT_SCTX(buf, kBufOptSofttabstop); buf->b_p_sts_nopaste = p_sts_nopaste; buf->b_p_vsts = xstrdup(p_vsts); - COPY_OPT_SCTX(buf, BV_VSTS); + COPY_OPT_SCTX(buf, kBufOptVarsofttabstop); if (p_vsts && p_vsts != empty_string_option) { tabstop_set(p_vsts, &buf->b_p_vsts_array); } else { @@ -5163,75 +5172,75 @@ void buf_copy_options(buf_T *buf, int flags) } buf->b_p_vsts_nopaste = p_vsts_nopaste ? xstrdup(p_vsts_nopaste) : NULL; buf->b_p_com = xstrdup(p_com); - COPY_OPT_SCTX(buf, BV_COM); + COPY_OPT_SCTX(buf, kBufOptComments); buf->b_p_cms = xstrdup(p_cms); - COPY_OPT_SCTX(buf, BV_CMS); + COPY_OPT_SCTX(buf, kBufOptCommentstring); buf->b_p_fo = xstrdup(p_fo); - COPY_OPT_SCTX(buf, BV_FO); + COPY_OPT_SCTX(buf, kBufOptFormatoptions); buf->b_p_flp = xstrdup(p_flp); - COPY_OPT_SCTX(buf, BV_FLP); + COPY_OPT_SCTX(buf, kBufOptFormatlistpat); buf->b_p_nf = xstrdup(p_nf); - COPY_OPT_SCTX(buf, BV_NF); + COPY_OPT_SCTX(buf, kBufOptNrformats); buf->b_p_mps = xstrdup(p_mps); - COPY_OPT_SCTX(buf, BV_MPS); + COPY_OPT_SCTX(buf, kBufOptMatchpairs); buf->b_p_si = p_si; - COPY_OPT_SCTX(buf, BV_SI); + COPY_OPT_SCTX(buf, kBufOptSmartindent); buf->b_p_channel = 0; buf->b_p_ci = p_ci; - COPY_OPT_SCTX(buf, BV_CI); + COPY_OPT_SCTX(buf, kBufOptCopyindent); buf->b_p_cin = p_cin; - COPY_OPT_SCTX(buf, BV_CIN); + COPY_OPT_SCTX(buf, kBufOptCindent); buf->b_p_cink = xstrdup(p_cink); - COPY_OPT_SCTX(buf, BV_CINK); + COPY_OPT_SCTX(buf, kBufOptCinkeys); buf->b_p_cino = xstrdup(p_cino); - COPY_OPT_SCTX(buf, BV_CINO); + COPY_OPT_SCTX(buf, kBufOptCinoptions); buf->b_p_cinsd = xstrdup(p_cinsd); - COPY_OPT_SCTX(buf, BV_CINSD); + COPY_OPT_SCTX(buf, kBufOptCinscopedecls); buf->b_p_lop = xstrdup(p_lop); - COPY_OPT_SCTX(buf, BV_LOP); + COPY_OPT_SCTX(buf, kBufOptLispoptions); // Don't copy 'filetype', it must be detected buf->b_p_ft = empty_string_option; buf->b_p_pi = p_pi; - COPY_OPT_SCTX(buf, BV_PI); + COPY_OPT_SCTX(buf, kBufOptPreserveindent); buf->b_p_cinw = xstrdup(p_cinw); - COPY_OPT_SCTX(buf, BV_CINW); + COPY_OPT_SCTX(buf, kBufOptCinwords); buf->b_p_lisp = p_lisp; - COPY_OPT_SCTX(buf, BV_LISP); + COPY_OPT_SCTX(buf, kBufOptLisp); // Don't copy 'syntax', it must be set buf->b_p_syn = empty_string_option; buf->b_p_smc = p_smc; - COPY_OPT_SCTX(buf, BV_SMC); + COPY_OPT_SCTX(buf, kBufOptSynmaxcol); buf->b_s.b_syn_isk = empty_string_option; buf->b_s.b_p_spc = xstrdup(p_spc); - COPY_OPT_SCTX(buf, BV_SPC); + COPY_OPT_SCTX(buf, kBufOptSpellcapcheck); compile_cap_prog(&buf->b_s); buf->b_s.b_p_spf = xstrdup(p_spf); - COPY_OPT_SCTX(buf, BV_SPF); + COPY_OPT_SCTX(buf, kBufOptSpellfile); buf->b_s.b_p_spl = xstrdup(p_spl); - COPY_OPT_SCTX(buf, BV_SPL); + COPY_OPT_SCTX(buf, kBufOptSpelllang); buf->b_s.b_p_spo = xstrdup(p_spo); - COPY_OPT_SCTX(buf, BV_SPO); + COPY_OPT_SCTX(buf, kBufOptSpelloptions); buf->b_s.b_p_spo_flags = spo_flags; buf->b_p_inde = xstrdup(p_inde); - COPY_OPT_SCTX(buf, BV_INDE); + COPY_OPT_SCTX(buf, kBufOptIndentexpr); buf->b_p_indk = xstrdup(p_indk); - COPY_OPT_SCTX(buf, BV_INDK); + COPY_OPT_SCTX(buf, kBufOptIndentkeys); buf->b_p_fp = empty_string_option; buf->b_p_fex = xstrdup(p_fex); - COPY_OPT_SCTX(buf, BV_FEX); + COPY_OPT_SCTX(buf, kBufOptFormatexpr); buf->b_p_sua = xstrdup(p_sua); - COPY_OPT_SCTX(buf, BV_SUA); + COPY_OPT_SCTX(buf, kBufOptSuffixesadd); buf->b_p_keymap = xstrdup(p_keymap); - COPY_OPT_SCTX(buf, BV_KMAP); + COPY_OPT_SCTX(buf, kBufOptKeymap); buf->b_kmap_state |= KEYMAP_INIT; // This isn't really an option, but copying the langmap and IME // state from the current buffer is better than resetting it. buf->b_p_iminsert = p_iminsert; - COPY_OPT_SCTX(buf, BV_IMI); + COPY_OPT_SCTX(buf, kBufOptIminsert); buf->b_p_imsearch = p_imsearch; - COPY_OPT_SCTX(buf, BV_IMS); + COPY_OPT_SCTX(buf, kBufOptImsearch); // options that are normally global but also have a local value // are not copied, start using the global value @@ -5252,16 +5261,16 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_def = empty_string_option; buf->b_p_inc = empty_string_option; buf->b_p_inex = xstrdup(p_inex); - COPY_OPT_SCTX(buf, BV_INEX); + COPY_OPT_SCTX(buf, kBufOptIncludeexpr); buf->b_p_cot = empty_string_option; buf->b_cot_flags = 0; buf->b_p_dict = empty_string_option; buf->b_p_tsr = empty_string_option; buf->b_p_tsrfu = empty_string_option; buf->b_p_qe = xstrdup(p_qe); - COPY_OPT_SCTX(buf, BV_QE); + COPY_OPT_SCTX(buf, kBufOptQuoteescape); buf->b_p_udf = p_udf; - COPY_OPT_SCTX(buf, BV_UDF); + COPY_OPT_SCTX(buf, kBufOptUndofile); buf->b_p_lw = empty_string_option; buf->b_p_menc = empty_string_option; @@ -5278,12 +5287,12 @@ void buf_copy_options(buf_T *buf, int flags) } } else { buf->b_p_isk = xstrdup(p_isk); - COPY_OPT_SCTX(buf, BV_ISK); + COPY_OPT_SCTX(buf, kBufOptIskeyword); did_isk = true; buf->b_p_ts = p_ts; - COPY_OPT_SCTX(buf, BV_TS); + COPY_OPT_SCTX(buf, kBufOptTabstop); buf->b_p_vts = xstrdup(p_vts); - COPY_OPT_SCTX(buf, BV_VTS); + COPY_OPT_SCTX(buf, kBufOptVartabstop); if (p_vts && p_vts != empty_string_option && !buf->b_p_vts_array) { tabstop_set(p_vts, &buf->b_p_vts_array); } else { @@ -5294,7 +5303,7 @@ void buf_copy_options(buf_T *buf, int flags) clear_string_option(&buf->b_p_bt); } buf->b_p_ma = p_ma; - COPY_OPT_SCTX(buf, BV_MA); + COPY_OPT_SCTX(buf, kBufOptModifiable); } } @@ -5645,7 +5654,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numM } } char *str; - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { str = options[opt_idx].fullname; if (is_option_hidden(opt_idx)) { continue; @@ -6306,11 +6315,11 @@ dict_T *get_winbuf_options(const int bufopt) { dict_T *const d = tv_dict_alloc(); - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { vimoption_T *opt = &options[opt_idx]; - if ((bufopt && (opt->indir & PV_BUF)) - || (!bufopt && (opt->indir & PV_WIN))) { + if ((bufopt && (option_has_scope(opt_idx, kOptScopeBuf))) + || (!bufopt && (option_has_scope(opt_idx, kOptScopeWin)))) { void *varp = get_varp(opt); if (varp != NULL) { @@ -6353,8 +6362,8 @@ Dict get_vimoption(String name, int scope, buf_T *buf, win_T *win, Arena *arena, Dict get_all_vimoptions(Arena *arena) { - Dict retval = arena_dict(arena, kOptIndexCount); - for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) { + Dict retval = arena_dict(arena, kOptCount); + for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) { Dict opt_dict = vimoption2dict(&options[opt_idx], OPT_GLOBAL, curbuf, curwin, arena); PUT_C(retval, options[opt_idx].fullname, DICT_OBJ(opt_dict)); } @@ -6363,15 +6372,16 @@ Dict get_all_vimoptions(Arena *arena) static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *win, Arena *arena) { + OptIndex opt_idx = get_opt_idx(opt); Dict dict = arena_dict(arena, 13); PUT_C(dict, "name", CSTR_AS_OBJ(opt->fullname)); PUT_C(dict, "shortname", CSTR_AS_OBJ(opt->shortname)); const char *scope; - if (opt->indir & PV_BUF) { + if (option_has_scope(opt_idx, kOptScopeBuf)) { scope = "buf"; - } else if (opt->indir & PV_WIN) { + } else if (option_has_scope(opt_idx, kOptScopeWin)) { scope = "win"; } else { scope = "global"; @@ -6380,7 +6390,7 @@ static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *w PUT_C(dict, "scope", CSTR_AS_OBJ(scope)); // welcome to the jungle - PUT_C(dict, "global_local", BOOLEAN_OBJ(opt->indir & PV_BOTH)); + PUT_C(dict, "global_local", BOOLEAN_OBJ(option_is_global_local(opt_idx))); PUT_C(dict, "commalist", BOOLEAN_OBJ(opt->flags & kOptFlagComma)); PUT_C(dict, "flaglist", BOOLEAN_OBJ(opt->flags & kOptFlagFlagList)); @@ -6391,11 +6401,11 @@ static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *w last_set = opt->last_set; } else { // Scope is either OPT_LOCAL or a fallback mode was requested. - if (opt->indir & PV_BUF) { - last_set = buf->b_p_script_ctx[opt->indir & PV_MASK]; + if (option_has_scope(opt_idx, kOptScopeBuf)) { + last_set = buf->b_p_script_ctx[opt->scope_idx[kOptScopeBuf]]; } - if (opt->indir & PV_WIN) { - last_set = win->w_p_script_ctx[opt->indir & PV_MASK]; + if (option_has_scope(opt_idx, kOptScopeWin)) { + last_set = win->w_p_script_ctx[opt->scope_idx[kOptScopeWin]]; } if (req_scope != OPT_LOCAL && last_set.script_ctx.sc_sid == 0) { last_set = opt->last_set; @@ -6412,24 +6422,3 @@ static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *w return dict; } - -/// Check if option is multitype (supports multiple types). -static bool option_is_multitype(OptIndex opt_idx) -{ - const OptTypeFlags type_flags = get_option(opt_idx)->type_flags; - assert(type_flags != 0); - return !is_power_of_two(type_flags); -} - -/// Check if option supports a specific type. -bool option_has_type(OptIndex opt_idx, OptValType type) -{ - // Ensure that type flags variable can hold all types. - STATIC_ASSERT(kOptValTypeSize <= sizeof(OptTypeFlags) * 8, - "Option type_flags cannot fit all option types"); - // Ensure that the type is valid before accessing type_flags. - assert(type > kOptValTypeNil && type < kOptValTypeSize); - // Bitshift 1 by the value of type to get the type's corresponding flag, and check if it's set in - // the type_flags bit field. - return get_option(opt_idx)->type_flags & (1 << type); -} diff --git a/src/nvim/option.h b/src/nvim/option.h index 138d90da97..cba5b00d95 100644 --- a/src/nvim/option.h +++ b/src/nvim/option.h @@ -13,56 +13,6 @@ #include "nvim/option_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep -/// The options that are local to a window or buffer have "indir" set to one of -/// these values. Special values: -/// PV_NONE: global option. -/// PV_WIN is added: window-local option -/// PV_BUF is added: buffer-local option -/// PV_BOTH is added: global option which also has a local value. -enum { - PV_BOTH = 0x1000, - PV_WIN = 0x2000, - PV_BUF = 0x4000, - PV_MASK = 0x0fff, -}; -#define OPT_WIN(x) (idopt_T)(PV_WIN + (int)(x)) -#define OPT_BUF(x) (idopt_T)(PV_BUF + (int)(x)) -#define OPT_BOTH(x) (idopt_T)(PV_BOTH + (int)(x)) - -/// WV_ and BV_ values get typecasted to this for the "indir" field -typedef enum { - PV_NONE = 0, - PV_MAXVAL = 0xffff, ///< to avoid warnings for value out of range -} idopt_T; - -// Options local to a window have a value local to a buffer and global to all -// buffers. Indicate this by setting "var" to VAR_WIN. -#define VAR_WIN ((char *)-1) - -typedef struct { - char *fullname; ///< full option name - char *shortname; ///< permissible abbreviation - uint32_t flags; ///< see above - OptTypeFlags type_flags; ///< option type flags, see OptValType - void *var; ///< global option: pointer to variable; - ///< window-local option: VAR_WIN; - ///< buffer-local option: global value - idopt_T indir; ///< global option: PV_NONE; - ///< local option: indirect option index - bool immutable; ///< option is immutable, trying to set its value will give an error. - - /// callback function to invoke after an option is modified to validate and - /// apply the new value. - opt_did_set_cb_T opt_did_set_cb; - - /// callback function to invoke when expanding possible values on the - /// cmdline. Only useful for string options. - opt_expand_cb_T opt_expand_cb; - - OptVal def_val; ///< default value - LastSet last_set; ///< script in which the option was last set -} vimoption_T; - /// flags for buf_copy_options() enum { BCO_ENTER = 1, ///< going to enter the buffer @@ -85,13 +35,6 @@ typedef enum { OPT_SKIPRTP = 0x80, ///< "skiprtp" in 'sessionoptions' } OptionSetFlags; -/// Return value from get_option_attrs(). -enum { - SOPT_GLOBAL = 0x01, ///< Option has global value - SOPT_WIN = 0x02, ///< Option has window-local value - SOPT_BUF = 0x04, ///< Option has buffer-local value -}; - /// Get name of OptValType as a string. static inline const char *optval_type_get_name(const OptValType type) { diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index e32edbf727..832e03148a 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -54,12 +54,20 @@ typedef enum { kOptValTypeNumber, kOptValTypeString, } OptValType; - /// Always update this whenever a new option type is added. #define kOptValTypeSize (kOptValTypeString + 1) - typedef uint32_t OptTypeFlags; +/// Scopes that an option can support. +typedef enum { + kOptScopeGlobal = 0, ///< Request global option value + kOptScopeWin, ///< Request window-local option value + kOptScopeBuf, ///< Request buffer-local option value +} OptScope; +/// Always update this whenever a new option scope is added. +#define kOptScopeSize (kOptScopeBuf + 1) +typedef uint8_t OptScopeFlags; + typedef union { // boolean options are actually tri-states because they have a third "None" value. TriState boolean; @@ -161,9 +169,26 @@ typedef struct { /// caller. typedef int (*opt_expand_cb_T)(optexpand_T *args, int *numMatches, char ***matches); -/// Requested option scopes for various functions in option.c -typedef enum { - kOptReqGlobal = 0, ///< Request global option value - kOptReqWin = 1, ///< Request window-local option value - kOptReqBuf = 2, ///< Request buffer-local option value -} OptReqScope; +typedef struct { + char *fullname; ///< full option name + char *shortname; ///< permissible abbreviation + uint32_t flags; ///< see above + OptTypeFlags type_flags; ///< option type flags, see OptValType + OptScopeFlags scope_flags; ///< option scope flags, see OptScope + void *var; ///< global option: pointer to variable; + ///< window-local option: NULL; + ///< buffer-local option: global value + ssize_t scope_idx[kOptScopeSize]; ///< index of option at every scope. + bool immutable; ///< option is immutable, trying to set it will give an error. + + /// callback function to invoke after an option is modified to validate and + /// apply the new value. + opt_did_set_cb_T opt_did_set_cb; + + /// callback function to invoke when expanding possible values on the + /// cmdline. Only useful for string options. + opt_expand_cb_T opt_expand_cb; + + OptVal def_val; ///< default value + LastSet last_set; ///< script in which the option was last set +} vimoption_T; diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 6fab0621f9..d61cba892b 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -7,7 +7,6 @@ --- @field alias? string|string[] --- @field short_desc? string|fun(): string --- @field varname? string ---- @field pv_name? string --- @field type vim.option_type|vim.option_type[] --- @field immutable? boolean --- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma' @@ -41,7 +40,7 @@ --- @field doc? string Default to show in options.txt --- @field meta? integer|boolean|string Default to use in Lua meta files ---- @alias vim.option_scope 'global'|'buffer'|'window' +--- @alias vim.option_scope 'global'|'buf'|'win' --- @alias vim.option_type 'boolean'|'number'|'string' --- @alias vim.option_value boolean|number|string @@ -81,6 +80,8 @@ end -- luacheck: ignore 621 return { cstr = cstr, + --- @type string[] + valid_scopes = { 'global', 'buf', 'win' }, --- @type vim.option_meta[] --- The order of the options MUST be alphabetic for ":set all". options = { @@ -173,7 +174,7 @@ return { ]=], full_name = 'arabic', redraw = { 'curswant' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('Arabic as a default second language'), type = 'boolean', }, @@ -236,7 +237,7 @@ return { a different way. ]=], full_name = 'autoindent', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('take indent for new line from previous line'), type = 'boolean', varname = 'p_ai', @@ -256,7 +257,7 @@ return { < ]=], full_name = 'autoread', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_('autom. read file when changed outside of Vim'), type = 'boolean', varname = 'p_ar', @@ -457,7 +458,7 @@ return { expand_cb = 'expand_set_backupcopy', full_name = 'backupcopy', list = 'onecomma', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_("make backup as a copy, don't rename the file"), type = 'string', varname = 'p_bkc', @@ -667,7 +668,7 @@ return { ]=], full_name = 'binary', redraw = { 'statuslines' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('read/write/edit file in binary mode'), type = 'boolean', varname = 'p_bin', @@ -695,7 +696,7 @@ return { full_name = 'bomb', no_mkrc = true, redraw = { 'statuslines' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('a Byte Order Mark to the file'), type = 'boolean', varname = 'p_bomb', @@ -729,7 +730,7 @@ return { ]=], full_name = 'breakindent', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('wrapped line repeats indent'), type = 'boolean', }, @@ -771,7 +772,7 @@ return { full_name = 'breakindentopt', list = 'onecomma', redraw = { 'current_buffer' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_("settings for 'breakindent'"), type = 'string', }, @@ -823,7 +824,7 @@ return { expand_cb = 'expand_set_bufhidden', full_name = 'bufhidden', noglob = true, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('what to do when buffer is no longer in window'), type = 'string', varname = 'p_bh', @@ -841,7 +842,7 @@ return { ]=], full_name = 'buflisted', noglob = true, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('whether the buffer shows up in the buffer list'), tags = { 'E85' }, type = 'boolean', @@ -900,7 +901,7 @@ return { expand_cb = 'expand_set_buftype', full_name = 'buftype', noglob = true, - scope = { 'buffer' }, + scope = { 'buf' }, tags = { 'E382' }, short_desc = N_('special type of buffer'), type = 'string', @@ -1015,7 +1016,7 @@ return { full_name = 'channel', no_mkrc = true, nodefault = true, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('Channel connected to the buffer'), type = 'number', varname = 'p_channel', @@ -1093,7 +1094,7 @@ return { option or 'indentexpr'. ]=], full_name = 'cindent', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('do C program indenting'), type = 'boolean', varname = 'p_cin', @@ -1111,7 +1112,7 @@ return { ]=], full_name = 'cinkeys', list = 'onecomma', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_("keys that trigger indent when 'cindent' is set"), type = 'string', varname = 'p_cink', @@ -1128,7 +1129,7 @@ return { ]=], full_name = 'cinoptions', list = 'onecomma', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_("how to do indenting when 'cindent' is set"), type = 'string', varname = 'p_cino', @@ -1146,7 +1147,7 @@ return { ]=], full_name = 'cinscopedecls', list = 'onecomma', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_("words that are recognized by 'cino-g'"), type = 'string', varname = 'p_cinsd', @@ -1165,7 +1166,7 @@ return { ]=], full_name = 'cinwords', list = 'onecomma', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_("words where 'si' and 'cin' add an indent"), type = 'string', varname = 'p_cinw', @@ -1267,7 +1268,7 @@ return { full_name = 'colorcolumn', list = 'onecomma', redraw = { 'current_window', 'highlight_only' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('columns to highlight'), type = 'string', }, @@ -1312,7 +1313,7 @@ return { ]=], full_name = 'comments', list = 'onecomma', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('patterns that can start a comment line'), tags = { 'E524', 'E525' }, type = 'string', @@ -1328,7 +1329,7 @@ return { Used for |commenting| and to add markers for folding, see |fold-marker|. ]=], full_name = 'commentstring', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('template for comments; used for fold marker'), tags = { 'E537' }, type = 'string', @@ -1385,7 +1386,7 @@ return { expand_cb = 'expand_set_complete', full_name = 'complete', list = 'onecomma', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('specify how Insert mode completion works'), tags = { 'E535' }, type = 'string', @@ -1407,7 +1408,7 @@ return { ]=], full_name = 'completefunc', func = true, - scope = { 'buffer' }, + scope = { 'buf' }, secure = true, short_desc = N_('function to be used for Insert mode completion'), type = 'string', @@ -1483,7 +1484,7 @@ return { expand_cb = 'expand_set_completeopt', full_name = 'completeopt', list = 'onecomma', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_('options for Insert mode completion'), type = 'string', varname = 'p_cot', @@ -1508,7 +1509,7 @@ return { enable_if = 'BACKSLASH_IN_FILENAME', expand_cb = 'expand_set_completeslash', full_name = 'completeslash', - scope = { 'buffer' }, + scope = { 'buf' }, type = 'string', varname = 'p_csl', }, @@ -1537,7 +1538,7 @@ return { full_name = 'concealcursor', list = 'flags', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('whether concealable text is hidden in cursor line'), type = 'string', }, @@ -1566,7 +1567,7 @@ return { ]=], full_name = 'conceallevel', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('whether concealable text is shown or hidden'), type = 'number', }, @@ -1604,7 +1605,7 @@ return { See 'preserveindent'. ]=], full_name = 'copyindent', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_("make 'autoindent' use existing indent structure"), type = 'boolean', varname = 'p_ci', @@ -1865,8 +1866,7 @@ return { taken into account. ]=], full_name = 'cursorbind', - pv_name = 'p_crbind', - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('move cursor in window as it moves in other windows'), type = 'boolean', }, @@ -1885,7 +1885,7 @@ return { ]=], full_name = 'cursorcolumn', redraw = { 'current_window', 'highlight_only' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('highlight the screen column of the cursor'), type = 'boolean', }, @@ -1900,7 +1900,7 @@ return { ]=], full_name = 'cursorline', redraw = { 'current_window', 'highlight_only' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('highlight the screen line of the cursor'), type = 'boolean', }, @@ -1928,7 +1928,7 @@ return { full_name = 'cursorlineopt', list = 'onecomma', redraw = { 'current_window', 'highlight_only' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_("settings for 'cursorline'"), type = 'string', }, @@ -1979,7 +1979,7 @@ return { < ]=], full_name = 'define', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_('pattern to be used to find a macro definition'), type = 'string', varname = 'p_def', @@ -2036,7 +2036,7 @@ return { full_name = 'dictionary', list = 'onecomma', normal_dname_chars = true, - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_('list of file names used for keyword completion'), type = 'string', varname = 'p_dict', @@ -2051,7 +2051,7 @@ return { full_name = 'diff', noglob = true, redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('diff mode for the current window'), type = 'boolean', }, @@ -2377,7 +2377,7 @@ return { full_name = 'endoffile', no_mkrc = true, redraw = { 'statuslines' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('write CTRL-Z for last line in file'), type = 'boolean', varname = 'p_eof', @@ -2403,7 +2403,7 @@ return { full_name = 'endofline', no_mkrc = true, redraw = { 'statuslines' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('write <EOL> for last line in file'), type = 'boolean', varname = 'p_eol', @@ -2448,7 +2448,7 @@ return { ]=], expand = true, full_name = 'equalprg', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, secure = true, short_desc = N_('external program to use for "=" command'), type = 'string', @@ -2504,7 +2504,7 @@ return { ]=], full_name = 'errorformat', list = 'onecomma', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_('description of the lines in the error file'), type = 'string', varname = 'p_efm', @@ -2540,7 +2540,7 @@ return { on, use CTRL-V<Tab>. See also |:retab| and |ins-expandtab|. ]=], full_name = 'expandtab', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('use spaces when <Tab> is inserted'), type = 'boolean', varname = 'p_et', @@ -2614,7 +2614,7 @@ return { full_name = 'fileencoding', no_mkrc = true, redraw = { 'statuslines', 'current_buffer' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('file encoding for multi-byte text'), tags = { 'E213' }, type = 'string', @@ -2710,7 +2710,7 @@ return { full_name = 'fileformat', no_mkrc = true, redraw = { 'curswant', 'statuslines' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('file format used for file I/O'), type = 'string', varname = 'p_ff', @@ -2829,7 +2829,7 @@ return { full_name = 'filetype', noglob = true, normal_fname_chars = true, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('type of file, used for autocommands'), type = 'string', varname = 'p_ft', @@ -2904,7 +2904,7 @@ return { full_name = 'fillchars', list = 'onecomma', redraw = { 'current_window' }, - scope = { 'global', 'window' }, + scope = { 'global', 'win' }, short_desc = N_('characters to use for displaying special items'), type = 'string', varname = 'p_fcs', @@ -2962,7 +2962,7 @@ return { ]=], full_name = 'findfunc', func = true, - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, secure = true, short_desc = N_('function called for :find'), tags = { 'E1514' }, @@ -2984,7 +2984,7 @@ return { ]=], full_name = 'fixendofline', redraw = { 'statuslines' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('make sure last line in file has <EOL>'), type = 'boolean', varname = 'p_fixeol', @@ -3024,7 +3024,7 @@ return { expand_cb = 'expand_set_foldcolumn', full_name = 'foldcolumn', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('width of the column used to indicate folds'), type = 'string', }, @@ -3042,7 +3042,7 @@ return { ]=], full_name = 'foldenable', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('set to display all folds open'), type = 'boolean', }, @@ -3067,7 +3067,7 @@ return { full_name = 'foldexpr', modelineexpr = true, redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('expression used when \'foldmethod\' is "expr"'), type = 'string', }, @@ -3083,7 +3083,7 @@ return { ]=], full_name = 'foldignore', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('ignore lines when \'foldmethod\' is "indent"'), type = 'string', }, @@ -3100,7 +3100,7 @@ return { ]=], full_name = 'foldlevel', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('close folds with a level higher than this'), type = 'number', }, @@ -3139,7 +3139,7 @@ return { full_name = 'foldmarker', list = 'onecomma', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('markers used when \'foldmethod\' is "marker"'), tags = { 'E536' }, type = 'string', @@ -3160,7 +3160,7 @@ return { expand_cb = 'expand_set_foldmethod', full_name = 'foldmethod', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('folding type'), type = 'string', }, @@ -3179,7 +3179,7 @@ return { ]=], full_name = 'foldminlines', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('minimum number of lines for a fold to be closed'), type = 'number', }, @@ -3194,7 +3194,7 @@ return { ]=], full_name = 'foldnestmax', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('maximum fold depth'), type = 'number', }, @@ -3267,7 +3267,7 @@ return { full_name = 'foldtext', modelineexpr = true, redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('expression used to display for a closed fold'), type = 'string', }, @@ -3319,7 +3319,7 @@ return { ]=], full_name = 'formatexpr', modelineexpr = true, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('expression used with "gq" command'), type = 'string', varname = 'p_fex', @@ -3339,7 +3339,7 @@ return { character and white space. ]=], full_name = 'formatlistpat', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('pattern used to recognize a list header'), type = 'string', varname = 'p_flp', @@ -3359,7 +3359,7 @@ return { expand_cb = 'expand_set_formatoptions', full_name = 'formatoptions', list = 'flags', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('how automatic formatting is to be done'), type = 'string', varname = 'p_fo', @@ -3382,7 +3382,7 @@ return { ]=], expand = true, full_name = 'formatprg', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, secure = true, short_desc = N_('name of external program used with "gq" command'), type = 'string', @@ -3490,7 +3490,7 @@ return { ]=], expand = true, full_name = 'grepprg', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, secure = true, short_desc = N_('program to use for ":grep"'), type = 'string', @@ -4132,8 +4132,7 @@ return { It is also used for the argument of commands like "r" and "f". ]=], full_name = 'iminsert', - pv_name = 'p_imi', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('use :lmap or IM in Insert mode'), type = 'number', varname = 'p_iminsert', @@ -4155,8 +4154,7 @@ return { option to a valid keymap name. ]=], full_name = 'imsearch', - pv_name = 'p_ims', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('use :lmap or IM when typing a search pattern'), type = 'number', varname = 'p_imsearch', @@ -4203,7 +4201,7 @@ return { See |option-backslash| about including spaces and backslashes. ]=], full_name = 'include', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_('pattern to be used to find an include file'), type = 'string', varname = 'p_inc', @@ -4245,7 +4243,7 @@ return { ]=], full_name = 'includeexpr', modelineexpr = true, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('expression used to process an include line'), type = 'string', varname = 'p_inex', @@ -4340,7 +4338,7 @@ return { ]=], full_name = 'indentexpr', modelineexpr = true, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('expression used to obtain the indent of a line'), type = 'string', varname = 'p_inde', @@ -4357,7 +4355,7 @@ return { ]=], full_name = 'indentkeys', list = 'onecomma', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_("keys that trigger indenting with 'indentexpr'"), type = 'string', varname = 'p_indk', @@ -4376,7 +4374,7 @@ return { With 'noinfercase' the match is used as-is. ]=], full_name = 'infercase', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('adjust case of match for keyword completion'), type = 'boolean', varname = 'p_inf', @@ -4507,7 +4505,7 @@ return { ]=], full_name = 'iskeyword', list = 'comma', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('characters included in keywords'), type = 'string', varname = 'p_isk', @@ -4608,9 +4606,8 @@ return { full_name = 'keymap', normal_fname_chars = true, pri_mkrc = true, - pv_name = 'p_kmap', redraw = { 'statuslines', 'current_buffer' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('name of a keyboard mapping'), type = 'string', varname = 'p_keymap', @@ -4662,7 +4659,7 @@ return { ]=], expand = true, full_name = 'keywordprg', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, secure = true, short_desc = N_('program to use for the "K" command'), type = 'string', @@ -4833,7 +4830,7 @@ return { ]=], full_name = 'linebreak', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('wrap long lines at a blank'), type = 'boolean', }, @@ -4897,7 +4894,7 @@ return { calling an external program if 'equalprg' is empty. ]=], full_name = 'lisp', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('indenting for Lisp'), type = 'boolean', varname = 'p_lisp', @@ -4919,8 +4916,7 @@ return { expand_cb = 'expand_set_lispoptions', full_name = 'lispoptions', list = 'onecomma', - pv_name = 'p_lop', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('options for lisp indenting'), type = 'string', varname = 'p_lop', @@ -4938,8 +4934,7 @@ return { ]=], full_name = 'lispwords', list = 'onecomma', - pv_name = 'p_lw', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_('words that change how lisp indenting works'), type = 'string', varname = 'p_lispwords', @@ -4966,7 +4961,7 @@ return { ]=], full_name = 'list', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('<Tab> and <EOL>'), type = 'boolean', }, @@ -5077,7 +5072,7 @@ return { full_name = 'listchars', list = 'onecomma', redraw = { 'current_window' }, - scope = { 'global', 'window' }, + scope = { 'global', 'win' }, short_desc = N_('characters for displaying in list mode'), type = 'string', varname = 'p_lcs', @@ -5158,7 +5153,7 @@ return { ]=], expand_cb = 'expand_set_encoding', full_name = 'makeencoding', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_('Converts the output of external commands'), type = 'string', varname = 'p_menc', @@ -5185,7 +5180,7 @@ return { ]=], expand = true, full_name = 'makeprg', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, secure = true, short_desc = N_('program to use for the ":make" command'), type = 'string', @@ -5215,7 +5210,7 @@ return { ]=], full_name = 'matchpairs', list = 'onecomma', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('pairs of characters that "%" can match'), type = 'string', varname = 'p_mps', @@ -5377,7 +5372,7 @@ return { no lines are checked. See |modeline|. ]=], full_name = 'modeline', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('recognize modelines at start or end of file'), type = 'boolean', varname = 'p_ml', @@ -5425,7 +5420,7 @@ return { ]=], full_name = 'modifiable', noglob = true, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('changes to the text are not possible'), tags = { 'E21' }, type = 'boolean', @@ -5461,7 +5456,7 @@ return { full_name = 'modified', no_mkrc = true, redraw = { 'statuslines' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('buffer has been modified'), type = 'boolean', varname = 'p_mod', @@ -5821,7 +5816,7 @@ return { expand_cb = 'expand_set_nrformats', full_name = 'nrformats', list = 'onecomma', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('number formats recognized for CTRL-A command'), type = 'string', varname = 'p_nf', @@ -5855,7 +5850,7 @@ return { ]=], full_name = 'number', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('print the line number in front of each line'), type = 'boolean', }, @@ -5877,7 +5872,7 @@ return { ]=], full_name = 'numberwidth', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('number of columns used for the line number'), type = 'number', }, @@ -5899,7 +5894,7 @@ return { ]=], full_name = 'omnifunc', func = true, - scope = { 'buffer' }, + scope = { 'buf' }, secure = true, short_desc = N_('function for filetype-specific completion'), type = 'string', @@ -6102,7 +6097,7 @@ return { expand = true, full_name = 'path', list = 'comma', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_('list of directories searched with "gf" et.al.'), tags = { 'E343', 'E345', 'E347', 'E854' }, type = 'string', @@ -6126,7 +6121,7 @@ return { Use |:retab| to clean up white space. ]=], full_name = 'preserveindent', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('preserve the indent structure when reindenting'), type = 'boolean', varname = 'p_pi', @@ -6156,7 +6151,7 @@ return { full_name = 'previewwindow', noglob = true, redraw = { 'statuslines' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('identifies the preview window'), tags = { 'E590' }, type = 'boolean', @@ -6275,7 +6270,7 @@ return { text "foo\"bar\\" considered to be one string. ]=], full_name = 'quoteescape', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('escape characters used in a string'), type = 'string', varname = 'p_qe', @@ -6297,7 +6292,7 @@ return { full_name = 'readonly', noglob = true, redraw = { 'statuslines' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('disallow writing the buffer'), type = 'boolean', varname = 'p_ro', @@ -6413,7 +6408,7 @@ return { ]=], full_name = 'relativenumber', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('show relative line number in front of each line'), type = 'boolean', }, @@ -6470,7 +6465,7 @@ return { ]=], full_name = 'rightleft', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('window is right-to-left oriented'), type = 'boolean', }, @@ -6490,7 +6485,7 @@ return { expand_cb = 'expand_set_rightleftcmd', full_name = 'rightleftcmd', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('commands for which editing works right-to-left'), type = 'string', }, @@ -6672,8 +6667,7 @@ return { ]=], full_name = 'scroll', no_mkrc = true, - pv_name = 'p_scroll', - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('lines to scroll with CTRL-U and CTRL-D'), type = 'number', }, @@ -6695,7 +6689,7 @@ return { ]=], full_name = 'scrollback', redraw = { 'current_buffer' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('lines to scroll with CTRL-U and CTRL-D'), type = 'number', varname = 'p_scbk', @@ -6716,8 +6710,7 @@ return { with scroll-binding, but ":split file" does not. ]=], full_name = 'scrollbind', - pv_name = 'p_scbind', - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('scroll in window as other windows scroll'), type = 'boolean', }, @@ -6754,7 +6747,7 @@ return { < For scrolling horizontally see 'sidescrolloff'. ]=], full_name = 'scrolloff', - scope = { 'global', 'window' }, + scope = { 'global', 'win' }, short_desc = N_('minimum nr. of lines above and below cursor'), type = 'number', varname = 'p_so', @@ -7404,7 +7397,7 @@ return { function to get the effective shiftwidth value. ]=], full_name = 'shiftwidth', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('number of spaces to use for (auto)indent step'), type = 'number', varname = 'p_sw', @@ -7502,7 +7495,7 @@ return { ]=], full_name = 'showbreak', redraw = { 'all_windows' }, - scope = { 'global', 'window' }, + scope = { 'global', 'win' }, short_desc = N_('string to use at the start of wrapped lines'), tags = { 'E595' }, type = 'string', @@ -7676,7 +7669,7 @@ return { < ]=], full_name = 'sidescrolloff', - scope = { 'global', 'window' }, + scope = { 'global', 'win' }, short_desc = N_('min. nr. of columns to left and right of cursor'), type = 'number', varname = 'p_siso', @@ -7706,7 +7699,7 @@ return { expand_cb = 'expand_set_signcolumn', full_name = 'signcolumn', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('when to display the sign column'), type = 'string', }, @@ -7752,7 +7745,7 @@ return { right. ]=], full_name = 'smartindent', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('smart autoindenting for C programs'), type = 'boolean', varname = 'p_si', @@ -7792,9 +7785,8 @@ return { NOTE: partly implemented, doesn't work yet for |gj| and |gk|. ]=], full_name = 'smoothscroll', - pv_name = 'p_sms', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_("scroll by screen lines when 'wrap' is set"), type = 'boolean', }, @@ -7819,7 +7811,7 @@ return { to anything other than an empty string. ]=], full_name = 'softtabstop', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('number of spaces that <Tab> uses while editing'), type = 'number', varname = 'p_sts', @@ -7833,7 +7825,7 @@ return { ]=], full_name = 'spell', redraw = { 'current_window', 'highlight_only' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('spell checking'), type = 'boolean', }, @@ -7854,7 +7846,7 @@ return { ]=], full_name = 'spellcapcheck', redraw = { 'current_buffer', 'highlight_only' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('pattern to locate end of a sentence'), type = 'string', varname = 'p_spc', @@ -7890,7 +7882,7 @@ return { expand = true, full_name = 'spellfile', list = 'onecomma', - scope = { 'buffer' }, + scope = { 'buf' }, secure = true, short_desc = N_('files where |zg| and |zw| store words'), type = 'string', @@ -7943,7 +7935,7 @@ return { full_name = 'spelllang', list = 'onecomma', redraw = { 'current_buffer', 'highlight_only' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('language(s) to do spell checking for'), type = 'string', varname = 'p_spl', @@ -7968,7 +7960,7 @@ return { full_name = 'spelloptions', list = 'onecomma', redraw = { 'current_buffer', 'highlight_only' }, - scope = { 'buffer' }, + scope = { 'buf' }, secure = true, type = 'string', varname = 'p_spo', @@ -8189,7 +8181,7 @@ return { ]=], full_name = 'statuscolumn', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, secure = true, short_desc = N_('custom format for the status column'), type = 'string', @@ -8413,7 +8405,7 @@ return { full_name = 'statusline', modelineexpr = true, redraw = { 'statuslines' }, - scope = { 'global', 'window' }, + scope = { 'global', 'win' }, short_desc = N_('custom format for the status line'), tags = { 'E540', 'E542' }, type = 'string', @@ -8454,7 +8446,7 @@ return { ]=], full_name = 'suffixesadd', list = 'onecomma', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('suffixes added when searching for a file'), type = 'string', varname = 'p_sua', @@ -8485,7 +8477,7 @@ return { ]=], full_name = 'swapfile', redraw = { 'statuslines' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('whether to use a swapfile for a buffer'), type = 'boolean', varname = 'p_swf', @@ -8545,7 +8537,7 @@ return { ]=], full_name = 'synmaxcol', redraw = { 'current_buffer' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('maximum column to find syntax items'), type = 'number', varname = 'p_smc', @@ -8582,7 +8574,7 @@ return { full_name = 'syntax', noglob = true, normal_fname_chars = true, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('syntax to be loaded for current buffer'), type = 'string', varname = 'p_syn', @@ -8705,7 +8697,7 @@ return { ]=], full_name = 'tabstop', redraw = { 'current_buffer' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('number of spaces that <Tab> in file uses'), type = 'number', varname = 'p_ts', @@ -8784,7 +8776,7 @@ return { ]=], expand_cb = 'expand_set_tagcase', full_name = 'tagcase', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_('how to handle case when searching in tags files'), type = 'string', varname = 'p_tc', @@ -8805,7 +8797,7 @@ return { ]=], full_name = 'tagfunc', func = true, - scope = { 'buffer' }, + scope = { 'buf' }, secure = true, short_desc = N_('function used to perform tag searches'), type = 'string', @@ -8862,7 +8854,7 @@ return { expand = true, full_name = 'tags', list = 'onecomma', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_('list of file names used by the tag command'), tags = { 'E433' }, type = 'string', @@ -9002,7 +8994,7 @@ return { ]=], full_name = 'textwidth', redraw = { 'current_buffer', 'highlight_only' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('maximum width of text that is being inserted'), type = 'number', varname = 'p_tw', @@ -9031,7 +9023,7 @@ return { full_name = 'thesaurus', list = 'onecomma', normal_dname_chars = true, - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_('list of thesaurus files for keyword completion'), type = 'string', varname = 'p_tsr', @@ -9051,7 +9043,7 @@ return { ]=], full_name = 'thesaurusfunc', func = true, - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, secure = true, short_desc = N_('function used for thesaurus completion'), type = 'string', @@ -9165,6 +9157,10 @@ return { expanded according to the rules used for 'statusline'. If it contains an invalid '%' format, the value is used as-is and no error or warning will be given when the value is set. + + The default behaviour is equivalent to: >vim + set titlestring=%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim + < This option cannot be set in a modeline when 'modelineexpr' is off. Example: >vim @@ -9285,7 +9281,7 @@ return { When 'undofile' is turned off the undo file is NOT deleted. ]=], full_name = 'undofile', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('save undo information in a file'), type = 'boolean', varname = 'p_udf', @@ -9314,7 +9310,7 @@ return { Also see |clear-undo|. ]=], full_name = 'undolevels', - scope = { 'global', 'buffer' }, + scope = { 'global', 'buf' }, short_desc = N_('maximum number of changes that can be undone'), type = 'number', varname = 'p_ul', @@ -9401,7 +9397,7 @@ return { ]=], full_name = 'varsofttabstop', list = 'comma', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('list of numbers of spaces that <Tab> uses while editing'), type = 'string', varname = 'p_vsts', @@ -9424,7 +9420,7 @@ return { full_name = 'vartabstop', list = 'comma', redraw = { 'current_buffer' }, - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('list of numbers of spaces that <Tab> in file uses'), type = 'string', varname = 'p_vts', @@ -9570,7 +9566,7 @@ return { full_name = 'virtualedit', list = 'onecomma', redraw = { 'curswant' }, - scope = { 'global', 'window' }, + scope = { 'global', 'win' }, short_desc = N_('when to use virtual editing'), type = 'string', varname = 'p_ve', @@ -9917,7 +9913,7 @@ return { full_name = 'winbar', modelineexpr = true, redraw = { 'statuslines' }, - scope = { 'global', 'window' }, + scope = { 'global', 'win' }, short_desc = N_('custom format for the window bar'), type = 'string', varname = 'p_wbr', @@ -9935,7 +9931,7 @@ return { ]=], full_name = 'winblend', redraw = { 'current_window', 'highlight_only' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('Controls transparency level for floating windows'), type = 'number', }, @@ -9974,8 +9970,7 @@ return { command has a "!" modifier, it can force switching buffers. ]=], full_name = 'winfixbuf', - pv_name = 'p_wfb', - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('pin a window to a specific buffer'), type = 'boolean', }, @@ -9990,7 +9985,7 @@ return { ]=], full_name = 'winfixheight', redraw = { 'statuslines' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('keep window height when opening/closing windows'), type = 'boolean', }, @@ -10004,7 +9999,7 @@ return { ]=], full_name = 'winfixwidth', redraw = { 'statuslines' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('keep window width when opening/closing windows'), type = 'boolean', }, @@ -10065,7 +10060,7 @@ return { full_name = 'winhighlight', list = 'onecommacolon', redraw = { 'current_window', 'highlight_only' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('Setup window-local highlights'), type = 'string', }, @@ -10156,7 +10151,7 @@ return { ]=], full_name = 'wrap', redraw = { 'current_window' }, - scope = { 'window' }, + scope = { 'win' }, short_desc = N_('lines wrap and continue on the next line'), type = 'boolean', }, @@ -10173,7 +10168,7 @@ return { See also 'formatoptions' and |ins-textwidth|. ]=], full_name = 'wrapmargin', - scope = { 'buffer' }, + scope = { 'buf' }, short_desc = N_('chars from the right where wrapping starts'), type = 'number', varname = 'p_wm', diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 7df6a1a5d7..d1951d4f96 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -880,7 +880,7 @@ static void pum_adjust_info_position(win_T *wp, int height, int width) /// Used for nvim__complete_set /// /// @param selected the selected compl item. -/// @parma info Info string. +/// @param info Info string. /// @return a win_T pointer. win_T *pum_set_info(int selected, char *info) { diff --git a/src/nvim/search.c b/src/nvim/search.c index debc5697d1..159ab35f6f 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -1203,6 +1203,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, size_t patlen // Compute msg_row early. msg_start(); + msg_ext_set_kind("search_cmd"); // Get the offset, so we know how long it is. if (!cmd_silent diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index b37f01e769..0ddf4ffa38 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -516,6 +516,7 @@ void spell_suggest(int count) spell_find_suggest(line + curwin->w_cursor.col, badlen, &sug, limit, true, need_cap, true); + msg_ext_set_kind("list_cmd"); if (GA_EMPTY(&sug.su_ga)) { msg(_("Sorry, no suggestions"), 0); } else if (count > 0) { diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index ba64633df7..4e78067d46 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -760,7 +760,9 @@ void draw_tabline(void) bool modified = false; for (wincount = 0; wp != NULL; wp = wp->w_next, wincount++) { - if (bufIsChanged(wp->w_buffer)) { + if (!wp->w_config.focusable) { + wincount--; + } else if (bufIsChanged(wp->w_buffer)) { modified = true; } } diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c index 9095d4e8c9..06b3aa0411 100644 --- a/src/nvim/textformat.c +++ b/src/nvim/textformat.c @@ -863,7 +863,7 @@ int fex_format(linenr_T lnum, long count, int c) // Make a copy, the option could be changed while calling it. char *fex = xstrdup(curbuf->b_p_fex); - current_sctx = curbuf->b_p_script_ctx[BV_FEX].script_ctx; + current_sctx = curbuf->b_p_script_ctx[kBufOptFormatexpr].script_ctx; // Evaluate the function. if (use_sandbox) { diff --git a/src/nvim/tui/termkey/termkey-internal.h b/src/nvim/tui/termkey/termkey-internal.h index 107591f950..97fae939c5 100644 --- a/src/nvim/tui/termkey/termkey-internal.h +++ b/src/nvim/tui/termkey/termkey-internal.h @@ -47,7 +47,7 @@ struct TermKey { int canonflags; unsigned char *buffer; size_t buffstart; // First offset in buffer - size_t buffcount; // NUMBER of entires valid in buffer + size_t buffcount; // NUMBER of entries valid in buffer size_t buffsize; // Total malloc'ed size size_t hightide; // Position beyond buffstart at which peekkey() should next start // normally 0, but see also termkey_interpret_csi diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 7c81110ae9..eba821a53d 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -721,7 +721,7 @@ void ui_call_event(char *name, bool fast, Array args) // Prompt messages should be shown immediately so must be safe if (strcmp(name, "msg_show") == 0) { char *kind = args.items[0].data.string.data; - fast = !kind || (strncmp(kind, "confirm", 7) != 0 && strcmp(kind, "return_prompt") != 0); + fast = !kind || ((strncmp(kind, "confirm", 7) != 0 && strstr(kind, "_prompt") == NULL)); } map_foreach(&ui_event_cbs, ui_event_ns_id, event_cb, { diff --git a/src/nvim/window.c b/src/nvim/window.c index 5f17d3220d..c3f3e075f1 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -6755,8 +6755,8 @@ void win_comp_scroll(win_T *wp) if (wp->w_p_scr != old_w_p_scr) { // Used by "verbose set scroll". - wp->w_p_script_ctx[WV_SCROLL].script_ctx.sc_sid = SID_WINLAYOUT; - wp->w_p_script_ctx[WV_SCROLL].script_ctx.sc_lnum = 0; + wp->w_p_script_ctx[kWinOptScroll].script_ctx.sc_sid = SID_WINLAYOUT; + wp->w_p_script_ctx[kWinOptScroll].script_ctx.sc_lnum = 0; } } diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c index 4698487708..b8a51d686d 100644 --- a/src/nvim/winfloat.c +++ b/src/nvim/winfloat.c @@ -412,8 +412,8 @@ win_T *win_float_create(bool enter, bool new_buf) return handle_error_and_cleanup(wp, &err); } buf->b_p_bl = false; // unlist - set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL, 0, kOptReqBuf, - buf); + set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL, 0, + kOptScopeBuf, buf); win_set_buf(wp, buf, &err); if (ERROR_SET(&err)) { return handle_error_and_cleanup(wp, &err); diff --git a/src/uncrustify.cfg b/src/uncrustify.cfg index 50cee638af..91a72fa318 100644 --- a/src/uncrustify.cfg +++ b/src/uncrustify.cfg @@ -1,4 +1,4 @@ -# Uncrustify-0.79.0_f +# Uncrustify-0.80.1_f # # General options @@ -422,6 +422,10 @@ sp_invariant_paren = ignore # ignore/add/remove/force sp_after_invariant_paren = ignore # ignore/add/remove/force # Add or remove space before empty statement ';' on 'if', 'for' and 'while'. +# examples: +# if (b) <here> ; +# for (a=1; a<10; a++) <here> ; +# while (*p++ = ' ') <here> ; sp_special_semi = ignore # ignore/add/remove/force # Add or remove space before ';'. @@ -1103,6 +1107,10 @@ sp_after_emb_cmt = force # ignore/add/remove/force # Default: 1 sp_num_after_emb_cmt = 1 # unsigned number +# Embedded comment spacing options have higher priority (== override) +# than other spacing options (comma, parenthesis, braces, ...) +sp_emb_cmt_priority = false # true/false + # (Java) Add or remove space between an annotation and the open parenthesis. sp_annotation_paren = ignore # ignore/add/remove/force @@ -1127,6 +1135,9 @@ force_tab_after_define = false # true/false # Add or remove space between two strings. sp_string_string = force # ignore/add/remove/force +# Add or remove space 'struct' and a type. +sp_struct_type = ignore # ignore/add/remove/force + # # Indenting options # diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index c7aa1f48da..3f1e378bc1 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -3582,6 +3582,15 @@ describe('API', function() command('highlight Special guifg=SlateBlue') end) + it('validation', function() + eq("Invalid 'chunk': expected Array, got String", pcall_err(api.nvim_echo, { 'msg' }, 1, {})) + eq( + 'Invalid chunk: expected Array with 1 or 2 Strings', + pcall_err(api.nvim_echo, { { '', '', '' } }, 1, {}) + ) + eq('Invalid hl_group: text highlight', pcall_err(api.nvim_echo, { { '', false } }, 1, {})) + end) + it('should clear cmdline message before echo', function() feed(':call nvim_echo([["msg"]], v:false, {})<CR>') screen:expect { @@ -3606,6 +3615,18 @@ describe('API', function() msg_a{15:msg_b}{16:msg_c} | ]], } + async_meths.nvim_echo({ + { 'msg_d' }, + { 'msg_e', api.nvim_get_hl_id_by_name('Statement') }, + { 'msg_f', api.nvim_get_hl_id_by_name('Special') }, + }, true, {}) + screen:expect { + grid = [[ + ^ | + {1:~ }|*6 + msg_d{15:msg_e}{16:msg_f} | + ]], + } end) it('can show highlighted multiline', function() diff --git a/test/functional/autocmd/completedone_spec.lua b/test/functional/autocmd/completedone_spec.lua index 33beb16db2..36dc73842d 100644 --- a/test/functional/autocmd/completedone_spec.lua +++ b/test/functional/autocmd/completedone_spec.lua @@ -32,7 +32,7 @@ describe('CompleteDone', function() feed('<Esc>') eq('cancel', eval('g:donereason')) end) - it('when overriden by another complete()', function() + it('when overridden by another complete()', function() call('complete', call('col', '.'), { 'bar', 'baz' }) eq('cancel', eval('g:donereason')) end) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index e885164c20..f3c477d210 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -277,10 +277,8 @@ describe('startup', function() -- nvim <vim args> -l foo.lua <vim args> assert_l_out( - -- luacheck: ignore 611 (Line contains only whitespaces) [[ wrap - bufs: nvim args: 7 lua args: { "-c", "set wrap?", @@ -345,7 +343,7 @@ describe('startup', function() local screen = Screen.new(25, 3) -- Remote UI connected by --embed. -- TODO: a lot of tests in this file already use the new default color scheme. - -- once we do the batch update of tests to use it, remove this workarond + -- once we do the batch update of tests to use it, remove this workaround screen._default_attr_ids = nil command([[echo has('ttyin') has('ttyout')]]) screen:expect([[ diff --git a/test/functional/editor/defaults_spec.lua b/test/functional/editor/defaults_spec.lua index 7110d216f8..82d285cc9a 100644 --- a/test/functional/editor/defaults_spec.lua +++ b/test/functional/editor/defaults_spec.lua @@ -172,6 +172,30 @@ describe('default', function() first line]]) end) + + it('supports dot repetition', function() + n.clear({ args_rm = { '--cmd' } }) + n.insert([[first line]]) + n.feed('[<Space>') + n.feed('.') + n.expect([[ + + + first line]]) + end) + + it('supports dot repetition and a count', function() + n.clear({ args_rm = { '--cmd' } }) + n.insert([[first line]]) + n.feed('[<Space>') + n.feed('3.') + n.expect([[ + + + + + first line]]) + end) end) describe(']<Space>', function() @@ -196,6 +220,29 @@ describe('default', function() ]]) end) + + it('supports dot repetition', function() + n.clear({ args_rm = { '--cmd' } }) + n.insert([[first line]]) + n.feed(']<Space>') + n.feed('.') + n.expect([[ + first line + + ]]) + end) + + it('supports dot repetition and a count', function() + n.clear({ args_rm = { '--cmd' } }) + n.insert([[first line]]) + n.feed(']<Space>') + n.feed('2.') + n.expect([[ + first line + + + ]]) + end) end) end) end) diff --git a/test/functional/legacy/normal_spec.lua b/test/functional/legacy/normal_spec.lua index 717ebbba70..1ae22a83bd 100644 --- a/test/functional/legacy/normal_spec.lua +++ b/test/functional/legacy/normal_spec.lua @@ -102,4 +102,33 @@ describe('normal', function() ]], }) end) + + -- oldtest: Test_normal_gm() + it('gm sets curswant correctly', function() + screen:try_resize(75, 10) + exec([[ + call setline(1, repeat([" abcd\tefgh\tij"], 10)) + call cursor(1, 1) + ]]) + feed('jVjzf') + -- gm + feed('gmk') + eq(18, fn.virtcol('.')) + -- g0 + feed('gj0k') + eq(1, fn.virtcol('.')) + -- g^ + feed('jg^k') + eq(3, fn.virtcol('.')) + exec('call cursor(10, 1)') + -- gm + feed('gmk') + eq(18, fn.virtcol('.')) + -- g0 + feed('gj0k') + eq(1, fn.virtcol('.')) + -- g^ + feed('jg^k') + eq(3, fn.virtcol('.')) + end) end) diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua index 574c837f92..b6011d5268 100644 --- a/test/functional/lua/filetype_spec.lua +++ b/test/functional/lua/filetype_spec.lua @@ -161,6 +161,18 @@ describe('vim.filetype', function() end end end) + + it('.get_option() cleans up buffer on error', function() + api.nvim_create_autocmd('FileType', { pattern = 'foo', command = 'lua error()' }) + + local buf = api.nvim_get_current_buf() + + exec_lua(function() + pcall(vim.filetype.get_option, 'foo', 'lisp') + end) + + eq(buf, api.nvim_get_current_buf()) + end) end) describe('filetype.lua', function() diff --git a/test/functional/lua/system_spec.lua b/test/functional/lua/system_spec.lua index afbada007d..3f847ca3be 100644 --- a/test/functional/lua/system_spec.lua +++ b/test/functional/lua/system_spec.lua @@ -114,7 +114,7 @@ describe('vim.system', function() end) if t.is_os('win') then - it('can resolve windows command extentions.', function() + it('can resolve windows command extensions', function() t.write_file('test.bat', 'echo hello world') system_sync({ 'chmod', '+x', 'test.bat' }) system_sync({ './test' }) diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua index 6d4be4d1f2..f1cf657d78 100644 --- a/test/functional/lua/ui_event_spec.lua +++ b/test/functional/lua/ui_event_spec.lua @@ -142,7 +142,7 @@ describe('vim.ui_attach', function() 'msg_history_show', { { 'echomsg', { { 0, 'message1', 0 } } }, - { '', { { 0, 'message2', 0 } } }, + { 'lua_print', { { 0, 'message2', 0 } } }, { 'echomsg', { { 0, 'message3', 0 } } }, }, }, @@ -285,6 +285,26 @@ describe('vim.ui_attach', function() }, }, }) + feed('<esc>:call inputlist(["Select:", "One", "Two"])<cr>') + screen:expect({ + grid = [[ + E122: {10:Function} Foo already exists, add !| + to replace it | + Type number and <Enter> or click with th| + e mouse (q or empty cancels): | + {1:^~ }| + ]], + messages = { + { + content = { { 'Select:\nOne\nTwo\n' } }, + kind = 'list_cmd', + }, + { + content = { { 'Type number and <Enter> or click with the mouse (q or empty cancels): ' } }, + kind = 'number_prompt', + }, + }, + }) end) end) diff --git a/test/functional/plugin/lsp/testutil.lua b/test/functional/plugin/lsp/testutil.lua index a36cbac568..95fc22b96b 100644 --- a/test/functional/plugin/lsp/testutil.lua +++ b/test/functional/plugin/lsp/testutil.lua @@ -182,16 +182,17 @@ function M.test_rpc_server(config) ) end local client = setmetatable({}, { - __index = function(_, name) + __index = function(t, name) -- Workaround for not being able to yield() inside __index for Lua 5.1 :( -- Otherwise I would just return the value here. - return function(...) + return function(arg1, ...) + local ismethod = arg1 == t return exec_lua(function(...) - if type(_G.TEST_RPC_CLIENT[name]) == 'function' then - return _G.TEST_RPC_CLIENT[name](...) - else - return _G.TEST_RPC_CLIENT[name] + local client = _G.TEST_RPC_CLIENT + if type(client[name]) == 'function' then + return client[name](ismethod and client or arg1, ...) end + return client[name] end, ...) end end, diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 5222216faf..f14e24bb19 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -232,7 +232,7 @@ describe('LSP', function() -- client is a dummy object which will queue up commands to be run -- once the server initializes. It can't accept lua callbacks or -- other types that may be unserializable for now. - client.stop() + client:stop() end, -- If the program timed out, then code will be nil. on_exit = function(code, signal) @@ -254,8 +254,8 @@ describe('LSP', function() test_rpc_server { test_name = 'basic_init', on_init = function(client) - client.notify('test') - client.stop() + client:notify('test') + client:stop() end, on_exit = function(code, signal) eq(101, code, 'exit code') -- See fake-lsp-server.lua @@ -275,7 +275,7 @@ describe('LSP', function() test_rpc_server({ test_name = 'basic_init_did_change_configuration', on_init = function(client, _) - client.stop() + client:stop() end, on_exit = function(code, signal) eq(0, code, 'exit code') @@ -333,9 +333,9 @@ describe('LSP', function() test_name = 'basic_init', on_init = function(client) eq(0, client.server_capabilities().textDocumentSync.change) - client.request('shutdown') - client.notify('exit') - client.stop() + client:request('shutdown') + client:notify('exit') + client:stop() end, on_exit = function(code, signal) eq(0, code, 'exit code') @@ -377,7 +377,7 @@ describe('LSP', function() end, on_init = function(_client) client = _client - client.notify('finish') + client:notify('finish') end, on_exit = function(code, signal) eq(0, code, 'exit code') @@ -395,7 +395,7 @@ describe('LSP', function() return vim.lsp.buf_is_attached(_G.BUFFER, _G.TEST_RPC_CLIENT_ID) end) ) - client.stop() + client:stop() end end, } @@ -430,7 +430,7 @@ describe('LSP', function() return vim.lsp.buf_attach_client(_G.BUFFER, _G.TEST_RPC_CLIENT_ID) end) ) - client.notify('finish') + client:notify('finish') end, on_handler = function(_, _, ctx) if ctx.method == 'finish' then @@ -439,7 +439,7 @@ describe('LSP', function() return vim.lsp.buf_detach_client(_G.BUFFER, _G.TEST_RPC_CLIENT_ID) end) eq('basic_init', api.nvim_get_var('lsp_detached')) - client.stop() + client:stop() end end, } @@ -472,7 +472,7 @@ describe('LSP', function() return keymap.callback == vim.lsp.buf.hover end) ) - client.stop() + client:stop() end end, on_exit = function(_, _) @@ -524,7 +524,7 @@ describe('LSP', function() eq('v:lua.vim.lsp.tagfunc', get_buf_option('tagfunc', BUFFER_1)) eq('v:lua.vim.lsp.omnifunc', get_buf_option('omnifunc', BUFFER_2)) eq('v:lua.vim.lsp.formatexpr()', get_buf_option('formatexpr', BUFFER_2)) - client.stop() + client:stop() end end, on_exit = function(_, _) @@ -554,7 +554,7 @@ describe('LSP', function() eq('tfu', get_buf_option('tagfunc')) eq('ofu', get_buf_option('omnifunc')) eq('fex', get_buf_option('formatexpr')) - client.stop() + client:stop() end end, on_exit = function(_, _) @@ -711,10 +711,10 @@ describe('LSP', function() ctx.method, result ) - client.notify('workspace/configuration', server_result) + client:notify('workspace/configuration', server_result) end if ctx.method == 'shutdown' then - client.stop() + client:stop() end end, } @@ -756,7 +756,7 @@ describe('LSP', function() test_rpc_server { test_name = 'basic_check_capabilities', on_init = function(client) - client.stop() + client:stop() local full_kind = exec_lua(function() return require 'vim.lsp.protocol'.TextDocumentSyncKind.Full end) @@ -798,7 +798,7 @@ describe('LSP', function() vim.api.nvim_exec_autocmds('BufWritePost', { buffer = _G.BUFFER, modeline = false }) end) else - client.stop() + client:stop() end end, } @@ -898,7 +898,7 @@ describe('LSP', function() end) end) else - client.stop() + client:stop() end end, }) @@ -929,20 +929,20 @@ describe('LSP', function() vim.api.nvim_exec_autocmds('BufWritePost', { buffer = _G.BUFFER, modeline = false }) end) else - client.stop() + client:stop() end end, } end) - it('client.supports_methods() should validate capabilities', function() + it('client:supports_methods() should validate capabilities', function() local expected_handlers = { { NIL, {}, { method = 'shutdown', client_id = 1 } }, } test_rpc_server { test_name = 'capabilities_for_client_supports_method', on_init = function(client) - client.stop() + client:stop() local expected_sync_capabilities = { change = 1, openClose = true, @@ -958,11 +958,11 @@ describe('LSP', function() eq(true, client.server_capabilities().codeLensProvider.resolveProvider) -- known methods for resolved capabilities - eq(true, client.supports_method('textDocument/hover')) - eq(false, client.supports_method('textDocument/definition')) + 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')) + eq(true, client:supports_method('unknown-method')) end, on_exit = function(code, signal) eq(0, code, 'exit code') @@ -989,7 +989,7 @@ describe('LSP', function() end) end, on_init = function(client) - client.stop() + client:stop() exec_lua(function() vim.lsp.buf.type_definition() end) @@ -1018,7 +1018,7 @@ describe('LSP', function() end) end, on_init = function(client) - client.stop() + client:stop() exec_lua(function() vim.lsp.buf.type_definition() end) @@ -1042,7 +1042,7 @@ describe('LSP', function() test_rpc_server { test_name = 'check_forward_request_cancelled', on_init = function(_client) - _client.request('error_code_test') + _client:request('error_code_test') client = _client end, on_exit = function(code, signal) @@ -1053,7 +1053,7 @@ describe('LSP', function() on_handler = function(err, _, ctx) eq(table.remove(expected_handlers), { err, {}, ctx }, 'expected handler') if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1072,7 +1072,7 @@ describe('LSP', function() test_rpc_server { test_name = 'check_forward_content_modified', on_init = function(_client) - _client.request('error_code_test') + _client:request('error_code_test') client = _client end, on_exit = function(code, signal) @@ -1084,10 +1084,10 @@ describe('LSP', function() eq(table.remove(expected_handlers), { err, _, ctx }, 'expected handler') -- if ctx.method == 'error_code_test' then client.notify("finish") end if ctx.method ~= 'finish' then - client.notify('finish') + client:notify('finish') end if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1103,13 +1103,13 @@ describe('LSP', function() test_name = 'check_pending_request_tracked', on_init = function(_client) client = _client - client.request('slow_request') + client:request('slow_request') local request = exec_lua(function() return _G.TEST_RPC_CLIENT.requests[2] end) eq('slow_request', request.method) eq('pending', request.type) - client.notify('release') + client:notify('release') end, on_exit = function(code, signal) eq(0, code, 'exit code') @@ -1123,10 +1123,10 @@ describe('LSP', function() return _G.TEST_RPC_CLIENT.requests[2] end) eq(nil, request) - client.notify('finish') + client:notify('finish') end if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1141,14 +1141,14 @@ describe('LSP', function() test_name = 'check_cancel_request_tracked', on_init = function(_client) client = _client - client.request('slow_request') - client.cancel_request(2) + client:request('slow_request') + client:cancel_request(2) local request = exec_lua(function() return _G.TEST_RPC_CLIENT.requests[2] end) eq('slow_request', request.method) eq('cancel', request.type) - client.notify('release') + client:notify('release') end, on_exit = function(code, signal) eq(0, code, 'exit code') @@ -1162,7 +1162,7 @@ describe('LSP', function() end) eq(nil, request) if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1178,19 +1178,19 @@ describe('LSP', function() test_name = 'check_tracked_requests_cleared', on_init = function(_client) client = _client - client.request('slow_request') + client:request('slow_request') local request = exec_lua(function() return _G.TEST_RPC_CLIENT.requests[2] end) eq('slow_request', request.method) eq('pending', request.type) - client.cancel_request(2) + client:cancel_request(2) request = exec_lua(function() return _G.TEST_RPC_CLIENT.requests[2] end) eq('slow_request', request.method) eq('cancel', request.type) - client.notify('release') + client:notify('release') end, on_exit = function(code, signal) eq(0, code, 'exit code') @@ -1204,10 +1204,10 @@ describe('LSP', function() return _G.TEST_RPC_CLIENT.requests[2] end) eq(nil, request) - client.notify('finish') + client:notify('finish') end if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1225,11 +1225,11 @@ describe('LSP', function() command('let g:requests = 0') command('autocmd LspRequest * let g:requests+=1') client = _client - client.request('slow_request') + client:request('slow_request') eq(1, eval('g:requests')) - client.cancel_request(2) + client:cancel_request(2) eq(2, eval('g:requests')) - client.notify('release') + client:notify('release') end, on_exit = function(code, signal) eq(0, code, 'exit code') @@ -1240,10 +1240,10 @@ describe('LSP', function() on_handler = function(err, _, ctx) eq(table.remove(expected_handlers), { err, {}, ctx }, 'expected handler') if ctx.method == 'slow_request' then - client.notify('finish') + client:notify('finish') end if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1277,7 +1277,7 @@ describe('LSP', function() end) eq(full_kind, client.server_capabilities().textDocumentSync.change) eq(true, client.server_capabilities().textDocumentSync.openClose) - client.notify('finish') + client:notify('finish') end, on_exit = function(code, signal) eq(0, code, 'exit code') @@ -1286,7 +1286,7 @@ describe('LSP', function() on_handler = function(err, result, ctx) eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1333,11 +1333,11 @@ describe('LSP', function() end, on_handler = function(err, result, ctx) if ctx.method == 'start' then - client.notify('finish') + client:notify('finish') end eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1378,11 +1378,11 @@ describe('LSP', function() end, on_handler = function(err, result, ctx) if ctx.method == 'start' then - client.notify('finish') + client:notify('finish') end eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1428,11 +1428,11 @@ describe('LSP', function() 'boop', }) end) - client.notify('finish') + client:notify('finish') end eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1479,11 +1479,11 @@ describe('LSP', function() 'boop', }) end) - client.notify('finish') + client:notify('finish') end eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1529,7 +1529,7 @@ describe('LSP', function() end, on_init = function(_client) client = _client - eq(true, client.supports_method('textDocument/inlayHint')) + eq(true, client:supports_method('textDocument/inlayHint')) exec_lua(function() assert(vim.lsp.buf_attach_client(_G.BUFFER, _G.TEST_RPC_CLIENT_ID)) end) @@ -1545,11 +1545,11 @@ describe('LSP', function() end) end if ctx.method == 'textDocument/inlayHint' then - client.notify('finish') + client:notify('finish') end eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1598,11 +1598,11 @@ describe('LSP', function() '123boop', }) end) - client.notify('finish') + client:notify('finish') end eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1652,11 +1652,11 @@ describe('LSP', function() '123boop', }) end) - client.notify('finish') + client:notify('finish') end eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1699,11 +1699,11 @@ describe('LSP', function() on_handler = function(err, result, ctx) if ctx.method == 'start' then n.command('normal! 1Go') - client.notify('finish') + client:notify('finish') end eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1752,11 +1752,11 @@ describe('LSP', function() 'boop', }) end) - client.notify('finish') + client:notify('finish') end eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1806,11 +1806,11 @@ describe('LSP', function() }) vim.api.nvim_command(_G.BUFFER .. 'bwipeout') end) - client.notify('finish') + client:notify('finish') end eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -1830,7 +1830,7 @@ describe('LSP', function() on_setup = function() end, on_init = function(_client) client = _client - client.stop(true) + client:stop(true) end, on_exit = function(code, signal) eq(0, code, 'exit code') @@ -1882,7 +1882,7 @@ describe('LSP', function() on_handler = function(err, result, ctx) eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') if ctx.method == 'finish' then - client.stop() + client:stop() end end, } @@ -2348,7 +2348,7 @@ describe('LSP', function() test_rpc_server { test_name = 'basic_init', on_init = function(client, _) - client.stop() + client:stop() end, -- If the program timed out, then code will be nil. on_exit = function(code, signal) @@ -3499,7 +3499,7 @@ describe('LSP', function() } return vim.lsp.util.convert_signature_help_to_markdown_lines(signature_help, 'zig', { '(' }) end) - -- Note that although the higlight positions below are 0-indexed, the 2nd parameter + -- Note that although the highlight positions below are 0-indexed, the 2nd parameter -- corresponds to the 3rd line because the first line is the ``` from the -- Markdown block. local expected = { 3, 4, 3, 11 } @@ -4284,7 +4284,7 @@ describe('LSP', function() end) ) end - client.stop() + client:stop() end end, } @@ -4331,7 +4331,7 @@ describe('LSP', function() return type(vim.lsp.commands['dummy2']) end) ) - client.stop() + client:stop() end end, } @@ -4372,7 +4372,7 @@ describe('LSP', function() vim.lsp.buf.code_action() end) elseif ctx.method == 'shutdown' then - client.stop() + client:stop() end end, }) @@ -4447,7 +4447,7 @@ describe('LSP', function() return type(vim.lsp.commands['executed_type_annotate']) end) ) - client.stop() + client:stop() end end, } @@ -4564,7 +4564,7 @@ describe('LSP', function() end) eq({ command = 'Dummy', title = 'Lens1' }, cmd) elseif ctx.method == 'shutdown' then - client.stop() + client:stop() end end, } @@ -4653,7 +4653,7 @@ describe('LSP', function() end) eq({ command = 'Dummy', title = 'Lens2' }, response) elseif ctx.method == 'shutdown' then - client.stop() + client:stop() end end, } @@ -4762,7 +4762,7 @@ describe('LSP', function() return notify_msg end) eq('[LSP] Format request failed, no matching language servers.', notify_msg) - client.stop() + client:stop() end, } end) @@ -4795,7 +4795,7 @@ describe('LSP', function() end) eq(nil, notify_msg) elseif ctx.method == 'shutdown' then - client.stop() + client:stop() end end, } @@ -4836,7 +4836,7 @@ describe('LSP', function() end) eq(nil, notify_msg) elseif ctx.method == 'shutdown' then - client.stop() + client:stop() end end, } @@ -4883,7 +4883,7 @@ describe('LSP', function() end) eq(nil, notify_msg) elseif ctx.method == 'shutdown' then - client.stop() + client:stop() end end, } @@ -4930,7 +4930,7 @@ describe('LSP', function() end) eq({ handler_called = true }, result) elseif ctx.method == 'shutdown' then - client.stop() + client:stop() end end, } @@ -5477,7 +5477,7 @@ describe('LSP', function() result[#result + 1] = { method = method, fname = fname, - supported = client.supports_method(method, { bufnr = bufnr }), + supported = client:supports_method(method, { bufnr = bufnr }), } end diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua index 722442acbd..2b54ea93e0 100644 --- a/test/functional/provider/clipboard_spec.lua +++ b/test/functional/provider/clipboard_spec.lua @@ -544,7 +544,7 @@ describe('clipboard (with fake clipboard.vim)', function() ]]) feed('gg^<C-v>') -- Goto start of top line enter visual block mode feed('3ljy^k') -- yank 4x2 block & goto initial location - feed('P') -- Paste it infront + feed('P') -- Paste it before cursor expect([[ aabbaabbcc ddeeddeeff diff --git a/test/functional/testnvim.lua b/test/functional/testnvim.lua index 60b2f872fc..43c38d18c0 100644 --- a/test/functional/testnvim.lua +++ b/test/functional/testnvim.lua @@ -800,81 +800,6 @@ function M.exec_capture(code) return M.api.nvim_exec2(code, { output = true }).output end ---- @param f function ---- @return table<string,any> -local function get_upvalues(f) - local i = 1 - local upvalues = {} --- @type table<string,any> - while true do - local n, v = debug.getupvalue(f, i) - if not n then - break - end - upvalues[n] = v - i = i + 1 - end - return upvalues -end - ---- @param f function ---- @param upvalues table<string,any> -local function set_upvalues(f, upvalues) - local i = 1 - while true do - local n = debug.getupvalue(f, i) - if not n then - break - end - if upvalues[n] then - debug.setupvalue(f, i, upvalues[n]) - end - i = i + 1 - end -end - ---- @type fun(f: function): table<string,any> -_G.__get_upvalues = nil - ---- @type fun(f: function, upvalues: table<string,any>) -_G.__set_upvalues = nil - ---- @param self table<string,function> ---- @param bytecode string ---- @param upvalues table<string,any> ---- @param ... any[] ---- @return any[] result ---- @return table<string,any> upvalues -local function exec_lua_handler(self, bytecode, upvalues, ...) - local f = assert(loadstring(bytecode)) - self.set_upvalues(f, upvalues) - local ret = { f(...) } --- @type any[] - --- @type table<string,any> - local new_upvalues = self.get_upvalues(f) - - do -- Check return value types for better error messages - local invalid_types = { - ['thread'] = true, - ['function'] = true, - ['userdata'] = true, - } - - for k, v in pairs(ret) do - if invalid_types[type(v)] then - error( - string.format( - "Return index %d with value '%s' of type '%s' cannot be serialized over RPC", - k, - tostring(v), - type(v) - ) - ) - end - end - end - - return ret, new_upvalues -end - --- Execute Lua code in the wrapped Nvim session. --- --- When `code` is passed as a function, it is converted into Lua byte code. @@ -921,52 +846,7 @@ function M.exec_lua(code, ...) end assert(session, 'no Nvim session') - - if not session.exec_lua_setup then - assert( - session:request( - 'nvim_exec_lua', - [[ - _G.__test_exec_lua = { - get_upvalues = loadstring((select(1,...))), - set_upvalues = loadstring((select(2,...))), - handler = loadstring((select(3,...))) - } - setmetatable(_G.__test_exec_lua, { __index = _G.__test_exec_lua }) - ]], - { string.dump(get_upvalues), string.dump(set_upvalues), string.dump(exec_lua_handler) } - ) - ) - session.exec_lua_setup = true - end - - local stat, rv = session:request( - 'nvim_exec_lua', - 'return { _G.__test_exec_lua:handler(...) }', - { string.dump(code), get_upvalues(code), ... } - ) - - if not stat then - error(rv[2]) - end - - --- @type any[], table<string,any> - local ret, upvalues = unpack(rv) - - -- Update upvalues - if next(upvalues) then - local caller = debug.getinfo(2) - local f = caller.func - -- On PUC-Lua, if the function is a tail call, then func will be nil. - -- In this case we need to use the current function. - if not f then - assert(caller.source == '=(tail call)') - f = debug.getinfo(1).func - end - set_upvalues(f, upvalues) - end - - return unpack(ret, 1, table.maxn(ret)) + return require('test.functional.testnvim.exec_lua')(session, 2, code, ...) end function M.get_pathsep() diff --git a/test/functional/testnvim/exec_lua.lua b/test/functional/testnvim/exec_lua.lua new file mode 100644 index 0000000000..ddd9905ce7 --- /dev/null +++ b/test/functional/testnvim/exec_lua.lua @@ -0,0 +1,148 @@ +--- @param f function +--- @return table<string,any> +local function get_upvalues(f) + local i = 1 + local upvalues = {} --- @type table<string,any> + while true do + local n, v = debug.getupvalue(f, i) + if not n then + break + end + upvalues[n] = v + i = i + 1 + end + return upvalues +end + +--- @param f function +--- @param upvalues table<string,any> +local function set_upvalues(f, upvalues) + local i = 1 + while true do + local n = debug.getupvalue(f, i) + if not n then + break + end + if upvalues[n] then + debug.setupvalue(f, i, upvalues[n]) + end + i = i + 1 + end +end + +--- @param messages string[] +--- @param ... ... +local function add_print(messages, ...) + local msg = {} --- @type string[] + for i = 1, select('#', ...) do + msg[#msg + 1] = tostring(select(i, ...)) + end + table.insert(messages, table.concat(msg, '\t')) +end + +local invalid_types = { + ['thread'] = true, + ['function'] = true, + ['userdata'] = true, +} + +--- @param r any[] +local function check_returns(r) + for k, v in pairs(r) do + if invalid_types[type(v)] then + error( + string.format( + "Return index %d with value '%s' of type '%s' cannot be serialized over RPC", + k, + tostring(v), + type(v) + ), + 2 + ) + end + end +end + +local M = {} + +--- This is run in the context of the remote Nvim instance. +--- @param bytecode string +--- @param upvalues table<string,any> +--- @param ... any[] +--- @return any[] result +--- @return table<string,any> upvalues +--- @return string[] messages +function M.handler(bytecode, upvalues, ...) + local messages = {} --- @type string[] + local orig_print = _G.print + + function _G.print(...) + add_print(messages, ...) + return orig_print(...) + end + + local f = assert(loadstring(bytecode)) + + set_upvalues(f, upvalues) + + -- Run in pcall so we can return any print messages + local ret = { pcall(f, ...) } --- @type any[] + + _G.print = orig_print + + local new_upvalues = get_upvalues(f) + + -- Check return value types for better error messages + check_returns(ret) + + return ret, new_upvalues, messages +end + +--- @param session test.Session +--- @param lvl integer +--- @param code function +--- @param ... ... +local function run(session, lvl, code, ...) + local stat, rv = session:request( + 'nvim_exec_lua', + [[return { require('test.functional.testnvim.exec_lua').handler(...) }]], + { string.dump(code), get_upvalues(code), ... } + ) + + if not stat then + error(rv[2], 2) + end + + --- @type any[], table<string,any>, string[] + local ret, upvalues, messages = unpack(rv) + + for _, m in ipairs(messages) do + print(m) + end + + if not ret[1] then + error(ret[2], 2) + end + + -- Update upvalues + if next(upvalues) then + local caller = debug.getinfo(lvl) + local i = 0 + + -- On PUC-Lua, if the function is a tail call, then func will be nil. + -- In this case we need to use the caller. + while not caller.func do + i = i + 1 + caller = debug.getinfo(lvl + i) + end + set_upvalues(caller.func, upvalues) + end + + return unpack(ret, 2, table.maxn(ret)) +end + +return setmetatable(M, { + __call = function(_, ...) + return run(...) + end, +}) diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 25f3465d46..9a6dfd8ed1 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -64,7 +64,7 @@ describe('ui/ext_messages', function() } end) - it('msg_show kind=confirm,confirm_sub,emsg,wmsg,quickfix', function() + it('msg_show kinds', function() feed('iline 1\nline 2<esc>') -- kind=confirm @@ -172,7 +172,7 @@ describe('ui/ext_messages', function() }, { content = { { 'E605: Exception not caught: foo', 9, 7 } }, - kind = '', + kind = 'emsg', }, { content = { { 'Press ENTER or type command to continue', 6, 19 } }, @@ -198,6 +198,48 @@ describe('ui/ext_messages', function() }, }, } + + -- search_cmd + feed('?line<cr>') + screen:expect({ + grid = [[ + ^line 1 | + line 2 | + {1:~ }|*3 + ]], + messages = { { + content = { { '?line ' } }, + kind = 'search_cmd', + } }, + }) + + -- highlight + feed(':hi ErrorMsg<cr>') + screen:expect({ + grid = [[ + ^line 1 | + line 2 | + {1:~ }|*3 + ]], + messages = { + { + content = { + { '\nErrorMsg ' }, + { 'xxx', 9, 7 }, + { ' ' }, + { 'ctermfg=', 18, 6 }, + { '15 ' }, + { 'ctermbg=', 18, 6 }, + { '1 ' }, + { 'guifg=', 18, 6 }, + { 'White ' }, + { 'guibg=', 18, 6 }, + { 'Red' }, + }, + kind = 'list_cmd', + }, + }, + }) end) it(':echoerr', function() @@ -408,34 +450,6 @@ describe('ui/ext_messages', function() } end) - it(':hi Group output', function() - feed(':hi ErrorMsg<cr>') - screen:expect { - grid = [[ - ^ | - {1:~ }|*4 - ]], - messages = { - { - content = { - { '\nErrorMsg ' }, - { 'xxx', 9, 7 }, - { ' ' }, - { 'ctermfg=', 18, 6 }, - { '15 ' }, - { 'ctermbg=', 18, 6 }, - { '1 ' }, - { 'guifg=', 18, 6 }, - { 'White ' }, - { 'guibg=', 18, 6 }, - { 'Red' }, - }, - kind = '', - }, - }, - } - end) - it("doesn't crash with column adjustment #10069", function() feed(':let [x,y] = [1,2]<cr>') feed(':let x y<cr>') @@ -445,8 +459,8 @@ describe('ui/ext_messages', function() {1:~ }|*4 ]], messages = { - { content = { { 'x #1' } }, kind = '' }, - { content = { { 'y #2' } }, kind = '' }, + { content = { { 'x #1' } }, kind = 'list_cmd' }, + { content = { { 'y #2' } }, kind = 'list_cmd' }, { content = { { 'Press ENTER or type command to continue', 6, 19 } }, kind = 'return_prompt', @@ -947,7 +961,7 @@ stack traceback: { '*', 18, 1 }, { ' k' }, }, - kind = '', + kind = 'list_cmd', }, }, } @@ -964,10 +978,12 @@ stack traceback: ^ | {1:~ }|*6 ]], - messages = { { - content = { { 'wildmenu wildmode' } }, - kind = '', - } }, + messages = { + { + content = { { 'wildmenu wildmode' } }, + kind = 'wildlist', + }, + }, cmdline = { { firstc = ':', @@ -983,43 +999,46 @@ stack traceback: feed('ihelllo<esc>') feed('z=') - screen:expect { + screen:expect({ grid = [[ - {100:helllo} | - {1:~ }|*3 - {1:^~ }| - ]], + {100:helllo} | + {1:~ }|*3 + {1:^~ }| + ]], messages = { { - content = { - { - 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\nType number and <Enter> or click with the mouse (q or empty cancels): ', - }, - }, - kind = '', + content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\n' } }, + kind = 'list_cmd', + }, + { + content = { { 'Type number and <Enter> or click with the mouse (q or empty cancels): ' } }, + kind = 'number_prompt', }, }, - } + }) feed('1') - screen:expect { + screen:expect({ grid = [[ - {100:helllo} | - {1:~ }|*3 - {1:^~ }| - ]], + {100:helllo} | + {1:~ }|*3 + {1:^~ }| + ]], messages = { { - content = { - { - 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\nType number and <Enter> or click with the mouse (q or empty cancels): ', - }, - }, + content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\n' } }, + kind = 'list_cmd', + }, + { + content = { { 'Type number and <Enter> or click with the mouse (q or empty cancels): ' } }, + kind = 'number_prompt', + }, + { + content = { { '1' } }, kind = '', }, - { content = { { '1' } }, kind = '' }, }, - } + }) feed('<cr>') screen:expect { @@ -1056,7 +1075,7 @@ stack traceback: {1:~ }|*4 ]], messages = { - { content = { { '\n 1 %a "[No Name]" line 1' } }, kind = '' }, + { content = { { '\n 1 %a "[No Name]" line 1' } }, kind = 'list_cmd' }, }, } @@ -1113,6 +1132,33 @@ stack traceback: }) eq(showmode, 1) end) + + it('emits single message for multiline print())', function() + exec_lua([[print("foo\nbar\nbaz")]]) + screen:expect({ + messages = { + { + content = { { 'foo\nbar\nbaz' } }, + kind = 'lua_print', + }, + }, + }) + exec_lua([[print(vim.inspect({ foo = "bar" }))]]) + screen:expect({ + grid = [[ + ^ | + {1:~ }|*4 + ]], + messages = { + { + content = { { '{\n foo = "bar"\n}' } }, + kind = 'lua_print', + }, + }, + }) + exec_lua([[vim.print({ foo = "bar" })]]) + screen:expect_unchanged() + end) end) describe('ui/builtin messages', function() @@ -1848,7 +1894,7 @@ describe('ui/ext_messages', function() {3:[No Name] }| ]], messages = { - { content = { { ' cmdheight=0' } }, kind = '' }, + { content = { { ' cmdheight=0' } }, kind = 'list_cmd' }, }, }) @@ -1864,7 +1910,7 @@ describe('ui/ext_messages', function() {3:[No Name] }| ]], messages = { - { content = { { ' laststatus=3' } }, kind = '' }, + { content = { { ' laststatus=3' } }, kind = 'list_cmd' }, }, }) @@ -1884,7 +1930,7 @@ describe('ui/ext_messages', function() {3:[No Name] }| ]], messages = { - { content = { { ' cmdheight=0' } }, kind = '' }, + { content = { { ' cmdheight=0' } }, kind = 'list_cmd' }, }, }) end) @@ -2062,8 +2108,6 @@ aliquip ex ea commodo consequat.]] end) it('can be quit with Lua #11224 #16537', function() - -- NOTE: adds "4" to message history, although not displayed initially - -- (triggered the more prompt). screen:try_resize(40, 5) feed(':lua for i=0,10 do print(i) end<cr>') screen:expect { @@ -2093,13 +2137,13 @@ aliquip ex ea commodo consequat.]] {4:-- More --}^ | ]], } - feed('j') + feed('G') screen:expect { grid = [[ - 1 | - 2 | - 3 | - 4 | + 7 | + 8 | + 9 | + 10 | {4:Press ENTER or type command to continue}^ | ]], } diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua index 57d76e54df..80e38d974a 100644 --- a/test/functional/ui/syntax_conceal_spec.lua +++ b/test/functional/ui/syntax_conceal_spec.lua @@ -198,7 +198,7 @@ describe('Screen', function() end) end) -- a region of text (implicit concealing) - it('cursor position is correct when entering Insert mode with cocu=ni #13916', function() + it('cursor position when entering Insert mode with cocu=ni #13916', function() insert([[foobarfoobarfoobar]]) -- move to end of line feed('$') @@ -217,6 +217,37 @@ describe('Screen', function() {4:-- INSERT --} | ]]) end) + + it('cursor position when scrolling in Normal mode with cocu=n #31271', function() + insert(('foo\n'):rep(9) .. 'foofoobarfoofoo' .. ('\nfoo'):rep(9)) + command('set concealcursor=n') + command('syn match Foo /bar/ conceal cchar=&') + feed('gg5<C-E>10gg$') + screen:expect([[ + foo |*4 + foofoo{1:&}foofo^o | + foo |*4 + | + ]]) + feed('zz') + screen:expect_unchanged() + feed('zt') + screen:expect([[ + foofoo{1:&}foofo^o | + foo |*8 + | + ]]) + feed('zt') + screen:expect_unchanged() + feed('zb') + screen:expect([[ + foo |*8 + foofoo{1:&}foofo^o | + | + ]]) + feed('zb') + screen:expect_unchanged() + end) end) -- match and conceal describe('let the conceal level be', function() diff --git a/test/functional/ui/tabline_spec.lua b/test/functional/ui/tabline_spec.lua index 4c6fd8fbda..6d212823eb 100644 --- a/test/functional/ui/tabline_spec.lua +++ b/test/functional/ui/tabline_spec.lua @@ -214,4 +214,43 @@ describe('tabline', function() api.nvim_input_mouse('middle', 'press', '', 0, 0, 1) eq({ 1, 1 }, api.nvim_eval('[tabpagenr(), tabpagenr("$")]')) end) + + it('does not show floats with focusable=false', function() + screen:set_default_attr_ids({ + [1] = { background = Screen.colors.Plum1 }, + [2] = { underline = true, background = Screen.colors.LightGrey }, + [3] = { bold = true }, + [4] = { reverse = true }, + [5] = { bold = true, foreground = Screen.colors.Blue1 }, + [6] = { foreground = Screen.colors.Fuchsia, bold = true }, + [7] = { foreground = Screen.colors.SeaGreen, bold = true }, + }) + command('tabnew') + api.nvim_open_win(0, false, { + focusable = false, + relative = 'editor', + height = 1, + width = 1, + row = 0, + col = 0, + }) + screen:expect { + grid = [[ + {1: }{2:[No Name] }{3: [No Name] }{4: }{2:X}| + ^ | + {5:~ }|*2 + | + ]], + } + command('tabs') + screen:expect { + grid = [[ + {6:Tab page 1} | + # [No Name] | + {6:Tab page 2} | + > [No Name] | + {7:Press ENTER or type command to continue}^ | + ]], + } + end) end) diff --git a/test/old/testdir/test_curswant.vim b/test/old/testdir/test_curswant.vim index e54cd4b280..c67cca5f7d 100644 --- a/test/old/testdir/test_curswant.vim +++ b/test/old/testdir/test_curswant.vim @@ -1,4 +1,7 @@ -" Tests for curswant not changing when setting an option +" Tests for not changing curswant + +source check.vim +source term_util.vim func Test_curswant() new @@ -19,5 +22,50 @@ func Test_curswant() let &ttimeoutlen=&ttimeoutlen call assert_equal(7, winsaveview().curswant) - enew! + bw! +endfunc + +func Test_normal_gm() + CheckRunVimInTerminal + let lines =<< trim END + call setline(1, repeat([" abcd\tefgh\tij"], 10)) + call cursor(1, 1) + END + call writefile(lines, 'XtestCurswant', 'D') + let buf = RunVimInTerminal('-S XtestCurswant', #{rows: 10}) + if has("folding") + call term_sendkeys(buf, "jVjzf") + " gm + call term_sendkeys(buf, "gmk") + call term_sendkeys(buf, ":echo virtcol('.')\<cr>") + call WaitFor({-> term_getline(buf, 10) =~ '^18\s\+'}) + " g0 + call term_sendkeys(buf, "jg0k") + call term_sendkeys(buf, ":echo virtcol('.')\<cr>") + call WaitFor({-> term_getline(buf, 10) =~ '^1\s\+'}) + " g^ + call term_sendkeys(buf, "jg^k") + call term_sendkeys(buf, ":echo virtcol('.')\<cr>") + call WaitFor({-> term_getline(buf, 10) =~ '^3\s\+'}) + endif + call term_sendkeys(buf, ":call cursor(10, 1)\<cr>") + " gm + call term_sendkeys(buf, "gmk") + call term_sendkeys(buf, ":echo virtcol('.')\<cr>") + call term_wait(buf) + call WaitFor({-> term_getline(buf, 10) =~ '^18\s\+'}) + " g0 + call term_sendkeys(buf, "g0k") + call term_sendkeys(buf, ":echo virtcol('.')\<cr>") + call WaitFor({-> term_getline(buf, 10) =~ '^1\s\+'}) + " g^ + call term_sendkeys(buf, "g^k") + call term_sendkeys(buf, ":echo virtcol('.')\<cr>") + call WaitFor({-> term_getline(buf, 10) =~ '^3\s\+'}) + " clean up + call StopVimInTerminal(buf) + wincmd p + wincmd c endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim index f70144b379..62e339c1ca 100644 --- a/test/old/testdir/test_filetype.vim +++ b/test/old/testdir/test_filetype.vim @@ -144,6 +144,7 @@ func s:GetFilenameChecks() abort \ 'bzl': ['file.bazel', 'file.bzl', 'WORKSPACE', 'WORKSPACE.bzlmod'], \ 'bzr': ['bzr_log.any', 'bzr_log.file'], \ 'c': ['enlightenment/file.cfg', 'file.qc', 'file.c', 'some-enlightenment/file.cfg', 'file.mdh', 'file.epro'], + \ 'c3': ['file.c3', 'file.c3i', 'file.c3t'], \ 'cabal': ['file.cabal'], \ 'cabalconfig': ['cabal.config', expand("$HOME/.config/cabal/config")] + s:WhenConfigHome('$XDG_CONFIG_HOME/cabal/config'), \ 'cabalproject': ['cabal.project', 'cabal.project.local'], @@ -353,7 +354,7 @@ func s:GetFilenameChecks() abort \ 'htmlm4': ['file.html.m4'], \ 'httest': ['file.htt', 'file.htb'], \ 'hurl': ['file.hurl'], - \ 'hyprlang': ['hyprlock.conf', 'hyprland.conf', 'hypridle.conf', 'hyprpaper.conf'], + \ 'hyprlang': ['hyprlock.conf', 'hyprland.conf', 'hypridle.conf', 'hyprpaper.conf', '/hypr/foo.conf'], \ 'i3config': ['/home/user/.i3/config', '/home/user/.config/i3/config', '/etc/i3/config', '/etc/xdg/i3/config'], \ 'ibasic': ['file.iba', 'file.ibi'], \ 'icemenu': ['/.icewm/menu', 'any/.icewm/menu'], @@ -392,6 +393,7 @@ func s:GetFilenameChecks() abort \ 'jsp': ['file.jsp'], \ 'julia': ['file.jl'], \ 'just': ['justfile', 'Justfile', '.justfile', 'config.just'], + \ 'karel': ['file.kl', 'file.KL'], \ 'kconfig': ['Kconfig', 'Kconfig.debug', 'Kconfig.file', 'Config.in', 'Config.in.host'], \ 'kdl': ['file.kdl'], \ 'kivy': ['file.kv'], @@ -421,7 +423,7 @@ func s:GetFilenameChecks() abort \ 'limits': ['/etc/limits', '/etc/anylimits.conf', '/etc/anylimits.d/file.conf', '/etc/limits.conf', '/etc/limits.d/file.conf', '/etc/some-limits.conf', '/etc/some-limits.d/file.conf', 'any/etc/limits', 'any/etc/limits.conf', 'any/etc/limits.d/file.conf', 'any/etc/some-limits.conf', 'any/etc/some-limits.d/file.conf'], \ 'liquidsoap': ['file.liq'], \ 'liquid': ['file.liquid'], - \ 'lisp': ['file.lsp', 'file.lisp', 'file.asd', 'file.el', 'file.cl', '.emacs', '.sawfishrc', 'sbclrc', '.sbclrc', 'file.stsg', 'any/local/share/supertux2/config'], + \ 'lisp': ['file.lsp', 'file.lisp', 'file.asd', 'file.el', '.emacs', '.sawfishrc', 'sbclrc', '.sbclrc', 'file.stsg', 'any/local/share/supertux2/config'], \ 'lite': ['file.lite', 'file.lt'], \ 'litestep': ['/LiteStep/any/file.rc', 'any/LiteStep/any/file.rc'], \ 'logcheck': ['/etc/logcheck/file.d-some/file', '/etc/logcheck/file.d/file', 'any/etc/logcheck/file.d-some/file', 'any/etc/logcheck/file.d/file'], @@ -504,6 +506,7 @@ func s:GetFilenameChecks() abort \ 'mplayerconf': ['mplayer.conf', '/.mplayer/config', 'any/.mplayer/config'], \ 'mrxvtrc': ['mrxvtrc', '.mrxvtrc'], \ 'msidl': ['file.odl', 'file.mof'], + \ 'mss': ['file.mss'], \ 'msql': ['file.msql'], \ 'mojo': ['file.mojo', 'file.🔥'], \ 'msmtp': ['.msmtprc'], @@ -1190,6 +1193,22 @@ func Test_cfg_file() filetype off endfunc +func Test_cl_file() + filetype on + + call writefile(['/*', ' * Xfile.cl', ' */', 'int f() {}'], 'Xfile.cl') + split Xfile.cl + call assert_equal('opencl', &filetype) + bwipe! + + call writefile(['()'], 'Xfile.cl') + split Xfile.cl + call assert_equal('lisp', &filetype) + bwipe! + + filetype off +endfunc + func Test_d_file() filetype on |