diff options
Diffstat (limited to 'runtime')
35 files changed, 1743 insertions, 336 deletions
diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index d66e04f5f2..4fdcfc0f33 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -111,7 +111,7 @@ if(NOT APPLE) install_helper( FILES ${CMAKE_CURRENT_SOURCE_DIR}/nvim.png - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps) + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps) endif() file(GLOB_RECURSE RUNTIME_PROGRAMS diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index de540405e6..001379c85d 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -423,10 +423,6 @@ function! s:check_python(version) abort \ ' This could lead to confusing error messages.') endif - if a:version == 3 && str2float(pyversion) < 3.3 - call health#report_warn('Python 3.3+ is recommended.') - endif - call health#report_info('Python version: ' . pyversion) if s:is_bad_response(status) diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 0d85d6b539..fe69d9076f 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -993,7 +993,7 @@ nvim_get_option_info({name}) *nvim_get_option_info()* Resulting dictionary has keys: • name: Name of the option (like 'filetype') • shortname: Shortened name of the option (like 'ft') - • type: type of option ("string", "integer" or "boolean") + • type: type of option ("string", "number" or "boolean") • default: The default value for the option • was_set: Whether the option was set. • last_set_sid: Last set script id (if any) @@ -1299,6 +1299,17 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* an external top-level window. Currently accepts no other positioning configuration together with this. + • `zindex`: Stacking order. floats with higher`zindex`go on top on floats with lower indices. Must + be larger than zero. The following screen + elements have hard-coded z-indices: + • 100: insert completion popupmenu + • 200: message scrollback + • 250: cmdline completion popupmenu (when + wildoptions+=pum) The default value for + floats are 50. In general, values below 100 + are recommended, unless there is a good + reason to overshadow builtin elements. + • `style`: Configure the appearance of the window. Currently only takes one non-empty value: • "minimal" Nvim will display the window with @@ -1705,6 +1716,12 @@ nvim_set_hl({ns_id}, {name}, {val}) *nvim_set_hl()* |nvim_get_hl_by_name|. in addition the following keys are also recognized: `default` : don't override existing definition, like `hi default` + `ctermfg` : sets foreground of cterm color + `ctermbg` : sets background of cterm color + `cterm` : cterm attribute map. sets attributed + for cterm colors. similer to `hi cterm` Note: by + default cterm attributes are same as attributes + of gui color nvim_set_keymap({mode}, {lhs}, {rhs}, {opts}) *nvim_set_keymap()* Sets a global |mapping| for the given mode. @@ -2269,7 +2286,12 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts}) • "overlay": display over the specified column, without shifting the underlying text. + • "right_align": display right aligned in the + window. + • virt_text_win_col : position the virtual text + at a fixed window column (starting from the + first text column) • virt_text_hide : hide the virtual text when the background text is selected or hidden due to horizontal scroll 'nowrap' diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index ae43aeeb25..dcdc2384dc 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -759,13 +759,15 @@ three lines: > 3:d<CR> is translated into: .,.+2d<CR> < -Visual Mode and Range *v_:* - *:star-visual-range* +Visual Mode and Range + *v_:* {Visual}: Starts a command-line with the Visual selected lines as a range. The code `:'<,'>` is used for this range, which makes it possible to select a similar line from the command-line history for repeating a command on different Visually selected lines. + +:* *:star* *:star-visual-range* When Visual mode was already ended, a short way to use the Visual area for a range is `:*`. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index bb352022f9..b7214d1390 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2394,6 +2394,7 @@ screenpos({winid}, {lnum}, {col}) Dict screen row and col of a text character screenrow() Number current cursor row search({pattern} [, {flags} [, {stopline} [, {timeout}]]]) Number search for {pattern} +searchcount([{options}]) Dict Get or update the last search count searchdecl({name} [, {global} [, {thisblock}]]) Number search for variable declaration searchpair({start}, {middle}, {end} [, {flags} [, {skip} [...]]]) @@ -4772,8 +4773,9 @@ getqflist([{what}]) *getqflist()* id get information for the quickfix list with |quickfix-ID|; zero means the id for the current list or the list specified by "nr" - idx index of the current entry in the quickfix - list specified by 'id' or 'nr'. + idx get information for the quickfix entry at this + index in the list specified by 'id' or 'nr'. + If set to zero, then uses the current entry. See |quickfix-index| items quickfix list entries lines parse a list of lines using 'efm' and return @@ -4806,7 +4808,7 @@ getqflist([{what}]) *getqflist()* If not present, set to "". id quickfix list ID |quickfix-ID|. If not present, set to 0. - idx index of the current entry in the list. If not + idx index of the quickfix entry in the list. If not present, set to 0. items quickfix list entries. If not present, set to an empty list. @@ -7293,6 +7295,126 @@ search({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *search()* The 'n' flag tells the function not to move the cursor. +searchcount([{options}]) *searchcount()* + Get or update the last search count, like what is displayed + without the "S" flag in 'shortmess'. This works even if + 'shortmess' does contain the "S" flag. + + This returns a Dictionary. The dictionary is empty if the + previous pattern was not set and "pattern" was not specified. + + key type meaning ~ + current |Number| current position of match; + 0 if the cursor position is + before the first match + exact_match |Boolean| 1 if "current" is matched on + "pos", otherwise 0 + total |Number| total count of matches found + incomplete |Number| 0: search was fully completed + 1: recomputing was timed out + 2: max count exceeded + + For {options} see further down. + + To get the last search count when |n| or |N| was pressed, call + this function with `recompute: 0` . This sometimes returns + wrong information because |n| and |N|'s maximum count is 99. + If it exceeded 99 the result must be max count + 1 (100). If + you want to get correct information, specify `recompute: 1`: > + + " result == maxcount + 1 (100) when many matches + let result = searchcount(#{recompute: 0}) + + " Below returns correct result (recompute defaults + " to 1) + let result = searchcount() +< + The function is useful to add the count to |statusline|: > + function! LastSearchCount() abort + let result = searchcount(#{recompute: 0}) + if empty(result) + return '' + endif + if result.incomplete ==# 1 " timed out + return printf(' /%s [?/??]', @/) + elseif result.incomplete ==# 2 " max count exceeded + if result.total > result.maxcount && + \ result.current > result.maxcount + return printf(' /%s [>%d/>%d]', @/, + \ result.current, result.total) + elseif result.total > result.maxcount + return printf(' /%s [%d/>%d]', @/, + \ result.current, result.total) + endif + endif + return printf(' /%s [%d/%d]', @/, + \ result.current, result.total) + endfunction + let &statusline .= '%{LastSearchCount()}' + + " Or if you want to show the count only when + " 'hlsearch' was on + " let &statusline .= + " \ '%{v:hlsearch ? LastSearchCount() : ""}' +< + You can also update the search count, which can be useful in a + |CursorMoved| or |CursorMovedI| autocommand: > + + autocmd CursorMoved,CursorMovedI * + \ let s:searchcount_timer = timer_start( + \ 200, function('s:update_searchcount')) + function! s:update_searchcount(timer) abort + if a:timer ==# s:searchcount_timer + call searchcount(#{ + \ recompute: 1, maxcount: 0, timeout: 100}) + redrawstatus + endif + endfunction +< + This can also be used to count matched texts with specified + pattern in the current buffer using "pattern": > + + " Count '\<foo\>' in this buffer + " (Note that it also updates search count) + let result = searchcount(#{pattern: '\<foo\>'}) + + " To restore old search count by old pattern, + " search again + call searchcount() +< + {options} must be a Dictionary. It can contain: + key type meaning ~ + recompute |Boolean| if |TRUE|, recompute the count + like |n| or |N| was executed. + otherwise returns the last + result by |n|, |N|, or this + function is returned. + (default: |TRUE|) + pattern |String| recompute if this was given + and different with |@/|. + this works as same as the + below command is executed + before calling this function > + let @/ = pattern +< (default: |@/|) + timeout |Number| 0 or negative number is no + timeout. timeout milliseconds + for recomputing the result + (default: 0) + maxcount |Number| 0 or negative number is no + limit. max count of matched + text while recomputing the + result. if search exceeded + total count, "total" value + becomes `maxcount + 1` + (default: 0) + pos |List| `[lnum, col, off]` value + when recomputing the result. + this changes "current" result + value. see |cursor()|, |getpos() + (default: cursor's position) + + searchdecl({name} [, {global} [, {thisblock}]]) *searchdecl()* Search for the declaration of {name}. @@ -7753,6 +7875,11 @@ setqflist({list} [, {action}[, {what}]]) *setqflist()* nr list number in the quickfix stack; zero means the current quickfix list and "$" means the last quickfix list. + quickfixtextfunc + function to get the text to display in the + quickfix window. Refer to + |quickfix-window-function| for an explanation + of how to write the function and an example. title quickfix list title text. See |quickfix-title| Unsupported keys in {what} are ignored. If the "nr" item is not present, then the current quickfix list diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index 925b3e5dbb..f739e2e88b 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -169,6 +169,7 @@ Vim would never have become what it is now, without the help of these people! Ken Takata fixes and features Kazunobu Kuriyama GTK 3 Christian Brabandt many fixes, features, user support, etc. + Yegappan Lakshmanan many quickfix features I wish to thank all the people that sent me bug reports and suggestions. The list is too long to mention them all here. Vim would not be the same without diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 5c2ee568c5..d5e8db4dd7 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -619,15 +619,15 @@ buf_request_sync({bufnr}, {method}, {params}, {timeout_ms}) Calls |vim.lsp.buf_request_all()| but blocks Nvim while awaiting the result. Parameters are the same as |vim.lsp.buf_request()| but the return result is different. - Wait maximum of {timeout_ms} (default 100) ms. + Wait maximum of {timeout_ms} (default 1000) ms. Parameters: ~ {bufnr} (number) Buffer handle, or 0 for current. {method} (string) LSP method name {params} (optional, table) Parameters to send to the server - {timeout_ms} (optional, number, default=100) Maximum time - in milliseconds to wait for a result. + {timeout_ms} (optional, number, default=1000) Maximum + time in milliseconds to wait for a result. Return: ~ Map of client_id:request_result. On timeout, cancel or @@ -651,6 +651,14 @@ client() *vim.lsp.client* {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(method, params, timeout_ms, bufnr) 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 dictionary, 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` . • notify(method, params) 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 @@ -939,6 +947,9 @@ add_workspace_folder({workspace_folder}) not provided, the user will be prompted for a path using |input()|. +call_hierarchy({method}) *vim.lsp.buf.call_hierarchy()* + TODO: Documentation + clear_references() *vim.lsp.buf.clear_references()* Removes document highlights from current buffer. @@ -1017,6 +1028,32 @@ formatting({options}) *vim.lsp.buf.formatting()* See also: ~ https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting + *vim.lsp.buf.formatting_seq_sync()* +formatting_seq_sync({options}, {timeout_ms}, {order}) + Formats the current buffer by sequentially requesting + formatting from attached clients. + + Useful when multiple clients with formatting capability are + attached. + + Since it's synchronous, can be used for running on save, to + make sure buffer is formatted prior to being saved. + {timeout_ms} is passed on to the |vim.lsp.client| `request_sync` method. Example: > + + vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_seq_sync()]] +< + + Parameters: ~ + {options} (optional, table) `FormattingOptions` + entries + {timeout_ms} (optional, number) Request timeout + {order} (optional, table) List of client names. + Formatting is requested from clients in the + following order: first all clients that are + not in the `order` list, then the remaining + clients in the order as they occur in the + `order` list. + *vim.lsp.buf.formatting_sync()* formatting_sync({options}, {timeout_ms}) Performs |vim.lsp.buf.formatting()| synchronously. @@ -1033,6 +1070,9 @@ formatting_sync({options}, {timeout_ms}) {options} Table with valid `FormattingOptions` entries {timeout_ms} (number) Request timeout + See also: ~ + |vim.lsp.buf.formatting_seq_sync| + hover() *vim.lsp.buf.hover()* Displays hover information about the symbol under the cursor in a floating window. Calling the function twice will jump @@ -1585,6 +1625,9 @@ buf_highlight_references({bufnr}, {references}) {references} List of `DocumentHighlight` objects to highlight +buf_lines({bufnr}) *vim.lsp.util.buf_lines()* + TODO: Documentation + character_offset({buf}, {row}, {col}) *vim.lsp.util.character_offset()* Returns the UTF-32 and UTF-16 offsets for a position in a certain buffer. @@ -1650,12 +1693,15 @@ convert_input_to_markdown_lines({input}, {contents}) https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover *vim.lsp.util.convert_signature_help_to_markdown_lines()* -convert_signature_help_to_markdown_lines({signature_help}) +convert_signature_help_to_markdown_lines({signature_help}, {ft}) Converts `textDocument/SignatureHelp` response to markdown lines. Parameters: ~ {signature_help} Response of `textDocument/SignatureHelp` + {ft} optional filetype that will be use as + the `lang` for the label markdown code + block Return: ~ list of lines of converted markdown. @@ -1683,65 +1729,6 @@ extract_completion_items({result}) See also: ~ https://microsoft.github.io/language-server-protocol/specification#textDocument_completion - *vim.lsp.util.fancy_floating_markdown()* -fancy_floating_markdown({contents}, {opts}) - Converts markdown into syntax highlighted regions by stripping - the code blocks and converting them into highlighted code. - This will by default insert a blank line separator after those - code block regions to improve readability. The result is shown - in a floating preview. - - Parameters: ~ - {contents} table of lines to show in window - {opts} dictionary with optional fields - • height of floating window - • width of floating window - • wrap_at character to wrap at for computing - height - • max_width maximal width of floating window - • max_height maximal height of floating window - • pad_left number of columns to pad contents - at left - • pad_right number of columns to pad contents - at right - • pad_top number of lines to pad contents at - top - • pad_bottom number of lines to pad contents - at bottom - • separator insert separator after code block - - Return: ~ - width,height size of float - -focusable_float({unique_name}, {fn}) *vim.lsp.util.focusable_float()* - Parameters: ~ - {unique_name} (string) Window variable - {fn} (function) should return create a new - window and return a tuple of - ({focusable_buffer_id}, {window_id}). if - {focusable_buffer_id} is a valid buffer id, - the newly created window will be the new - focus associated with the current buffer - via the tag `unique_name` . - - Return: ~ - (pbufnr, pwinnr) if `fn()` has created a new window; nil - otherwise - - *vim.lsp.util.focusable_preview()* -focusable_preview({unique_name}, {fn}) - Focuses/unfocuses the floating preview window associated with - the current buffer via the window variable `unique_name` . If - no such preview window exists, makes a new one. - - Parameters: ~ - {unique_name} (string) Window variable - {fn} (function) The return values of this - function will be passed directly to - |vim.lsp.util.open_floating_preview()|, in - the case that a new floating window should - be created - get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()* Returns visual width of tabstop. @@ -1755,6 +1742,22 @@ get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()* See also: ~ |softtabstop| +get_line({uri}, {row}) *vim.lsp.util.get_line()* + Parameters: ~ + {uri} string uri of the resource to get the line from + {row} number zero-indexed line number + + Return: ~ + string the line at row in filename + +get_lines({uri}, {rows}) *vim.lsp.util.get_lines()* + Parameters: ~ + {uri} string uri of the resource to get the lines from + {rows} number[] zero-indexed line numbers + + Return: ~ + table<number string> a table mapping rows to lines + get_progress_messages() *vim.lsp.util.get_progress_messages()* TODO: Documentation @@ -1897,7 +1900,7 @@ parse_snippet({input}) *vim.lsp.util.parse_snippet()* Return: ~ (string) parsed snippet -preview_location({location}) *vim.lsp.util.preview_location()* +preview_location({location}, {opts}) *vim.lsp.util.preview_location()* Previews a location in a floating window behavior depends on type of location: @@ -1946,6 +1949,41 @@ set_qflist({items}) *vim.lsp.util.set_qflist()* Parameters: ~ {items} (table) list of items + *vim.lsp.util.stylize_markdown()* +stylize_markdown({bufnr}, {contents}, {opts}) + Converts markdown into syntax highlighted regions by stripping + the code blocks and converting them into highlighted code. + This will by default insert a blank line separator after those + code block regions to improve readability. + + This method configures the given buffer and returns the lines + to set. + + If you want to open a popup with fancy markdown, use + `open_floating_preview` instead + + Parameters: ~ + {contents} table of lines to show in window + {opts} dictionary with optional fields + • height of floating window + • width of floating window + • wrap_at character to wrap at for computing + height + • max_width maximal width of floating window + • max_height maximal height of floating window + • pad_left number of columns to pad contents + at left + • pad_right number of columns to pad contents + at right + • pad_top number of lines to pad contents at + top + • pad_bottom number of lines to pad contents + at bottom + • separator insert separator after code block + + Return: ~ + width,height size of float + symbols_to_items({symbols}, {bufnr}) *vim.lsp.util.symbols_to_items()* Converts symbols to quickfix list items. diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index be01966d42..05404a2e35 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -650,42 +650,6 @@ vim.empty_dict() *vim.empty_dict()* Note: if numeric keys are added to the table, the metatable will be ignored and the dict converted to a list/array anyway. -vim.region({bufnr}, {pos1}, {pos2}, {type}, {inclusive}) *vim.region()* - Converts a selection specified by the buffer ({bufnr}), starting - position ({pos1}, a zero-indexed pair `{line1,column1}`), ending - position ({pos2}, same format as {pos1}), the type of the register - for the selection ({type}, see |regtype|), and a boolean indicating - whether the selection is inclusive or not, into a zero-indexed table - of linewise selections of the form `{linenr = {startcol, endcol}}` . - - *vim.register_keystroke_callback()* -vim.register_keystroke_callback({fn}, {ns_id}) - Register a lua {fn} with an {ns_id} to be run after every keystroke. - - Parameters: ~ - {fn}: (function): Function to call on keystroke. - It should take one argument, which is a string. - The string will contain the literal keys typed. - See |i_CTRL-V| - - If {fn} is `nil`, it removes the callback for the - associated {ns_id}. - - {ns_id}: (number) Namespace ID. If not passed or 0, will generate - and return a new namespace ID from |nvim_create_namespace()| - - Return: ~ - (number) Namespace ID associated with {fn} - - NOTE: {fn} will be automatically removed if an error occurs while - calling. This is to prevent the annoying situation of every keystroke - erroring while trying to remove a broken callback. - - NOTE: {fn} will receive the keystrokes after mappings have been - evaluated - - NOTE: {fn} will *NOT* be cleared from |nvim_buf_clear_namespace()| - vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()* Sends {event} to {channel} via |RPC| and returns immediately. If {channel} is 0, the event is broadcast to all channels. @@ -912,9 +876,167 @@ vim.env *vim.env* print(vim.env.TERM) < - *lua-vim-options* -From Lua you can work with editor |options| by reading and setting items in -these Lua tables: + *lua-vim-options* + *lua-vim-opt* + *lua-vim-set* + *lua-vim-optlocal* + *lua-vim-setlocal* + +In vimL, there is a succint and simple way to set options. For more +information, see |set-option|. In Lua, the corresponding method is `vim.opt`. + +`vim.opt` provides several conveniences for setting and controlling options +from within Lua. + + Examples: ~ + + To set a boolean toggle: + In vimL: + `set number` + + In Lua: + `vim.opt.number = true` + + To set an array of values: + In vimL: + `set wildignore=*.o,*.a,__pycache__` + + In Lua, there are two ways you can do this now. One is very similar to + the vimL way: + `vim.opt.wildignore = '*.o,*.a,__pycache__'` + + However, vim.opt also supports a more elegent way of setting + list-style options, but using lua tables: + `vim.opt.wildignore = { '*.o', '*.a', '__pycache__' }` + + To replicate the behavior of |:set+=|, use: > + + -- vim.opt supports appending options via the "+" operator + vim.opt.wildignore = vim.opt.wildignore + { "*.pyc", "node_modules" } + + -- or using the `:append(...)` method + vim.opt.wildignore:append { "*.pyc", "node_modules" } +< + + To replicate the behavior of |:set^=|, use: > + + -- vim.opt supports prepending options via the "^" operator + vim.opt.wildignore = vim.opt.wildignore ^ { "new_first_value" } + + -- or using the `:prepend(...)` method + vim.opt.wildignore:prepend { "new_first_value" } +< + To replicate the behavior of |:set-=|, use: > + + -- vim.opt supports removing options via the "-" operator + vim.opt.wildignore = vim.opt.wildignore - { "node_modules" } + + -- or using the `:remove(...)` method + vim.opt.wildignore:remove { "node_modules" } +< + To set a map of values: + In vimL: + `set listchars=space:_,tab:>~` + + In Lua: + `vim.opt.listchars = { space = '_', tab = '>~' }` + + +In any of the above examples, to replicate the behavior |setlocal|, use +`vim.opt_local`. Additionally, to replicate the behavior of |setglobal|, use +`vim.opt_global`. + *vim.opt* + +|vim.opt| returns an Option object. + +For example: `local listchar_object = vim.opt.listchar` + +An `Option` has the following methods: + + + *vim.opt:get()* +Option:get() + + Returns a lua-representation of the option. Boolean, number and string + values will be returned in exactly the same fashion. + + For values that are comma-separated lists, an array will be returned with + the values as entries in the array: > + vim.cmd [[set wildignore=*.pyc,*.o]] + + print(vim.inspect(vim.opt.wildignore:get())) + -- { "*.pyc", "*.o", } + + for _, ignore_pattern in ipairs(vim.opt.wildignore:get()) do + print("Will ignore:", ignore_pattern) + end + -- Will ignore: *.pyc + -- Will ignore: *.o +< + For values that are comma-separated maps, a table will be returned with + the names as keys and the values as entries: > + vim.cmd [[set listchars=space:_,tab:>~]] + + print(vim.inspect(vim.opt.listchars:get())) + -- { space = "_", tab = ">~", } + + for char, representation in pairs(vim.opt.listchars:get()) do + print(char, "->", representation) + end +< + For values that are lists of flags, a set will be returned with the flags + as keys and `true` as entries. > + vim.cmd [[set formatoptions=njtcroql]] + + print(vim.inspect(vim.opt.formatoptions:get())) + -- { n = true, j = true, c = true, ... } + + local format_opts = vim.opt.formatoptions:get() + if format_opts.j then + print("J is enabled!") + end +< + *vim.opt:append()* +Option:append(value) + + Append a value to string-style options. See |:set+=| + + These are equivalent: + `vim.opt.formatoptions:append('j')` + `vim.opt.formatoptions = vim.opt.formatoptions + 'j'` + + *vim.opt:prepend()* +Option:prepend(value) + + Prepend a value to string-style options. See |:set^=| + + These are equivalent: + `vim.opt.wildignore:prepend('*.o')` + `vim.opt.wildignore = vim.opt.wildignore ^ '*.o'` + + *vim.opt:remove()* +Option:remove(value) + + Remove a value from string-style options. See |:set-=| + + These are equivalent: + `vim.opt.wildignore:remove('*.pyc')` + `vim.opt.wildignore = vim.opt.wildignore - '*.pyc'` + + +In general, using `vim.opt` will provide the expected result when the user is +used to interacting with editor |options| via `set`. There are still times +where the user may want to set particular options via a shorthand in Lua, +which is where |vim.o|, |vim.bo|, |vim.wo|, and |vim.go| come into play. + +The behavior of |vim.o|, |vim.bo|, |vim.wo|, and |vim.go| is designed to +follow that of |:set|, |:setlocal|, and |:setglobal| which can be seen in the +table below: + + lua command global_value local_value ~ +vim.o :set set set +vim.bo/vim.wo :setlocal - set +vim.go :setglobal set - vim.o *vim.o* Get or set editor options, like |:set|. Invalid key is an error. @@ -922,14 +1044,36 @@ vim.o *vim.o* vim.o.cmdheight = 4 print(vim.o.columns) + +vim.go *vim.go* + Get or set an |option|. Invalid key is an error. + + This is a wrapper around |nvim_set_option()| and |nvim_get_option()|. + + NOTE: This is different than |vim.o| because this ONLY sets the global + option, which generally produces confusing behavior for options with + |global-local| values. + + Example: > + vim.go.cmdheight = 4 +< + vim.bo *vim.bo* Get or set buffer-scoped |local-options|. Invalid key is an error. + + This is a wrapper around |nvim_buf_set_option()| and + |nvim_buf_get_option()|. + Example: > vim.bo.buflisted = true print(vim.bo.comments) vim.wo *vim.wo* Get or set window-scoped |local-options|. Invalid key is an error. + + This is a wrapper around |nvim_win_set_option()| and + |nvim_win_get_option()|. + Example: > vim.wo.cursorcolumn = true print(vim.wo.foldmarker) @@ -938,6 +1082,23 @@ vim.wo *vim.wo* ============================================================================== Lua module: vim *lua-vim* +defer_fn({fn}, {timeout}) *vim.defer_fn()* + Defers calling `fn` until `timeout` ms passes. + + Use to do a one-shot timer that calls `fn` Note: The {fn} is |schedule_wrap|ped automatically, so API + functions are safe to call. + + Parameters: ~ + {fn} Callback to call once `timeout` expires + {timeout} Number of milliseconds to wait before calling + `fn` + + Return: ~ + timer luv timer object + +insert_keys({obj}) *vim.insert_keys()* + TODO: Documentation + inspect({object}, {options}) *vim.inspect()* Return a human-readable representation of the given object. @@ -945,9 +1106,19 @@ inspect({object}, {options}) *vim.inspect()* https://github.com/kikito/inspect.lua https://github.com/mpeterv/vinspect -make_meta_accessor({get}, {set}, {del}) *vim.make_meta_accessor()* +make_dict_accessor({scope}) *vim.make_dict_accessor()* TODO: Documentation +notify({msg}, {log_level}, {_opts}) *vim.notify()* + Notification provider without a runtime, writes to :Messages + + Parameters: ~ + {msg} Content of the notification to show to the + user + {log_level} Optional log level + {opts} Dictionary with optional options (timeout, + etc) + paste({lines}, {phase}) *vim.paste()* Paste handler, invoked by |nvim_paste()| when a conforming UI (such as the |TUI|) pastes text into the editor. @@ -980,6 +1151,53 @@ paste({lines}, {phase}) *vim.paste()* See also: ~ |paste| +region({bufnr}, {pos1}, {pos2}, {regtype}, {inclusive}) *vim.region()* + Get a table of lines with start, end columns for a region + marked by two points + + Parameters: ~ + {bufnr} number of buffer + {pos1} (line, column) tuple marking beginning of + region + {pos2} (line, column) tuple marking end of region + {regtype} type of selection (:help setreg) + {inclusive} boolean indicating whether the selection is + end-inclusive + + Return: ~ + region lua table of the form {linenr = {startcol,endcol}} + + *vim.register_keystroke_callback()* +register_keystroke_callback({fn}, {ns_id}) + Register a lua {fn} with an {id} to be run after every + keystroke. + + If {fn} is nil, it removes the callback for the associated + {ns_id} + Note: + {fn} will not be cleared from |nvim_buf_clear_namespace()| + + Note: + {fn} will receive the keystrokes after mappings have been + evaluated + + Parameters: ~ + {fn} function: Function to call. It should take one + argument, which is a string. The string will contain + the literal keys typed. See |i_CTRL-V| + {ns_id} number? Namespace ID. If not passed or 0, will + generate and return a new namespace ID from + |nvim_create_namesapce()| + + Return: ~ + number Namespace ID associated with {fn} + + Note: + {fn} will be automatically removed if an error occurs + while calling. This is to prevent the annoying situation + of every keystroke erroring while trying to remove a + broken callback. + schedule_wrap({cb}) *vim.schedule_wrap()* Defers callback `cb` until the Nvim API is safe to call. diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index ee42edf154..77cbf7d9b7 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -360,6 +360,22 @@ Overview of which map command works in which mode. More details below. :cmap :cnoremap :cunmap Command-line :tmap :tnoremap :tunmap Terminal +Same information in a table: + *map-table* + Mode | Norm | Ins | Cmd | Vis | Sel | Opr | Term | Lang | ~ +Command +------+-----+-----+-----+-----+-----+------+------+ ~ +[nore]map | yes | - | - | yes | yes | yes | - | - | +n[nore]map | yes | - | - | - | - | - | - | - | +[nore]map! | - | yes | yes | - | - | - | - | - | +i[nore]map | - | yes | - | - | - | - | - | - | +c[nore]map | - | - | yes | - | - | - | - | - | +v[nore]map | - | - | - | yes | yes | - | - | - | +x[nore]map | - | - | - | yes | - | - | - | - | +s[nore]map | - | - | - | - | yes | - | - | - | +o[nore]map | - | - | - | - | - | yes | - | - | +t[nore]map | - | - | - | - | - | - | yes | - | +l[nore]map | - | yes | yes | - | - | - | - | yes | + COMMANDS MODES ~ Normal Visual+Select Operator-pending ~ diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 2527a91cc8..cc9696e536 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -595,10 +595,6 @@ A jump table for the options with a short description can be found at |Q_op|. set to one of CJK locales. See Unicode Standard Annex #11 (http://www.unicode.org/reports/tr11). - Vim may set this option automatically at startup time when Vim is - compiled with the |+termresponse| feature and if t_u7 is set to the - escape sequence to request cursor position report. - *'autochdir'* *'acd'* *'noautochdir'* *'noacd'* 'autochdir' 'acd' boolean (default off) global @@ -4603,6 +4599,19 @@ A jump table for the options with a short description can be found at |Q_op|. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. + *'quickfixtextfunc'* *'qftf'* +'quickfixtextfunc' 'qftf' string (default "") + global + This option specifies a function to be used to get the text to display + in the quickfix and location list windows. This can be used to + customize the information displayed in the quickfix or location window + for each entry in the corresponding quickfix or location list. See + |quickfix-window-function| for an explanation of how to write the + function and an example. + + This option cannot be set from a |modeline| or in the |sandbox|, for + security reasons. + *'quoteescape'* *'qe'* 'quoteescape' 'qe' string (default "\") local to buffer @@ -5043,6 +5052,7 @@ A jump table for the options with a short description can be found at |Q_op|. global values for local options) options all options and mappings (also global values for local options) + skiprtp exclude 'runtimepath' and 'packpath' from the options resize size of the Vim window: 'lines' and 'columns' sesdir the directory in which the session file is located will become the current directory (useful with @@ -5923,6 +5933,18 @@ A jump table for the options with a short description can be found at |Q_op|. Note that there is no '%' before the closing '}'. The expression cannot contain a '}' character, call a function to work around that. See |stl-%{| below. + {% - This is almost same as { except the result of the expression is + re-evaluated as a statusline format string. Thus if the + return value of expr contains % items they will get expanded. + The expression can contain the } character, the end of + expression is denoted by %}. + The For example: > + func! Stl_filename() abort + return "%t" + endfunc +< `stl=%{Stl_filename()}` results in `"%t"` + `stl=%{%Stl_filename()%}` results in `"Name of current file"` + } - End of `{%` expression ( - Start of item group. Can be used for setting the width and alignment of a section. Must be followed by %) somewhere. ) - End of item group. No width fields allowed. @@ -6417,7 +6439,7 @@ A jump table for the options with a short description can be found at |Q_op|. a single <Esc> is assumed. Many TUI cursor key codes start with <Esc>. On very slow systems this may fail, causing cursor keys not to work - sometimes. If you discover this problem you can ":set ttimeout=9999". + sometimes. If you discover this problem you can ":set ttimeoutlen=9999". Nvim will wait for the next character to arrive after an <Esc>. *'timeoutlen'* *'tm'* diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index 5adafd7877..4b61cd4c25 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -3181,8 +3181,8 @@ window, then the one window will be horizontally split (by default). If there's more than one window, the previous window will be re-used on the selected file/directory. If the previous window's associated buffer has been modified, and there's only one window with that buffer, then -the user will be asked if s/he wishes to save the buffer first (yes, -no, or cancel). +the user will be asked if they wish to save the buffer first (yes, no, or +cancel). Related Actions |netrw-cr| |netrw-o| |netrw-t| |netrw-v| Associated setting variables: diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index db6b759af6..a937cfee98 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -1014,13 +1014,6 @@ commands can be combined to create a NewGrep command: > updated. With the [!] any changes in the current buffer are abandoned. - 'f' When the 'f' flag is specified, fuzzy string - matching is used to find matching lines. In this - case, {pattern} is treated as a literal string - instead of a regular expression. See - |matchfuzzy()| for more info about fuzzy - matching. - |QuickFixCmdPre| and |QuickFixCmdPost| are triggered. A file that is opened for matching may use a buffer number, but it is reused if possible to avoid @@ -1889,5 +1882,67 @@ be used. See the description further above how to make such a filter known by Vim. +============================================================================= +10. Customizing the quickfix window *quickfix-window-function* + +The default format for the lines displayed in the quickfix window and location +list window is: + + <filename>|<lnum> col <col>|<text> + +The values displayed in each line correspond to the "bufnr", "lnum", "col" and +"text" fields returned by the |getqflist()| function. + +For some quickfix/location lists, the displayed text need to be customized. +For example, if only the filename is present for a quickfix entry, then the +two "|" field separator characters after the filename are not needed. Another +use case is to customize the path displayed for a filename. By default, the +complete path (which may be too long) is displayed for files which are not +under the current directory tree. The file path may need to be simplified to a +common parent directory. + +The displayed text can be customized by setting the 'quickfixtextfunc' option +to a Vim function. This function will be called with a dict argument and +should return a List of strings to be displayed in the quickfix or location +list window. The dict argument will have the following fields: + + quickfix set to 1 when called for a quickfix list and 0 when called for + a location list. + winid for a location list, set to the id of the window with the + location list. For a quickfix list, set to 0. Can be used in + getloclist() to get the location list entry. + id quickfix or location list identifier + start_idx index of the first entry for which text should be returned + end_idx index of the last entry for which text should be returned + +The function should return a single line of text to display in the quickfix +window for each entry from start_idx to end_idx. The function can obtain +information about the entries using the |getqflist()| function and specifying +the quickfix list identifier "id". For a location list, getloclist() function +can be used with the 'winid' argument. + +If a quickfix or location list specific customization is needed, then the +'quickfixtextfunc' attribute of the list can be set using the |setqflist()| or +|setloclist()| function. This overrides the global 'quickfixtextfunc' option. + +The example below displays the list of old files (|v:oldfiles|) in a quickfix +window. As there is no line, column number and error text information +associated with each entry, the 'quickfixtextfunc' function returns only the +filename. +Example: > + " create a quickfix list from v:oldfiles + call setqflist([], ' ', {'lines' : v:oldfiles, 'efm' : '%f', + \ 'quickfixtextfunc' : 'QfOldFiles'}) + func QfOldFiles(info) + " get information about a range of quickfix entries + let items = getqflist({'id' : a:info.id, 'items' : 1}).items + let l = [] + for idx in range(a:info.start_idx - 1, a:info.end_idx - 1) + " use the simplified file name + call add(l, fnamemodify(bufname(items[idx].bufnr), ':p:.')) + endfor + return l + endfunc +< vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index b237d70760..dd05084652 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -50,6 +50,11 @@ Multiple repeats *multi-repeat* :[range]v[global]/{pattern}/[cmd] Same as :g!. +Example: > + :g/^Obsolete/d _ +Using the underscore after `:d` avoids clobbering registers or the clipboard. +This also makes it faster. + Instead of the '/' which surrounds the {pattern}, you can use any other single byte character, but not an alphabetic character, '\', '"' or '|'. This is useful if you want to include a '/' in the search pattern or diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 95e00720b1..b159f655fa 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1969,7 +1969,7 @@ LEX *lex.vim* *ft-lex-syntax* Lex uses brute-force synchronizing as the "^%%$" section delimiter gives no clue as to what section follows. Consequently, the value for > :syn sync minlines=300 -may be changed by the user if s/he is experiencing synchronization +may be changed by the user if they are experiencing synchronization difficulties (such as may happen with large lex files). @@ -3009,11 +3009,11 @@ variables in your vimrc: < (dash users should use posix) -If there's no "#! ..." line, and the user hasn't availed himself/herself of a -default sh.vim syntax setting as just shown, then syntax/sh.vim will assume -the Bourne shell syntax. No need to quote RFCs or market penetration -statistics in error reports, please -- just select the default version of the -sh your system uses and install the associated "let..." in your <.vimrc>. +If there's no "#! ..." line, and the user hasn't availed themself of a default +sh.vim syntax setting as just shown, then syntax/sh.vim will assume the Bourne +shell syntax. No need to quote RFCs or market penetration statistics in error +reports, please -- just select the default version of the sh your system uses +and install the associated "let..." in your <.vimrc>. The syntax/sh.vim file provides several levels of syntax-based folding: > diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 1f4b5d3097..39522898f9 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -212,6 +212,11 @@ Here is a list of built-in predicates : ((identifier) @foo (#contains? @foo "foo")) ((identifier) @foo-bar (#contains @foo-bar "foo" "bar")) < + `any-of?` *ts-predicate-any-of?* + Will check if the text is the same as any of the following + arguments : > + ((identifier) @foo (#any-of? @foo "foo" "bar")) +< *lua-treesitter-not-predicate* Each predicate has a `not-` prefixed predicate that is just the negation of the predicate. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 4cba5a33d0..f92cb3c509 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1875,7 +1875,7 @@ NOT LOADING It's possible that a user doesn't always want to load this plugin. Or the system administrator has dropped it in the system-wide plugin directory, but a -user has his own plugin he wants to use. Then the user must have a chance to +user has their own plugin they want to use. Then the user must have a chance to disable loading this specific plugin. This will make it possible: > 6 if exists("g:loaded_typecorr") @@ -1908,7 +1908,7 @@ item can be used: > The "<Plug>TypecorrAdd;" thing will do the work, more about that further on. -The user can set the "mapleader" variable to the key sequence that he wants +The user can set the "mapleader" variable to the key sequence that they want this mapping to start with. Thus if the user has done: > let mapleader = "_" @@ -1919,7 +1919,7 @@ will be used, which is a backslash. Then a map for "\a" will be defined. Note that <unique> is used, this will cause an error message if the mapping already happened to exist. |:map-<unique>| -But what if the user wants to define his own key sequence? We can allow that +But what if the user wants to define their own key sequence? We can allow that with this mechanism: > 21 if !hasmapto('<Plug>TypecorrAdd;') @@ -1928,7 +1928,7 @@ with this mechanism: > This checks if a mapping to "<Plug>TypecorrAdd;" already exists, and only defines the mapping from "<Leader>a" if it doesn't. The user then has a -chance of putting this in his vimrc file: > +chance of putting this in their vimrc file: > map ,c <Plug>TypecorrAdd; @@ -2033,7 +2033,7 @@ Now let's add a user command to add a correction: > The user command is defined only if no command with the same name already exists. Otherwise we would get an error here. Overriding the existing user command with ":command!" is not a good idea, this would probably make the user -wonder why the command he defined himself doesn't work. |:command| +wonder why the command they defined themself doesn't work. |:command| SCRIPT VARIABLES @@ -2285,7 +2285,7 @@ An example of how to define functionality in a filetype plugin: > |hasmapto()| is used to check if the user has already defined a map to <Plug>JavaImport;. If not, then the filetype plugin defines the default mapping. This starts with |<LocalLeader>|, which allows the user to select -the key(s) he wants filetype plugin mappings to start with. The default is a +the key(s) they want filetype plugin mappings to start with. The default is a backslash. "<unique>" is used to give an error message if the mapping already exists or overlaps with an existing mapping. diff --git a/runtime/doc/usr_42.txt b/runtime/doc/usr_42.txt index 99da1359c2..ff3ae7057a 100644 --- a/runtime/doc/usr_42.txt +++ b/runtime/doc/usr_42.txt @@ -209,8 +209,8 @@ argument: > :amenu <silent> Mine.Next\ File :call <SID>NextFile()<CR> Don't use "<silent>" too often. It is not needed for short commands. If you -make a menu for someone else, being able to see the executed command will give -him a hint about what he could have typed, instead of using the mouse. +make a menu for someone else, being able to see the executed command will +give them a hint about what they could have typed, instead of using the mouse. LISTING MENUS diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 2c39fdb53c..bb30495d77 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -194,8 +194,7 @@ Options: 'cpoptions' flags: |cpo-_| 'display' flags: "msgsep" minimizes scrolling when showing messages 'guicursor' works in the terminal - 'fillchars' flags: "msgsep" (see 'display'), "foldopen", "foldsep", - "foldclose" + 'fillchars' flags: "msgsep" (see 'display') 'foldcolumn' supports up to 9 dynamic/fixed columns 'inccommand' shows interactive results for |:substitute|-like commands 'pumblend' pseudo-transparent popupmenu diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 474a447284..ed70ac5ce8 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -714,7 +714,7 @@ au BufNewFile,BufRead *.haml setf haml au BufNewFile,BufRead *.hsm setf hamster " Haskell -au BufNewFile,BufRead *.hs,*.hsc,*.hs-boot setf haskell +au BufNewFile,BufRead *.hs,*.hsc,*.hs-boot,*.hsig setf haskell au BufNewFile,BufRead *.lhs setf lhaskell au BufNewFile,BufRead *.chs setf chaskell au BufNewFile,BufRead cabal.project setf cabalproject diff --git a/runtime/ftplugin/meson.vim b/runtime/ftplugin/meson.vim index e432ebf196..d48fa1dfd1 100644 --- a/runtime/ftplugin/meson.vim +++ b/runtime/ftplugin/meson.vim @@ -1,6 +1,7 @@ " Vim filetype plugin file " Language: meson " License: VIM License +" Maintainer: Liam Beguin <liambeguin@gmail.com> " Original Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com> " Last Change: 2018 Nov 27 diff --git a/runtime/ftplugin/systemverilog.vim b/runtime/ftplugin/systemverilog.vim index 4d0f565fcc..e350427022 100644 --- a/runtime/ftplugin/systemverilog.vim +++ b/runtime/ftplugin/systemverilog.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: SystemVerilog " Maintainer: kocha <kocha.lsifrontend@gmail.com> -" Last Change: 12-Aug-2013. +" Last Change: 07-May-2021 if exists("b:did_ftplugin") finish @@ -9,3 +9,36 @@ endif " Behaves just like Verilog runtime! ftplugin/verilog.vim + +let s:cpo_save = &cpo +set cpo&vim + +" Add SystemVerilog keywords for matchit plugin. +if exists("loaded_matchit") + let b:match_words = + \ '\<begin\>:\<end\>,' . + \ '\<case\>\|\<casex\>\|\<casez\>:\<endcase\>,' . + \ '\<module\>:\<endmodule\>,' . + \ '\<if\>:`\@<!\<else\>,' . + \ '\<function\>:\<endfunction\>,' . + \ '`ifn\?def\>:`elsif\>:`else\>:`endif\>,' . + \ '\<task\>:\<endtask\>,' . + \ '\<specify\>:\<endspecify\>,' . + \ '\<config\>:\<endconfig\>,' . + \ '\<generate\>:\<endgenerate\>,' . + \ '\<fork\>:\<join\>\|\<join_any\>\|\<join_none\>,' . + \ '\<primitive\>:\<endprimitive\>,' . + \ '\<table\>:\<endtable\>,' . + \ '\<checker\>:\<endchecker\>,' . + \ '\<class\>:\<endclass\>,' . + \ '\<clocking\>:\<endclocking\>,' . + \ '\<gruop\>:\<endgruop\>,' . + \ '\<interface\>:\<endinterface\>,' . + \ '\<package\>:\<endpackage\>,' . + \ '\<program\>:\<endprogram\>,' . + \ '\<property\>:\<endproperty\>,' . + \ '\<sequence\>:\<endsequence\>' +endif + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/indent/meson.vim b/runtime/indent/meson.vim index f116781f74..549209ca33 100644 --- a/runtime/indent/meson.vim +++ b/runtime/indent/meson.vim @@ -2,6 +2,7 @@ " Language: Meson " License: VIM License " Maintainer: Nirbheek Chauhan <nirbheek.chauhan@gmail.com> +" Liam Beguin <liambeguin@gmail.com> " Original Authors: David Bustos <bustos@caltech.edu> " Bram Moolenaar <Bram@vim.org> " Last Change: 2019 Oct 18 diff --git a/runtime/lua/vim/F.lua b/runtime/lua/vim/F.lua index 5887e978b9..7925ff6e44 100644 --- a/runtime/lua/vim/F.lua +++ b/runtime/lua/vim/F.lua @@ -20,5 +20,12 @@ function F.npcall(fn, ...) return F.ok_or_nil(pcall(fn, ...)) end +--- Wrap a function to return nil if it fails, otherwise the value +function F.nil_wrap(fn) + return function(...) + return F.npcall(fn, ...) + end +end + return F diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua new file mode 100644 index 0000000000..02d1154df4 --- /dev/null +++ b/runtime/lua/vim/_meta.lua @@ -0,0 +1,631 @@ +-- prevents luacheck from making lints for setting things on vim +local vim = assert(vim) + +local a = vim.api +local validate = vim.validate + +local SET_TYPES = setmetatable({ + SET = 0, + LOCAL = 1, + GLOBAL = 2, +}, { __index = error }) + +local options_info = {} +for _, v in pairs(a.nvim_get_all_options_info()) do + options_info[v.name] = v + if v.shortname ~= "" then options_info[v.shortname] = v end +end + +local is_global_option = function(info) return info.scope == "global" end +local is_buffer_option = function(info) return info.scope == "buf" end +local is_window_option = function(info) return info.scope == "win" end + +local get_scoped_options = function(scope) + local result = {} + for name, option_info in pairs(options_info) do + if option_info.scope == scope then + result[name] = true + end + end + + return result +end + +local buf_options = get_scoped_options("buf") +local glb_options = get_scoped_options("global") +local win_options = get_scoped_options("win") + +local function make_meta_accessor(get, set, del, validator) + validator = validator or function() return true end + + validate { + get = {get, 'f'}; + set = {set, 'f'}; + del = {del, 'f', true}; + validator = {validator, 'f'}; + } + + local mt = {} + function mt:__newindex(k, v) + if not validator(k) then + return + end + + if del and v == nil then + return del(k) + end + return set(k, v) + end + function mt:__index(k) + if not validator(k) then + return + end + + return get(k) + end + return setmetatable({}, mt) +end + +vim.env = make_meta_accessor(function(k) + local v = vim.fn.getenv(k) + if v == vim.NIL then + return nil + end + return v +end, vim.fn.setenv) + +do -- buffer option accessor + local function new_buf_opt_accessor(bufnr) + local function get(k) + if bufnr == nil and type(k) == "number" then + return new_buf_opt_accessor(k) + end + + return a.nvim_buf_get_option(bufnr or 0, k) + end + + local function set(k, v) + return a.nvim_buf_set_option(bufnr or 0, k, v) + end + + return make_meta_accessor(get, set, nil, function(k) + if type(k) == 'string' then + if win_options[k] then + error(string.format([['%s' is a window option, not a buffer option. See ":help %s"]], k, k)) + elseif glb_options[k] then + error(string.format([['%s' is a global option, not a buffer option. See ":help %s"]], k, k)) + end + end + + return true + end) + end + + vim.bo = new_buf_opt_accessor(nil) +end + +do -- window option accessor + local function new_win_opt_accessor(winnr) + local function get(k) + if winnr == nil and type(k) == "number" then + return new_win_opt_accessor(k) + end + return a.nvim_win_get_option(winnr or 0, k) + end + + local function set(k, v) + return a.nvim_win_set_option(winnr or 0, k, v) + end + + return make_meta_accessor(get, set, nil, function(k) + if type(k) == 'string' then + if buf_options[k] then + error(string.format([['%s' is a buffer option, not a window option. See ":help %s"]], k, k)) + elseif glb_options[k] then + error(string.format([['%s' is a global option, not a window option. See ":help %s"]], k, k)) + end + end + + return true + end) + end + + vim.wo = new_win_opt_accessor(nil) +end + +--[[ +Local window setter + +buffer options: does not get copied when split + nvim_set_option(buf_opt, value) -> sets the default for NEW buffers + this sets the hidden global default for buffer options + + nvim_buf_set_option(...) -> sets the local value for the buffer + + set opt=value, does BOTH global default AND buffer local value + setlocal opt=value, does ONLY buffer local value + +window options: gets copied + does not need to call nvim_set_option because nobody knows what the heck this does⸮ + We call it anyway for more readable code. + + + Command global value local value + :set option=value set set + :setlocal option=value - set +:setglobal option=value set - +--]] +local function set_scoped_option(k, v, set_type) + local info = options_info[k] + + -- Don't let people do setlocal with global options. + -- That is a feature that doesn't make sense. + if set_type == SET_TYPES.LOCAL and is_global_option(info) then + error(string.format("Unable to setlocal option: '%s', which is a global option.", k)) + end + + -- Only `setlocal` skips setting the default/global value + -- This will more-or-less noop for window options, but that's OK + if set_type ~= SET_TYPES.LOCAL then + a.nvim_set_option(k, v) + end + + if is_window_option(info) then + if set_type ~= SET_TYPES.GLOBAL then + a.nvim_win_set_option(0, k, v) + end + elseif is_buffer_option(info) then + if set_type == SET_TYPES.LOCAL + or (set_type == SET_TYPES.SET and not info.global_local) then + a.nvim_buf_set_option(0, k, v) + end + end +end + +--[[ +Local window getter + + Command global value local value + :set option? - display + :setlocal option? - display +:setglobal option? display - +--]] +local function get_scoped_option(k, set_type) + local info = assert(options_info[k], "Must be a valid option: " .. tostring(k)) + + if set_type == SET_TYPES.GLOBAL or is_global_option(info) then + return a.nvim_get_option(k) + end + + if is_buffer_option(info) then + local was_set, value = pcall(a.nvim_buf_get_option, 0, k) + if was_set then return value end + + if info.global_local then + return a.nvim_get_option(k) + end + + error("buf_get: This should not be able to happen, given my understanding of options // " .. k) + end + + if is_window_option(info) then + if vim.api.nvim_get_option_info(k).was_set then + local was_set, value = pcall(a.nvim_win_get_option, 0, k) + if was_set then return value end + end + + return a.nvim_get_option(k) + end + + error("This fallback case should not be possible. " .. k) +end + +-- vim global option +-- this ONLY sets the global option. like `setglobal` +vim.go = make_meta_accessor(a.nvim_get_option, a.nvim_set_option) + +-- vim `set` style options. +-- it has no additional metamethod magic. +vim.o = make_meta_accessor( + function(k) return get_scoped_option(k, SET_TYPES.SET) end, + function(k, v) return set_scoped_option(k, v, SET_TYPES.SET) end +) + +---@brief [[ +--- vim.opt, vim.opt_local and vim.opt_global implementation +--- +--- To be used as helpers for working with options within neovim. +--- For information on how to use, see :help vim.opt +--- +---@brief ]] + +--- Preserves the order and does not mutate the original list +local remove_duplicate_values = function(t) + local result, seen = {}, {} + if type(t) == "function" then error(debug.traceback("asdf")) end + for _, v in ipairs(t) do + if not seen[v] then + table.insert(result, v) + end + + seen[v] = true + end + + return result +end + +-- TODO(tjdevries): Improve option metadata so that this doesn't have to be hardcoded. +-- Can be done in a separate PR. +local key_value_options = { + fillchars = true, + listchars = true, + winhl = true, +} + +---@class OptionType +--- Option Type Enum +local OptionTypes = setmetatable({ + BOOLEAN = 0, + NUMBER = 1, + STRING = 2, + ARRAY = 3, + MAP = 4, + SET = 5, +}, { + __index = function(_, k) error("Not a valid OptionType: " .. k) end, + __newindex = function(_, k) error("Cannot set a new OptionType: " .. k) end, +}) + +--- Convert a vimoption_T style dictionary to the correct OptionType associated with it. +---@return OptionType +local get_option_type = function(name, info) + if info.type == "boolean" then + return OptionTypes.BOOLEAN + elseif info.type == "number" then + return OptionTypes.NUMBER + elseif info.type == "string" then + if not info.commalist and not info.flaglist then + return OptionTypes.STRING + end + + if key_value_options[name] then + assert(info.commalist, "Must be a comma list to use key:value style") + return OptionTypes.MAP + end + + if info.flaglist then + return OptionTypes.SET + elseif info.commalist then + return OptionTypes.ARRAY + end + + error("Fallthrough in OptionTypes") + else + error("Not a known info.type:" .. info.type) + end +end + + +--- Convert a lua value to a vimoption_T value +local convert_value_to_vim = (function() + -- Map of functions to take a Lua style value and convert to vimoption_T style value. + -- Each function takes (info, lua_value) -> vim_value + local to_vim_value = { + [OptionTypes.BOOLEAN] = function(_, value) return value end, + [OptionTypes.NUMBER] = function(_, value) return value end, + [OptionTypes.STRING] = function(_, value) return value end, + + [OptionTypes.SET] = function(_, value) + if type(value) == "string" then return value end + local result = '' + for k in pairs(value) do + result = result .. k + end + + return result + end, + + [OptionTypes.ARRAY] = function(_, value) + if type(value) == "string" then return value end + return table.concat(remove_duplicate_values(value), ",") + end, + + [OptionTypes.MAP] = function(_, value) + if type(value) == "string" then return value end + if type(value) == "function" then error(debug.traceback("asdf")) end + + local result = {} + for opt_key, opt_value in pairs(value) do + table.insert(result, string.format("%s:%s", opt_key, opt_value)) + end + + table.sort(result) + return table.concat(result, ",") + end, + } + + return function(name, info, value) + return to_vim_value[get_option_type(name, info)](info, value) + end +end)() + +--- Converts a vimoption_T style value to a Lua value +local convert_value_to_lua = (function() + -- Map of OptionType to functions that take vimoption_T values and conver to lua values. + -- Each function takes (info, vim_value) -> lua_value + local to_lua_value = { + [OptionTypes.BOOLEAN] = function(_, value) return value end, + [OptionTypes.NUMBER] = function(_, value) return value end, + [OptionTypes.STRING] = function(_, value) return value end, + + [OptionTypes.ARRAY] = function(_, value) + if type(value) == "table" then + value = remove_duplicate_values(value) + return value + end + + return vim.split(value, ",") + end, + + [OptionTypes.SET] = function(info, value) + if type(value) == "table" then return value end + + assert(info.flaglist, "That is the only one I know how to handle") + + local result = {} + for i = 1, #value do + result[value:sub(i, i)] = true + end + + return result + end, + + [OptionTypes.MAP] = function(info, raw_value) + if type(raw_value) == "table" then return raw_value end + + assert(info.commalist, "Only commas are supported currently") + + local result = {} + + local comma_split = vim.split(raw_value, ",") + for _, key_value_str in ipairs(comma_split) do + local key, value = unpack(vim.split(key_value_str, ":")) + key = vim.trim(key) + value = vim.trim(value) + + result[key] = value + end + + return result + end, + } + + return function(name, info, option_value) + return to_lua_value[get_option_type(name, info)](info, option_value) + end +end)() + +--- Handles the mutation of various different values. +local value_mutator = function(name, info, current, new, mutator) + return mutator[get_option_type(name, info)](current, new) +end + +--- Handles the '^' operator +local prepend_value = (function() + local methods = { + [OptionTypes.NUMBER] = function() + error("The '^' operator is not currently supported for") + end, + + [OptionTypes.STRING] = function(left, right) + return right .. left + end, + + [OptionTypes.ARRAY] = function(left, right) + for i = #right, 1, -1 do + table.insert(left, 1, right[i]) + end + + return left + end, + + [OptionTypes.MAP] = function(left, right) + return vim.tbl_extend("force", left, right) + end, + + [OptionTypes.SET] = function(left, right) + return vim.tbl_extend("force", left, right) + end, + } + + return function(name, info, current, new) + return value_mutator( + name, info, convert_value_to_lua(name, info, current), convert_value_to_lua(name, info, new), methods + ) + end +end)() + +--- Handles the '+' operator +local add_value = (function() + local methods = { + [OptionTypes.NUMBER] = function(left, right) + return left + right + end, + + [OptionTypes.STRING] = function(left, right) + return left .. right + end, + + [OptionTypes.ARRAY] = function(left, right) + for _, v in ipairs(right) do + table.insert(left, v) + end + + return left + end, + + [OptionTypes.MAP] = function(left, right) + return vim.tbl_extend("force", left, right) + end, + + [OptionTypes.SET] = function(left, right) + return vim.tbl_extend("force", left, right) + end, + } + + return function(name, info, current, new) + return value_mutator( + name, info, convert_value_to_lua(name, info, current), convert_value_to_lua(name, info, new), methods + ) + end +end)() + +--- Handles the '-' operator +local remove_value = (function() + local remove_one_item = function(t, val) + if vim.tbl_islist(t) then + local remove_index = nil + for i, v in ipairs(t) do + if v == val then + remove_index = i + end + end + + if remove_index then + table.remove(t, remove_index) + end + else + t[val] = nil + end + end + + local methods = { + [OptionTypes.NUMBER] = function(left, right) + return left - right + end, + + [OptionTypes.STRING] = function() + error("Subtraction not supported for strings.") + end, + + [OptionTypes.ARRAY] = function(left, right) + if type(right) == "string" then + remove_one_item(left, right) + else + for _, v in ipairs(right) do + remove_one_item(left, v) + end + end + + return left + end, + + [OptionTypes.MAP] = function(left, right) + if type(right) == "string" then + left[right] = nil + else + for _, v in ipairs(right) do + left[v] = nil + end + end + + return left + end, + + [OptionTypes.SET] = function(left, right) + if type(right) == "string" then + left[right] = nil + else + for _, v in ipairs(right) do + left[v] = nil + end + end + + return left + end, + } + + return function(name, info, current, new) + return value_mutator(name, info, convert_value_to_lua(name, info, current), new, methods) + end +end)() + +local create_option_metatable = function(set_type) + local set_mt, option_mt + + local make_option = function(name, value) + local info = assert(options_info[name], "Not a valid option name: " .. name) + + if type(value) == "table" and getmetatable(value) == option_mt then + assert(name == value._name, "must be the same value, otherwise that's weird.") + + value = value._value + end + + return setmetatable({ + _name = name, + _value = value, + _info = info, + }, option_mt) + end + + -- TODO(tjdevries): consider supporting `nil` for set to remove the local option. + -- vim.cmd [[set option<]] + + option_mt = { + -- To set a value, instead use: + -- opt[my_option] = value + _set = function(self) + local value = convert_value_to_vim(self._name, self._info, self._value) + set_scoped_option(self._name, value, set_type) + + return self + end, + + get = function(self) + return convert_value_to_lua(self._name, self._info, self._value) + end, + + append = function(self, right) + return self:__add(right):_set() + end, + + __add = function(self, right) + return make_option(self._name, add_value(self._name, self._info, self._value, right)) + end, + + prepend = function(self, right) + return self:__pow(right):_set() + end, + + __pow = function(self, right) + return make_option(self._name, prepend_value(self._name, self._info, self._value, right)) + end, + + remove = function(self, right) + return self:__sub(right):_set() + end, + + __sub = function(self, right) + return make_option(self._name, remove_value(self._name, self._info, self._value, right)) + end + } + option_mt.__index = option_mt + + set_mt = { + __index = function(_, k) + return make_option(k, get_scoped_option(k, set_type)) + end, + + __newindex = function(_, k, v) + local opt = make_option(k, v) + opt:_set() + end, + } + + return set_mt +end + +vim.opt = setmetatable({}, create_option_metatable(SET_TYPES.SET)) +vim.opt_local = setmetatable({}, create_option_metatable(SET_TYPES.LOCAL)) +vim.opt_global = setmetatable({}, create_option_metatable(SET_TYPES.GLOBAL)) diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 341a3e82fc..5dd7109bb0 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -297,26 +297,30 @@ local function pick_call_hierarchy_item(call_hierarchy_items) return choice end +local function call_hierarchy(method) + local params = util.make_position_params() + request('textDocument/prepareCallHierarchy', params, function(err, _, result) + if err then + vim.notify(err.message, vim.log.levels.WARN) + return + end + local call_hierarchy_item = pick_call_hierarchy_item(result) + vim.lsp.buf_request(0, method, { item = call_hierarchy_item }) + end) +end + --- Lists all the call sites of the symbol under the cursor in the --- |quickfix| window. If the symbol can resolve to multiple --- items, the user can pick one in the |inputlist|. function M.incoming_calls() - local params = util.make_position_params() - request('textDocument/prepareCallHierarchy', params, function(_, _, result) - local call_hierarchy_item = pick_call_hierarchy_item(result) - vim.lsp.buf_request(0, 'callHierarchy/incomingCalls', { item = call_hierarchy_item }) - end) + call_hierarchy('callHierarchy/incomingCalls') end --- Lists all the items that are called by the symbol under the --- cursor in the |quickfix| window. If the symbol can resolve to --- multiple items, the user can pick one in the |inputlist|. function M.outgoing_calls() - local params = util.make_position_params() - request('textDocument/prepareCallHierarchy', params, function(_, _, result) - local call_hierarchy_item = pick_call_hierarchy_item(result) - vim.lsp.buf_request(0, 'callHierarchy/outgoingCalls', { item = call_hierarchy_item }) - end) + call_hierarchy('callHierarchy/outgoingCalls') end --- List workspace folders. diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 6f2f846a3b..6a7dc1bbb0 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -1151,6 +1151,7 @@ function M.show_line_diagnostics(opts, bufnr, line_nr, client_id) end end + opts.focus_id = "line_diagnostics" local popup_bufnr, winnr = util.open_floating_preview(lines, 'plaintext', opts) for i, hi in ipairs(highlights) do local prefixlen, hiname = unpack(hi) diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 525ec4ce5b..6ae54ea253 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -260,24 +260,18 @@ end --- - See |vim.api.nvim_open_win()| function M.hover(_, method, result, _, _, config) config = config or {} - local bufnr, winnr = util.focusable_float(method, function() - if not (result and result.contents) then - -- return { 'No information available' } - return - end - local markdown_lines = util.convert_input_to_markdown_lines(result.contents) - markdown_lines = util.trim_empty_lines(markdown_lines) - if vim.tbl_isempty(markdown_lines) then - -- return { 'No information available' } - return - end - local bufnr, winnr = util.fancy_floating_markdown(markdown_lines, { - border = config.border - }) - util.close_preview_autocmd({"CursorMoved", "BufHidden", "InsertCharPre"}, winnr) - return bufnr, winnr - end) - return bufnr, winnr + config.focus_id = method + if not (result and result.contents) then + -- return { 'No information available' } + return + end + local markdown_lines = util.convert_input_to_markdown_lines(result.contents) + markdown_lines = util.trim_empty_lines(markdown_lines) + if vim.tbl_isempty(markdown_lines) then + -- return { 'No information available' } + return + end + return util.open_floating_preview(markdown_lines, "markdown", config) end --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover @@ -335,23 +329,21 @@ M['textDocument/implementation'] = location_handler --- - See |vim.api.nvim_open_win()| function M.signature_help(_, method, result, _, bufnr, config) config = config or {} + config.focus_id = method -- When use `autocmd CompleteDone <silent><buffer> lua vim.lsp.buf.signature_help()` to call signatureHelp handler -- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `<silent>` to ignore if not (result and result.signatures and result.signatures[1]) then print('No signature help available') return end - local lines = util.convert_signature_help_to_markdown_lines(result) + local ft = api.nvim_buf_get_option(bufnr, 'filetype') + local lines = util.convert_signature_help_to_markdown_lines(result, ft) lines = util.trim_empty_lines(lines) if vim.tbl_isempty(lines) then print('No signature help available') return end - local syntax = api.nvim_buf_get_option(bufnr, 'syntax') - local p_bufnr, _ = util.focusable_preview(method, function() - return lines, util.try_trim_markdown_code_blocks(lines), config - end) - api.nvim_buf_set_option(p_bufnr, 'syntax', syntax) + return util.open_floating_preview(lines, "markdown", config) end --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 0cabd1a0d4..98835d6708 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -523,27 +523,33 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) decoded.error = convert_NIL(decoded.error) decoded.result = convert_NIL(decoded.result) + -- We sent a number, so we expect a number. + local result_id = tonumber(decoded.id) + -- Do not surface RequestCancelled or ContentModified to users, it is RPC-internal. if decoded.error then + local mute_error = false if decoded.error.code == protocol.ErrorCodes.RequestCancelled then local _ = log.debug() and log.debug("Received cancellation ack", decoded) + mute_error = true elseif decoded.error.code == protocol.ErrorCodes.ContentModified then local _ = log.debug() and log.debug("Received content modified ack", decoded) + mute_error = true end - local result_id = tonumber(decoded.id) - -- Clear any callback since this is cancelled now. - -- This is safe to do assuming that these conditions hold: - -- - The server will not send a result callback after this cancellation. - -- - If the server sent this cancellation ACK after sending the result, the user of this RPC - -- client will ignore the result themselves. - if result_id then - message_callbacks[result_id] = nil + + if mute_error then + -- Clear any callback since this is cancelled now. + -- This is safe to do assuming that these conditions hold: + -- - The server will not send a result callback after this cancellation. + -- - If the server sent this cancellation ACK after sending the result, the user of this RPC + -- client will ignore the result themselves. + if result_id then + message_callbacks[result_id] = nil + end + return end - return end - -- We sent a number, so we expect a number. - local result_id = tonumber(decoded.id) local callback = message_callbacks[result_id] if callback then message_callbacks[result_id] = nil diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index ce8468aa8a..e16b02fa6c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -4,6 +4,7 @@ local validate = vim.validate local api = vim.api local list_extend = vim.list_extend local highlight = require 'vim.highlight' +local uv = vim.loop local npcall = vim.F.npcall local split = vim.split @@ -73,7 +74,7 @@ function M.set_lines(lines, A, B, new_lines) -- way the LSP describes the range including the last newline is by -- specifying a line number after what we would call the last line. local i_n = math.min(B[1] + 1, #lines) - if not (i_0 >= 1 and i_0 <= #lines and i_n >= 1 and i_n <= #lines) then + if not (i_0 >= 1 and i_0 <= #lines + 1 and i_n >= 1 and i_n <= #lines) then error("Invalid range: "..vim.inspect{A = A; B = B; #lines, new_lines}) end local prefix = "" @@ -566,13 +567,15 @@ end --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion local function get_completion_word(item) if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= "" then - if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then + local insert_text_format = protocol.InsertTextFormat[item.insertTextFormat] + if insert_text_format == "PlainText" or insert_text_format == nil then return item.textEdit.newText else return M.parse_snippet(item.textEdit.newText) end elseif item.insertText ~= nil and item.insertText ~= "" then - if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then + local insert_text_format = protocol.InsertTextFormat[item.insertTextFormat] + if insert_text_format == "PlainText" or insert_text_format == nil then return item.insertText else return M.parse_snippet(item.insertText) @@ -802,9 +805,10 @@ end --- Converts `textDocument/SignatureHelp` response to markdown lines. --- --@param signature_help Response of `textDocument/SignatureHelp` +--@param ft optional filetype that will be use as the `lang` for the label markdown code block --@returns list of lines of converted markdown. --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp -function M.convert_signature_help_to_markdown_lines(signature_help) +function M.convert_signature_help_to_markdown_lines(signature_help, ft) if not signature_help.signatures then return end @@ -822,7 +826,12 @@ function M.convert_signature_help_to_markdown_lines(signature_help) if not signature then return end - vim.list_extend(contents, vim.split(signature.label, '\n', true)) + local label = signature.label + if ft then + -- wrap inside a code block so fancy_markdown can render it properly + label = ("```%s\n%s\n```"):format(ft, label) + end + vim.list_extend(contents, vim.split(label, '\n', true)) if signature.documentation then M.convert_input_to_markdown_lines(signature.documentation, contents) end @@ -914,23 +923,6 @@ function M.make_floating_popup_options(width, height, opts) } end -local function _should_add_to_tagstack(new_item) - local stack = vim.fn.gettagstack() - - -- Check if we're at the bottom of the tagstack. - if stack.curidx <= 1 then return true end - - local top_item = stack.items[stack.curidx-1] - - -- Check if the item at the top of the tagstack is exactly the - -- same as the one we want to push. - if top_item.tagname ~= new_item.tagname then return true end - for i, v in ipairs(top_item.from) do - if v ~= new_item.from[i] then return true end - end - return false -end - --- Jumps to a location. --- --@param location (`Location`|`LocationLink`) @@ -939,36 +931,22 @@ function M.jump_to_location(location) -- location may be Location or LocationLink local uri = location.uri or location.targetUri if uri == nil then return end - - local from_bufnr = vim.fn.bufnr('%') - local from = {from_bufnr, vim.fn.line('.'), vim.fn.col('.'), 0} - local item = {tagname=vim.fn.expand('<cword>'), from=from} - + local bufnr = vim.uri_to_bufnr(uri) -- Save position in jumplist - vim.cmd("mark '") + vim.cmd "normal! m'" + + -- Push a new item into tagstack + local from = {vim.fn.bufnr('%'), vim.fn.line('.'), vim.fn.col('.'), 0} + local items = {{tagname=vim.fn.expand('<cword>'), from=from}} + vim.fn.settagstack(vim.fn.win_getid(), {items=items}, 't') --- Jump to new location (adjusting for UTF-16 encoding of characters) - local bufnr = vim.uri_to_bufnr(uri) api.nvim_set_current_buf(bufnr) api.nvim_buf_set_option(0, 'buflisted', true) local range = location.range or location.targetSelectionRange local row = range.start.line local col = get_line_byte_from_position(0, range.start) - -- This prevents the tagstack to be filled with items that provide - -- no motion when CTRL-T is pressed because they're both the source - -- and the destination. - local motionless = - bufnr == from_bufnr and - row+1 == from[2] and col+1 == from[3] - if not motionless and _should_add_to_tagstack(item) then - local winid = vim.fn.win_getid() - local items = {item} - vim.fn.settagstack(winid, {items=items}, 't') - end - - -- Jump to new location api.nvim_win_set_cursor(0, {row + 1, col}) - return true end @@ -980,7 +958,7 @@ end --- --@param location a single `Location` or `LocationLink` --@returns (bufnr,winnr) buffer and window number of floating window or nil -function M.preview_location(location) +function M.preview_location(location, opts) -- location may be LocationLink or Location (more useful for the former) local uri = location.targetUri or location.uri if uri == nil then return end @@ -991,7 +969,15 @@ function M.preview_location(location) local range = location.targetRange or location.range local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line+1, false) local syntax = api.nvim_buf_get_option(bufnr, 'syntax') - return M.open_floating_preview(contents, syntax) + if syntax == "" then + -- When no syntax is set, we use filetype as fallback. This might not result + -- in a valid syntax definition. See also ft detection in fancy_floating_win. + -- An empty syntax is more common now with TreeSitter, since TS disables syntax. + syntax = api.nvim_buf_get_option(bufnr, 'filetype') + end + opts = opts or {} + opts.focus_id = "location" + return M.open_floating_preview(contents, syntax, opts) end --@private @@ -1013,7 +999,9 @@ end ---buffer id, the newly created window will be the new focus associated with ---the current buffer via the tag `unique_name`. --@returns (pbufnr, pwinnr) if `fn()` has created a new window; nil otherwise +---@deprecated please use open_floating_preview directly function M.focusable_float(unique_name, fn) + vim.notify("focusable_float is deprecated. Please use open_floating_preview and pass focus_id = [unique_name] instead", vim.log.levels.WARN) -- Go back to previous window if we are in a focusable one if npcall(api.nvim_win_get_var, 0, unique_name) then return api.nvim_command("wincmd p") @@ -1042,10 +1030,10 @@ end --@param fn (function) The return values of this function will be passed ---directly to |vim.lsp.util.open_floating_preview()|, in the case that a new ---floating window should be created +---@deprecated please use open_floating_preview directly function M.focusable_preview(unique_name, fn) - return M.focusable_float(unique_name, function() - return M.open_floating_preview(fn()) - end) + vim.notify("focusable_preview is deprecated. Please use open_floating_preview and pass focus_id = [unique_name] instead", vim.log.levels.WARN) + return M.open_floating_preview(fn(), {focus_id = unique_name}) end --- Trims empty lines from input and pad top and bottom with empty lines @@ -1077,13 +1065,20 @@ end --- TODO: refactor to separate stripping/converting and make use of open_floating_preview --- +--- @deprecated please use open_floating_preview directly +function M.fancy_floating_markdown(contents, opts) + vim.notify("fancy_floating_markdown is deprecated. Please use open_floating_preview and pass focus_id = [unique_name] instead", vim.log.levels.WARN) + return M.open_floating_preview(contents, "markdown", opts) +end + --- Converts markdown into syntax highlighted regions by stripping the code --- blocks and converting them into highlighted code. --- This will by default insert a blank line separator after those code block --- regions to improve readability. ---- The result is shown in a floating preview. +--- +--- This method configures the given buffer and returns the lines to set. +--- +--- If you want to open a popup with fancy markdown, use `open_floating_preview` instead --- ---@param contents table of lines to show in window ---@param opts dictionary with optional fields @@ -1098,7 +1093,7 @@ end --- - pad_bottom number of lines to pad contents at bottom --- - separator insert separator after code block ---@returns width,height size of float -function M.fancy_floating_markdown(contents, opts) +function M.stylize_markdown(bufnr, contents, opts) validate { contents = { contents, 't' }; opts = { opts, 't', true }; @@ -1115,12 +1110,16 @@ function M.fancy_floating_markdown(contents, opts) local ft = line:match("^```([a-zA-Z0-9_]*)$") -- local ft = line:match("^```(.*)$") -- TODO(ashkan): validate the filetype here. + local is_pre = line:match("^%s*<pre>%s*$") + if is_pre then + ft = "" + end if ft then local start = #stripped i = i + 1 while i <= #contents do line = contents[i] - if line == "```" then + if line == "```" or (is_pre and line:match("^%s*</pre>%s*$")) then i = i + 1 break end @@ -1149,55 +1148,69 @@ function M.fancy_floating_markdown(contents, opts) local insert_separator = opts.separator if insert_separator == nil then insert_separator = true end if insert_separator then - for i, h in ipairs(highlights) do - h.start = h.start + i - 1 - h.finish = h.finish + i - 1 + local offset = 0 + for _, h in ipairs(highlights) do + h.start = h.start + offset + h.finish = h.finish + offset + -- check if a seperator already exists and use that one instead of creating a new one if h.finish + 1 <= #stripped then - table.insert(stripped, h.finish + 1, string.rep("─", math.min(width, opts.wrap_at or width))) - height = height + 1 + if stripped[h.finish + 1]:match("^---+$") then + stripped[h.finish + 1] = string.rep("─", math.min(width, opts.wrap_at or width)) + else + table.insert(stripped, h.finish + 1, string.rep("─", math.min(width, opts.wrap_at or width))) + offset = offset + 1 + height = height + 1 + end end end end - -- Make the floating window. - local bufnr = api.nvim_create_buf(false, true) - local winnr = api.nvim_open_win(bufnr, false, M.make_floating_popup_options(width, height, opts)) vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, stripped) - api.nvim_buf_set_option(bufnr, 'modifiable', false) - - -- Switch to the floating window to apply the syntax highlighting. - -- This is because the syntax command doesn't accept a target. - local cwin = vim.api.nvim_get_current_win() - vim.api.nvim_set_current_win(winnr) - api.nvim_win_set_option(winnr, 'conceallevel', 2) - api.nvim_win_set_option(winnr, 'concealcursor', 'n') - vim.cmd("ownsyntax lsp_markdown") local idx = 1 --@private + -- keep track of syntaxes we already inlcuded. + -- no need to include the same syntax more than once + local langs = {} local function apply_syntax_to_region(ft, start, finish) - if ft == '' then return end + if ft == "" then + vim.cmd(string.format("syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend", start, finish + 1)) + return + end local name = ft..idx idx = idx + 1 local lang = "@"..ft:upper() - -- TODO(ashkan): better validation before this. - if not pcall(vim.cmd, string.format("syntax include %s syntax/%s.vim", lang, ft)) then - return + if not langs[lang] then + -- HACK: reset current_syntax, since some syntax files like markdown won't load if it is already set + pcall(vim.api.nvim_buf_del_var, bufnr, "current_syntax") + -- TODO(ashkan): better validation before this. + if not pcall(vim.cmd, string.format("syntax include %s syntax/%s.vim", lang, ft)) then + return + end + langs[lang] = true end - vim.cmd(string.format("syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s", name, start, finish + 1, lang)) - end - -- Previous highlight region. - -- TODO(ashkan): this wasn't working for some reason, but I would like to - -- make sure that regions between code blocks are definitely markdown. - -- local ph = {start = 0; finish = 1;} - for _, h in ipairs(highlights) do - -- apply_syntax_to_region('markdown', ph.finish, h.start) - apply_syntax_to_region(h.ft, h.start, h.finish) - -- ph = h + vim.cmd(string.format("syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s keepend", name, start, finish + 1, lang)) end - vim.api.nvim_set_current_win(cwin) - return bufnr, winnr + -- needs to run in the buffer for the regions to work + api.nvim_buf_call(bufnr, function() + -- we need to apply lsp_markdown regions speperately, since otherwise + -- markdown regions can "bleed" through the other syntax regions + -- and mess up the formatting + local last = 1 + for _, h in ipairs(highlights) do + if last < h.start then + apply_syntax_to_region("lsp_markdown", last, h.start - 1) + end + apply_syntax_to_region(h.ft, h.start, h.finish) + last = h.finish + 1 + end + if last < #stripped then + apply_syntax_to_region("lsp_markdown", last, #stripped) + end + end) + + return stripped end --- Creates autocommands to close a preview window when events happen. @@ -1206,7 +1219,9 @@ end --@param winnr (number) window id of preview window --@see |autocmd-events| function M.close_preview_autocmd(events, winnr) - api.nvim_command("autocmd "..table.concat(events, ',').." <buffer> ++once lua pcall(vim.api.nvim_win_close, "..winnr..", true)") + if #events > 0 then + api.nvim_command("autocmd "..table.concat(events, ',').." <buffer> ++once lua pcall(vim.api.nvim_win_close, "..winnr..", true)") + end end --@internal @@ -1291,13 +1306,16 @@ end --@param opts dictionary with optional fields -- - height of floating window -- - width of floating window --- - wrap_at character to wrap at for computing height +-- - wrap boolean enable wrapping of long lines (defaults to true) +-- - wrap_at character to wrap at for computing height when wrap is enabled -- - max_width maximal width of floating window -- - max_height maximal height of floating window -- - pad_left number of columns to pad contents at left -- - pad_right number of columns to pad contents at right -- - pad_top number of lines to pad contents at top -- - pad_bottom number of lines to pad contents at bottom +-- - focus_id if a popup with this id is opened, then focus it +-- - close_events list of events that closes the floating window --@returns bufnr,winnr buffer and window number of the newly created floating ---preview window function M.open_floating_preview(contents, syntax, opts) @@ -1307,27 +1325,77 @@ function M.open_floating_preview(contents, syntax, opts) opts = { opts, 't', true }; } opts = opts or {} + opts.wrap = opts.wrap ~= false -- wrapping by default + opts.stylize_markdown = opts.stylize_markdown ~= false + opts.close_events = opts.close_events or {"CursorMoved", "CursorMovedI", "BufHidden", "InsertCharPre"} + + local bufnr = api.nvim_get_current_buf() + + -- check if this popup is focusable and we need to focus + if opts.focus_id then + -- Go back to previous window if we are in a focusable one + local current_winnr = api.nvim_get_current_win() + if npcall(api.nvim_win_get_var, current_winnr, opts.focus_id) then + api.nvim_command("wincmd p") + return bufnr, current_winnr + end + do + local win = find_window_by_var(opts.focus_id, bufnr) + if win and api.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then + -- focus and return the existing buf, win + api.nvim_set_current_win(win) + api.nvim_command("stopinsert") + return api.nvim_win_get_buf(win), win + end + end + end + + local floating_bufnr = api.nvim_create_buf(false, true) + local do_stylize = syntax == "markdown" and opts.stylize_markdown + -- Clean up input: trim empty lines from the end, pad contents = M._trim(contents, opts) + if do_stylize then + -- applies the syntax and sets the lines to the buffer + contents = M.stylize_markdown(floating_bufnr, contents, opts) + else + if syntax then + api.nvim_buf_set_option(floating_bufnr, 'syntax', syntax) + end + api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) + end + -- Compute size of float needed to show (wrapped) lines - opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0)) + if opts.wrap then + opts.wrap_at = opts.wrap_at or api.nvim_win_get_width(0) + else + opts.wrap_at = nil + end local width, height = M._make_floating_popup_size(contents, opts) - local floating_bufnr = api.nvim_create_buf(false, true) - if syntax then - api.nvim_buf_set_option(floating_bufnr, 'syntax', syntax) - end local float_option = M.make_floating_popup_options(width, height, opts) local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option) - if syntax == 'markdown' then + if do_stylize then api.nvim_win_set_option(floating_winnr, 'conceallevel', 2) + api.nvim_win_set_option(floating_winnr, 'concealcursor', 'n') end - api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) + -- disable folding + api.nvim_win_set_option(floating_winnr, 'foldenable', false) + -- soft wrapping + api.nvim_win_set_option(floating_winnr, 'wrap', opts.wrap) + api.nvim_buf_set_option(floating_bufnr, 'modifiable', false) api.nvim_buf_set_option(floating_bufnr, 'bufhidden', 'wipe') - M.close_preview_autocmd({"CursorMoved", "CursorMovedI", "BufHidden", "BufLeave"}, floating_winnr) + api.nvim_buf_set_keymap(floating_bufnr, "n", "q", "<cmd>bdelete<cr>", {silent = true, noremap = true}) + M.close_preview_autocmd(opts.close_events, floating_winnr) + + -- save focus_id + if opts.focus_id then + api.nvim_win_set_var(floating_winnr, opts.focus_id, bufnr) + end + return floating_bufnr, floating_winnr end @@ -1366,6 +1434,88 @@ local position_sort = sort_by_key(function(v) return {v.start.line, v.start.character} end) +-- Gets the zero-indexed line from the given uri. +-- For non-file uris, we load the buffer and get the line. +-- If a loaded buffer exists, then that is used. +-- Otherwise we get the line using libuv which is a lot faster than loading the buffer. +--@param uri string uri of the resource to get the line from +--@param row number zero-indexed line number +--@return string the line at row in filename +function M.get_line(uri, row) + return M.get_lines(uri, { row })[row] +end + +-- Gets the zero-indexed lines from the given uri. +-- For non-file uris, we load the buffer and get the lines. +-- If a loaded buffer exists, then that is used. +-- Otherwise we get the lines using libuv which is a lot faster than loading the buffer. +--@param uri string uri of the resource to get the lines from +--@param rows number[] zero-indexed line numbers +--@return table<number string> a table mapping rows to lines +function M.get_lines(uri, rows) + rows = type(rows) == "table" and rows or { rows } + + local function buf_lines(bufnr) + local lines = {} + for _, row in pairs(rows) do + lines[row] = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { "" })[1] + end + return lines + end + + -- load the buffer if this is not a file uri + -- Custom language server protocol extensions can result in servers sending URIs with custom schemes. Plugins are able to load these via `BufReadCmd` autocmds. + if uri:sub(1, 4) ~= "file" then + local bufnr = vim.uri_to_bufnr(uri) + vim.fn.bufload(bufnr) + return buf_lines(bufnr) + end + + local filename = vim.uri_to_fname(uri) + + -- use loaded buffers if available + if vim.fn.bufloaded(filename) == 1 then + local bufnr = vim.fn.bufnr(filename, false) + return buf_lines(bufnr) + end + + -- get the data from the file + local fd = uv.fs_open(filename, "r", 438) + if not fd then return "" end + local stat = uv.fs_fstat(fd) + local data = uv.fs_read(fd, stat.size, 0) + uv.fs_close(fd) + + local lines = {} -- rows we need to retrieve + local need = 0 -- keep track of how many unique rows we need + for _, row in pairs(rows) do + if not lines[row] then + need = need + 1 + end + lines[row] = true + end + + local found = 0 + local lnum = 0 + + for line in string.gmatch(data, "([^\n]*)\n?") do + if lines[lnum] == true then + lines[lnum] = line + found = found + 1 + if found == need then break end + end + lnum = lnum + 1 + end + + -- change any lines we didn't find to the empty string + for i, line in pairs(lines) do + if line == true then + lines[i] = "" + end + end + return lines +end + --- Returns the items with the byte position calculated correctly and in sorted --- order, for display in quickfix and location lists. --- @@ -1394,14 +1544,24 @@ function M.locations_to_items(locations) for _, uri in ipairs(keys) do local rows = grouped[uri] table.sort(rows, position_sort) - local bufnr = vim.uri_to_bufnr(uri) - vim.fn.bufload(bufnr) local filename = vim.uri_to_fname(uri) + + -- list of row numbers + local uri_rows = {} + for _, temp in ipairs(rows) do + local pos = temp.start + local row = pos.line + table.insert(uri_rows, row) + end + + -- get all the lines for this uri + local lines = M.get_lines(uri, uri_rows) + for _, temp in ipairs(rows) do local pos = temp.start local row = pos.line - local line = (api.nvim_buf_get_lines(bufnr, row, row + 1, false) or {""})[1] - local col = M.character_offset(bufnr, row, pos.character) + local line = lines[row] or "" + local col = pos.character table.insert(items, { filename = filename, lnum = row + 1, @@ -1459,13 +1619,13 @@ function M.symbols_to_items(symbols, bufnr) kind = kind, text = '['..kind..'] '..symbol.name, }) - elseif symbol.range then -- DocumentSymbole type + elseif symbol.selectionRange then -- DocumentSymbole type local kind = M._get_symbol_kind_name(symbol.kind) table.insert(_items, { -- bufnr = _bufnr, filename = vim.api.nvim_buf_get_name(_bufnr), - lnum = symbol.range.start.line + 1, - col = symbol.range.start.character + 1, + lnum = symbol.selectionRange.start.line + 1, + col = symbol.selectionRange.start.character + 1, kind = kind, text = '['..kind..'] '..symbol.name }) @@ -1595,6 +1755,12 @@ function M.make_given_range_params(start_pos, end_pos) if B[2] > 0 then B = {B[1], M.character_offset(0, B[1], B[2])} end + -- we need to offset the end character position otherwise we loose the last + -- character of the selection, as LSP end position is exclusive + -- see https://microsoft.github.io/language-server-protocol/specification#range + if vim.o.selection ~= 'exclusive' then + B[2] = B[2] + 1 + end return { textDocument = M.make_text_document_params(), range = { diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 9b4d28e09a..b81eb18945 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -231,7 +231,7 @@ local predicate_handlers = { local compiled_vim_regexes = setmetatable({}, { __index = function(t, pattern) - local res = vim.regex(check_magic(vim.fn.escape(pattern, '\\'))) + local res = vim.regex(check_magic(pattern)) rawset(t, pattern, res) return res end @@ -260,7 +260,25 @@ local predicate_handlers = { end return false - end + end, + + ["any-of?"] = function(match, _, source, predicate) + local node = match[predicate[2]] + local node_text = M.get_node_text(node, source) + + -- Since 'predicate' will not be used by callers of this function, use it + -- to store a string set built from the list of words to check against. + local string_set = predicate["string_set"] + if not string_set then + string_set = {} + for i=3,#predicate do + string_set[predicate[i]] = true + end + predicate["string_set"] = string_set + end + + return string_set[node_text] + end, } -- As we provide lua-match? also expose vim-match? diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index a62df1dcec..ae1274f81f 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -2,7 +2,7 @@ " " Author: Bram Moolenaar " Copyright: Vim license applies, see ":help license" -" Last Change: 2021 Jan 03 +" Last Change: 2021 May 16 " " WORK IN PROGRESS - Only the basics work " Note: On MS-Windows you need a recent version of gdb. The one included with @@ -246,7 +246,9 @@ func s:StartDebug_term(dict) let s:gdbbuf = gdb_job_info['buffer'] let s:gdbwin = win_getid(winnr()) - " Set arguments to be run + " Set arguments to be run. First wait a bit to make detecting gdb a bit + " more reliable. + sleep 200m if len(proc_args) call chansend(s:gdb_job_id, 'set args ' . join(proc_args) . "\r") endif diff --git a/runtime/syntax/dts.vim b/runtime/syntax/dts.vim index cd4bd776b7..d75b9c178a 100644 --- a/runtime/syntax/dts.vim +++ b/runtime/syntax/dts.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: dts/dtsi (device tree files) " Maintainer: Daniel Mack <vim@zonque.org> -" Last Change: 2013 Oct 20 +" Last Change: 2021 May 15 if exists("b:current_syntax") finish @@ -9,7 +9,7 @@ endif syntax region dtsComment start="/\*" end="\*/" syntax match dtsReference "&[[:alpha:][:digit:]_]\+" -syntax region dtsBinaryProperty start="\[" end="\]" +syntax region dtsBinaryProperty start="\[" end="\]" syntax match dtsStringProperty "\".*\"" syntax match dtsKeyword "/.\{-1,\}/" syntax match dtsLabel "^[[:space:]]*[[:alpha:][:digit:]_]\+:" @@ -18,6 +18,38 @@ syntax region dtsCellProperty start="<" end=">" contains=dtsReference,dtsBinar syntax region dtsCommentInner start="/\*" end="\*/" syntax match dtsCommentLine "//.*$" +" Accept %: for # (C99) +syn region cPreCondit start="^\s*\zs\(%:\|#\)\s*\(if\|ifdef\|ifndef\|elif\)\>" skip="\\$" end="$" keepend contains=cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError +syn match cPreConditMatch display "^\s*\zs\(%:\|#\)\s*\(else\|endif\)\>" +if !exists("c_no_if0") + syn cluster cCppOutInGroup contains=cCppInIf,cCppInElse,cCppInElse2,cCppOutIf,cCppOutIf2,cCppOutElse,cCppInSkip,cCppOutSkip + syn region cCppOutWrapper start="^\s*\zs\(%:\|#\)\s*if\s\+0\+\s*\($\|//\|/\*\|&\)" end=".\@=\|$" contains=cCppOutIf,cCppOutElse,@NoSpell fold + syn region cCppOutIf contained start="0\+" matchgroup=cCppOutWrapper end="^\s*\(%:\|#\)\s*endif\>" contains=cCppOutIf2,cCppOutElse + if !exists("c_no_if0_fold") + syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\(%:\|#\)\s*\(else\>\|elif\s\+\(0\+\s*\($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell fold + else + syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\(%:\|#\)\s*\(else\>\|elif\s\+\(0\+\s*\($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell + endif + syn region cCppOutElse contained matchgroup=cCppOutWrapper start="^\s*\(%:\|#\)\s*\(else\|elif\)" end="^\s*\(%:\|#\)\s*endif\>"me=s-1 contains=TOP,cPreCondit + syn region cCppInWrapper start="^\s*\zs\(%:\|#\)\s*if\s\+0*[1-9]\d*\s*\($\|//\|/\*\||\)" end=".\@=\|$" contains=cCppInIf,cCppInElse fold + syn region cCppInIf contained matchgroup=cCppInWrapper start="\d\+" end="^\s*\(%:\|#\)\s*endif\>" contains=TOP,cPreCondit + if !exists("c_no_if0_fold") + syn region cCppInElse contained start="^\s*\(%:\|#\)\s*\(else\>\|elif\s\+\(0*[1-9]\d*\s*\($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2 fold + else + syn region cCppInElse contained start="^\s*\(%:\|#\)\s*\(else\>\|elif\s\+\(0*[1-9]\d*\s*\($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2 + endif + syn region cCppInElse2 contained matchgroup=cCppInWrapper start="^\s*\(%:\|#\)\s*\(else\|elif\)\([^/]\|/[^/*]\)*" end="^\s*\(%:\|#\)\s*endif\>"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell + syn region cCppOutSkip contained start="^\s*\(%:\|#\)\s*\(if\>\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\(%:\|#\)\s*endif\>" contains=cSpaceError,cCppOutSkip + syn region cCppInSkip contained matchgroup=cCppInWrapper start="^\s*\(%:\|#\)\s*\(if\s\+\(\d\+\s*\($\|//\|/\*\||\|&\)\)\@!\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\(%:\|#\)\s*endif\>" containedin=cCppOutElse,cCppInIf,cCppInSkip contains=TOP,cPreProc +endif +syn region cIncluded display contained start=+"+ skip=+\\\\\|\\"+ end=+"+ +syn match cIncluded display contained "<[^>]*>" +syn match cInclude display "^\s*\zs\(%:\|#\)\s*include\>\s*["<]" contains=cIncluded +"syn match cLineSkip "\\$" +syn cluster cPreProcGroup contains=cPreCondit,cIncluded,cInclude,cDefine,cErrInParen,cErrInBracket,cUserLabel,cSpecial,cOctalZero,cCppOutWrapper,cCppInWrapper,@cCppOutInGroup,cFormat,cNumber,cFloat,cOctal,cOctalError,cNumbersCom,cString,cCommentSkip,cCommentString,cComment2String,@cCommentGroup,cCommentStartError,cParen,cBracket,cMulti,cBadBlock +syn region cDefine start="^\s*\zs\(%:\|#\)\s*\(define\|undef\)\>" skip="\\$" end="$" keepend contains=ALLBUT,@cPreProcGroup,@Spell +syn region cPreProc start="^\s*\zs\(%:\|#\)\s*\(pragma\>\|line\>\|warning\>\|warn\>\|error\>\)" skip="\\$" end="$" keepend contains=ALLBUT,@cPreProcGroup,@Spell + hi def link dtsCellProperty Number hi def link dtsBinaryProperty Number hi def link dtsStringProperty String @@ -26,5 +58,21 @@ hi def link dtsLabel Label hi def link dtsNode Structure hi def link dtsReference Macro hi def link dtsComment Comment -hi def link dtsCommentInner Comment +hi def link dtsCommentInner Comment hi def link dtsCommentLine Comment + +hi def link cInclude Include +hi def link cPreProc PreProc +hi def link cDefine Macro +hi def link cIncluded cString +hi def link cString String + +hi def link cCppInWrapper cCppOutWrapper +hi def link cCppOutWrapper cPreCondit +hi def link cPreConditMatch cPreCondit +hi def link cPreCondit PreCondit +hi def link cCppOutSkip cCppOutIf2 + +hi def link cCppInElse2 cCppOutIf2 +hi def link cCppOutIf2 cCppOut +hi def link cCppOut Comment diff --git a/runtime/syntax/lsp_markdown.vim b/runtime/syntax/lsp_markdown.vim index d5c1414f01..d9b50be54c 100644 --- a/runtime/syntax/lsp_markdown.vim +++ b/runtime/syntax/lsp_markdown.vim @@ -4,7 +4,9 @@ " URL: http://neovim.io " Remark: Uses markdown syntax file -runtime! syntax/markdown.vim +" always source the system included markdown instead of any other installed +" markdown.vim syntax files +execute 'source' expand('<sfile>:p:h') .. '/markdown.vim' syn cluster mkdNonListItem add=mkdEscape,mkdNbsp diff --git a/runtime/syntax/meson.vim b/runtime/syntax/meson.vim index 78de2ab89e..7e6b565f19 100644 --- a/runtime/syntax/meson.vim +++ b/runtime/syntax/meson.vim @@ -2,6 +2,7 @@ " Language: Meson " License: VIM License " Maintainer: Nirbheek Chauhan <nirbheek.chauhan@gmail.com> +" Liam Beguin <liambeguin@gmail.com> " Last Change: 2019 Oct 18 " Credits: Zvezdan Petkovic <zpetkovic@acm.org> " Neil Schemenauer <nas@meson.ca> @@ -17,11 +18,7 @@ " let meson_space_error_highlight = 1 " -" For version 5.x: Clear all syntax items. -" For version 6.x: Quit when a syntax file was already loaded. -if version < 600 - syntax clear -elseif exists("b:current_syntax") +if exists("b:current_syntax") finish endif @@ -32,8 +29,9 @@ set cpo&vim " http://mesonbuild.com/Syntax.html syn keyword mesonConditional elif else if endif -syn keyword mesonRepeat foreach endforeach -syn keyword mesonOperator and not or +syn keyword mesonRepeat foreach endforeach +syn keyword mesonOperator and not or in +syn keyword mesonStatement continue break syn match mesonComment "#.*$" contains=mesonTodo,@Spell syn keyword mesonTodo FIXME NOTE NOTES TODO XXX contained @@ -117,10 +115,12 @@ syn keyword mesonBuiltin \ subdir \ subdir_done \ subproject + \ summary \ target_machine \ test \ vcs_tag \ warning + \ range if exists("meson_space_error_highlight") " trailing whitespace @@ -130,31 +130,20 @@ if exists("meson_space_error_highlight") syn match mesonSpaceError display "\t\+ " endif -if version >= 508 || !exists("did_meson_syn_inits") - if version <= 508 - let did_meson_syn_inits = 1 - command -nargs=+ HiLink hi link <args> - else - command -nargs=+ HiLink hi def link <args> - endif - - " The default highlight links. Can be overridden later. - HiLink mesonStatement Statement - HiLink mesonConditional Conditional - HiLink mesonRepeat Repeat - HiLink mesonOperator Operator - HiLink mesonComment Comment - HiLink mesonTodo Todo - HiLink mesonString String - HiLink mesonEscape Special - HiLink mesonNumber Number - HiLink mesonBuiltin Function - HiLink mesonConstant Number - if exists("meson_space_error_highlight") - HiLink mesonSpaceError Error - endif - - delcommand HiLink +" The default highlight links. Can be overridden later. +hi def link mesonStatement Statement +hi def link mesonConditional Conditional +hi def link mesonRepeat Repeat +hi def link mesonOperator Operator +hi def link mesonComment Comment +hi def link mesonTodo Todo +hi def link mesonString String +hi def link mesonEscape Special +hi def link mesonNumber Number +hi def link mesonBuiltin Function +hi def link mesonConstant Number +if exists("meson_space_error_higlight") + hi def link mesonSpaceError Error endif let b:current_syntax = "meson" diff --git a/runtime/tutor/tutor.tutor b/runtime/tutor/tutor.tutor index b46fcc4836..c2d5268e3d 100644 --- a/runtime/tutor/tutor.tutor +++ b/runtime/tutor/tutor.tutor @@ -216,7 +216,7 @@ starting with "$". ## INTERACTIVE ELEMENTS *interactive* As visible in this very document, vim-tutor-mode includes some interactive -elements to provide feedback to the user about his progress. If the text in +elements to provide feedback to the user about their progress. If the text in these elements satisfies some set condition, a ✓ sign will appear in the gutter to the left. Otherwise, a ✗ sign is displayed. |