diff options
Diffstat (limited to 'runtime')
27 files changed, 603 insertions, 129 deletions
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index c2195fa02d..07f37d604f 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -132,6 +132,12 @@ function! provider#clipboard#Executable() abort let s:copy['*'] = s:copy['+'] let s:paste['*'] = s:paste['+'] return 'win32yank' + elseif executable('termux-clipboard-set') + let s:copy['+'] = ['termux-clipboard-set'] + let s:paste['+'] = ['termux-clipboard-get'] + let s:copy['*'] = s:copy['+'] + let s:paste['*'] = s:paste['+'] + return 'termux-clipboard' elseif !empty($TMUX) && executable('tmux') let s:copy['+'] = ['tmux', 'load-buffer', '-'] let s:paste['+'] = ['tmux', 'save-buffer', '-'] diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index bd34411065..1e287281cf 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -538,6 +538,21 @@ nvim__screenshot({path}) *nvim__screenshot()* Attributes: ~ {fast} +nvim__set_hl_ns({ns_id}) *nvim__set_hl_ns()* + Set active namespace for highlights. + + NB: this function can be called from async contexts, but the + semantics are not yet well-defined. To start with + |nvim_set_decoration_provider| on_win and on_line callbacks + are explicitly allowed to change the namespace during a redraw + cycle. + + Attributes: ~ + {fast} + + Parameters: ~ + {ns_id} the namespace to activate + nvim__stats() *nvim__stats()* Gets internal stats. @@ -599,6 +614,22 @@ nvim_call_function({fn}, {args}) *nvim_call_function()* Return: ~ Result of the function call +nvim_chan_send({chan}, {data}) *nvim_chan_send()* + Send data to channel `id` . For a job, it writes it to the + stdin of the process. For the stdio channel |channel-stdio|, + it writes to Nvim's stdout. For an internal terminal instance + (|nvim_open_term()|) it writes directly to terimal output. See + |channel-bytes| for more information. + + This function writes raw data, not RPC messages. If the + channel was created with `rpc=true` then the channel expects + RPC messages, use |vim.rpcnotify()| and |vim.rpcrequest()| + instead. + + Parameters: ~ + {chan} id of the channel + {data} data to write. 8-bit clean: can contain NUL bytes. + nvim_command({command}) *nvim_command()* Executes an ex-command. @@ -1160,6 +1191,27 @@ nvim_notify({msg}, {log_level}, {opts}) *nvim_notify()* {log_level} The log level {opts} Reserved for future use. +nvim_open_term({buffer}, {opts}) *nvim_open_term()* + Open a terminal instance in a buffer + + By default (and currently the only option) the terminal will + not be connected to an external process. Instead, input send + on the channel will be echoed directly by the terminal. This + is useful to disply ANSI terminal sequences returned as part + of a rpc message, or similar. + + Note: to directly initiate the terminal using the right size, + display the buffer in a configured window before calling this. + For instance, for a floating display, first create an empty + buffer using |nvim_create_buf()|, then display it using + |nvim_open_win()|, and then call this function. Then + |nvim_chan_send()| cal be called immediately to process + sequences in a virtual terminal having the intended size. + + Parameters: ~ + {buffer} the buffer to use (expected to be empty) + {opts} Optional parameters. Reserved for future use. + nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* Open a new window. @@ -1415,8 +1467,8 @@ nvim_put({lines}, {type}, {after}, {follow}) *nvim_put()* • "c" |charwise| mode • "l" |linewise| mode • "" guess by contents, see |setreg()| - {after} If true insert after cursor (like |p|), or before (like - |P|). + {after} If true insert after cursor (like |p|), or + before (like |P|). {follow} If true place cursor at end of inserted text. *nvim_replace_termcodes()* @@ -1623,21 +1675,6 @@ nvim_set_hl({ns_id}, {name}, {val}) *nvim_set_hl()* keys are also recognized: `default` : don't override existing definition, like `hi default` -nvim_set_hl_ns({ns_id}) *nvim_set_hl_ns()* - Set active namespace for highlights. - - NB: this function can be called from async contexts, but the - semantics are not yet well-defined. To start with - |nvim_set_decoration_provider| on_win and on_line callbacks - are explicitly allowed to change the namespace during a redraw - cycle. - - Attributes: ~ - {fast} - - Parameters: ~ - {ns_id} the namespace to activate - nvim_set_keymap({mode}, {lhs}, {rhs}, {opts}) *nvim_set_keymap()* Sets a global |mapping| for the given mode. @@ -1737,7 +1774,7 @@ nvim__buf_stats({buffer}) *nvim__buf_stats()* TODO: Documentation *nvim_buf_add_highlight()* -nvim_buf_add_highlight({buffer}, {src_id}, {hl_group}, {line}, {col_start}, +nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, {col_start}, {col_end}) Adds a highlight to buffer. @@ -2202,6 +2239,19 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts}) column, without shifting the underlying text. + • virt_text_hide : hide the virtual text when + the background text is selected or hidden due + to horizontal scroll 'nowrap' + • hl_mode : control how highlights are combined + with the highlights of the text. Currently + only affects virt_text highlights, but might + affect`hl_group`in later versions. + • "replace": only show the virt_text color. + This is the default + • "combine": combine with background text + color + • "blend": blend with background text color. + • ephemeral : for use with |nvim_set_decoration_provider| callbacks. The mark will only be used for the current redraw @@ -2468,6 +2518,21 @@ nvim_win_get_width({window}) *nvim_win_get_width()* Return: ~ Width as a count of columns +nvim_win_hide({window}) *nvim_win_hide()* + Closes the window and hide the buffer it contains (like + |:hide| with a |window-ID|). + + Like |:hide| the buffer becomes hidden unless another window + is editing it, or 'bufhidden' is `unload` , `delete` or `wipe` + as opposed to |:close| or |nvim_win_close|, which will close + the buffer. + + Attributes: ~ + not allowed when |textlock| is active + + Parameters: ~ + {window} Window handle, or 0 for current window + nvim_win_is_valid({window}) *nvim_win_is_valid()* Checks if a window is valid diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index f3ed086933..310d244fbc 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -445,6 +445,9 @@ SHIFTING LINES LEFT OR RIGHT *shift-left-right* *<* <{motion} Shift {motion} lines one 'shiftwidth' leftwards. + If the 'shiftwidth' option is set to zero, the amount + of indent is calculated at the first non-blank + character in the line. *<<* << Shift [count] lines one 'shiftwidth' leftwards. @@ -455,6 +458,9 @@ SHIFTING LINES LEFT OR RIGHT *shift-left-right* *>* >{motion} Shift {motion} lines one 'shiftwidth' rightwards. + If the 'shiftwidth' option is set to zero, the amount + of indent is calculated at the first non-blank + character in the line. *>>* >> Shift [count] lines one 'shiftwidth' rightwards. @@ -934,6 +940,10 @@ This replaces each 'E' character with a euro sign. Read more in |<Char->|. this (that's a good habit anyway). `:retab!` may also change a sequence of spaces by <Tab> characters, which can mess up a printf(). + A list of tab widths separated by commas may be used + in place of a single tabstop. Each value in the list + represents the width of one tabstop, except the final + value which applies to all following tabstops. *retab-example* Example for using autocommands and ":retab" to edit a file which is stored diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index be7c026f5a..78c7a8a69f 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -444,7 +444,7 @@ as a key. To avoid having to put quotes around every key the #{} form can be used. This does require the key to consist only of ASCII letters, digits, '-' and '_'. Example: > - let mydict = #{zero: 0, one_key: 1, two-key: 2, 333: 3} + :let mydict = #{zero: 0, one_key: 1, two-key: 2, 333: 3} Note that 333 here is the string "333". Empty keys are not possible with #{}. A value can be any expression. Using a Dictionary for a value creates a @@ -2047,6 +2047,7 @@ assert_inrange({lower}, {upper}, {actual} [, {msg}]) Number assert {actual} is inside the range assert_match({pat}, {text} [, {msg}]) Number assert {pat} matches {text} +assert_nobeep({cmd}) Number assert {cmd} does not cause a beep assert_notequal({exp}, {act} [, {msg}]) Number assert {exp} is not equal {act} assert_notmatch({pat}, {text} [, {msg}]) @@ -2308,7 +2309,6 @@ perleval({expr}) any evaluate |perl| expression pow({x}, {y}) Float {x} to the power of {y} prevnonblank({lnum}) Number line nr of non-blank line <= {lnum} printf({fmt}, {expr1}...) String format text -prompt_addtext({buf}, {expr}) none add text to a prompt buffer prompt_setcallback({buf}, {expr}) none set prompt callback function prompt_setinterrupt({buf}, {text}) none set prompt interrupt function prompt_setprompt({buf}, {text}) none set prompt text @@ -2393,7 +2393,7 @@ sha256({string}) String SHA256 checksum of {string} shellescape({string} [, {special}]) String escape {string} for use as shell command argument -shiftwidth() Number effective value of 'shiftwidth' +shiftwidth([{col}]) Number effective value of 'shiftwidth' sign_define({name} [, {dict}]) Number define or update a sign sign_getdefined([{name}]) List get a list of defined signs sign_getplaced([{expr} [, {dict}]]) @@ -2430,7 +2430,7 @@ strcharpart({str}, {start} [, {len}]) String {len} characters of {str} at character {start} strdisplaywidth({expr} [, {col}]) Number display length of the String {expr} -strftime({format} [, {time}]) String time in specified format +strftime({format} [, {time}]) String format time with a specified format strgetchar({str}, {index}) Number get char {index} from {str} stridx({haystack}, {needle} [, {start}]) Number index of {needle} in {haystack} @@ -2439,6 +2439,8 @@ strlen({expr}) Number length of the String {expr} strpart({str}, {start} [, {len} [, {chars}]]) String {len} bytes/chars of {str} at byte {start} +strptime({format}, {timestring}) + Number Convert {timestring} to unix timestamp strridx({haystack}, {needle} [, {start}]) Number last index of {needle} in {haystack} strtrans({expr}) String translate string to make it printable @@ -2498,6 +2500,8 @@ win_gotoid({expr}) Number go to |window-ID| {expr} win_id2tabwin({expr}) List get tab and window nr from |window-ID| win_id2win({expr}) Number get window nr from |window-ID| win_screenpos({nr}) List get screen position of window {nr} +win_splitmove({nr}, {target} [, {options}]) + none move window {nr} to split of {target} winbufnr({nr}) Number buffer number of window {nr} wincol() Number window column of the cursor winheight({nr}) Number height of window {nr} @@ -2635,7 +2639,8 @@ argv([{nr} [, {winid}]) assert_beeps({cmd}) *assert_beeps()* Run {cmd} and add an error message to |v:errors| if it does NOT produce a beep or visual bell. - Also see |assert_fails()| and |assert-return|. + Also see |assert_fails()|, |assert_nobeep()| and + |assert-return|. *assert_equal()* assert_equal({expected}, {actual}, [, {msg}]) @@ -2718,6 +2723,11 @@ assert_match({pattern}, {actual} [, {msg}]) < Will result in a string to be added to |v:errors|: test.vim line 12: Pattern '^f.*o$' does not match 'foobar' ~ +assert_nobeep({cmd}) *assert_nobeep()* + Run {cmd} and add an error message to |v:errors| if it + produces a beep or visual bell. + Also see |assert_beeps()|. + *assert_notequal()* assert_notequal({expected}, {actual} [, {msg}]) The opposite of `assert_equal()`: add an error message to @@ -4981,7 +4991,7 @@ getwininfo([{winid}]) *getwininfo()* getwinpos([{timeout}]) *getwinpos()* The result is a list with two numbers, the result of - getwinposx() and getwinposy() combined: + |getwinposx()| and |getwinposy()| combined: [x-pos, y-pos] {timeout} can be used to specify how long to wait in msec for a response from the terminal. When omitted 100 msec is used. @@ -5851,7 +5861,7 @@ list2str({list} [, {utf8}]) *list2str()* < localtime() *localtime()* Return the current time, measured as seconds since 1st Jan - 1970. See also |strftime()| and |getftime()|. + 1970. See also |strftime()|, |strptime()| and |getftime()|. log({expr}) *log()* @@ -7895,7 +7905,7 @@ shellescape({string} [, {special}]) *shellescape()* < See also |::S|. -shiftwidth() *shiftwidth()* +shiftwidth([{col}]) *shiftwidth()* Returns the effective value of 'shiftwidth'. This is the 'shiftwidth' value unless it is zero, in which case it is the 'tabstop' value. To be backwards compatible in indent @@ -7911,6 +7921,11 @@ shiftwidth() *shiftwidth()* endif < And then use s:sw() instead of &sw. + When there is one argument {col} this is used as column number + for which to return the 'shiftwidth' value. This matters for the + 'vartabstop' feature. If no {col} argument is given, column 1 + will be assumed. + sign_define({name} [, {dict}]) *sign_define()* Define a new sign named {name} or modify the attributes of an existing sign. This is similar to the |:sign-define| command. @@ -8486,7 +8501,7 @@ strftime({format} [, {time}]) *strftime()* {format} depends on your system, thus this is not portable! See the manual page of the C function strftime() for the format. The maximum length of the result is 80 characters. - See also |localtime()| and |getftime()|. + See also |localtime()|, |getftime()| and |strptime()|. The language can be changed with the |:language| command. Examples: > :echo strftime("%c") Sun Apr 27 11:49:23 1997 @@ -8576,6 +8591,31 @@ strpart({src}, {start} [, {len} [, {chars}]]) *strpart()* example, to get the character under the cursor: > strpart(getline("."), col(".") - 1, 1, v:true) < +strptime({format}, {timestring}) *strptime()* + The result is a Number, which is a unix timestamp representing + the date and time in {timestring}, which is expected to match + the format specified in {format}. + + The accepted {format} depends on your system, thus this is not + portable! See the manual page of the C function strptime() + for the format. Especially avoid "%c". The value of $TZ also + matters. + + If the {timestring} cannot be parsed with {format} zero is + returned. If you do not know the format of {timestring} you + can try different {format} values until you get a non-zero + result. + + See also |strftime()|. + Examples: > + :echo strptime("%Y %b %d %X", "1997 Apr 27 11:49:23") +< 862156163 > + :echo strftime("%c", strptime("%y%m%d %T", "970427 11:53:55")) +< Sun Apr 27 11:53:55 1997 > + :echo strftime("%c", strptime("%Y%m%d%H%M%S", "19970427115355") + 3600) +< Sun Apr 27 12:53:55 1997 + + strridx({haystack}, {needle} [, {start}]) *strridx()* The result is a Number, which gives the byte index in {haystack} of the last occurrence of the String {needle}. @@ -9370,6 +9410,25 @@ win_screenpos({nr}) *win_screenpos()* Return [0, 0] if the window cannot be found in the current tabpage. +win_splitmove({nr}, {target} [, {options}]) *win_splitmove()* + Move the window {nr} to a new split of the window {target}. + This is similar to moving to {target}, creating a new window + using |:split| but having the same contents as window {nr}, and + then closing {nr}. + + Both {nr} and {target} can be window numbers or |window-ID|s. + + Returns zero for success, non-zero for failure. + + {options} is a Dictionary with the following optional entries: + "vertical" When TRUE, the split is created vertically, + like with |:vsplit|. + "rightbelow" When TRUE, the split is made below or to the + right (if vertical). When FALSE, it is done + above or to the left (if vertical). When not + present, the values of 'splitbelow' and + 'splitright' are used. + *winbufnr()* winbufnr({nr}) The result is a Number, which is the number of the buffer associated with window {nr}. {nr} can be the window number or diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 24f9dd7a69..67a10c7efb 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -749,15 +749,6 @@ start_client({config}) *vim.lsp.start_client()* The following parameters describe fields in the {config} table. -> - - -- In init function for the client, you can do: - local custom_init = function(client) - if client.config.flags then - client.config.flags.allow_incremental_sync = true - end - end -< Parameters: ~ {root_dir} (required, string) Directory where the @@ -799,6 +790,8 @@ start_client({config}) *vim.lsp.start_client()* See `initialize` in the LSP spec. {name} (string, default=client-id) Name in log messages. + {get_language_id} function(bufnr, filetype) -> language + ID as string. Defaults to the filetype. {offset_encoding} (default="utf-16") One of "utf-8", "utf-16", or "utf-32" which is the encoding that the LSP server expects. @@ -854,8 +847,8 @@ start_client({config}) *vim.lsp.start_client()* {flags} A table with flags for the client. The current (experimental) flags are: • allow_incremental_sync (bool, default - false): Allow using on_line callbacks - for lsp + true): Allow using incremental sync + for buffer edits Return: ~ Client id. |vim.lsp.get_client_by_id()| Note: client may @@ -1469,8 +1462,8 @@ show_line_diagnostics({opts}, {bufnr}, {line_nr}, {client_id}) ============================================================================== Lua module: vim.lsp.handlers *lsp-handlers* - *vim.lsp.handlers.progress_callback()* -progress_callback({_}, {_}, {params}, {client_id}) + *vim.lsp.handlers.progress_handler()* +progress_handler({_}, {_}, {params}, {client_id}) See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand @@ -1547,6 +1540,18 @@ close_preview_autocmd({events}, {winnr}) See also: ~ |autocmd-events| + *vim.lsp.util.compute_diff()* +compute_diff({old_lines}, {new_lines}, {start_line_idx}, {end_line_idx}) + Returns the range table for the difference between old and new + lines + + Parameters: ~ + {old_lines} table list of lines + {new_lines} table list of lines + + Return: ~ + table start_line_idx and start_col_idx of range + *vim.lsp.util.convert_input_to_markdown_lines()* convert_input_to_markdown_lines({input}, {contents}) Converts any of `MarkedString` | `MarkedString[]` | @@ -1581,6 +1586,12 @@ convert_signature_help_to_markdown_lines({signature_help}) See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp +create_file({change}) *vim.lsp.util.create_file()* + TODO: Documentation + +delete_file({change}) *vim.lsp.util.delete_file()* + TODO: Documentation + *vim.lsp.util.extract_completion_items()* extract_completion_items({result}) Can be used to extract the completion items from a `textDocument/completion` request, which may return one of `CompletionItem[]` , `CompletionList` or null. @@ -1788,12 +1799,12 @@ make_workspace_params({added}, {removed}) {removed} *vim.lsp.util.open_floating_preview()* -open_floating_preview({contents}, {filetype}, {opts}) +open_floating_preview({contents}, {syntax}, {opts}) Shows contents in a floating window. Parameters: ~ {contents} table of lines to show in window - {filetype} string of filetype to set for opened buffer + {syntax} string of syntax to set for opened buffer {opts} dictionary with optional fields Return: ~ @@ -1824,6 +1835,10 @@ preview_location({location}) *vim.lsp.util.preview_location()* (bufnr,winnr) buffer and window number of floating window or nil +rename({old_fname}, {new_fname}, {opts}) *vim.lsp.util.rename()* + Parameters: ~ + {opts} (table) + set_lines({lines}, {A}, {B}, {new_lines}) *vim.lsp.util.set_lines()* Replaces text in a range with new text. diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 0bbed56662..c2fc25431c 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1055,6 +1055,18 @@ list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()* See also: ~ |vim.tbl_extend()| +list_slice({list}, {start}, {finish}) *vim.list_slice()* + Creates a copy of a table containing only elements from start + to end (inclusive) + + Parameters: ~ + {list} table table + {start} integer Start range of slice + {finish} integer End range of slice + + Return: ~ + Copy of table sliced from start to finish (inclusive) + pesc({s}) *vim.pesc()* Escapes magic chars in a Lua pattern. diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index edec4a8de7..73ac3763d0 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -224,6 +224,20 @@ text before the cursor and start omni completion when some condition is met. For abbreviations |v:char| is set to the character that was typed to trigger the abbreviation. You can use this to decide how to expand the {lhs}. You should not either insert or change the v:char. + +Also, keep in mind that the expression may be evaluated when looking for +typeahead, before the previous command has been executed. For example: > + func StoreColumn() + let g:column = col('.') + return 'x' + endfunc + nnoremap <expr> x StoreColumn() + nmap ! f!x +You will notice that g:column has the value from before executing "fx", +because "z" is evaluated before "fx" is executed. +This can be solved by inserting <Ignore> before the character that is +expression-mapped: > + nmap ! f!<Ignore>x Be very careful about side effects! The expression is evaluated while obtaining characters, you may very well make the command dysfunctional. @@ -276,7 +290,7 @@ as a special key. *<Cmd>* *:map-cmd* The <Cmd> pseudokey begins a "command mapping", which executes the command directly (without changing modes). Where you might use ":...<CR>" in the -{lhs} of a mapping, you can instead use "<Cmd>...<CR>". +{rhs} of a mapping, you can instead use "<Cmd>...<CR>". Example: > noremap x <Cmd>echo mode(1)<cr> < @@ -300,7 +314,7 @@ Note: *E5520* <Cmd> commands must terminate, that is, they must be followed by <CR> in the -{lhs} of the mapping definition. |Command-line| mode is never entered. +{rhs} of the mapping definition. |Command-line| mode is never entered. 1.3 MAPPING AND MODES *:map-modes* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index c4d5df84cf..04310ca8d4 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3716,6 +3716,10 @@ A jump table for the options with a short description can be found at |Q_op|. *lcs-space* space:c Character to show for a space. When omitted, spaces are left blank. + *lcs-lead* + lead:c Character to show for leading spaces. When omitted, + leading spaces are blank. Overrides the "space" + setting for leading spaces. *lcs-trail* trail:c Character to show for trailing spaces. When omitted, trailing spaces are blank. Overrides the "space" @@ -5621,6 +5625,9 @@ A jump table for the options with a short description can be found at |Q_op|. The 'L' flag in 'cpoptions' changes how tabs are used when 'list' is set. + The value of 'softtabstop' will be ignored if |'varsofttabstop'| is set + to anything other than an empty string. + *'spell'* *'nospell'* 'spell' boolean (default off) local to window @@ -6158,6 +6165,9 @@ A jump table for the options with a short description can be found at |Q_op|. though. Otherwise aligned comments will be wrong when 'tabstop' is changed. + The value of 'tabstop' will be ignored if |'vartabstop'| is set to + anything other than an empty string. + *'tagbsearch'* *'tbs'* *'notagbsearch'* *'notbs'* 'tagbsearch' 'tbs' boolean (default on) global @@ -6542,6 +6552,38 @@ A jump table for the options with a short description can be found at |Q_op|. written to disk (see |crash-recovery|). Also used for the |CursorHold| autocommand event. + *'varsofttabstop'* *'vsts'* +'varsofttabstop' 'vsts' string (default "") + local to buffer + A list of the number of spaces that a <Tab> counts for while editing, + such as inserting a <Tab> or using <BS>. It "feels" like variable- + width <Tab>s are being inserted, while in fact a mixture of spaces + and <Tab>s is used. Tab widths are separated with commas, with the + final value applying to all subsequent tabs. + + For example, when editing assembly language files where statements + start in the 8th column and comments in the 40th, it may be useful + to use the following: > + :set varsofttabstop=8,32,8 +< This will set soft tabstops at the 8th and 40th columns, and at every + 8th column thereafter. + + Note that the value of |'softtabstop'| will be ignored while + 'varsofttabstop' is set. + + *'vartabstop'* *'vts'* +'vartabstop' 'vts' string (default "") + local to buffer + A list of the number of spaces that a <Tab> in the file counts for, + separated by commas. Each value corresponds to one tab, with the + final value applying to all subsequent tabs. For example: > + :set vartabstop=4,20,10,8 +< This will make the first tab 4 spaces wide, the second 20 spaces, + the third 10 spaces, and all following tabs 8 spaces. + + Note that the value of |'tabstop'| will be ignored while 'vartabstop' + is set. + *'verbose'* *'vbs'* 'verbose' 'vbs' number (default 0) global diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index b45e9ed450..aeee02a1e0 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -4689,6 +4689,7 @@ in their own color. highlighting for groups added by the user! Uses the current value of 'background' to decide which default colors to use. + If there was a default link, restore it. |:hi-link| :hi[ghlight] clear {group-name} :hi[ghlight] {group-name} NONE diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index 23db809543..7d09ca86ac 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -337,11 +337,11 @@ the same as above, with a "p" prepended. A static tag is a tag that is defined for a specific file. In a C program this could be a static function. -In Vi jumping to a tag sets the current search pattern. This means that -the "n" command after jumping to a tag does not search for the same pattern -that it did before jumping to the tag. Vim does not do this as we consider it -to be a bug. You can still find the tag search pattern in the search history. -If you really want the old Vi behavior, set the 't' flag in 'cpoptions'. +In Vi jumping to a tag sets the current search pattern. This means that the +"n" command after jumping to a tag does not search for the same pattern that +it did before jumping to the tag. Vim does not do this as we consider it to +be a bug. If you really want the old Vi behavior, set the 't' flag in +'cpoptions'. *tag-binary-search* Vim uses binary searching in the tags file to find the desired tag quickly @@ -419,8 +419,7 @@ would otherwise go unnoticed. Example: > In Vi the ":tag" command sets the last search pattern when the tag is searched for. In Vim this is not done, the previous search pattern is still remembered, -unless the 't' flag is present in 'cpoptions'. The search pattern is always -put in the search history, so you can modify it if searching fails. +unless the 't' flag is present in 'cpoptions'. *tags-option* The 'tags' option is a list of file names. Each of these files is searched @@ -847,19 +846,25 @@ like |CTRL-]|. The function used for generating the taglist is specified by setting the 'tagfunc' option. The function will be called with three arguments: - a:pattern The tag identifier used during the tag search. - a:flags List of flags to control the function behavior. + a:pattern The tag identifier or pattern used during the tag search. + a:flags String containing flags to control the function behavior. a:info Dict containing the following entries: buf_ffname Full filename which can be used for priority. user_data Custom data String, if stored in the tag stack previously by tagfunc. -Currently two flags may be passed to the tag function: +Currently up to three flags may be passed to the tag function: 'c' The function was invoked by a normal command being processed (mnemonic: the tag function may use the context around the cursor to perform a better job of generating the tag list.) 'i' In Insert mode, the user was completing a tag (with - |i_CTRL-X_CTRL-]|). + |i_CTRL-X_CTRL-]| or 'completeopt' contains `t`). + 'r' The first argument to tagfunc should be interpreted as a + |pattern| (see |tag-regexp|), such as when using: > + :tag /pat +< It is also given when completing in insert mode. + If this flag is not present, the argument is usually taken + literally as the full tag name. Note that when 'tagfunc' is set, the priority of the tags described in |tag-priority| does not apply. Instead, the priority is exactly as the diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 1696d3b9ba..343f4a62c2 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -32,6 +32,12 @@ retained for the lifetime of a buffer but this is subject to change. A plugin should keep a reference to the parser object as long as it wants incremental updates. + *vim.treesitter.language_version* +To check which language version is compiled with neovim, the number is stored +within `vim.treesitter.language_version`. This number is not too helpful +unless you are wondering about compatibility between different versions of +compiled grammars. + Parser files *treesitter-parsers* Parsers are the heart of tree-sitter. They are libraries that tree-sitter will diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 63c899da0c..ccf47044e7 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -788,6 +788,7 @@ Date and Time: *date-functions* *time-functions* getftime() get last modification time of a file localtime() get current time in seconds strftime() convert time to a string + strptime() convert a date/time string to time reltime() get the current or elapsed time accurately reltimestr() convert reltime() result to a string reltimefloat() convert reltime() result to a Float @@ -957,6 +958,7 @@ Testing: *test-functions* assert_true() assert that an expression is true assert_exception() assert that a command throws an exception assert_beeps() assert that a command beeps + assert_nobeep() assert that a command does not cause a beep assert_fails() assert that a command fails Timers: *timer-functions* diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 0c1e216164..eadc1c04a0 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -70,6 +70,8 @@ the differences. - |matchit| plugin is enabled. To disable it in your config: > :let loaded_matchit = 1 +- |g:vimsyn_embed| defaults to "l" to enable Lua highlighting + ============================================================================== 3. New Features *nvim-features* diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 6a13e67ac5..52cd2dcbfd 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -164,6 +164,9 @@ au BufNewFile,BufRead *.mar setf vmasm " Atlas au BufNewFile,BufRead *.atl,*.as setf atlas +" Atom is based on XML +au BufNewFile,BufRead *.atom setf xml + " Autoit v3 au BufNewFile,BufRead *.au3 setf autoit @@ -1272,6 +1275,11 @@ au BufNewFile,BufRead .povrayrc setf povini " Povray, Pascal, PHP or assembly au BufNewFile,BufRead *.inc call dist#ft#FTinc() +" PowerShell +au BufNewFile,BufRead *.ps1,*.psd1,*.psm1,*.pssc setf ps1 +au BufNewFile,BufRead *.ps1xml setf ps1xml +au BufNewFile,BufRead *.cdxml,*.psc1 setf xml + " Printcap and Termcap au BufNewFile,BufRead *printcap \ let b:ptcap_type = "print" | setf ptcap @@ -1330,6 +1338,9 @@ au BufNewFile,BufRead *.pml setf promela au BufNewFile,BufRead *.proto setf proto au BufNewFile,BufRead *.pbtxt setf pbtxt +" Poke +au BufNewFile,BufRead *.pk setf poke + " Protocols au BufNewFile,BufRead */etc/protocols setf protocols @@ -1394,6 +1405,9 @@ else au BufNewFile,BufRead *.rmd,*.smd setf rmd endif +" RSS looks like XML +au BufNewFile,BufRead *.rss setf xml + " R reStructuredText file if has("fname_case") au BufNewFile,BufRead *.Rrst,*.rrst,*.Srst,*.srst setf rrst diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 6c5c2c5062..563ffc479e 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -38,11 +38,13 @@ lsp._request_name_to_capability = { ['textDocument/declaration'] = 'declaration'; ['textDocument/typeDefinition'] = 'type_definition'; ['textDocument/documentSymbol'] = 'document_symbol'; - ['textDocument/workspaceSymbol'] = 'workspace_symbol'; ['textDocument/prepareCallHierarchy'] = 'call_hierarchy'; ['textDocument/rename'] = 'rename'; ['textDocument/codeAction'] = 'code_action'; + ['textDocument/codeLens'] = 'code_lens'; + ['codeLens/resolve'] = 'code_lens_resolve'; ['workspace/executeCommand'] = 'execute_command'; + ['workspace/symbol'] = 'workspace_symbol'; ['textDocument/references'] = 'find_references'; ['textDocument/rangeFormatting'] = 'document_range_formatting'; ['textDocument/formatting'] = 'document_formatting'; @@ -228,6 +230,7 @@ local function validate_client_config(config) before_init = { config.before_init, "f", true }; offset_encoding = { config.offset_encoding, "s", true }; flags = { config.flags, "t", true }; + get_language_id = { config.get_language_id, "f", true }; } local cmd, cmd_args = lsp._cmd_parts(config.cmd) @@ -262,8 +265,11 @@ end --@param bufnr (Number) Number of the buffer, or 0 for current --@param client Client object local function text_document_did_open_handler(bufnr, client) - local allow_incremental_sync = if_nil(client.config.flags.allow_incremental_sync, false) - if allow_incremental_sync then + local use_incremental_sync = ( + if_nil(client.config.flags.allow_incremental_sync, true) + and client.resolved_capabilities.text_document_did_change == protocol.TextDocumentSyncKind.Incremental + ) + if use_incremental_sync then if not client._cached_buffers then client._cached_buffers = {} end @@ -275,17 +281,31 @@ local function text_document_did_open_handler(bufnr, client) if not vim.api.nvim_buf_is_loaded(bufnr) then return end + local filetype = nvim_buf_get_option(bufnr, 'filetype') + local params = { textDocument = { version = 0; uri = vim.uri_from_bufnr(bufnr); - -- TODO make sure our filetypes are compatible with languageId names. - languageId = nvim_buf_get_option(bufnr, 'filetype'); + languageId = client.config.get_language_id(bufnr, filetype); text = buf_get_full_text(bufnr); } } client.notify('textDocument/didOpen', params) util.buf_versions[bufnr] = params.textDocument.version + + -- Next chance we get, we should re-do the diagnostics + vim.schedule(function() + vim.lsp.handlers["textDocument/publishDiagnostics"]( + nil, + "textDocument/publishDiagnostics", + { + diagnostics = vim.lsp.diagnostic.get(bufnr, client.id), + uri = vim.uri_from_bufnr(bufnr), + }, + client.id + ) + end) end -- FIXME: DOC: Shouldn't need to use a dummy function @@ -407,6 +427,9 @@ end --- --@param name (string, default=client-id) Name in log messages. --- +--@param get_language_id function(bufnr, filetype) -> language ID as string. +--- Defaults to the filetype. +--- --@param offset_encoding (default="utf-16") One of "utf-8", "utf-16", --- or "utf-32" which is the encoding that the LSP server expects. Client does --- not verify this is correct. @@ -445,16 +468,7 @@ end --@param trace: "off" | "messages" | "verbose" | nil passed directly to the language --- server in the initialize request. Invalid/empty values will default to "off" --@param flags: A table with flags for the client. The current (experimental) flags are: ---- - allow_incremental_sync (bool, default false): Allow using on_line callbacks for lsp ---- ---- <pre> ---- -- In attach function for the client, you can do: ---- local custom_attach = function(client) ---- if client.config.flags then ---- client.config.flags.allow_incremental_sync = true ---- end ---- end ---- </pre> +--- - allow_incremental_sync (bool, default true): Allow using incremental sync for buffer edits --- --@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be --- fully initialized. Use `on_init` to do any actions once @@ -466,6 +480,11 @@ function lsp.start_client(config) config.flags = config.flags or {} config.settings = config.settings or {} + -- By default, get_language_id just returns the exact filetype it is passed. + -- It is possible to pass in something that will calculate a different filetype, + -- to be sent by the client. + config.get_language_id = config.get_language_id or function(_, filetype) return filetype end + local client_id = next_client_id() local handlers = config.handlers or {} @@ -831,34 +850,30 @@ do end util.buf_versions[bufnr] = changedtick - -- Lazy initialize these because clients may not even need them. - local incremental_changes = once(function(client) + + local incremental_changes = function(client) local lines = nvim_buf_get_lines(bufnr, 0, -1, true) local startline = math.min(firstline + 1, math.min(#client._cached_buffers[bufnr], #lines)) - local endline = math.min(-(#lines - new_lastline), 0) - local incremental_change = vim.lsp.util.compute_diff(client._cached_buffers[bufnr], lines, startline, endline) + local endline = math.min(-(#lines - new_lastline), -1) + local incremental_change = vim.lsp.util.compute_diff( + client._cached_buffers[bufnr], lines, startline, endline, client.offset_encoding or "utf-16") client._cached_buffers[bufnr] = lines return incremental_change - end) + end + local full_changes = once(function() return { text = buf_get_full_text(bufnr); }; end) - local uri = vim.uri_from_bufnr(bufnr) - for_each_buffer_client(bufnr, function(client, _client_id) - local allow_incremental_sync = if_nil(client.config.flags.allow_incremental_sync, false) + local uri = vim.uri_from_bufnr(bufnr) + for_each_buffer_client(bufnr, function(client) + local allow_incremental_sync = if_nil(client.config.flags.allow_incremental_sync, true) local text_document_did_change = client.resolved_capabilities.text_document_did_change local changes if text_document_did_change == protocol.TextDocumentSyncKind.None then return - --[=[ TODO(ashkan) there seem to be problems with the byte_sizes sent by - -- neovim right now so only send the full content for now. In general, we - -- can assume that servers *will* support both versions anyway, as there - -- is no way to specify the sync capability by the client. - -- See https://github.com/palantir/python-language-server/commit/cfd6675bc10d5e8dbc50fc50f90e4a37b7178821#diff-f68667852a14e9f761f6ebf07ba02fc8 for an example of pyls handling both. - --]=] elseif not allow_incremental_sync or text_document_did_change == protocol.TextDocumentSyncKind.Full then changes = full_changes(client) elseif text_document_did_change == protocol.TextDocumentSyncKind.Incremental then @@ -916,10 +931,25 @@ function lsp.buf_attach_client(bufnr, client_id) all_buffer_active_clients[bufnr] = buffer_client_ids local uri = vim.uri_from_bufnr(bufnr) - nvim_command(string.format("autocmd BufWritePost <buffer=%d> lua vim.lsp._text_document_did_save_handler(0)", bufnr)) + local buf_did_save_autocommand = [=[ + augroup lsp_c_%d_b_%d_did_save + au! + au BufWritePost <buffer=%d> lua vim.lsp._text_document_did_save_handler(0) + augroup END + ]=] + vim.api.nvim_exec(string.format(buf_did_save_autocommand, client_id, bufnr, bufnr), false) -- First time, so attach and set up stuff. vim.api.nvim_buf_attach(bufnr, false, { on_lines = text_document_did_change_handler; + on_reload = function() + local params = { textDocument = { uri = uri; } } + for_each_buffer_client(bufnr, function(client, _) + if client.resolved_capabilities.text_document_open_close then + client.notify('textDocument/didClose', params) + end + text_document_did_open_handler(bufnr, client) + end) + end; on_detach = function() local params = { textDocument = { uri = uri; } } for_each_buffer_client(bufnr, function(client, _) diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index a1f24706c0..4e82c46fef 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -270,8 +270,10 @@ local function set_diagnostic_cache(diagnostics, bufnr, client_id) diagnostic.severity = DiagnosticSeverity.Error end -- Account for servers that place diagnostics on terminating newline - local start = diagnostic.range.start - start.line = math.min(start.line, buf_line_count - 1) + if buf_line_count > 0 then + local start = diagnostic.range.start + start.line = math.min(start.line, buf_line_count - 1) + end end diagnostic_cache[bufnr][client_id] = diagnostics diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 0cf80e1443..eacbd90077 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -304,7 +304,7 @@ M['textDocument/typeDefinition'] = location_handler M['textDocument/implementation'] = location_handler --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp -M['textDocument/signatureHelp'] = function(_, method, result) +M['textDocument/signatureHelp'] = function(_, method, result, _, bufnr) -- 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 @@ -317,9 +317,11 @@ M['textDocument/signatureHelp'] = function(_, method, result) print('No signature help available') return end - util.focusable_preview(method, function() + 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) end) + api.nvim_buf_set_option(p_bufnr, 'syntax', syntax) end --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 388f65c180..7e43eb84de 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -749,6 +749,9 @@ function protocol.make_client_capabilities() }; workspaceFolders = true; applyEdit = true; + workspaceEdit = { + resourceOperations = {'rename', 'create', 'delete',}, + }; }; callHierarchy = { dynamicRegistration = false; @@ -975,6 +978,16 @@ function protocol.resolve_capabilities(server_capabilities) general_properties.rename = true end + if server_capabilities.codeLensProvider == nil then + general_properties.code_lens = false + general_properties.code_lens_resolve = false + elseif type(server_capabilities.codeLensProvider) == 'table' then + general_properties.code_lens = true + general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider or false + else + error("The server sent invalid codeLensProvider") + end + if server_capabilities.codeActionProvider == nil then general_properties.code_action = false elseif type(server_capabilities.codeActionProvider) == 'boolean' diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 4e2dd7c8e8..1aa8326514 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -315,8 +315,10 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) dispatchers = { dispatchers, 't', true }; } - if not (vim.fn.executable(cmd) == 1) then - error(string.format("The given command %q is not executable.", cmd)) + if extra_spawn_params and extra_spawn_params.cwd then + assert(is_dir(extra_spawn_params.cwd), "cwd must be a directory") + elseif not (vim.fn.executable(cmd) == 1) then + error(string.format("The given command %q is not executable.", cmd)) end if dispatchers then local user_dispatchers = dispatchers @@ -370,9 +372,6 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) } if extra_spawn_params then spawn_params.cwd = extra_spawn_params.cwd - if spawn_params.cwd then - assert(is_dir(spawn_params.cwd), "cwd must be a directory") - end spawn_params.env = env_merge(extra_spawn_params.env) end handle, pid = uv.spawn(cmd, spawn_params, onexit) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 88a5fb468f..ec1131ae1f 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -357,8 +357,11 @@ end --- Returns the range table for the difference between old and new lines --@param old_lines table list of lines --@param new_lines table list of lines +--@param start_line_idx int line to begin search for first difference +--@param end_line_idx int line to begin search for last difference +--@param offset_encoding string encoding requested by language server --@returns table start_line_idx and start_col_idx of range -function M.compute_diff(old_lines, new_lines, start_line_idx, end_line_idx) +function M.compute_diff(old_lines, new_lines, start_line_idx, end_line_idx, offset_encoding) local start_line, start_char = first_difference(old_lines, new_lines, start_line_idx) local end_line, end_char = last_difference(vim.list_slice(old_lines, start_line, #old_lines), vim.list_slice(new_lines, start_line, #new_lines), start_char, end_line_idx) @@ -373,10 +376,19 @@ function M.compute_diff(old_lines, new_lines, start_line_idx, end_line_idx) adj_end_char = #old_lines[#old_lines + end_line + 1] + end_char + 1 end + local _ + if offset_encoding == "utf-16" then + _, start_char = vim.str_utfindex(old_lines[start_line], start_char - 1) + _, end_char = vim.str_utfindex(old_lines[#old_lines + end_line + 1], adj_end_char) + else + start_char = start_char - 1 + end_char = adj_end_char + end + local result = { range = { - start = { line = start_line - 1, character = start_char - 1}, - ["end"] = { line = adj_end_line, character = adj_end_char} + start = { line = start_line - 1, character = start_char}, + ["end"] = { line = adj_end_line, character = end_char} }, text = text, rangeLength = length + 1, @@ -518,13 +530,13 @@ end --- precedence is as follows: textEdit.newText > insertText > label --@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 then + if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= "" then if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then return item.textEdit.newText else return M.parse_snippet(item.textEdit.newText) end - elseif item.insertText ~= nil then + elseif item.insertText ~= nil and item.insertText ~= "" then if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then return item.insertText else @@ -612,6 +624,62 @@ function M.text_document_completion_list_to_complete_items(result, prefix) return matches end + +--- Rename old_fname to new_fname +-- +--@param opts (table) +-- overwrite? bool +-- ignoreIfExists? bool +function M.rename(old_fname, new_fname, opts) + opts = opts or {} + local bufnr = vim.fn.bufadd(old_fname) + vim.fn.bufload(bufnr) + local target_exists = vim.loop.fs_stat(new_fname) ~= nil + if target_exists and not opts.overwrite or opts.ignoreIfExists then + vim.notify('Rename target already exists. Skipping rename.') + return + end + local ok, err = os.rename(old_fname, new_fname) + assert(ok, err) + api.nvim_buf_call(bufnr, function() + vim.cmd('saveas! ' .. vim.fn.fnameescape(new_fname)) + end) +end + + +local function create_file(change) + local opts = change.options or {} + -- from spec: Overwrite wins over `ignoreIfExists` + local fname = vim.uri_to_fname(change.uri) + if not opts.ignoreIfExists or opts.overwrite then + local file = io.open(fname, 'w') + file:close() + end + vim.fn.bufadd(fname) +end + + +local function delete_file(change) + local opts = change.options or {} + local fname = vim.uri_to_fname(change.uri) + local stat = vim.loop.fs_stat(fname) + if opts.ignoreIfNotExists and not stat then + return + end + assert(stat, "Cannot delete not existing file or folder " .. fname) + local flags + if stat and stat.type == 'directory' then + flags = opts.recursive and 'rf' or 'd' + else + flags = '' + end + local bufnr = vim.fn.bufadd(fname) + local result = tonumber(vim.fn.delete(fname, flags)) + assert(result == 0, 'Could not delete file: ' .. fname .. ', stat: ' .. vim.inspect(stat)) + api.nvim_buf_delete(bufnr, { force = true }) +end + + --- Applies a `WorkspaceEdit`. --- --@param workspace_edit (table) `WorkspaceEdit` @@ -619,8 +687,17 @@ end function M.apply_workspace_edit(workspace_edit) if workspace_edit.documentChanges then for idx, change in ipairs(workspace_edit.documentChanges) do - if change.kind then - -- TODO(ashkan) handle CreateFile/RenameFile/DeleteFile + if change.kind == "rename" then + M.rename( + vim.uri_to_fname(change.oldUri), + vim.uri_to_fname(change.newUri), + change.options + ) + elseif change.kind == 'create' then + create_file(change) + elseif change.kind == 'delete' then + delete_file(change) + elseif change.kind then error(string.format("Unsupported change: %q", vim.inspect(change))) else M.apply_text_document_edit(change, idx) @@ -846,8 +923,8 @@ function M.preview_location(location) end local range = location.targetRange or location.range local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line+1, false) - local filetype = api.nvim_buf_get_option(bufnr, 'filetype') - return M.open_floating_preview(contents, filetype) + local syntax = api.nvim_buf_get_option(bufnr, 'syntax') + return M.open_floating_preview(contents, syntax) end --@private @@ -1032,8 +1109,10 @@ function M.fancy_floating_markdown(contents, opts) -- 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 markdown") + vim.cmd("ownsyntax lsp_markdown") local idx = 1 --@private local function apply_syntax_to_region(ft, start, finish) @@ -1134,7 +1213,7 @@ end --- Shows contents in a floating window. --- --@param contents table of lines to show in window ---@param filetype string of filetype to set for opened buffer +--@param syntax string of syntax to set for opened buffer --@param opts dictionary with optional fields -- - height of floating window -- - width of floating window @@ -1147,10 +1226,10 @@ end -- - pad_bottom number of lines to pad contents at bottom --@returns bufnr,winnr buffer and window number of the newly created floating ---preview window -function M.open_floating_preview(contents, filetype, opts) +function M.open_floating_preview(contents, syntax, opts) validate { contents = { contents, 't' }; - filetype = { filetype, 's', true }; + syntax = { syntax, 's', true }; opts = { opts, 't', true }; } opts = opts or {} @@ -1163,12 +1242,12 @@ function M.open_floating_preview(contents, filetype, opts) local width, height = M._make_floating_popup_size(contents, opts) local floating_bufnr = api.nvim_create_buf(false, true) - if filetype then - api.nvim_buf_set_option(floating_bufnr, 'filetype', filetype) + 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 filetype == 'markdown' then + if syntax == 'markdown' then api.nvim_win_set_option(floating_winnr, 'conceallevel', 2) end api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 3af66b134c..cac0ab864b 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -10,13 +10,11 @@ local parsers = {} local M = vim.tbl_extend("error", query, language) +M.language_version = vim._ts_get_language_version() + setmetatable(M, { __index = function (t, k) - if k == "TSHighlighter" then - a.nvim_err_writeln("vim.TSHighlighter is deprecated, please use vim.treesitter.highlighter") - t[k] = require'vim.treesitter.highlighter' - return t[k] - elseif k == "highlighter" then + if k == "highlighter" then t[k] = require'vim.treesitter.highlighter' return t[k] end diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 6b833c6d35..fe7e1052c9 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -12,6 +12,16 @@ TSHighlighterQuery.__index = TSHighlighterQuery local ns = a.nvim_create_namespace("treesitter/highlighter") +local _default_highlights = {} +local _link_default_highlight_once = function(from, to) + if not _default_highlights[from] then + _default_highlights[from] = true + vim.cmd(string.format("highlight default link %s %s", from, to)) + end + + return from +end + -- These are conventions defined by nvim-treesitter, though it -- needs to be user extensible also. TSHighlighter.hl_map = { @@ -70,9 +80,12 @@ function TSHighlighterQuery.new(lang, query_string) self.hl_cache = setmetatable({}, { __index = function(table, capture) - local hl = self:get_hl_from_capture(capture) - rawset(table, capture, hl) + local hl, is_vim_highlight = self:_get_hl_from_capture(capture) + if not is_vim_highlight then + hl = _link_default_highlight_once(lang .. hl, hl) + end + rawset(table, capture, hl) return hl end }) @@ -90,16 +103,16 @@ function TSHighlighterQuery:query() return self._query end -function TSHighlighterQuery:get_hl_from_capture(capture) +--- Get the hl from capture. +--- Returns a tuple { highlight_name: string, is_builtin: bool } +function TSHighlighterQuery:_get_hl_from_capture(capture) local name = self._query.captures[capture] if is_highlight_name(name) then -- From "Normal.left" only keep "Normal" - return vim.split(name, '.', true)[1] + return vim.split(name, '.', true)[1], true else - -- Default to false to avoid recomputing - local hl = TSHighlighter.hl_map[name] - return hl and a.nvim_get_hl_id_by_name(hl) or 0 + return TSHighlighter.hl_map[name] or name, false end end @@ -208,6 +221,9 @@ local function on_line_impl(self, buf, line) local state = self:get_highlight_state(tstree) local highlighter_query = self:get_query(tree:lang()) + -- Some injected languages may not have highlight queries. + if not highlighter_query:query() then return end + if state.iter == nil then state.iter = highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1) end diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 8b94348994..f40e1d5294 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -8,10 +8,33 @@ Query.__index = Query local M = {} +local function dedupe_files(files) + local result = {} + local seen = {} + + for _, path in ipairs(files) do + if not seen[path] then + table.insert(result, path) + seen[path] = true + end + end + + return result +end + +local function safe_read(filename, read_quantifier) + local file, err = io.open(filename, 'r') + if not file then + error(err) + end + local content = file:read(read_quantifier) + io.close(file) + return content +end function M.get_query_files(lang, query_name, is_included) local query_path = string.format('queries/%s/%s.scm', lang, query_name) - local lang_files = a.nvim_get_runtime_file(query_path, true) + local lang_files = dedupe_files(a.nvim_get_runtime_file(query_path, true)) if #lang_files == 0 then return {} end @@ -25,7 +48,7 @@ function M.get_query_files(lang, query_name, is_included) local MODELINE_FORMAT = "^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$" for _, file in ipairs(lang_files) do - local modeline = io.open(file, 'r'):read('*l') + local modeline = safe_read(file, '*l') if modeline then local langlist = modeline:match(MODELINE_FORMAT) @@ -60,7 +83,7 @@ local function read_query_files(filenames) local contents = {} for _,filename in ipairs(filenames) do - table.insert(contents, io.open(filename, 'r'):read('*a')) + table.insert(contents, safe_read(filename, '*a')) end return table.concat(contents, '') @@ -77,6 +100,27 @@ local function new_match_metadata() return setmetatable({}, match_metatable) end +--- The explicitly set queries from |vim.treesitter.query.set_query()| +local explicit_queries = setmetatable({}, { + __index = function(t, k) + local lang_queries = {} + rawset(t, k, lang_queries) + + return lang_queries + end, +}) + +--- Sets the runtime query {query_name} for {lang} +--- +--- This allows users to override any runtime files and/or configuration +--- set by plugins. +---@param lang string: The language to use for the query +---@param query_name string: The name of the query (i.e. "highlights") +---@param text string: The query text (unparsed). +function M.set_query(lang, query_name, text) + explicit_queries[lang][query_name] = M.parse_query(lang, text) +end + --- Returns the runtime query {query_name} for {lang}. -- -- @param lang The language to use for the query @@ -84,6 +128,10 @@ end -- -- @return The corresponding query, parsed. function M.get_query(lang, query_name) + if explicit_queries[lang][query_name] then + return explicit_queries[lang][query_name] + end + local query_files = M.get_query_files(lang, query_name) local query_string = read_query_files(query_files) diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 6b3328a5d4..60dc6fdd31 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -810,6 +810,14 @@ call <SID>OptionL("ts") call append("$", "shiftwidth\tnumber of spaces used for each step of (auto)indent") call append("$", "\t(local to buffer)") call <SID>OptionL("sw") +if has("vartabs") + call append("$", "vartabstop\tlist of number of spaces a tab counts for") + call append("$", "\t(local to buffer)") + call <SID>OptionL("vts") + call append("$", "varsofttabstop\tlist of number of spaces a soft tabsstop counts for") + call append("$", "\t(local to buffer)") + call <SID>OptionL("vsts") +endif call append("$", "smarttab\ta <Tab> in an indent inserts 'shiftwidth' spaces") call <SID>BinOptionG("sta", &sta) call append("$", "softtabstop\tif non-zero, number of spaces to insert for a <Tab>") diff --git a/runtime/queries/c/highlights.scm b/runtime/queries/c/highlights.scm index 96b43cf0d0..260750a85b 100644 --- a/runtime/queries/c/highlights.scm +++ b/runtime/queries/c/highlights.scm @@ -14,6 +14,7 @@ "union" "volatile" "goto" + "register" ] @keyword [ @@ -81,6 +82,8 @@ "|=" "&=" "^=" + ">>=" + "<<=" "--" "++" ] @operator @@ -117,7 +120,6 @@ (preproc_arg) (preproc_defined) ] @function.macro -; TODO (preproc_arg) @embedded (field_identifier) @property (statement_identifier) @label @@ -129,13 +131,22 @@ (type_descriptor) ] @type -(declaration type: [(identifier) (type_identifier)] @type) -(cast_expression type: [(identifier) (type_identifier)] @type) +(declaration (type_qualifier) @type) +(cast_expression type: (type_descriptor) @type) (sizeof_expression value: (parenthesized_expression (identifier) @type)) ((identifier) @constant (#match? @constant "^[A-Z][A-Z0-9_]+$")) +;; Preproc def / undef +(preproc_def + name: (_) @constant) +(preproc_call + directive: (preproc_directive) @_u + argument: (_) @constant + (#eq? @_u "#undef")) + + (comment) @comment ;; Parameters diff --git a/runtime/syntax/lsp_markdown.vim b/runtime/syntax/lsp_markdown.vim new file mode 100644 index 0000000000..d5c1414f01 --- /dev/null +++ b/runtime/syntax/lsp_markdown.vim @@ -0,0 +1,15 @@ +" Vim syntax file +" Language: lsp_markdown +" Maintainer: Michael Lingelbach <m.j.lbach@gmail.com +" URL: http://neovim.io +" Remark: Uses markdown syntax file + +runtime! syntax/markdown.vim + +syn cluster mkdNonListItem add=mkdEscape,mkdNbsp + +syntax region mkdEscape matchgroup=mkdEscape start=/\\\ze[\\\x60*{}\[\]()#+\-,.!_>~|"$%&'\/:;<=?@^ ]/ end=/.\zs/ keepend contains=mkdEscapeCh oneline concealends +syntax match mkdEscapeCh /./ contained +syntax match mkdNbsp / / conceal cchar= + +hi def link mkdEscape special diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 1a37af1c8a..92348d57ec 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -616,7 +616,7 @@ syn region vimGlobal matchgroup=Statement start='\<v\%[global]!\=/' skip='\\.' e " g:vimsyn_embed =~# 'r' : embed ruby " g:vimsyn_embed =~# 't' : embed tcl if !exists("g:vimsyn_embed") - let g:vimsyn_embed= 0 + let g:vimsyn_embed = 'l' endif " [-- lua --] {{{3 |