diff options
Diffstat (limited to 'runtime')
37 files changed, 1242 insertions, 1051 deletions
diff --git a/runtime/autoload/lsp.vim b/runtime/autoload/lsp.vim deleted file mode 100644 index 4c8f8b396a..0000000000 --- a/runtime/autoload/lsp.vim +++ /dev/null @@ -1,45 +0,0 @@ -function! lsp#add_filetype_config(config) abort - call luaeval('vim.lsp.add_filetype_config(_A)', a:config) -endfunction - -function! lsp#set_log_level(level) abort - call luaeval('vim.lsp.set_log_level(_A)', a:level) -endfunction - -function! lsp#get_log_path() abort - return luaeval('vim.lsp.get_log_path()') -endfunction - -function! lsp#omnifunc(findstart, base) abort - return luaeval("vim.lsp.omnifunc(_A[1], _A[2])", [a:findstart, a:base]) -endfunction - -function! lsp#text_document_hover() abort - lua vim.lsp.buf_request(nil, 'textDocument/hover', vim.lsp.protocol.make_text_document_position_params()) - return '' -endfunction - -function! lsp#text_document_declaration() abort - lua vim.lsp.buf_request(nil, 'textDocument/declaration', vim.lsp.protocol.make_text_document_position_params()) - return '' -endfunction - -function! lsp#text_document_definition() abort - lua vim.lsp.buf_request(nil, 'textDocument/definition', vim.lsp.protocol.make_text_document_position_params()) - return '' -endfunction - -function! lsp#text_document_signature_help() abort - lua vim.lsp.buf_request(nil, 'textDocument/signatureHelp', vim.lsp.protocol.make_text_document_position_params()) - return '' -endfunction - -function! lsp#text_document_type_definition() abort - lua vim.lsp.buf_request(nil, 'textDocument/typeDefinition', vim.lsp.protocol.make_text_document_position_params()) - return '' -endfunction - -function! lsp#text_document_implementation() abort - lua vim.lsp.buf_request(nil, 'textDocument/implementation', vim.lsp.protocol.make_text_document_position_params()) - return '' -endfunction diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index a9256638e1..e4c0080ae9 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -1,4 +1,4 @@ -" Maintainer: Anmol Sethi <anmol@aubble.com> +" Maintainer: Anmol Sethi <hi@nhooyr.io> if exists('s:loaded_man') finish @@ -64,33 +64,20 @@ function! man#open_page(count, count1, mods, ...) abort return endtry - call s:push_tag() - let bufname = 'man://'.name.(empty(sect)?'':'('.sect.')') - + let [l:buf, l:save_tfu] = [bufnr(), &tagfunc] try - set eventignore+=BufReadCmd + set tagfunc=man#goto_tag + let l:target = l:name . '(' . l:sect . ')' if a:mods !~# 'tab' && s:find_man() - execute 'silent keepalt edit' fnameescape(bufname) + execute 'silent keepalt tag' l:target else - execute 'silent keepalt' a:mods 'split' fnameescape(bufname) + execute 'silent keepalt' a:mods 'stag' l:target endif finally - set eventignore-=BufReadCmd - endtry - - try - let page = s:get_page(path) - catch - if a:mods =~# 'tab' || !s:find_man() - " a new window was opened - close - endif - call s:error(v:exception) - return + call setbufvar(l:buf, '&tagfunc', l:save_tfu) endtry let b:man_sect = sect - call s:put_page(page) endfunction function! man#read_page(ref) abort @@ -152,7 +139,7 @@ function! s:get_page(path) abort " Disable hard-wrap by using a big $MANWIDTH (max 1000 on some systems #9065). " Soft-wrap: ftplugin/man.vim sets wrap/breakindent/…. " Hard-wrap: driven by `man`. - let manwidth = !get(g:,'man_hardwrap') ? 999 : (empty($MANWIDTH) ? winwidth(0) : $MANWIDTH) + let manwidth = !get(g:,'man_hardwrap', 1) ? 999 : (empty($MANWIDTH) ? winwidth(0) : $MANWIDTH) " Force MANPAGER=cat to ensure Vim is not recursively invoked (by man-db). " http://comments.gmane.org/gmane.editors.vim.devel/29085 " Set MAN_KEEP_FORMATTING so Debian man doesn't discard backspaces. @@ -163,6 +150,9 @@ endfunction function! s:put_page(page) abort setlocal modifiable setlocal noreadonly + setlocal noswapfile + " git-ls-files(1) is all one keyword/tag-target + setlocal iskeyword+=(,) silent keepjumps %delete _ silent put =a:page while getline(1) =~# '^\s*$' @@ -254,24 +244,6 @@ function! s:verify_exists(sect, name) abort return s:extract_sect_and_name_path(path) + [path] endfunction -let s:tag_stack = [] - -function! s:push_tag() abort - let s:tag_stack += [{ - \ 'buf': bufnr('%'), - \ 'lnum': line('.'), - \ 'col': col('.'), - \ }] -endfunction - -function! man#pop_tag() abort - if !empty(s:tag_stack) - let tag = remove(s:tag_stack, -1) - execute 'silent' tag['buf'].'buffer' - call cursor(tag['lnum'], tag['col']) - endif -endfunction - " extracts the name and sect out of 'path/name.sect' function! s:extract_sect_and_name_path(path) abort let tail = fnamemodify(a:path, ':t') @@ -356,14 +328,18 @@ function! man#complete(arg_lead, cmd_line, cursor_pos) abort return s:complete(sect, sect, name) endfunction -function! s:complete(sect, psect, name) abort +function! s:get_paths(sect, name) abort try let mandirs = join(split(s:system(['man', s:find_arg]), ':\|\n'), ',') catch call s:error(v:exception) return endtry - let pages = globpath(mandirs,'man?/'.a:name.'*.'.a:sect.'*', 0, 1) + return globpath(mandirs,'man?/'.a:name.'*.'.a:sect.'*', 0, 1) +endfunction + +function! s:complete(sect, psect, name) abort + let pages = s:get_paths(a:sect, a:name) " We remove duplicates in case the same manpage in different languages was found. return uniq(sort(map(pages, 's:format_candidate(v:val, a:psect)'), 'i')) endfunction @@ -383,6 +359,10 @@ function! s:format_candidate(path, psect) abort endfunction function! man#init_pager() abort + " https://github.com/neovim/neovim/issues/6828 + let og_modifiable = &modifiable + setlocal modifiable + if getline(1) =~# '^\s*$' silent keepjumps 1delete _ else @@ -400,6 +380,31 @@ function! man#init_pager() abort if -1 == match(bufname('%'), 'man:\/\/') " Avoid duplicate buffers, E95. execute 'silent file man://'.tolower(fnameescape(ref)) endif + + let &l:modifiable = og_modifiable +endfunction + +function! man#goto_tag(pattern, flags, info) abort + let [l:sect, l:name] = man#extract_sect_and_name_ref(a:pattern) + + let l:paths = s:get_paths(l:sect, l:name) + let l:structured = [] + + for l:path in l:paths + let l:n = s:extract_sect_and_name_path(l:path)[1] + let l:structured += [{ 'name': l:n, 'path': l:path }] + endfor + + " sort by relevance - exact matches first, then the previous order + call sort(l:structured, { a, b -> a.name ==? l:name ? -1 : b.name ==? l:name ? 1 : 0 }) + + return map(l:structured, { + \ _, entry -> { + \ 'name': entry.name, + \ 'filename': 'man://' . entry.path, + \ 'cmd': '1' + \ } + \ }) endfunction call s:init() diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 57a72e6173..d52a9a8409 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -19,6 +19,7 @@ API Usage *api-rpc* *RPC* *rpc* *msgpack-rpc* RPC is the typical way to control Nvim programmatically. Nvim implements the MessagePack-RPC protocol: + https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md https://github.com/msgpack/msgpack/blob/0b8f5ac/spec.md Many clients use the API: user interfaces (GUIs), remote plugins, scripts like @@ -478,6 +479,29 @@ created for extmark changes. ============================================================================== Global Functions *api-global* +nvim_exec({src}, {output}) *nvim_exec()* + Executes Vimscript (multiline block of Ex-commands), like + anonymous |:source|. + + Unlike |nvim_command()| this function supports heredocs, + script-scope (s:), etc. + + On execution error: fails with VimL error, does not update + v:errmsg. + + Parameters: ~ + {src} Vimscript code + {output} Capture and return all (non-error, non-shell + |:!|) output + + Return: ~ + Output (non-error, non-shell |:!|) if `output` is true, + else empty string. + + See also: ~ + |execute()| + |nvim_command()| + nvim_command({command}) *nvim_command()* Executes an ex-command. @@ -487,6 +511,9 @@ nvim_command({command}) *nvim_command()* Parameters: ~ {command} Ex-command string + See also: ~ + |nvim_exec()| + nvim_get_hl_by_name({name}, {rgb}) *nvim_get_hl_by_name()* Gets a highlight definition by name. @@ -608,19 +635,9 @@ nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special}) replace_termcodes cpoptions -nvim_command_output({command}) *nvim_command_output()* - Executes an ex-command and returns its (non-error) output. - Shell |:!| output is not captured. - - On execution error: fails with VimL error, does not update - v:errmsg. - - Parameters: ~ - {command} Ex-command string - nvim_eval({expr}) *nvim_eval()* - Evaluates a VimL expression (:help expression). Dictionaries - and Lists are recursively expanded. + Evaluates a VimL |expression|. Dictionaries and Lists are + recursively expanded. On execution error: fails with VimL error, does not update v:errmsg. @@ -631,7 +648,7 @@ nvim_eval({expr}) *nvim_eval()* Return: ~ Evaluation result or expanded object -nvim_execute_lua({code}, {args}) *nvim_execute_lua()* +nvim_exec_lua({code}, {args}) *nvim_exec_lua()* Execute Lua code. Parameters (if any) are available as `...` inside the chunk. The chunk can return a value. @@ -933,10 +950,11 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* 'number', 'relativenumber', 'cursorline', 'cursorcolumn', 'foldcolumn', 'spell' and 'list' options. 'signcolumn' is changed to - `auto` . The end-of-buffer region is hidden - by setting `eob` flag of 'fillchars' to a - space char, and clearing the |EndOfBuffer| - region in 'winhighlight'. + `auto` and 'colorcolumn' is cleared. The + end-of-buffer region is hidden by setting + `eob` flag of 'fillchars' to a space char, + and clearing the |EndOfBuffer| region in + 'winhighlight'. Return: ~ Window handle, or 0 on error @@ -1021,7 +1039,7 @@ nvim_put({lines}, {type}, {after}, {follow}) *nvim_put()* {type} Edit behavior: any |getregtype()| result, or: • "b" |blockwise-visual| mode (may include width, e.g. "b3") - • "c" |characterwise| mode + • "c" |charwise| mode • "l" |linewise| mode • "" guess by contents, see |setreg()| {after} Insert after cursor (like |p|), or before (like @@ -1796,81 +1814,93 @@ nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}) Returns position for a given extmark id Parameters: ~ - {buffer} The buffer handle - {namespace} a identifier returned previously with - nvim_create_namespace - {id} the extmark id + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {id} Extmark id Return: ~ (row, col) tuple or empty list () if extmark id was absent *nvim_buf_get_extmarks()* nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) - List extmarks in a range (inclusive) - - range ends can be specified as (row, col) tuples, as well as - extmark ids in the same namespace. In addition, 0 and -1 works - as shorthands for (0,0) and (-1,-1) respectively, so that all - marks in the buffer can be queried as: + Gets extmarks in "traversal order" from a |charwise| region + defined by buffer positions (inclusive, 0-indexed + |api-indexing|). + + Region can be given as (row,col) tuples, or valid extmark ids + (whose positions define the bounds). 0 and -1 are understood + as (0,0) and (-1,-1) respectively, thus the following are + equivalent: +> + nvim_buf_get_extmarks(0, my_ns, 0, -1, {}) + nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {}) +< - all_marks = nvim_buf_get_extmarks(0, my_ns, 0, -1, {}) + If `end` is less than `start` , traversal works backwards. + (Useful with `limit` , to get the first marks prior to a given + position.) - If end is a lower position than start, then the range will be - traversed backwards. This is mostly useful with limited - amount, to be able to get the first marks prior to a given - position. + Example: +> + local a = vim.api + local pos = a.nvim_win_get_cursor(0) + local ns = a.nvim_create_namespace('my-plugin') + -- Create new extmark at line 1, column 1. + local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {}) + -- Create new extmark at line 3, column 1. + local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {}) + -- Get extmarks only from line 3. + local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {}) + -- Get all marks in this buffer + namespace. + local all = a.nvim_buf_get_extmarks(0, ns, 0, -1, {}) + print(vim.inspect(ms)) +< Parameters: ~ - {buffer} The buffer handle - {ns_id} An id returned previously from - nvim_create_namespace - {start} One of: extmark id, (row, col) or 0, -1 for - buffer ends - {end} One of: extmark id, (row, col) or 0, -1 for - buffer ends - {opts} additional options. Supports the keys: - • amount: Maximum number of marks to return + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {start} Start of range, given as (row, col) or valid + extmark id (whose position defines the bound) + {end} End of range, given as (row, col) or valid + extmark id (whose position defines the bound) + {opts} Optional parameters. Keys: + • limit: Maximum number of marks to return Return: ~ - [[extmark_id, row, col], ...] + List of [extmark_id, row, col] tuples in "traversal + order". *nvim_buf_set_extmark()* nvim_buf_set_extmark({buffer}, {ns_id}, {id}, {line}, {col}, {opts}) - Create or update an extmark at a position - - If an invalid namespace is given, an error will be raised. + Creates or updates an extmark. - To create a new extmark, pass in id=0. The new extmark id will - be returned. To move an existing mark, pass in its id. - - It is also allowed to create a new mark by passing in a - previously unused id, but the caller must then keep track of - existing and unused ids itself. This is mainly useful over - RPC, to avoid needing to wait for the return value. + To create a new extmark, pass id=0. The extmark id will be + returned. It is also allowed to create a new mark by passing + in a previously unused id, but the caller must then keep track + of existing and unused ids itself. (Useful over RPC, to avoid + waiting for the return value.) Parameters: ~ - {buffer} The buffer handle - {ns_id} a identifier returned previously with - nvim_create_namespace - {id} The extmark's id or 0 to create a new mark. - {line} The row to set the extmark to. - {col} The column to set the extmark to. + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {id} Extmark id, or 0 to create new + {line} Line number where to place the mark + {col} Column where to place the mark {opts} Optional parameters. Currently not used. Return: ~ - the id of the extmark. + Id of the created/updated extmark nvim_buf_del_extmark({buffer}, {ns_id}, {id}) *nvim_buf_del_extmark()* - Remove an extmark + Removes an extmark. Parameters: ~ - {buffer} The buffer handle - {ns_id} a identifier returned previously with - nvim_create_namespace - {id} The extmarks's id + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {id} Extmark id Return: ~ - true on success, false if the extmark was not found. + true if the extmark was found, else false *nvim_buf_add_highlight()* nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, @@ -1915,8 +1945,8 @@ nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, *nvim_buf_clear_namespace()* nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end}) - Clears namespaced objects, highlights and virtual text, from a - line range + Clears namespaced objects (highlights, extmarks, virtual text) + from a region. Lines are 0-indexed. |api-indexing| To clear the namespace in the entire buffer, specify line_start=0 and line_end=-1. diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 34ea083f96..18dfa587e8 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -559,16 +559,14 @@ CmdlineLeave Before leaving the command-line (including *CmdwinEnter* CmdwinEnter After entering the command-line window. Useful for setting options specifically for - this special type of window. This is - triggered _instead_ of BufEnter and WinEnter. + this special type of window. <afile> is set to a single character, indicating the type of command-line. |cmdwin-char| *CmdwinLeave* CmdwinLeave Before leaving the command-line window. Useful to clean up any global setting done - with CmdwinEnter. This is triggered _instead_ - of BufLeave and WinLeave. + with CmdwinEnter. <afile> is set to a single character, indicating the type of command-line. |cmdwin-char| diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index bd3f22a371..dcebbc524c 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -90,7 +90,7 @@ start and end of the motion are not in the same line, and there are only blanks before the start and there are no non-blanks after the end of the motion, the delete becomes linewise. This means that the delete also removes the line of blanks that you might expect to remain. Use the |o_v| operator to -force the motion to be characterwise. +force the motion to be charwise. Trying to delete an empty region of text (e.g., "d0" in the first column) is an error when 'cpoptions' includes the 'E' flag. @@ -1074,7 +1074,7 @@ also use these commands to move text from one file to another, because Vim preserves all registers when changing buffers (the CTRL-^ command is a quick way to toggle between two files). - *linewise-register* *characterwise-register* + *linewise-register* *charwise-register* You can repeat the put commands with "." (except for :put) and undo them. If the command that was used to get the text into the register was |linewise|, Vim inserts the text below ("p") or above ("P") the line where the cursor is. @@ -1116,10 +1116,9 @@ this happen. However, if the width of the block is not a multiple of a <Tab> width and the text after the inserted block contains <Tab>s, that text may be misaligned. -Note that after a characterwise yank command, Vim leaves the cursor on the -first yanked character that is closest to the start of the buffer. This means -that "yl" doesn't move the cursor, but "yh" moves the cursor one character -left. +Note that after a charwise yank command, Vim leaves the cursor on the first +yanked character that is closest to the start of the buffer. This means that +"yl" doesn't move the cursor, but "yh" moves the cursor one character left. Rationale: In Vi the "y" command followed by a backwards motion would sometimes not move the cursor to the first yanked character, because redisplaying was skipped. In Vim it always moves to diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index ee1f76e4e4..b31177ce0e 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -1122,11 +1122,9 @@ edited as described in |cmdwin-char|. AUTOCOMMANDS -Two autocommand events are used: |CmdwinEnter| and |CmdwinLeave|. Since this -window is of a special type, the WinEnter, WinLeave, BufEnter and BufLeave -events are not triggered. You can use the Cmdwin events to do settings -specifically for the command-line window. Be careful not to cause side -effects! +Two autocommand events are used: |CmdwinEnter| and |CmdwinLeave|. You can use +the Cmdwin events to do settings specifically for the command-line window. +Be careful not to cause side effects! Example: > :au CmdwinEnter : let b:cpt_save = &cpt | set cpt=. :au CmdwinLeave : let &cpt = b:cpt_save diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index b76a37810c..7c6b9ad1d3 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -14,6 +14,8 @@ updated. API ~ *nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead. +*nvim_command_output()* Use |nvim_exec()| instead. +*nvim_execute_lua()* Use |nvim_exec_lua()| instead. Commands ~ *:rv* diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index ba887a83c8..09c5b7c4ad 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -236,10 +236,11 @@ with a {thing} that groups functions under a common concept). Use existing common {action} names if possible: add Append to, or insert into, a collection - get Get a thing (or group of things by query) - set Set a thing (or group of things) del Delete a thing (or group of things) + exec Execute code + get Get a thing (or group of things by query) list Get all things + set Set a thing (or group of things) Use consistent names for {thing} in all API functions. E.g. a buffer is called "buf" everywhere, not "buffer" in some places and "buf" in others. @@ -349,8 +350,8 @@ External UIs are expected to implement these common features: chords (<C-,> <C-Enter> <C-S-x> <D-x>) and patterns ("shift shift") that do not potentially conflict with Nvim defaults, plugins, etc. - Consider the "option_set" |ui-global| event as a hint for other GUI - behaviors. UI-related options ('guifont', 'ambiwidth', …) are published in - this event. + behaviors. Various UI-related options ('guifont', 'ambiwidth', …) are + published in this event. See also "mouse_on", "mouse_off". vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 84a893a205..ab65cc4560 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4727,7 +4727,7 @@ getreg([{regname} [, 1 [, {list}]]]) *getreg()* getregtype([{regname}]) *getregtype()* The result is a String, which is type of register {regname}. The value will be one of: - "v" for |characterwise| text + "v" for |charwise| text "V" for |linewise| text "<CTRL-V>{width}" for |blockwise-visual| text "" for an empty or unknown register @@ -4969,9 +4969,11 @@ has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The < *feature-list* List of supported pseudo-feature names: acl |ACL| support + bsd BSD system (not macOS, use "mac" for that). iconv Can use |iconv()| for conversion. +shellslash Can use backslashes in filenames (Windows) clipboard |clipboard| provider is available. + mac MacOS system. nvim This is Nvim. python2 Legacy Vim |python2| interface. |has-python| python3 Legacy Vim |python3| interface. |has-python| @@ -4981,6 +4983,7 @@ has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The unix Unix system. *vim_starting* True during |startup|. win32 Windows system (32 or 64 bit). + win64 Windows system (64 bit). wsl WSL (Windows Subsystem for Linux) system *has-patch* @@ -6131,7 +6134,7 @@ mode([expr]) Return a string that indicates the current mode. n Normal no Operator-pending - nov Operator-pending (forced characterwise |o_v|) + nov Operator-pending (forced charwise |o_v|) noV Operator-pending (forced linewise |o_V|) noCTRL-V Operator-pending (forced blockwise |o_CTRL-V|) niI Normal using |i_CTRL-O| in |Insert-mode| @@ -7441,7 +7444,7 @@ setreg({regname}, {value} [, {options}]) If {options} contains "a" or {regname} is upper case, then the value is appended. {options} can also contain a register type specification: - "c" or "v" |characterwise| mode + "c" or "v" |charwise| mode "l" or "V" |linewise| mode "b" or "<CTRL-V>" |blockwise-visual| mode If a number immediately follows "b" or "<CTRL-V>" then this is @@ -9708,7 +9711,7 @@ This does NOT work: > register, "@/" for the search pattern. If the result of {expr1} ends in a <CR> or <NL>, the register will be linewise, otherwise it will be set to - characterwise. + charwise. This can be used to clear the last search pattern: > :let @/ = "" < This is different from searching for an empty string, diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index c579c390c6..c649688d99 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -549,7 +549,9 @@ Variables: *b:man_default_sects* Comma-separated, ordered list of preferred sections. For example in C one usually wants section 3 or 2: > :let b:man_default_sections = '3,2' -*g:man_hardwrap* Hard-wrap to $MANWIDTH. May improve layout. +*g:man_hardwrap* Hard-wrap to $MANWIDTH or window width if $MANWIDTH is + empty. Enabled by default. Set |FALSE| to enable soft + wrapping. To use Nvim as a manpager: > export MANPAGER='nvim +Man!' @@ -558,10 +560,13 @@ Note that when running `man` from the shell and with that `MANPAGER` in your environment, `man` will pre-format the manpage using `groff`. Thus, Neovim will inevitably display the manual page as it was passed to it from stdin. One of the caveats of this is that the width will _always_ be hard-wrapped and not -soft wrapped as with `:Man`. You can set in your environment: > +soft wrapped as with `g:man_hardwrap=0`. You can set in your environment: > export MANWIDTH=999 -So `groff`'s pre-formatting output will be the same as with `:Man` i.e soft-wrapped. +So `groff`'s pre-formatting output will be the same as with `g:man_hardwrap=0` i.e soft-wrapped. + +To disable bold highlighting: > + :highlight link manBold Normal PDF *ft-pdf-plugin* diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 211b7be2cc..ed9853a8da 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -404,7 +404,7 @@ tag char note action in Normal mode ~ |t| t{char} 1 cursor till before Nth occurrence of {char} to the right |u| u 2 undo changes -|v| v start characterwise Visual mode +|v| v start charwise Visual mode |w| w 1 cursor N words forward |x| ["x]x 2 delete N characters under and after the cursor [into register x] @@ -866,7 +866,7 @@ These can be used after an operator, but before a {motion} has been entered. tag char action in Operator-pending mode ~ ----------------------------------------------------------------------- -|o_v| v force operator to work characterwise +|o_v| v force operator to work charwise |o_V| V force operator to work linewise |o_CTRL-V| CTRL-V force operator to work blockwise @@ -978,7 +978,7 @@ tag command note action in Visual mode ~ |v_r| r 2 replace highlighted area with a character |v_s| s 2 delete highlighted area and start insert |v_u| u 2 make highlighted area lowercase -|v_v| v make Visual mode characterwise or stop +|v_v| v make Visual mode charwise or stop Visual mode |v_x| x 2 delete the highlighted area |v_y| y yank the highlighted area diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index 3292489eda..c59ed43a47 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -271,7 +271,7 @@ and <> are part of what you type, the context should make this clear. operator is pending. - Ex commands can be used to move the cursor. This can be used to call a function that does some complicated motion. - The motion is always characterwise exclusive, no matter + The motion is always charwise exclusive, no matter what ":" command is used. This means it's impossible to include the last character of a line without the line break (unless 'virtualedit' is set). diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 26850b3683..c4c164ab6c 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -1,18 +1,18 @@ -*lsp.txt* The Language Server Protocol +*lsp.txt* Nvim LSP API - NVIM REFERENCE MANUAL + NVIM REFERENCE MANUAL -Neovim Language Server Protocol (LSP) API +Nvim Language Server Protocol (LSP) API *lsp* -Neovim exposes a powerful API that conforms to Microsoft's published Language -Server Protocol specification. The documentation can be found here: +Nvim is a client to the Language Server Protocol: https://microsoft.github.io/language-server-protocol/ + Type |gO| to see the table of contents. ================================================================================ - *lsp-api* +LSP API *lsp-api* Neovim exposes a API for the language server protocol. To get the real benefits of this API, a language server must be installed. @@ -24,106 +24,15 @@ After installing a language server to your machine, you must let Neovim know how to start and interact with that language server. To do so, you can either: -- Use the |vim.lsp.add_filetype_config()|, which solves the common use-case of - a single server for one or more filetypes. This can also be used from vim - via |lsp#add_filetype_config()|. +- Use https://github.com/neovim/nvim-lsp and one of the existing servers there + or set up a new one using the `nvim_lsp/skeleton` interface (and contribute + it if you find it useful). This uses |vim.lsp.start_client()| under the + hood. - Or |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()|. These are the backbone of the LSP API. These are easy to use enough for basic or more complex configurations such as in |lsp-advanced-js-example|. ================================================================================ - *lsp-filetype-config* - -These are utilities specific to filetype based configurations. - - *lsp#add_filetype_config()* - *vim.lsp.add_filetype_config()* -lsp#add_filetype_config({config}) for Vim. -vim.lsp.add_filetype_config({config}) for Lua - - These are functions which can be used to create a simple configuration which - will start a language server for a list of filetypes based on the |FileType| - event. - It will lazily start start the server, meaning that it will only start once - a matching filetype is encountered. - - The {config} options are the same as |vim.lsp.start_client()|, but - with a few additions and distinctions: - - Additional parameters:~ - `filetype` - {string} or {list} of filetypes to attach to. - `name` - A unique identifying string among all other servers configured with - |vim.lsp.add_filetype_config|. - - Differences:~ - `root_dir` - Will default to |getcwd()| instead of being required. - - NOTE: the function options in {config} like {config.on_init} are for Lua - callbacks, not Vim callbacks. -> - " Go example - call lsp#add_filetype_config({ - \ 'filetype': 'go', - \ 'name': 'gopls', - \ 'cmd': 'gopls' - \ }) - " Python example - call lsp#add_filetype_config({ - \ 'filetype': 'python', - \ 'name': 'pyls', - \ 'cmd': 'pyls' - \ }) - " Rust example - call lsp#add_filetype_config({ - \ 'filetype': 'rust', - \ 'name': 'rls', - \ 'cmd': 'rls', - \ 'capabilities': { - \ 'clippy_preference': 'on', - \ 'all_targets': v:false, - \ 'build_on_save': v:true, - \ 'wait_to_build': 0 - \ }}) -< -> - -- From Lua - vim.lsp.add_filetype_config { - name = "clangd"; - filetype = {"c", "cpp"}; - cmd = "clangd -background-index"; - capabilities = { - offsetEncoding = {"utf-8", "utf-16"}; - }; - on_init = vim.schedule_wrap(function(client, result) - if result.offsetEncoding then - client.offset_encoding = result.offsetEncoding - end - end) - } -< - *vim.lsp.copy_filetype_config()* -vim.lsp.copy_filetype_config({existing_name}, [{override_config}]) - - You can use this to copy an existing filetype configuration and change it by - specifying {override_config} which will override any properties in the - existing configuration. If you don't specify a new unique name with - {override_config.name} then it will try to create one and return it. - - Returns:~ - `name` the new configuration name. - - *vim.lsp.get_filetype_client_by_name()* -vim.lsp.get_filetype_client_by_name({name}) - - Use this to look up a client by its name created from - |vim.lsp.add_filetype_config()|. - - Returns nil if the client is not active or the name is not valid. - -================================================================================ *lsp-core-api* These are the core api functions for working with clients. You will mainly be using |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()| for operations @@ -178,8 +87,8 @@ vim.lsp.start_client({config}) `callbacks` A {table} of whose keys are language server method names and the values are `function(err, method, params, client_id)` See |lsp-callbacks| for - more. This will be combined with |lsp-builtin-callbacks| to provide - defaults. + more. This will be combined with |lsp-default-callbacks| to resolve + the callbacks for a client as a fallback. `init_options` A {table} of values to pass in the initialization request as @@ -203,6 +112,12 @@ vim.lsp.start_client({config}) `vim.lsp.client_errors[code]` can be used to retrieve a human understandable string. + `before_init(initialize_params, config)` + A function which is called *before* the request `initialize` is completed. + `initialize_params` contains the parameters we are sending to the server + and `config` is the config that was passed to `start_client()` for + convenience. You can use this to modify parameters before they are sent. + `on_init(client, initialize_result)` A function which is called after the request `initialize` is completed. `initialize_result` contains `capabilities` and anything else the server @@ -346,75 +261,81 @@ vim.lsp.rpc_response_error({code}, [{message}], [{data}]) the server. ================================================================================ - *vim.lsp.builtin_callbacks* - -The |vim.lsp.builtin_callbacks| table contains the default |lsp-callbacks| -that are used when creating a new client. The keys are the LSP method names. - -The following requests and notifications have built-in callbacks defined to -handle the response in an idiomatic way. - - textDocument/completion - textDocument/declaration - textDocument/definition - textDocument/hover - textDocument/implementation - textDocument/rename - textDocument/signatureHelp - textDocument/typeDefinition +LSP CALLBACKS *lsp-callbacks* + +DEFAULT CALLBACKS ~ + *vim.lsp.default_callbacks* +The `vim.lsp.default_callbacks` table defines default callbacks used when +creating a new client. Keys are LSP method names: > + + :lua print(vim.inspect(vim.tbl_keys(vim.lsp.default_callbacks))) + +These LSP requests/notifications are defined by default: + + textDocument/publishDiagnostics window/logMessage window/showMessage -You can check these via `vim.tbl_keys(vim.lsp.builtin_callbacks)`. +You can check these via `vim.tbl_keys(vim.lsp.default_callbacks)`. -These will be automatically used and can be overridden by users (either by -modifying the |vim.lsp.builtin_callbacks| object or on a per-client basis -by passing in a table via the {callbacks} parameter on |vim.lsp.start_client| -or |vim.lsp.add_filetype_config|. +These will be used preferrentially in `vim.lsp.buf` methods when handling +requests. They will also be used when responding to server requests and +notifications. -More information about callbacks can be found in |lsp-callbacks|. +Use cases: +- Users can modify this to customize to their preferences. +- UI plugins can modify this by assigning to + `vim.lsp.default_callbacks[method]` so as to provide more specialized + handling, allowing you to leverage the UI capabilities available. UIs should + try to be conscientious of any existing changes the user may have set + already by checking for existing values. -================================================================================ - *lsp-callbacks* +Any callbacks passed directly to `request` methods on a server client will +have the highest precedence, followed by the `default_callbacks`. + +You can override the default handlers, +- globally: by modifying the `vim.lsp.default_callbacks` table +- per-client: by passing the {callbacks} table parameter to + |vim.lsp.start_client| + +Each handler has this signature: > + + function(err, method, params, client_id) Callbacks are functions which are called in a variety of situations by the client. Their signature is `function(err, method, params, client_id)` They can -be set by the {callbacks} parameter for |vim.lsp.start_client| and -|vim.lsp.add_filetype_config| or via the |vim.lsp.builtin_callbacks|. - -This will be called for: -- notifications from the server, where `err` will always be `nil` -- requests initiated by the server. The parameter `err` will be `nil` here as - well. - For these, you can respond by returning two values: `result, err` The - err must be in the format of an RPC error, which is - `{ code, message, data? }` - You can use |vim.lsp.rpc_response_error()| to help with creating this object. -- as a callback for requests initiated by the client if the request doesn't - explicitly specify a callback (such as in |vim.lsp.buf_request|). +be set by the {callbacks} parameter for |vim.lsp.start_client| or via the +|vim.lsp.default_callbacks|. + +Handlers are called for: +- Notifications from the server (`err` is always `nil`). +- Requests initiated by the server (`err` is always `nil`). + The handler can respond by returning two values: `result, err` + where `err` must be shaped like an RPC error: + `{ code, message, data? }` + You can use |vim.lsp.rpc_response_error()| to create this object. +- Handling requests initiated by the client if the request doesn't explicitly + specify a callback (such as in |vim.lsp.buf_request|). ================================================================================ - *vim.lsp.protocol* -vim.lsp.protocol +VIM.LSP.PROTOCOL *vim.lsp.protocol* - Contains constants as described in the Language Server Protocol - specification and helper functions for creating protocol related objects. +The `vim.lsp.protocol` module provides constants defined in the LSP +specification, and helper functions for creating protocol-related objects. https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md - Useful examples are `vim.lsp.protocol.ErrorCodes`. These objects allow - reverse lookup by either the number or string name. +Useful examples are `vim.lsp.protocol.ErrorCodes`. These objects allow reverse +lookup by either the number or string name. e.g. vim.lsp.protocol.TextDocumentSyncKind.Full == 1 vim.lsp.protocol.TextDocumentSyncKind[1] == "Full" Utility functions used internally are: - `vim.lsp.make_client_capabilities()` + `vim.lsp.protocol.make_client_capabilities()` Make a ClientCapabilities object. These are the builtin capabilities. - `vim.lsp.make_text_document_position_params()` - Make a TextDocumentPositionParams object. - `vim.lsp.resolve_capabilities(server_capabilites)` + `vim.lsp.protocol.resolve_capabilities(server_capabilites)` Creates a normalized object describing capabilities from the server capabilities. @@ -482,18 +403,16 @@ vim.lsp.buf_notify({bufnr}, {method}, {params}) ================================================================================ *lsp-logging* - *lsp#set_log_level()* -lsp#set_log_level({level}) + *vim.lsp.set_log_level()* +vim.lsp.set_log_level({level}) You can set the log level for language server client logging. Possible values: "trace", "debug", "info", "warn", "error" Default: "warn" - Example: `call lsp#set_log_level("debug")` + Example: `lua vim.lsp.set_log_level("debug")` - *lsp#get_log_path()* *vim.lsp.get_log_path()* -lsp#get_log_path() vim.lsp.get_log_path() Returns the path that LSP logs are written. @@ -508,46 +427,46 @@ vim.lsp.log_levels ================================================================================ *lsp-omnifunc* *vim.lsp.omnifunc()* - *lsp#omnifunc* -lsp#omnifunc({findstart}, {base}) vim.lsp.omnifunc({findstart}, {base}) To configure omnifunc, add the following in your init.vim: > - set omnifunc=lsp#omnifunc + " Configure for python + autocmd Filetype python setl omnifunc=v:lua.vim.lsp.omnifunc + + " Or with on_attach + start_client { + ... + on_attach = function(client, bufnr) + vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc') + end; + } - " This is optional, but you may find it useful - autocmd CompleteDone * pclose + " This is optional, but you may find it useful + autocmd CompleteDone * pclose < ================================================================================ - *lsp-vim-functions* +LSP FUNCTIONS *lsp-vim-functions* + +To use the functions from vim, it is recommended to use |v:lua| to interface +with the Lua functions. No direct vim functions are provided, but the +interface is still easy to use from mappings. These methods can be used in mappings and are the equivalent of using the request from lua as follows: > - lua vim.lsp.buf_request(0, "textDocument/hover", vim.lsp.protocol.make_text_document_position_params()) -< - - lsp#text_document_declaration() - lsp#text_document_definition() - lsp#text_document_hover() - lsp#text_document_implementation() - lsp#text_document_signature_help() - lsp#text_document_type_definition() - -> " Example config - autocmd Filetype rust,python,go,c,cpp setl omnifunc=lsp#omnifunc - nnoremap <silent> ;dc :call lsp#text_document_declaration()<CR> - nnoremap <silent> ;df :call lsp#text_document_definition()<CR> - nnoremap <silent> ;h :call lsp#text_document_hover()<CR> - nnoremap <silent> ;i :call lsp#text_document_implementation()<CR> - nnoremap <silent> ;s :call lsp#text_document_signature_help()<CR> - nnoremap <silent> ;td :call lsp#text_document_type_definition()<CR> + autocmd Filetype rust,python,go,c,cpp setl omnifunc=v:lua.vim.lsp.omnifunc + nnoremap <silent> ;dc <cmd>lua vim.lsp.buf.declaration()<CR> + nnoremap <silent> ;df <cmd>lua vim.lsp.buf.definition()<CR> + nnoremap <silent> ;h <cmd>lua vim.lsp.buf.hover()<CR> + nnoremap <silent> ;i <cmd>lua vim.lsp.buf.implementation()<CR> + nnoremap <silent> ;s <cmd>lua vim.lsp.buf.signature_help()<CR> + nnoremap <silent> ;td <cmd>lua vim.lsp.buf.type_definition()<CR> < ================================================================================ - *lsp-advanced-js-example* +LSP EXAMPLE *lsp-advanced-js-example* For more advanced configurations where just filtering by filetype isn't sufficient, you can use the `vim.lsp.start_client()` and @@ -569,7 +488,7 @@ The example will: local stat = vim.loop.fs_stat(filename) return stat and stat.type == 'directory' or false end - + local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/" -- Asumes filepath is a file. local function dirname(filepath) @@ -580,11 +499,11 @@ The example will: end) return result, is_changed end - + local function path_join(...) return table.concat(vim.tbl_flatten {...}, path_sep) end - + -- Ascend the buffer's path until we find the rootdir. -- is_root_path is a function which returns bool local function buffer_find_root_dir(bufnr, is_root_path) @@ -606,7 +525,7 @@ The example will: end end end - + -- A table to store our root_dir to client_id lookup. We want one LSP per -- root directory, and this is how we assert that. local javascript_lsps = {} @@ -617,14 +536,14 @@ The example will: ["typescript"] = true; ["typescript.jsx"] = true; } - + -- Create a template configuration for a server to start, minus the root_dir -- which we will specify later. local javascript_lsp_config = { name = "javascript"; cmd = { path_join(os.getenv("JAVASCRIPT_LANGUAGE_SERVER_DIRECTORY"), "lib", "language-server-stdio.js") }; } - + -- This needs to be global so that we can call it from the autocmd. function check_start_javascript_lsp() local bufnr = vim.api.nvim_get_current_buf() @@ -641,7 +560,7 @@ The example will: end) -- We couldn't find a root directory, so ignore this file. if not root_dir then return end - + -- Check if we have a client alredy or start and store it. local client_id = javascript_lsps[root_dir] if not client_id then @@ -655,7 +574,7 @@ The example will: -- are already attached. vim.lsp.buf_attach_client(bufnr, client_id) end - + vim.api.nvim_command [[autocmd BufReadPost * lua check_start_javascript_lsp()]] < diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index edcf246295..c0da06ffe3 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -876,16 +876,23 @@ tbl_add_reverse_lookup({o}) *vim.tbl_add_reverse_lookup()* Parameters: ~ {o} table The table to add the reverse to. -list_extend({dst}, {src}) *vim.list_extend()* +list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()* Extends a list-like table with the values of another list-like table. + NOTE: This mutates dst! + Parameters: ~ - {dst} The list which will be modified and appended to. - {src} The list from which values will be inserted. + {dst} list which will be modified and appended to. + {src} list from which values will be inserted. + {start} Start index on src. defaults to 1 + {finish} Final index on src. defaults to #src + + Return: ~ + dst See also: ~ - |extend()| + |vim.tbl_extend()| tbl_flatten({t}) *vim.tbl_flatten()* Creates a copy of a list-like table such that any nested diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index abe86749c4..58c0d832e6 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -786,7 +786,7 @@ g@{motion} Call the function set by the 'operatorfunc' option. character of the text. The function is called with one String argument: "line" {motion} was |linewise| - "char" {motion} was |characterwise| + "char" {motion} was |charwise| "block" {motion} was |blockwise-visual| Although "block" would rarely appear, since it can only result from Visual mode where "g@" is not useful. diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index 965b062728..bcfd985e71 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -679,8 +679,8 @@ no argument has been specified. Invalid argument: {arg} Duplicate argument: {arg} -An Ex command or function has been executed, but an invalid argument has been -specified. +Ex command or function has been executed, but an invalid argument was +specified. Or a non-executable command was given to |system()|. *E488* > Trailing characters diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index e93c833c76..07ff4cf030 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -60,11 +60,11 @@ After applying the operator the cursor is mostly left at the start of the text that was operated upon. For example, "yfe" doesn't move the cursor, but "yFe" moves the cursor leftwards to the "e" where the yank started. - *linewise* *characterwise* + *linewise* *charwise* *characterwise* The operator either affects whole lines, or the characters between the start and end position. Generally, motions that move between lines affect lines (are linewise), and motions that move within a line affect characters (are -characterwise). However, there are some exceptions. +charwise). However, there are some exceptions. *exclusive* *inclusive* Character motion is either inclusive or exclusive. When inclusive, the @@ -106,10 +106,10 @@ This cannot be repeated: > d:if 1<CR> call search("f")<CR> endif<CR> -Note that when using ":" any motion becomes characterwise exclusive. +Note that when using ":" any motion becomes charwise exclusive. *forced-motion* -FORCING A MOTION TO BE LINEWISE, CHARACTERWISE OR BLOCKWISE +FORCING A MOTION TO BE LINEWISE, CHARWISE OR BLOCKWISE When a motion is not of the type you would like to use, you can force another type by using "v", "V" or CTRL-V just after the operator. @@ -121,22 +121,22 @@ deletes from the cursor position until the character below the cursor > d<C-V>j deletes the character under the cursor and the character below the cursor. > -Be careful with forcing a linewise movement to be used characterwise or -blockwise, the column may not always be defined. +Be careful with forcing a linewise movement to be used charwise or blockwise, +the column may not always be defined. *o_v* v When used after an operator, before the motion command: Force - the operator to work characterwise, also when the motion is + the operator to work charwise, also when the motion is linewise. If the motion was linewise, it will become |exclusive|. - If the motion already was characterwise, toggle + If the motion already was charwise, toggle inclusive/exclusive. This can be used to make an exclusive motion inclusive and an inclusive motion exclusive. *o_V* V When used after an operator, before the motion command: Force the operator to work linewise, also when the motion is - characterwise. + charwise. *o_CTRL-V* CTRL-V When used after an operator, before the motion command: Force @@ -508,36 +508,36 @@ aw "a word", select [count] words (see |word|). Leading or trailing white space is included, but not counted. When used in Visual linewise mode "aw" switches to - Visual characterwise mode. + Visual charwise mode. *v_iw* *iw* iw "inner word", select [count] words (see |word|). White space between words is counted too. When used in Visual linewise mode "iw" switches to - Visual characterwise mode. + Visual charwise mode. *v_aW* *aW* aW "a WORD", select [count] WORDs (see |WORD|). Leading or trailing white space is included, but not counted. When used in Visual linewise mode "aW" switches to - Visual characterwise mode. + Visual charwise mode. *v_iW* *iW* iW "inner WORD", select [count] WORDs (see |WORD|). White space between words is counted too. When used in Visual linewise mode "iW" switches to - Visual characterwise mode. + Visual charwise mode. *v_as* *as* as "a sentence", select [count] sentences (see |sentence|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. *v_is* *is* is "inner sentence", select [count] sentences (see |sentence|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. *v_ap* *ap* ap "a paragraph", select [count] paragraphs (see @@ -558,14 +558,14 @@ a[ "a [] block", select [count] '[' ']' blocks. This goes backwards to the [count] unclosed '[', and finds the matching ']'. The enclosed text is selected, including the '[' and ']'. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. i] *v_i]* *v_i[* *i]* *i[* i[ "inner [] block", select [count] '[' ']' blocks. This goes backwards to the [count] unclosed '[', and finds the matching ']'. The enclosed text is selected, excluding the '[' and ']'. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. a) *v_a)* *a)* *a(* a( *vab* *v_ab* *v_a(* *ab* @@ -573,54 +573,54 @@ ab "a block", select [count] blocks, from "[count] [(" to the matching ')', including the '(' and ')' (see |[(|). Does not include white space outside of the parenthesis. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. i) *v_i)* *i)* *i(* i( *vib* *v_ib* *v_i(* *ib* ib "inner block", select [count] blocks, from "[count] [(" to the matching ')', excluding the '(' and ')' (see |[(|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. a> *v_a>* *v_a<* *a>* *a<* a< "a <> block", select [count] <> blocks, from the [count]'th unmatched '<' backwards to the matching '>', including the '<' and '>'. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. i> *v_i>* *v_i<* *i>* *i<* i< "inner <> block", select [count] <> blocks, from the [count]'th unmatched '<' backwards to the matching '>', excluding the '<' and '>'. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. *v_at* *at* at "a tag block", select [count] tag blocks, from the [count]'th unmatched "<aaa>" backwards to the matching "</aaa>", including the "<aaa>" and "</aaa>". See |tag-blocks| about the details. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. *v_it* *it* it "inner tag block", select [count] tag blocks, from the [count]'th unmatched "<aaa>" backwards to the matching "</aaa>", excluding the "<aaa>" and "</aaa>". See |tag-blocks| about the details. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. a} *v_a}* *a}* *a{* a{ *v_aB* *v_a{* *aB* aB "a Block", select [count] Blocks, from "[count] [{" to the matching '}', including the '{' and '}' (see |[{|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. i} *v_i}* *i}* *i{* i{ *v_iB* *v_i{* *iB* iB "inner Block", select [count] Blocks, from "[count] [{" to the matching '}', excluding the '{' and '}' (see |[{|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. a" *v_aquote* *aquote* a' *v_a'* *a'* @@ -634,7 +634,7 @@ a` *v_a`* *a`* start of the line. Any trailing white space is included, unless there is none, then leading white space is included. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. Repeating this object in Visual mode another string is included. A count is currently not used. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 30aebfc5ff..95265fa153 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2362,7 +2362,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'fillchars'* *'fcs'* 'fillchars' 'fcs' string (default "") - local to window + global or local to window |global-local| Characters to fill the statuslines and vertical separators. It is a comma separated list of items: @@ -3671,7 +3671,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'listchars'* *'lcs'* 'listchars' 'lcs' string (default: "tab:> ,trail:-,nbsp:+" Vi default: "eol:$") - local to window + global or local to window |global-local| Strings to use in 'list' mode and for the |:list| command. It is a comma separated list of string settings. @@ -6014,6 +6014,8 @@ A jump table for the options with a short description can be found at |Q_op|. vsplit Just like "split" but split vertically. newtab Like "split", but open a new tab page. Overrules "split" when both are present. + uselast If included, jump to the previously used window when + jumping to errors with |quickfix| commands. *'synmaxcol'* *'smc'* 'synmaxcol' 'smc' number (default 3000) diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 7dbbb2d424..e3f0d593a7 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -1270,7 +1270,7 @@ exactly four MessagePack objects: Key Type Def Description ~ rt UInteger 0 Register type: No Description ~ - 0 |characterwise-register| + 0 |charwise-register| 1 |linewise-register| 2 |blockwise-register| rw UInteger 0 Register width. Only valid diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index a2f19593ae..d5f4a59ab3 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -201,8 +201,8 @@ the editor. sent from Nvim, like for |ui-cmdline|. ["mode_change", mode, mode_idx] - The mode changed. The first parameter `mode` is a string representing - the current mode. `mode_idx` is an index into the array received in + Editor mode changed. The `mode` parameter is a string representing + the current mode. `mode_idx` is an index into the array emitted in the `mode_info_set` event. UIs should change the cursor style according to the properties specified in the corresponding item. The set of modes reported will change in new versions of Nvim, for @@ -211,11 +211,11 @@ the editor. ["mouse_on"] ["mouse_off"] - Tells the client whether mouse support, as determined by |'mouse'| - option, is considered to be active in the current mode. This is mostly - useful for a terminal frontend, or other situations where Nvim mouse - would conflict with other usages of the mouse. It is safe for a client - to ignore this and always send mouse events. + |'mouse'| was enabled/disabled in the current editor mode. Useful for + a terminal UI, or other situations where Nvim mouse would conflict + with other usages of the mouse. UIs may ignore this and always send + mouse input, because 'mouse' decides the behavior of |nvim_input()| + implicitly. ["busy_start"] ["busy_stop"] diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt index ccbbc092ec..0052382044 100644 --- a/runtime/doc/visual.txt +++ b/runtime/doc/visual.txt @@ -48,7 +48,7 @@ position. ============================================================================== 2. Starting and stopping Visual mode *visual-start* - *v* *characterwise-visual* + *v* *charwise-visual* [count]v Start Visual mode per character. With [count] select the same number of characters or lines as used for the last Visual operation, but at @@ -74,7 +74,7 @@ position. If you use <Esc>, click the left mouse button or use any command that does a jump to another buffer while in Visual mode, the highlighting stops -and no text is affected. Also when you hit "v" in characterwise Visual mode, +and no text is affected. Also when you hit "v" in charwise Visual mode, "CTRL-V" in blockwise Visual mode or "V" in linewise Visual mode. If you hit CTRL-Z the highlighting stops and the editor is suspended or a new shell is started |CTRL-Z|. @@ -477,7 +477,7 @@ Commands in Select mode: Otherwise, typed characters are handled as in Visual mode. When using an operator in Select mode, and the selection is linewise, the -selected lines are operated upon, but like in characterwise selection. For +selected lines are operated upon, but like in charwise selection. For example, when a whole line is deleted, it can later be pasted in the middle of a line. @@ -510,7 +510,7 @@ gV Avoid the automatic reselection of the Visual area selection. *gh* -gh Start Select mode, characterwise. This is like "v", +gh Start Select mode, charwise. This is like "v", but starts Select mode instead of Visual mode. Mnemonic: "get highlighted". diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 8ce45b6a50..e27d234a96 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2019 Aug 26 +" Last Change: 2019 Nov 26 " Listen very carefully, I will say this only once if exists("did_load_filetypes") @@ -796,8 +796,8 @@ au BufNewFile,BufRead *.java,*.jav setf java " JavaCC au BufNewFile,BufRead *.jj,*.jjt setf javacc -" JavaScript, ECMAScript -au BufNewFile,BufRead *.js,*.javascript,*.es,*.mjs setf javascript +" JavaScript, ECMAScript, ES module script, CommonJS script +au BufNewFile,BufRead *.js,*.javascript,*.es,*.mjs,*.cjs setf javascript " JavaScript with React au BufNewFile,BufRead *.jsx setf javascriptreact diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim index 6c7f095f62..081181cfe9 100644 --- a/runtime/ftplugin/man.vim +++ b/runtime/ftplugin/man.vim @@ -1,4 +1,4 @@ -" Maintainer: Anmol Sethi <anmol@aubble.com> +" Maintainer: Anmol Sethi <hi@nhooyr.io> " Previous Maintainer: SungHyun Nam <goweol@gmail.com> if exists('b:did_ftplugin') || &filetype !=# 'man' @@ -20,13 +20,12 @@ setlocal wrap breakindent linebreak setlocal nonumber norelativenumber setlocal foldcolumn=0 colorcolumn=0 nolist nofoldenable +setlocal tagfunc=man#goto_tag + if !exists('g:no_plugin_maps') && !exists('g:no_man_maps') nnoremap <silent> <buffer> j gj nnoremap <silent> <buffer> k gk nnoremap <silent> <buffer> gO :call man#show_toc()<CR> - nnoremap <silent> <buffer> <C-]> :Man<CR> - nnoremap <silent> <buffer> K :Man<CR> - nnoremap <silent> <buffer> <C-T> :call man#pop_tag()<CR> if 1 == bufnr('%') || s:pager nnoremap <silent> <buffer> <nowait> q :lclose<CR>:q<CR> else diff --git a/runtime/keymap/russian-jcukenwintype.vim b/runtime/keymap/russian-jcukenwintype.vim new file mode 100644 index 0000000000..25d6286e24 --- /dev/null +++ b/runtime/keymap/russian-jcukenwintype.vim @@ -0,0 +1,112 @@ +" Vim Keymap file for russian characters, layout 'jcuken', MS Windows +" Typewriter variant (slightly incompatible with XFree86 'ru' keymap - +" makes use of NUMERO SIGN) +" Useful mainly with utf-8 but may work with other encodings + +" Derived from russian-jcuken.vim by Artem Chuprina <ran@ran.pp.ru> +" Typewriter variant of standart russian layout +" Maintainer: Denis Proskurin <danwerspb@gmail.com> +" Last Changed: 2015 May 15 + +" All characters are given literally, conversion to another encoding (e.g., +" UTF-8) should work. + +scriptencoding utf-8 + +let b:keymap_name = "ru" + +loadkeymap +F А CYRILLIC CAPITAL LETTER A +< Б CYRILLIC CAPITAL LETTER BE +D В CYRILLIC CAPITAL LETTER VE +U Г CYRILLIC CAPITAL LETTER GHE +L Д CYRILLIC CAPITAL LETTER DE +T Е CYRILLIC CAPITAL LETTER IE +? Ё CYRILLIC CAPITAL LETTER IO +: Ж CYRILLIC CAPITAL LETTER ZHE +P З CYRILLIC CAPITAL LETTER ZE +B И CYRILLIC CAPITAL LETTER I +Q Й CYRILLIC CAPITAL LETTER SHORT I +R К CYRILLIC CAPITAL LETTER KA +K Л CYRILLIC CAPITAL LETTER EL +V М CYRILLIC CAPITAL LETTER EM +Y Н CYRILLIC CAPITAL LETTER EN +J О CYRILLIC CAPITAL LETTER O +G П CYRILLIC CAPITAL LETTER PE +H Р CYRILLIC CAPITAL LETTER ER +C С CYRILLIC CAPITAL LETTER ES +N Т CYRILLIC CAPITAL LETTER TE +E У CYRILLIC CAPITAL LETTER U +A Ф CYRILLIC CAPITAL LETTER EF +{ Х CYRILLIC CAPITAL LETTER HA +W Ц CYRILLIC CAPITAL LETTER TSE +X Ч CYRILLIC CAPITAL LETTER CHE +I Ш CYRILLIC CAPITAL LETTER SHA +O Щ CYRILLIC CAPITAL LETTER SHCHA +} Ъ CYRILLIC CAPITAL LETTER HARD SIGN +S Ы CYRILLIC CAPITAL LETTER YERU +M Ь CYRILLIC CAPITAL LETTER SOFT SIGN +\" Э CYRILLIC CAPITAL LETTER E +> Ю CYRILLIC CAPITAL LETTER YU +Z Я CYRILLIC CAPITAL LETTER YA +f а CYRILLIC SMALL LETTER A +, б CYRILLIC SMALL LETTER BE +d в CYRILLIC SMALL LETTER VE +u г CYRILLIC SMALL LETTER GHE +l д CYRILLIC SMALL LETTER DE +t е CYRILLIC SMALL LETTER IE +/ ё CYRILLIC SMALL LETTER IO +; ж CYRILLIC SMALL LETTER ZHE +p з CYRILLIC SMALL LETTER ZE +b и CYRILLIC SMALL LETTER I +q й CYRILLIC SMALL LETTER SHORT I +r к CYRILLIC SMALL LETTER KA +k л CYRILLIC SMALL LETTER EL +v м CYRILLIC SMALL LETTER EM +y н CYRILLIC SMALL LETTER EN +j о CYRILLIC SMALL LETTER O +g п CYRILLIC SMALL LETTER PE +h р CYRILLIC SMALL LETTER ER +c с CYRILLIC SMALL LETTER ES +n т CYRILLIC SMALL LETTER TE +e у CYRILLIC SMALL LETTER U +a ф CYRILLIC SMALL LETTER EF +[ х CYRILLIC SMALL LETTER HA +w ц CYRILLIC SMALL LETTER TSE +x ч CYRILLIC SMALL LETTER CHE +i ш CYRILLIC SMALL LETTER SHA +o щ CYRILLIC SMALL LETTER SHCHA +] ъ CYRILLIC SMALL LETTER HARD SIGN +s ы CYRILLIC SMALL LETTER YERU +m ь CYRILLIC SMALL LETTER SOFT SIGN +' э CYRILLIC SMALL LETTER E +. ю CYRILLIC SMALL LETTER YU +z я CYRILLIC SMALL LETTER YA +` | +1 № +2 - +3 / +4 " +5 : +6 , +7 . +8 _ +9 ? +0 % +- ! += ; +~ + +! 1 +@ 2 +# 3 +$ 4 +% 5 +^ 6 +& 7 +* 8 +( 9 +) 0 +_ = ++ \\ +\\ ) +\| ( diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 9dbe03dace..82f4fda66f 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1,9 +1,10 @@ -local builtin_callbacks = require 'vim.lsp.builtin_callbacks' +local default_callbacks = require 'vim.lsp.callbacks' local log = require 'vim.lsp.log' local lsp_rpc = require 'vim.lsp.rpc' local protocol = require 'vim.lsp.protocol' local util = require 'vim.lsp.util' +local vim = vim local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option = vim.api.nvim_err_writeln, vim.api.nvim_buf_get_lines, vim.api.nvim_command, vim.api.nvim_buf_get_option local uv = vim.loop @@ -12,7 +13,8 @@ local validate = vim.validate local lsp = { protocol = protocol; - builtin_callbacks = builtin_callbacks; + callbacks = default_callbacks; + buf = require'vim.lsp.buf'; util = util; -- Allow raw RPC access. rpc = lsp_rpc; @@ -23,9 +25,13 @@ local lsp = { -- format_rpc_error = lsp_rpc.format_rpc_error; } --- TODO consider whether 'eol' or 'fixeol' should change the nvim_buf_get_lines that send. -- TODO improve handling of scratch buffers with LSP attached. +local function err_message(...) + nvim_err_writeln(table.concat(vim.tbl_flatten{...})) + nvim_command("redraw") +end + local function resolve_bufnr(bufnr) validate { bufnr = { bufnr, 'n', true } } if bufnr == nil or bufnr == 0 then @@ -93,11 +99,7 @@ local function for_each_buffer_client(bufnr, callback) return end for client_id in pairs(client_ids) do - -- This is unlikely to happen. Could only potentially happen in a race - -- condition between literally a single statement. - -- We could skip this error, but let's error for now. local client = active_clients[client_id] - -- or error(string.format("Client %d has already shut down.", client_id)) if client then callback(client, client_id) end @@ -155,13 +157,13 @@ local function validate_client_config(config) root_dir = { config.root_dir, is_dir, "directory" }; callbacks = { config.callbacks, "t", true }; capabilities = { config.capabilities, "t", true }; - -- cmd = { config.cmd, "s", false }; cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), "directory" }; cmd_env = { config.cmd_env, "f", true }; name = { config.name, 's', true }; on_error = { config.on_error, "f", true }; on_exit = { config.on_exit, "f", true }; on_init = { config.on_init, "f", true }; + before_init = { config.before_init, "f", true }; offset_encoding = { config.offset_encoding, "s", true }; } local cmd, cmd_args = validate_command(config.cmd) @@ -175,6 +177,14 @@ local function validate_client_config(config) } end +local function buf_get_full_text(bufnr) + local text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, true), '\n') + if nvim_buf_get_option(bufnr, 'eol') then + text = text .. '\n' + end + return text +end + local function text_document_did_open_handler(bufnr, client) if not client.resolved_capabilities.text_document_open_close then return @@ -188,7 +198,7 @@ local function text_document_did_open_handler(bufnr, client) uri = vim.uri_from_bufnr(bufnr); -- TODO make sure our filetypes are compatible with languageId names. languageId = nvim_buf_get_option(bufnr, 'filetype'); - text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, false), '\n'); + text = buf_get_full_text(bufnr); } } client.notify('textDocument/didOpen', params) @@ -254,6 +264,12 @@ end -- possible errors. `vim.lsp.client_errors[code]` can be used to retrieve a -- human understandable string. -- +-- before_init(initialize_params, config): A function which is called *before* +-- the request `initialize` is completed. `initialize_params` contains +-- the parameters we are sending to the server and `config` is the config that +-- was passed to `start_client()` for convenience. You can use this to modify +-- parameters before they are sent. +-- -- on_init(client, initialize_result): A function which is called after the -- request `initialize` is completed. `initialize_result` contains -- `capabilities` and anything else the server may send. For example, `clangd` @@ -283,19 +299,19 @@ function lsp.start_client(config) local client_id = next_client_id() - local callbacks = tbl_extend("keep", config.callbacks or {}, builtin_callbacks) - -- Copy metatable if it has one. - if config.callbacks and config.callbacks.__metatable then - setmetatable(callbacks, getmetatable(config.callbacks)) - end + local callbacks = config.callbacks or {} local name = config.name or tostring(client_id) local log_prefix = string.format("LSP[%s]", name) local handlers = {} + local function resolve_callback(method) + return callbacks[method] or default_callbacks[method] + end + function handlers.notification(method, params) local _ = log.debug() and log.debug('notification', method, params) - local callback = callbacks[method] + local callback = resolve_callback(method) if callback then -- Method name is provided here for convenience. callback(nil, method, params, client_id) @@ -304,7 +320,7 @@ function lsp.start_client(config) function handlers.server_request(method, params) local _ = log.debug() and log.debug('server_request', method, params) - local callback = callbacks[method] + local callback = resolve_callback(method) if callback then local _ = log.debug() and log.debug("server_request: found callback for", method) return callback(nil, method, params, client_id) @@ -315,12 +331,12 @@ function lsp.start_client(config) function handlers.on_error(code, err) local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err }) - nvim_err_writeln(string.format('%s: Error %s: %q', log_prefix, lsp.client_errors[code], vim.inspect(err))) + err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err)) if config.on_error then local status, usererr = pcall(config.on_error, code, err) if not status then local _ = log.error() and log.error(log_prefix, "user on_error failed", { err = usererr }) - nvim_err_writeln(log_prefix.." user on_error failed: "..tostring(usererr)) + err_message(log_prefix, ' user on_error failed: ', tostring(usererr)) end end end @@ -328,9 +344,19 @@ function lsp.start_client(config) function handlers.on_exit(code, signal) active_clients[client_id] = nil uninitialized_clients[client_id] = nil - for _, client_ids in pairs(all_buffer_active_clients) do + local active_buffers = {} + for bufnr, client_ids in pairs(all_buffer_active_clients) do + if client_ids[client_id] then + table.insert(active_buffers, bufnr) + end client_ids[client_id] = nil end + -- Buffer level cleanup + vim.schedule(function() + for _, bufnr in ipairs(active_buffers) do + util.buf_clear_diagnostics(bufnr) + end + end) if config.on_exit then pcall(config.on_exit, code, signal, client_id) end @@ -372,7 +398,6 @@ function lsp.start_client(config) -- The rootUri of the workspace. Is null if no folder is open. If both -- `rootPath` and `rootUri` are set `rootUri` wins. rootUri = vim.uri_from_fname(config.root_dir); --- rootUri = vim.uri_from_fname(vim.fn.expand("%:p:h")); -- User provided initialization options. initializationOptions = config.init_options; -- The capabilities provided by the client (editor or tool) @@ -396,11 +421,15 @@ function lsp.start_client(config) -- } workspaceFolders = nil; } + if config.before_init then + -- TODO(ashkan) handle errors here. + pcall(config.before_init, initialize_params, config) + end local _ = log.debug() and log.debug(log_prefix, "initialize_params", initialize_params) rpc.request('initialize', initialize_params, function(init_err, result) assert(not init_err, tostring(init_err)) assert(result, "server sent empty result") - rpc.notify('initialized', {}) + rpc.notify('initialized', {[vim.type_idx]=vim.types.dictionary}) client.initialized = true uninitialized_clients[client_id] = nil client.server_capabilities = assert(result.capabilities, "initialize result doesn't contain capabilities") @@ -432,15 +461,15 @@ function lsp.start_client(config) local function unsupported_method(method) local msg = "server doesn't support "..method local _ = log.warn() and log.warn(msg) - nvim_err_writeln(msg) + err_message(msg) return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg) end --- Checks capabilities before rpc.request-ing. function client.request(method, params, callback) if not callback then - callback = client.callbacks[method] - or error(string.format("request callback is empty and no default was found for client %s", client.name)) + callback = resolve_callback(method) + or error("not found: request callback for client "..client.name) end local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback) -- TODO keep these checks or just let it go anyway? @@ -551,7 +580,7 @@ do end) local full_changes = once(function() return { - text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, false), "\n"); + text = buf_get_full_text(bufnr); }; end) local uri = vim.uri_from_bufnr(bufnr) @@ -844,7 +873,7 @@ function lsp.omnifunc(findstart, base) position = { -- 0-indexed for both line and character line = pos[1] - 1, - character = pos[2], + character = vim.str_utfindex(line, pos[2]), }; -- The completion context. This is only available if the client specifies -- to send this using `ClientCapabilities.textDocument.completion.contextSupport === true` @@ -869,134 +898,8 @@ function lsp.omnifunc(findstart, base) end end ---- ---- FileType based configuration utility ---- - -local all_filetype_configs = {} - --- Lookup a filetype config client by its name. -function lsp.get_filetype_client_by_name(name) - local config = all_filetype_configs[name] - if config.client_id then - return active_clients[config.client_id] - end -end - -local function start_filetype_config(config) - config.client_id = lsp.start_client(config) - nvim_command(string.format( - "autocmd FileType %s silent lua vim.lsp.buf_attach_client(0, %d)", - table.concat(config.filetypes, ','), - config.client_id)) - return config.client_id -end - --- Easy configuration option for common LSP use-cases. --- This will lazy initialize the client when the filetypes specified are --- encountered and attach to those buffers. --- --- The configuration options are the same as |vim.lsp.start_client()|, but --- with a few additions and distinctions: --- --- Additional parameters: --- - filetype: {string} or {list} of filetypes to attach to. --- - name: A unique string among all other servers configured with --- |vim.lsp.add_filetype_config|. --- --- Differences: --- - root_dir: will default to |getcwd()| --- -function lsp.add_filetype_config(config) - -- Additional defaults. - -- Keep a copy of the user's input for debugging reasons. - local user_config = config - config = tbl_extend("force", {}, user_config) - config.root_dir = config.root_dir or uv.cwd() - -- Validate config. - validate_client_config(config) - validate { - name = { config.name, 's' }; - } - assert(config.filetype, "config must have 'filetype' key") - - local filetypes - if type(config.filetype) == 'string' then - filetypes = { config.filetype } - elseif type(config.filetype) == 'table' then - filetypes = config.filetype - assert(not tbl_isempty(filetypes), "config.filetype must not be an empty table") - else - error("config.filetype must be a string or a list of strings") - end - - if all_filetype_configs[config.name] then - -- If the client exists, then it is likely that they are doing some kind of - -- reload flow, so let's not throw an error here. - if all_filetype_configs[config.name].client_id then - -- TODO log here? It might be unnecessarily annoying. - return - end - error(string.format('A configuration with the name %q already exists. They must be unique', config.name)) - end - - all_filetype_configs[config.name] = tbl_extend("keep", config, { - client_id = nil; - filetypes = filetypes; - user_config = user_config; - }) - - nvim_command(string.format( - "autocmd FileType %s ++once silent lua vim.lsp._start_filetype_config_client(%q)", - table.concat(filetypes, ','), - config.name)) -end - --- Create a copy of an existing configuration, and override config with values --- from new_config. --- This is useful if you wish you create multiple LSPs with different root_dirs --- or other use cases. --- --- You can specify a new unique name, but if you do not, a unique name will be --- created like `name-dup_count`. --- --- existing_name: the name of the existing config to copy. --- new_config: the new configuration options. @see |vim.lsp.start_client()|. --- @returns string the new name. -function lsp.copy_filetype_config(existing_name, new_config) - local config = all_filetype_configs[existing_name] - or error(string.format("Configuration with name %q doesn't exist", existing_name)) - config = tbl_extend("force", config, new_config or {}) - config.client_id = nil - config.original_config_name = existing_name - - -- If the user didn't rename it, we will. - if config.name == existing_name then - -- Create a new, unique name. - local duplicate_count = 0 - for _, conf in pairs(all_filetype_configs) do - if conf.original_config_name == existing_name then - duplicate_count = duplicate_count + 1 - end - end - config.name = string.format("%s-%d", existing_name, duplicate_count + 1) - end - print("New config name:", config.name) - lsp.add_filetype_config(config) - return config.name -end - --- Autocmd handler to actually start the client when an applicable filetype is --- encountered. -function lsp._start_filetype_config_client(name) - local config = all_filetype_configs[name] - -- If it exists and is running, don't make it again. - if config.client_id and active_clients[config.client_id] then - -- TODO log here? - return - end - lsp.buf_attach_client(0, start_filetype_config(config)) - return config.client_id +function lsp.client_is_stopped(client_id) + return active_clients[client_id] == nil end --- @@ -1023,7 +926,7 @@ end -- Print some debug information about all LSP related things. -- The output of this function should not be relied upon and may change. function lsp.print_debug_info() - print(vim.inspect({ clients = active_clients, filetype_configs = all_filetype_configs })) + print(vim.inspect({ clients = active_clients })) end -- Log level dictionary with reverse lookup as well. diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua new file mode 100644 index 0000000000..a6a05fb095 --- /dev/null +++ b/runtime/lua/vim/lsp/buf.lua @@ -0,0 +1,136 @@ +local vim = vim +local validate = vim.validate +local api = vim.api +local vfn = vim.fn +local util = require 'vim.lsp.util' +local list_extend = vim.list_extend + +local M = {} + +local function ok_or_nil(status, ...) + if not status then return end + return ... +end +local function npcall(fn, ...) + return ok_or_nil(pcall(fn, ...)) +end + +local function request(method, params, callback) + validate { + method = {method, 's'}; + callback = {callback, 'f', true}; + } + return vim.lsp.buf_request(0, method, params, callback) +end + +function M.hover() + local params = util.make_position_params() + request('textDocument/hover', params) +end + +function M.peek_definition() + local params = util.make_position_params() + request('textDocument/peekDefinition', params) +end + + +function M.declaration() + local params = util.make_position_params() + request('textDocument/declaration', params) +end + +function M.definition() + local params = util.make_position_params() + request('textDocument/definition', params) +end + +function M.type_definition() + local params = util.make_position_params() + request('textDocument/typeDefinition', params) +end + +function M.implementation() + local params = util.make_position_params() + request('textDocument/implementation', params) +end + +function M.signature_help() + local params = util.make_position_params() + request('textDocument/signatureHelp', params) +end + +-- TODO(ashkan) ? +function M.completion(context) + local params = util.make_position_params() + params.context = context + return request('textDocument/completion', params) +end + +function M.formatting(options) + validate { options = {options, 't', true} } + options = vim.tbl_extend('keep', options or {}, { + tabSize = vim.bo.tabstop; + insertSpaces = vim.bo.expandtab; + }) + local params = { + textDocument = { uri = vim.uri_from_bufnr(0) }; + options = options; + } + return request('textDocument/formatting', params) +end + +function M.range_formatting(options, start_pos, end_pos) + validate { + options = {options, 't', true}; + start_pos = {start_pos, 't', true}; + end_pos = {end_pos, 't', true}; + } + options = vim.tbl_extend('keep', options or {}, { + tabSize = vim.bo.tabstop; + insertSpaces = vim.bo.expandtab; + }) + local A = list_extend({}, start_pos or api.nvim_buf_get_mark(0, '<')) + local B = list_extend({}, end_pos or api.nvim_buf_get_mark(0, '>')) + -- convert to 0-index + A[1] = A[1] - 1 + B[1] = B[1] - 1 + -- account for encoding. + if A[2] > 0 then + A = {A[1], util.character_offset(0, A[1], A[2])} + end + if B[2] > 0 then + B = {B[1], util.character_offset(0, B[1], B[2])} + end + local params = { + textDocument = { uri = vim.uri_from_bufnr(0) }; + range = { + start = { line = A[1]; character = A[2]; }; + ["end"] = { line = B[1]; character = B[2]; }; + }; + options = options; + } + return request('textDocument/rangeFormatting', params) +end + +function M.rename(new_name) + -- TODO(ashkan) use prepareRename + -- * result: [`Range`](#range) \| `{ range: Range, placeholder: string }` \| `null` describing the range of the string to rename and optionally a placeholder text of the string content to be renamed. If `null` is returned then it is deemed that a 'textDocument/rename' request is not valid at the given position. + local params = util.make_position_params() + new_name = new_name or npcall(vfn.input, "New Name: ") + if not (new_name and #new_name > 0) then return end + params.newName = new_name + request('textDocument/rename', params) +end + +function M.references(context) + validate { context = { context, 't', true } } + local params = util.make_position_params() + params.context = context or { + includeDeclaration = true; + } + params[vim.type_idx] = vim.types.dictionary + request('textDocument/references', params) +end + +return M +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/builtin_callbacks.lua b/runtime/lua/vim/lsp/builtin_callbacks.lua deleted file mode 100644 index cc739ce3ad..0000000000 --- a/runtime/lua/vim/lsp/builtin_callbacks.lua +++ /dev/null @@ -1,296 +0,0 @@ ---- Implements the following default callbacks: --- --- vim.api.nvim_buf_set_lines(0, 0, 0, false, vim.tbl_keys(vim.lsp.builtin_callbacks)) --- - --- textDocument/completion --- textDocument/declaration --- textDocument/definition --- textDocument/hover --- textDocument/implementation --- textDocument/publishDiagnostics --- textDocument/rename --- textDocument/signatureHelp --- textDocument/typeDefinition --- TODO codeLens/resolve --- TODO completionItem/resolve --- TODO documentLink/resolve --- TODO textDocument/codeAction --- TODO textDocument/codeLens --- TODO textDocument/documentHighlight --- TODO textDocument/documentLink --- TODO textDocument/documentSymbol --- TODO textDocument/formatting --- TODO textDocument/onTypeFormatting --- TODO textDocument/rangeFormatting --- TODO textDocument/references --- window/logMessage --- window/showMessage - -local log = require 'vim.lsp.log' -local protocol = require 'vim.lsp.protocol' -local util = require 'vim.lsp.util' -local api = vim.api - -local function split_lines(value) - return vim.split(value, '\n', true) -end - -local builtin_callbacks = {} - --- textDocument/completion --- https://microsoft.github.io/language-server-protocol/specification#textDocument_completion -builtin_callbacks['textDocument/completion'] = function(_, _, result) - if not result or vim.tbl_isempty(result) then - return - end - local pos = api.nvim_win_get_cursor(0) - local row, col = pos[1], pos[2] - local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) - local line_to_cursor = line:sub(col+1) - - local matches = util.text_document_completion_list_to_complete_items(result, line_to_cursor) - local match_result = vim.fn.matchstrpos(line_to_cursor, '\\k\\+$') - local match_start, match_finish = match_result[2], match_result[3] - - vim.fn.complete(col + 1 - (match_finish - match_start), matches) -end - --- textDocument/rename -builtin_callbacks['textDocument/rename'] = function(_, _, result) - if not result then return end - util.workspace_apply_workspace_edit(result) -end - -local function uri_to_bufnr(uri) - return vim.fn.bufadd((vim.uri_to_fname(uri))) -end - -builtin_callbacks['textDocument/publishDiagnostics'] = function(_, _, result) - if not result then return end - local uri = result.uri - local bufnr = uri_to_bufnr(uri) - if not bufnr then - api.nvim_err_writeln(string.format("LSP.publishDiagnostics: Couldn't find buffer for %s", uri)) - return - end - util.buf_clear_diagnostics(bufnr) - util.buf_diagnostics_save_positions(bufnr, result.diagnostics) - util.buf_diagnostics_underline(bufnr, result.diagnostics) - util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) - -- util.buf_loclist(bufnr, result.diagnostics) -end - --- textDocument/hover --- https://microsoft.github.io/language-server-protocol/specification#textDocument_hover --- @params MarkedString | MarkedString[] | MarkupContent -builtin_callbacks['textDocument/hover'] = function(_, _, result) - if result == nil or vim.tbl_isempty(result) then - return - end - - if result.contents ~= nil then - local markdown_lines = util.convert_input_to_markdown_lines(result.contents) - if vim.tbl_isempty(markdown_lines) then - markdown_lines = { 'No information available' } - end - util.open_floating_preview(markdown_lines, 'markdown') - end -end - -builtin_callbacks['textDocument/peekDefinition'] = function(_, _, result) - if result == nil or vim.tbl_isempty(result) then return end - -- TODO(ashkan) what to do with multiple locations? - result = result[1] - local bufnr = uri_to_bufnr(result.uri) - assert(bufnr) - local start = result.range.start - local finish = result.range["end"] - util.open_floating_peek_preview(bufnr, start, finish, { offset_x = 1 }) - util.open_floating_preview({"*Peek:*", string.rep(" ", finish.character - start.character + 1) }, 'markdown', { offset_y = -(finish.line - start.line) }) -end - ---- Convert SignatureHelp response to preview contents. --- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp -local function signature_help_to_preview_contents(input) - if not input.signatures then - return - end - --The active signature. If omitted or the value lies outside the range of - --`signatures` the value defaults to zero or is ignored if `signatures.length - --=== 0`. Whenever possible implementors should make an active decision about - --the active signature and shouldn't rely on a default value. - local contents = {} - local active_signature = input.activeSignature or 0 - -- If the activeSignature is not inside the valid range, then clip it. - if active_signature >= #input.signatures then - active_signature = 0 - end - local signature = input.signatures[active_signature + 1] - if not signature then - return - end - vim.list_extend(contents, split_lines(signature.label)) - if signature.documentation then - util.convert_input_to_markdown_lines(signature.documentation, contents) - end - if input.parameters then - local active_parameter = input.activeParameter or 0 - -- If the activeParameter is not inside the valid range, then clip it. - if active_parameter >= #input.parameters then - active_parameter = 0 - end - local parameter = signature.parameters and signature.parameters[active_parameter] - if parameter then - --[=[ - --Represents a parameter of a callable-signature. A parameter can - --have a label and a doc-comment. - interface ParameterInformation { - --The label of this parameter information. - -- - --Either a string or an inclusive start and exclusive end offsets within its containing - --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 - --string representation as `Position` and `Range` does. - -- - --*Note*: a label of type string should be a substring of its containing signature label. - --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. - label: string | [number, number]; - --The human-readable doc-comment of this parameter. Will be shown - --in the UI but can be omitted. - documentation?: string | MarkupContent; - } - --]=] - -- TODO highlight parameter - if parameter.documentation then - util.convert_input_to_markdown_lines(parameter.documentation, contents) - end - end - end - return contents -end - --- textDocument/signatureHelp --- https://microsoft.github.io/language-server-protocol/specification#textDocument_signatureHelp -builtin_callbacks['textDocument/signatureHelp'] = function(_, _, result) - if result == nil or vim.tbl_isempty(result) then - return - end - - -- TODO show empty popup when signatures is empty? - if #result.signatures > 0 then - local markdown_lines = signature_help_to_preview_contents(result) - if vim.tbl_isempty(markdown_lines) then - markdown_lines = { 'No signature available' } - end - util.open_floating_preview(markdown_lines, 'markdown') - end -end - -local function update_tagstack() - local bufnr = api.nvim_get_current_buf() - local line = vim.fn.line('.') - local col = vim.fn.col('.') - local tagname = vim.fn.expand('<cWORD>') - local item = { bufnr = bufnr, from = { bufnr, line, col, 0 }, tagname = tagname } - local winid = vim.fn.win_getid() - local tagstack = vim.fn.gettagstack(winid) - - local action - - if tagstack.length == tagstack.curidx then - action = 'r' - tagstack.items[tagstack.curidx] = item - elseif tagstack.length > tagstack.curidx then - action = 'r' - if tagstack.curidx > 1 then - tagstack.items = table.insert(tagstack.items[tagstack.curidx - 1], item) - else - tagstack.items = { item } - end - else - action = 'a' - tagstack.items = { item } - end - - tagstack.curidx = tagstack.curidx + 1 - vim.fn.settagstack(winid, tagstack, action) -end - -local function handle_location(result) - -- We can sometimes get a list of locations, so set the first value as the - -- only value we want to handle - -- TODO(ashkan) was this correct^? We could use location lists. - if result[1] ~= nil then - result = result[1] - end - if result.uri == nil then - api.nvim_err_writeln('[LSP] Could not find a valid location') - return - end - local result_file = vim.uri_to_fname(result.uri) - local bufnr = vim.fn.bufadd(result_file) - update_tagstack() - api.nvim_set_current_buf(bufnr) - local start = result.range.start - api.nvim_win_set_cursor(0, {start.line + 1, start.character}) -end - -local function location_callback(_, method, result) - if result == nil or vim.tbl_isempty(result) then - local _ = log.info() and log.info(method, 'No location found') - return nil - end - handle_location(result) - return true -end - -local location_callbacks = { - -- https://microsoft.github.io/language-server-protocol/specification#textDocument_declaration - 'textDocument/declaration'; - -- https://microsoft.github.io/language-server-protocol/specification#textDocument_definition - 'textDocument/definition'; - -- https://microsoft.github.io/language-server-protocol/specification#textDocument_implementation - 'textDocument/implementation'; - -- https://microsoft.github.io/language-server-protocol/specification#textDocument_typeDefinition - 'textDocument/typeDefinition'; -} - -for _, location_method in ipairs(location_callbacks) do - builtin_callbacks[location_method] = location_callback -end - -local function log_message(_, _, result, client_id) - local message_type = result.type - local message = result.message - local client = vim.lsp.get_client_by_id(client_id) - local client_name = client and client.name or string.format("id=%d", client_id) - if not client then - api.nvim_err_writeln(string.format("LSP[%s] client has shut down after sending the message", client_name)) - end - if message_type == protocol.MessageType.Error then - -- Might want to not use err_writeln, - -- but displaying a message with red highlights or something - api.nvim_err_writeln(string.format("LSP[%s] %s", client_name, message)) - else - local message_type_name = protocol.MessageType[message_type] - api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message)) - end - return result -end - -builtin_callbacks['window/showMessage'] = log_message -builtin_callbacks['window/logMessage'] = log_message - --- Add boilerplate error validation and logging for all of these. -for k, fn in pairs(builtin_callbacks) do - builtin_callbacks[k] = function(err, method, params, client_id) - local _ = log.debug() and log.debug('builtin_callback', method, { params = params, client_id = client_id, err = err }) - if err then - error(tostring(err)) - end - return fn(err, method, params, client_id) - end -end - -return builtin_callbacks --- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua new file mode 100644 index 0000000000..4fc3f74519 --- /dev/null +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -0,0 +1,223 @@ +local log = require 'vim.lsp.log' +local protocol = require 'vim.lsp.protocol' +local util = require 'vim.lsp.util' +local vim = vim +local api = vim.api + +local M = {} + +local function err_message(...) + api.nvim_err_writeln(table.concat(vim.tbl_flatten{...})) + api.nvim_command("redraw") +end + +M['workspace/applyEdit'] = function(_, _, workspace_edit) + if not workspace_edit then return end + -- TODO(ashkan) Do something more with label? + if workspace_edit.label then + print("Workspace edit", workspace_edit.label) + end + util.apply_workspace_edit(workspace_edit.edit) +end + +M['textDocument/publishDiagnostics'] = function(_, _, result) + if not result then return end + local uri = result.uri + local bufnr = vim.uri_to_bufnr(uri) + if not bufnr then + err_message("LSP.publishDiagnostics: Couldn't find buffer for ", uri) + return + end + util.buf_clear_diagnostics(bufnr) + util.buf_diagnostics_save_positions(bufnr, result.diagnostics) + util.buf_diagnostics_underline(bufnr, result.diagnostics) + util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) + -- util.set_loclist(result.diagnostics) +end + +M['textDocument/references'] = function(_, _, result) + if not result then return end + util.set_qflist(result) + api.nvim_command("copen") + api.nvim_command("wincmd p") +end + +M['textDocument/rename'] = function(_, _, result) + if not result then return end + util.apply_workspace_edit(result) +end + +M['textDocument/rangeFormatting'] = function(_, _, result) + if not result then return end + util.apply_text_edits(result) +end + +M['textDocument/formatting'] = function(_, _, result) + if not result then return end + util.apply_text_edits(result) +end + +M['textDocument/completion'] = function(_, _, result) + if vim.tbl_isempty(result or {}) then return end + local row, col = unpack(api.nvim_win_get_cursor(0)) + local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) + local line_to_cursor = line:sub(col+1) + + local matches = util.text_document_completion_list_to_complete_items(result, line_to_cursor) + vim.fn.complete(col, matches) +end + +M['textDocument/hover'] = function(_, method, result) + util.focusable_preview(method, function() + if not (result and result.contents) then + return { 'No information available' } + 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' } + end + return markdown_lines, util.try_trim_markdown_code_blocks(markdown_lines) + end) +end + +local function location_callback(_, method, result) + if result == nil or vim.tbl_isempty(result) then + local _ = log.info() and log.info(method, 'No location found') + return nil + end + util.jump_to_location(result[1]) + if #result > 1 then + util.set_qflist(result) + api.nvim_command("copen") + api.nvim_command("wincmd p") + end +end + +M['textDocument/declaration'] = location_callback +M['textDocument/definition'] = location_callback +M['textDocument/typeDefinition'] = location_callback +M['textDocument/implementation'] = location_callback + +--- Convert SignatureHelp response to preview contents. +-- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp +local function signature_help_to_preview_contents(input) + if not input.signatures then + return + end + --The active signature. If omitted or the value lies outside the range of + --`signatures` the value defaults to zero or is ignored if `signatures.length + --=== 0`. Whenever possible implementors should make an active decision about + --the active signature and shouldn't rely on a default value. + local contents = {} + local active_signature = input.activeSignature or 0 + -- If the activeSignature is not inside the valid range, then clip it. + if active_signature >= #input.signatures then + active_signature = 0 + end + local signature = input.signatures[active_signature + 1] + if not signature then + return + end + vim.list_extend(contents, vim.split(signature.label, '\n', true)) + if signature.documentation then + util.convert_input_to_markdown_lines(signature.documentation, contents) + end + if input.parameters then + local active_parameter = input.activeParameter or 0 + -- If the activeParameter is not inside the valid range, then clip it. + if active_parameter >= #input.parameters then + active_parameter = 0 + end + local parameter = signature.parameters and signature.parameters[active_parameter] + if parameter then + --[=[ + --Represents a parameter of a callable-signature. A parameter can + --have a label and a doc-comment. + interface ParameterInformation { + --The label of this parameter information. + -- + --Either a string or an inclusive start and exclusive end offsets within its containing + --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 + --string representation as `Position` and `Range` does. + -- + --*Note*: a label of type string should be a substring of its containing signature label. + --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. + label: string | [number, number]; + --The human-readable doc-comment of this parameter. Will be shown + --in the UI but can be omitted. + documentation?: string | MarkupContent; + } + --]=] + -- TODO highlight parameter + if parameter.documentation then + util.convert_input_to_markdown_lines(parameter.documentation, contents) + end + end + end + return contents +end + +M['textDocument/signatureHelp'] = function(_, method, result) + util.focusable_preview(method, function() + if not (result and result.signatures and result.signatures[1]) then + return { 'No signature available' } + end + -- TODO show popup when signatures is empty? + local lines = signature_help_to_preview_contents(result) + lines = util.trim_empty_lines(lines) + if vim.tbl_isempty(lines) then + return { 'No signature available' } + end + return lines, util.try_trim_markdown_code_blocks(lines) + end) +end + +M['textDocument/peekDefinition'] = function(_, _, result, _) + if not (result and result[1]) then return end + local loc = result[1] + local bufnr = vim.uri_to_bufnr(loc.uri) or error("not found: "..tostring(loc.uri)) + local start = loc.range.start + local finish = loc.range["end"] + util.open_floating_peek_preview(bufnr, start, finish, { offset_x = 1 }) + local headbuf = util.open_floating_preview({"Peek:"}, nil, { + offset_y = -(finish.line - start.line); + width = finish.character - start.character + 2; + }) + -- TODO(ashkan) change highlight group? + api.nvim_buf_add_highlight(headbuf, -1, 'Keyword', 0, -1) +end + +local function log_message(_, _, result, client_id) + local message_type = result.type + local message = result.message + local client = vim.lsp.get_client_by_id(client_id) + local client_name = client and client.name or string.format("id=%d", client_id) + if not client then + err_message("LSP[", client_name, "] client has shut down after sending the message") + end + if message_type == protocol.MessageType.Error then + err_message("LSP[", client_name, "] ", message) + else + local message_type_name = protocol.MessageType[message_type] + api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message)) + end + return result +end + +M['window/showMessage'] = log_message +M['window/logMessage'] = log_message + +-- Add boilerplate error validation and logging for all of these. +for k, fn in pairs(M) do + M[k] = function(err, method, params, client_id) + local _ = log.debug() and log.debug('default_callback', method, { params = params, client_id = client_id, err = err }) + if err then + error(tostring(err)) + end + return fn(err, method, params, client_id) + end +end + +return M +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 1413a88ce2..ead90cc75a 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -10,7 +10,6 @@ end --[=[ -- Useful for interfacing with: --- https://github.com/microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md -- https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md function transform_schema_comments() nvim.command [[silent! '<,'>g/\/\*\*\|\*\/\|^$/d]] @@ -681,19 +680,6 @@ function protocol.make_client_capabilities() } end -function protocol.make_text_document_position_params() - local position = vim.api.nvim_win_get_cursor(0) - return { - textDocument = { - uri = vim.uri_from_bufnr() - }; - position = { - line = position[1] - 1; - character = position[2]; - } - } -end - --[=[ export interface DocumentFilter { --A language id, like `typescript`. diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index e0ec8863d6..a558f66a42 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -340,6 +340,7 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para local decoded, err = json_decode(body) if not decoded then on_error(client_errors.INVALID_SERVER_JSON, err) + return end local _ = log.debug() and log.debug("decoded", decoded) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index f96e0f01a8..b9990ed082 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1,6 +1,8 @@ local protocol = require 'vim.lsp.protocol' +local vim = vim local validate = vim.validate local api = vim.api +local list_extend = vim.list_extend local M = {} @@ -9,7 +11,13 @@ local function split_lines(value) return split(value, '\n', true) end -local list_extend = vim.list_extend +local function ok_or_nil(status, ...) + if not status then return end + return ... +end +local function npcall(fn, ...) + return ok_or_nil(pcall(fn, ...)) +end --- Find the longest shared prefix between prefix and word. -- e.g. remove_prefix("123tes", "testing") == "ting" @@ -26,11 +34,90 @@ local function remove_prefix(prefix, word) return word:sub(prefix_length + 1) end -local function resolve_bufnr(bufnr) - if bufnr == nil or bufnr == 0 then - return api.nvim_get_current_buf() +-- TODO(ashkan) @performance this could do less copying. +function M.set_lines(lines, A, B, new_lines) + -- 0-indexing to 1-indexing + local i_0 = A[1] + 1 + local i_n = B[1] + 1 + if not (i_0 >= 1 and i_0 <= #lines and i_n >= 1 and i_n <= #lines) then + error("Invalid range: "..vim.inspect{A = A; B = B; #lines, new_lines}) + end + local prefix = "" + local suffix = lines[i_n]:sub(B[2]+1) + if A[2] > 0 then + prefix = lines[i_0]:sub(1, A[2]) + end + local n = i_n - i_0 + 1 + if n ~= #new_lines then + for _ = 1, n - #new_lines do table.remove(lines, i_0) end + for _ = 1, #new_lines - n do table.insert(lines, i_0, '') end + end + for i = 1, #new_lines do + lines[i - 1 + i_0] = new_lines[i] + end + if #suffix > 0 then + local i = i_0 + #new_lines - 1 + lines[i] = lines[i]..suffix + end + if #prefix > 0 then + lines[i_0] = prefix..lines[i_0] + end + return lines +end + +local function sort_by_key(fn) + return function(a,b) + local ka, kb = fn(a), fn(b) + assert(#ka == #kb) + for i = 1, #ka do + if ka[i] ~= kb[i] then + return ka[i] < kb[i] + end + end + -- every value must have been equal here, which means it's not less than. + return false + end +end +local edit_sort_key = sort_by_key(function(e) + return {e.A[1], e.A[2], e.i} +end) + +function M.apply_text_edits(text_edits, bufnr) + if not next(text_edits) then return end + local start_line, finish_line = math.huge, -1 + local cleaned = {} + for i, e in ipairs(text_edits) do + start_line = math.min(e.range.start.line, start_line) + finish_line = math.max(e.range["end"].line, finish_line) + -- TODO(ashkan) sanity check ranges for overlap. + table.insert(cleaned, { + i = i; + A = {e.range.start.line; e.range.start.character}; + B = {e.range["end"].line; e.range["end"].character}; + lines = vim.split(e.newText, '\n', true); + }) + end + + -- Reverse sort the orders so we can apply them without interfering with + -- eachother. Also add i as a sort key to mimic a stable sort. + table.sort(cleaned, edit_sort_key) + local lines = api.nvim_buf_get_lines(bufnr, start_line, finish_line + 1, false) + local fix_eol = api.nvim_buf_get_option(bufnr, 'fixeol') + local set_eol = fix_eol and api.nvim_buf_line_count(bufnr) == finish_line + 1 + if set_eol and #lines[#lines] ~= 0 then + table.insert(lines, '') + end + + for i = #cleaned, 1, -1 do + local e = cleaned[i] + local A = {e.A[1] - start_line, e.A[2]} + local B = {e.B[1] - start_line, e.B[2]} + lines = M.set_lines(lines, A, B, e.lines) end - return bufnr + if set_eol and #lines[#lines] == 0 then + table.remove(lines) + end + api.nvim_buf_set_lines(bufnr, start_line, finish_line + 1, false, lines) end -- local valid_windows_path_characters = "[^<>:\"/\\|?*]" @@ -40,30 +127,6 @@ end -- function M.glob_to_regex(glob) -- end ---- Apply the TextEdit response. --- @params TextEdit [table] see https://microsoft.github.io/language-server-protocol/specification -function M.text_document_apply_text_edit(text_edit, bufnr) - bufnr = resolve_bufnr(bufnr) - local range = text_edit.range - local start = range.start - local finish = range['end'] - local new_lines = split_lines(text_edit.newText) - if start.character == 0 and finish.character == 0 then - api.nvim_buf_set_lines(bufnr, start.line, finish.line, false, new_lines) - return - end - api.nvim_err_writeln('apply_text_edit currently only supports character ranges starting at 0') - error('apply_text_edit currently only supports character ranges starting at 0') - return - -- TODO test and finish this support for character ranges. --- local lines = api.nvim_buf_get_lines(0, start.line, finish.line + 1, false) --- local suffix = lines[#lines]:sub(finish.character+2) --- local prefix = lines[1]:sub(start.character+2) --- new_lines[#new_lines] = new_lines[#new_lines]..suffix --- new_lines[1] = prefix..new_lines[1] --- api.nvim_buf_set_lines(0, start.line, finish.line, false, new_lines) -end - -- textDocument/completion response returns one of CompletionItem[], CompletionList or null. -- https://microsoft.github.io/language-server-protocol/specification#textDocument_completion function M.extract_completion_items(result) @@ -78,18 +141,15 @@ end --- Apply the TextDocumentEdit response. -- @params TextDocumentEdit [table] see https://microsoft.github.io/language-server-protocol/specification -function M.text_document_apply_text_document_edit(text_document_edit, bufnr) - -- local text_document = text_document_edit.textDocument - -- TODO use text_document_version? - -- local text_document_version = text_document.version - - -- TODO technically, you could do this without doing multiple buf_get/set - -- by getting the full region (smallest line and largest line) and doing - -- the edits on the buffer, and then applying the buffer at the end. - -- I'm not sure if that's better. - for _, text_edit in ipairs(text_document_edit.edits) do - M.text_document_apply_text_edit(text_edit, bufnr) +function M.apply_text_document_edit(text_document_edit) + local text_document = text_document_edit.textDocument + local bufnr = vim.uri_to_bufnr(text_document.uri) + -- TODO(ashkan) check this is correct. + if api.nvim_buf_get_changedtick(bufnr) > text_document.version then + print("Buffer ", text_document.uri, " newer than edits.") + return end + M.apply_text_edits(text_document_edit.edits, bufnr) end function M.get_current_line_to_cursor() @@ -145,32 +205,27 @@ function M.text_document_completion_list_to_complete_items(result, line_prefix) end -- @params WorkspaceEdit [table] see https://microsoft.github.io/language-server-protocol/specification -function M.workspace_apply_workspace_edit(workspace_edit) +function M.apply_workspace_edit(workspace_edit) if workspace_edit.documentChanges then for _, change in ipairs(workspace_edit.documentChanges) do if change.kind then -- TODO(ashkan) handle CreateFile/RenameFile/DeleteFile error(string.format("Unsupported change: %q", vim.inspect(change))) else - M.text_document_apply_text_document_edit(change) + M.apply_text_document_edit(change) end end return end - if workspace_edit.changes == nil or #workspace_edit.changes == 0 then + local all_changes = workspace_edit.changes + if not (all_changes and not vim.tbl_isempty(all_changes)) then return end - for uri, changes in pairs(workspace_edit.changes) do - local fname = vim.uri_to_fname(uri) - -- TODO improve this approach. Try to edit open buffers without switching. - -- Not sure how to handle files which aren't open. This is deprecated - -- anyway, so I guess it could be left as is. - api.nvim_command('edit '..fname) - for _, change in ipairs(changes) do - M.text_document_apply_text_edit(change) - end + for uri, changes in pairs(all_changes) do + local bufnr = vim.uri_to_bufnr(uri) + M.apply_text_edits(changes, bufnr) end end @@ -255,35 +310,77 @@ function M.make_floating_popup_options(width, height, opts) } end +function M.jump_to_location(location) + if location.uri == nil then return end + local bufnr = vim.uri_to_bufnr(location.uri) + -- TODO(ashkan) use tagfunc here to update tagstack. + api.nvim_set_current_buf(bufnr) + local row = location.range.start.line + local col = location.range.start.character + local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] + col = vim.str_byteindex(line, col) + api.nvim_win_set_cursor(0, {row + 1, col}) + return true +end + +local function find_window_by_var(name, value) + for _, win in ipairs(api.nvim_list_wins()) do + if npcall(api.nvim_win_get_var, win, name) == value then + return win + end + end +end + +-- Check if a window with `unique_name` tagged is associated with the current +-- buffer. If not, make a new preview. +-- +-- fn()'s return values will be passed directly to open_floating_preview in the +-- case that a new floating window should be created. +function M.focusable_preview(unique_name, fn) + if npcall(api.nvim_win_get_var, 0, unique_name) then + return api.nvim_command("wincmd p") + end + local bufnr = api.nvim_get_current_buf() + do + local win = find_window_by_var(unique_name, bufnr) + if win then + api.nvim_set_current_win(win) + api.nvim_command("stopinsert") + return + end + end + local pbufnr, pwinnr = M.open_floating_preview(fn()) + api.nvim_win_set_var(pwinnr, unique_name, bufnr) + return pbufnr, pwinnr +end + function M.open_floating_preview(contents, filetype, opts) validate { contents = { contents, 't' }; filetype = { filetype, 's', true }; opts = { opts, 't', true }; } + opts = opts or {} -- Trim empty lines from the end. - for i = #contents, 1, -1 do - if #contents[i] == 0 then - table.remove(contents) - else - break + contents = M.trim_empty_lines(contents) + + local width = opts.width + local height = opts.height or #contents + if not width then + width = 0 + for i, line in ipairs(contents) do + -- Clean up the input and add left pad. + line = " "..line:gsub("\r", "") + -- TODO(ashkan) use nvim_strdisplaywidth if/when that is introduced. + local line_width = vim.fn.strdisplaywidth(line) + width = math.max(line_width, width) + contents[i] = line end + -- Add right padding of 1 each. + width = width + 1 end - local width = 0 - local height = #contents - for i, line in ipairs(contents) do - -- Clean up the input and add left pad. - line = " "..line:gsub("\r", "") - -- TODO(ashkan) use nvim_strdisplaywidth if/when that is introduced. - local line_width = vim.fn.strdisplaywidth(line) - width = math.max(line_width, width) - contents[i] = line - end - -- Add right padding of 1 each. - width = width + 1 - local floating_bufnr = api.nvim_create_buf(false, true) if filetype then api.nvim_buf_set_option(floating_bufnr, 'filetype', filetype) @@ -295,7 +392,8 @@ function M.open_floating_preview(contents, filetype, opts) end api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) api.nvim_buf_set_option(floating_bufnr, 'modifiable', false) - api.nvim_command("autocmd CursorMoved <buffer> ++once lua pcall(vim.api.nvim_win_close, "..floating_winnr..", true)") + -- TODO make InsertCharPre disappearing optional? + api.nvim_command("autocmd CursorMoved,BufHidden,InsertCharPre <buffer> ++once lua pcall(vim.api.nvim_win_close, "..floating_winnr..", true)") return floating_bufnr, floating_winnr end @@ -342,6 +440,11 @@ do local diagnostic_ns = api.nvim_create_namespace("vim_lsp_diagnostics") + local underline_highlight_name = "LspDiagnosticsUnderline" + api.nvim_command(string.format("highlight default %s gui=underline cterm=underline", underline_highlight_name)) + + local severity_highlights = {} + local default_severity_highlight = { [protocol.DiagnosticSeverity.Error] = { guifg = "Red" }; [protocol.DiagnosticSeverity.Warning] = { guifg = "Orange" }; @@ -349,60 +452,17 @@ do [protocol.DiagnosticSeverity.Hint] = { guifg = "LightGrey" }; } - local underline_highlight_name = "LspDiagnosticsUnderline" - api.nvim_command(string.format("highlight %s gui=underline cterm=underline", underline_highlight_name)) - - local function find_color_rgb(color) - local rgb_hex = api.nvim_get_color_by_name(color) - validate { color = {color, function() return rgb_hex ~= -1 end, "valid color name"} } - return rgb_hex - end - - --- Determine whether to use black or white text - -- Ref: https://stackoverflow.com/a/1855903/837964 - -- https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color - local function color_is_bright(r, g, b) - -- Counting the perceptive luminance - human eye favors green color - local luminance = (0.299*r + 0.587*g + 0.114*b)/255 - if luminance > 0.5 then - return true -- Bright colors, black font - else - return false -- Dark colors, white font - end - end - - local severity_highlights = {} - - function M.set_severity_highlights(highlights) - validate {highlights = {highlights, 't'}} - for severity, default_color in pairs(default_severity_highlight) do - local severity_name = protocol.DiagnosticSeverity[severity] - local highlight_name = "LspDiagnostics"..severity_name - local hi_info = highlights[severity] or default_color - -- Try to fill in the foreground color with a sane default. - if not hi_info.guifg and hi_info.guibg then - -- TODO(ashkan) move this out when bitop is guaranteed to be included. - local bit = require 'bit' - local band, rshift = bit.band, bit.rshift - local rgb = find_color_rgb(hi_info.guibg) - local is_bright = color_is_bright(rshift(rgb, 16), band(rshift(rgb, 8), 0xFF), band(rgb, 0xFF)) - hi_info.guifg = is_bright and "Black" or "White" - end - if not hi_info.ctermfg and hi_info.ctermbg then - -- TODO(ashkan) move this out when bitop is guaranteed to be included. - local bit = require 'bit' - local band, rshift = bit.band, bit.rshift - local rgb = find_color_rgb(hi_info.ctermbg) - local is_bright = color_is_bright(rshift(rgb, 16), band(rshift(rgb, 8), 0xFF), band(rgb, 0xFF)) - hi_info.ctermfg = is_bright and "Black" or "White" - end - local cmd_parts = {"highlight", highlight_name} - for k, v in pairs(hi_info) do - table.insert(cmd_parts, k.."="..v) - end - api.nvim_command(table.concat(cmd_parts, ' ')) - severity_highlights[severity] = highlight_name + -- Initialize default severity highlights + for severity, hi_info in pairs(default_severity_highlight) do + local severity_name = protocol.DiagnosticSeverity[severity] + local highlight_name = "LspDiagnostics"..severity_name + -- Try to fill in the foreground color with a sane default. + local cmd_parts = {"highlight", "default", highlight_name} + for k, v in pairs(hi_info) do + table.insert(cmd_parts, k.."="..v) end + api.nvim_command(table.concat(cmd_parts, ' ')) + severity_highlights[severity] = highlight_name end function M.buf_clear_diagnostics(bufnr) @@ -411,9 +471,6 @@ do api.nvim_buf_clear_namespace(bufnr, diagnostic_ns, 0, -1) end - -- Initialize with the defaults. - M.set_severity_highlights(default_severity_highlight) - function M.get_severity_highlight_name(severity) return severity_highlights[severity] end @@ -527,30 +584,141 @@ do end end -function M.buf_loclist(bufnr, locations) - local targetwin - for _, winnr in ipairs(api.nvim_list_wins()) do - local winbuf = api.nvim_win_get_buf(winnr) - if winbuf == bufnr then - targetwin = winnr - break - end - end - if not targetwin then return end +local position_sort = sort_by_key(function(v) + return {v.line, v.character} +end) +-- Returns the items with the byte position calculated correctly and in sorted +-- order. +function M.locations_to_items(locations) local items = {} - local path = api.nvim_buf_get_name(bufnr) + local grouped = setmetatable({}, { + __index = function(t, k) + local v = {} + rawset(t, k, v) + return v + end; + }) for _, d in ipairs(locations) do - -- TODO: URL parsing here? local start = d.range.start - table.insert(items, { - filename = path, - lnum = start.line + 1, - col = start.character + 1, - text = d.message, - }) + local fname = assert(vim.uri_to_fname(d.uri)) + table.insert(grouped[fname], start) + end + local keys = vim.tbl_keys(grouped) + table.sort(keys) + -- TODO(ashkan) I wish we could do this lazily. + for _, fname in ipairs(keys) do + local rows = grouped[fname] + table.sort(rows, position_sort) + local i = 0 + for line in io.lines(fname) do + for _, pos in ipairs(rows) do + local row = pos.line + if i == row then + local col + if pos.character > #line then + col = #line + else + col = vim.str_byteindex(line, pos.character) + end + table.insert(items, { + filename = fname, + lnum = row + 1, + col = col + 1; + text = line; + }) + end + end + i = i + 1 + end + end + return items +end + +-- locations is Location[] +-- Only sets for the current window. +function M.set_loclist(locations) + vim.fn.setloclist(0, {}, ' ', { + title = 'Language Server'; + items = M.locations_to_items(locations); + }) +end + +-- locations is Location[] +function M.set_qflist(locations) + vim.fn.setqflist({}, ' ', { + title = 'Language Server'; + items = M.locations_to_items(locations); + }) +end + +-- Remove empty lines from the beginning and end. +function M.trim_empty_lines(lines) + local start = 1 + for i = 1, #lines do + if #lines[i] > 0 then + start = i + break + end + end + local finish = 1 + for i = #lines, 1, -1 do + if #lines[i] > 0 then + finish = i + break + end + end + return vim.list_extend({}, lines, start, finish) +end + +-- Accepts markdown lines and tries to reduce it to a filetype if it is +-- just a single code block. +-- Note: This modifies the input. +-- +-- Returns: filetype or 'markdown' if it was unchanged. +function M.try_trim_markdown_code_blocks(lines) + local language_id = lines[1]:match("^```(.*)") + if language_id then + local has_inner_code_fence = false + for i = 2, (#lines - 1) do + local line = lines[i] + if line:sub(1,3) == '```' then + has_inner_code_fence = true + break + end + end + -- No inner code fences + starting with code fence = hooray. + if not has_inner_code_fence then + table.remove(lines, 1) + table.remove(lines) + return language_id + end + end + return 'markdown' +end + +local str_utfindex = vim.str_utfindex +function M.make_position_params() + local row, col = unpack(api.nvim_win_get_cursor(0)) + row = row - 1 + local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] + col = str_utfindex(line, col) + return { + textDocument = { uri = vim.uri_from_bufnr(0) }; + position = { line = row; character = col; } + } +end + +-- @param buf buffer handle or 0 for current. +-- @param row 0-indexed line +-- @param col 0-indexed byte offset in line +function M.character_offset(buf, row, col) + local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1] + -- If the col is past the EOL, use the line length. + if col > #line then + return str_utfindex(line) end - vim.fn.setloclist(targetwin, items, ' ', 'Language Server') + return str_utfindex(line, col) end return M diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index ff89acc524..b5b04d7757 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -4,7 +4,7 @@ -- test-suite. If, in the future, Nvim itself is used to run the test-suite -- instead of "vanilla Lua", these functions could move to src/nvim/lua/vim.lua -local vim = {} +local vim = vim or {} --- Returns a deep copy of the given object. Non-table objects are copied as --- in a typical Lua assignment, whereas table objects are copied recursively. @@ -228,16 +228,24 @@ end --- Extends a list-like table with the values of another list-like table. --- ---NOTE: This *mutates* dst! ---@see |extend()| +--- NOTE: This mutates dst! +--- +--@see |vim.tbl_extend()| --- ---@param dst The list which will be modified and appended to. ---@param src The list from which values will be inserted. -function vim.list_extend(dst, src) - assert(type(dst) == 'table', "dst must be a table") - assert(type(src) == 'table', "src must be a table") - for _, v in ipairs(src) do - table.insert(dst, v) +--@param dst list which will be modified and appended to. +--@param src list from which values will be inserted. +--@param start Start index on src. defaults to 1 +--@param finish Final index on src. defaults to #src +--@returns dst +function vim.list_extend(dst, src, start, finish) + vim.validate { + dst = {dst, 't'}; + src = {src, 't'}; + start = {start, 'n', true}; + finish = {finish, 'n', true}; + } + for i = start or 1, finish or #src do + table.insert(dst, src[i]) end return dst end @@ -312,6 +320,26 @@ function vim.pesc(s) return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1') end +--- Test if `prefix` is a prefix of `s` for strings. +-- +-- @param s String to check +-- @param prefix Potential prefix +-- @return boolean True if prefix is a prefix of s +function vim.startswith(s, prefix) + vim.validate { s = {s, 's'}; prefix = {prefix, 's'}; } + return s:sub(1, #prefix) == prefix +end + +--- Test if `suffix` is a suffix of `s` for strings. +-- +-- @param s String to check +-- @param suffix Potential suffix +-- @return boolean True if suffix is a suffix of s +function vim.endswith(s, suffix) + vim.validate { s = {s, 's'}; suffix = {suffix, 's'}; } + return #suffix == 0 or s:sub(-#suffix) == suffix +end + --- Validates a parameter specification (types and values). --- --- Usage example: diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua index 0a6e0fcb97..1065f84f4c 100644 --- a/runtime/lua/vim/uri.lua +++ b/runtime/lua/vim/uri.lua @@ -77,13 +77,18 @@ local function uri_to_fname(uri) else uri = uri:gsub('^file://', '') end - return uri_decode(uri) end +-- Return or create a buffer for a uri. +local function uri_to_bufnr(uri) + return vim.fn.bufadd((uri_to_fname(uri))) +end + return { uri_from_fname = uri_from_fname, uri_from_bufnr = uri_from_bufnr, uri_to_fname = uri_to_fname, + uri_to_bufnr = uri_to_bufnr, } -- vim:sw=2 ts=2 et diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml index ceffc7ba98..025de1b5a9 100644 --- a/runtime/nvim.appdata.xml +++ b/runtime/nvim.appdata.xml @@ -26,6 +26,7 @@ </screenshots> <releases> + <release date="2019-11-06" version="0.4.3"/> <release date="2019-09-15" version="0.4.2"/> <release date="2019-09-15" version="0.4.1"/> <release date="2019-09-15" version="0.4.0"/> diff --git a/runtime/plugin/man.vim b/runtime/plugin/man.vim index e18a5528bb..e762eb3664 100644 --- a/runtime/plugin/man.vim +++ b/runtime/plugin/man.vim @@ -1,4 +1,4 @@ -" Maintainer: Anmol Sethi <anmol@aubble.com> +" Maintainer: Anmol Sethi <hi@nhooyr.io> if exists('g:loaded_man') finish diff --git a/runtime/syntax/man.vim b/runtime/syntax/man.vim index 6afe56a6e3..7ac02c3f63 100644 --- a/runtime/syntax/man.vim +++ b/runtime/syntax/man.vim @@ -1,4 +1,4 @@ -" Maintainer: Anmol Sethi <anmol@aubble.com> +" Maintainer: Anmol Sethi <hi@nhooyr.io> " Previous Maintainer: SungHyun Nam <goweol@gmail.com> if exists('b:current_syntax') @@ -30,6 +30,7 @@ endif if !exists('b:man_sect') call man#init_pager() endif + if b:man_sect =~# '^[023]' syntax case match syntax include @c $VIMRUNTIME/syntax/c.vim diff --git a/runtime/tutor/tutor.tutor b/runtime/tutor/tutor.tutor index c937bd686a..b46fcc4836 100644 --- a/runtime/tutor/tutor.tutor +++ b/runtime/tutor/tutor.tutor @@ -118,7 +118,7 @@ and are hidden by default. Links to them look like \[label\]\(\*anchor\*\) -6. Add the appropiate link: +6. Add the appropriate link: A link to the Links section A link to the [Links](*links*) section |