diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/autoload/health/provider.vim | 7 | ||||
-rw-r--r-- | runtime/autoload/man.vim | 100 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 8 | ||||
-rw-r--r-- | runtime/doc/lsp.txt | 46 | ||||
-rw-r--r-- | runtime/ftplugin/man.vim | 4 | ||||
-rw-r--r-- | runtime/lua/vim/lsp.lua | 1 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/buf.lua | 60 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/callbacks.lua | 27 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/protocol.lua | 4 |
9 files changed, 211 insertions, 46 deletions
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 4975dc66b8..0482cb7f3c 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -537,8 +537,8 @@ function! s:check_virtualenv() abort call add(errors, 'no Python executables found in the virtualenv '.bin_dir.' directory.') endif + let msg = '$VIRTUAL_ENV is set to: '.$VIRTUAL_ENV if len(errors) - let msg = '$VIRTUAL_ENV is set to: '.$VIRTUAL_ENV if len(venv_bins) let msg .= "\nAnd its ".bin_dir.' directory contains: ' \.join(map(venv_bins, "fnamemodify(v:val, ':t')"), ', ') @@ -551,7 +551,10 @@ function! s:check_virtualenv() abort let msg .= "\nSo invoking Python may lead to unexpected results." call health#report_warn(msg, keys(hints)) else - call health#report_ok('$VIRTUAL_ENV provides :python, :python3, et al.') + call health#report_info(msg) + call health#report_info('Python version: ' + \.system('python -c "import platform, sys; sys.stdout.write(platform.python_version())"')) + call health#report_ok('$VIRTUAL_ENV provides :!python.') endif endfunction diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 122ae357bc..930db3ceb7 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -52,13 +52,14 @@ function! man#open_page(count, count1, mods, ...) abort let ref = a:2.'('.a:1.')' endif try - let [sect, name] = man#extract_sect_and_name_ref(ref) + let [sect, name] = s:extract_sect_and_name_ref(ref) if a:count ==# a:count1 " v:count defaults to 0 which is a valid section, and v:count1 defaults to " 1, also a valid section. If they are equal, count explicitly set. let sect = string(a:count) endif - let [sect, name, path] = s:verify_exists(sect, name) + let path = s:verify_exists(sect, name) + let [sect, name] = s:extract_sect_and_name_path(path) catch call s:error(v:exception) return @@ -82,8 +83,9 @@ endfunction function! man#read_page(ref) abort try - let [sect, name] = man#extract_sect_and_name_ref(a:ref) - let [sect, name, path] = s:verify_exists(sect, name) + let [sect, name] = s:extract_sect_and_name_ref(a:ref) + let path = s:verify_exists(sect, name) + let [sect, name] = s:extract_sect_and_name_path(path) let page = s:get_page(path) catch call s:error(v:exception) @@ -194,7 +196,7 @@ endfunction " attempt to extract the name and sect out of 'name(sect)' " otherwise just return the largest string of valid characters in ref -function! man#extract_sect_and_name_ref(ref) abort +function! s:extract_sect_and_name_ref(ref) abort if a:ref[0] ==# '-' " try ':Man -pandoc' with this disabled. throw 'manpage name cannot start with ''-''' endif @@ -204,7 +206,7 @@ function! man#extract_sect_and_name_ref(ref) abort if empty(name) throw 'manpage reference cannot contain only parentheses' endif - return [get(b:, 'man_default_sects', ''), name] + return ['', name] endif let left = split(ref, '(') " see ':Man 3X curses' on why tolower. @@ -227,24 +229,62 @@ function! s:get_path(sect, name) abort return substitute(get(split(s:system(['man', s:find_arg, s:section_arg, a:sect, a:name])), 0, ''), '\n\+$', '', '') endfunction +" s:verify_exists attempts to find the path to a manpage +" based on the passed section and name. +" +" 1. If the passed section is empty, b:man_default_sects is used. +" 2. If manpage could not be found with the given sect and name, +" then another attempt is made with b:man_default_sects. +" 3. If it still could not be found, then we try again without a section. +" 4. If still not found but $MANSECT is set, then we try again with $MANSECT +" unset. +" +" This function is careful to avoid duplicating a search if a previous +" step has already done it. i.e if we use b:man_default_sects in step 1, +" then we don't do it again in step 2. function! s:verify_exists(sect, name) abort + let sect = a:sect + if empty(sect) + let sect = get(b:, 'man_default_sects', '') + endif + try - let path = s:get_path(a:sect, a:name) + return s:get_path(sect, a:name) catch /^command error (/ + endtry + + if !empty(get(b:, 'man_default_sects', '')) && sect !=# b:man_default_sects try - let path = s:get_path(get(b:, 'man_default_sects', ''), a:name) + return s:get_path(b:man_default_sects, a:name) catch /^command error (/ - let path = s:get_path('', a:name) endtry - endtry - " Extract the section from the path, because sometimes the actual section is - " more specific than what we provided to `man` (try `:Man 3 App::CLI`). - " Also on linux, name seems to be case-insensitive. So for `:Man PRIntf`, we - " still want the name of the buffer to be 'printf'. - return s:extract_sect_and_name_path(path) + [path] + endif + + if !empty(sect) + try + return s:get_path('', a:name) + catch /^command error (/ + endtry + endif + + if !empty($MANSECT) + try + let MANSECT = $MANSECT + unset $MANSECT + return s:get_path('', a:name) + catch /^command error (/ + finally + let $MANSECT = MANSECT + endtry + endif + + throw 'no manual entry for ' . a:name endfunction -" extracts the name and sect out of 'path/name.sect' +" Extracts the name/section from the 'path/name.sect', because sometimes the actual section is +" more specific than what we provided to `man` (try `:Man 3 App::CLI`). +" Also on linux, name seems to be case-insensitive. So for `:Man PRIntf`, we +" still want the name of the buffer to be 'printf'. function! s:extract_sect_and_name_path(path) abort let tail = fnamemodify(a:path, ':t') if a:path =~# '\.\%([glx]z\|bz2\|lzma\|Z\)$' " valid extensions @@ -275,7 +315,7 @@ function! s:error(msg) abort echohl None endfunction -" see man#extract_sect_and_name_ref on why tolower(sect) +" see s:extract_sect_and_name_ref on why tolower(sect) function! man#complete(arg_lead, cmd_line, cursor_pos) abort let args = split(a:cmd_line) let cmd_offset = index(args, 'Man') @@ -332,15 +372,26 @@ function! s:get_paths(sect, name, do_fallback) abort " callers must try-catch this, as some `man` implementations don't support `s:find_arg` try let mandirs = join(split(s:system(['man', s:find_arg]), ':\|\n'), ',') - return globpath(mandirs,'man?/'.a:name.'*.'.a:sect.'*', 0, 1) + let paths = globpath(mandirs, 'man?/'.a:name.'*.'.a:sect.'*', 0, 1) + try + " Prioritize the result from verify_exists as it obeys b:man_default_sects. + let first = s:verify_exists(a:sect, a:name) + let paths = filter(paths, 'v:val !=# first') + let paths = [first] + paths + catch + endtry + return paths catch if !a:do_fallback throw v:exception endif - " fallback to a single path, with the page we're trying to find - let [l:sect, l:name, l:path] = s:verify_exists(a:sect, a:name) - return [l:path] + " Fallback to a single path, with the page we're trying to find. + try + return [s:verify_exists(a:sect, a:name)] + catch + return [] + endtry endtry endfunction @@ -379,7 +430,7 @@ function! man#init_pager() abort " know the correct casing, cf. `man glDrawArraysInstanced`). let ref = substitute(matchstr(getline(1), '^[^)]\+)'), ' ', '_', 'g') try - let b:man_sect = man#extract_sect_and_name_ref(ref)[0] + let b:man_sect = s:extract_sect_and_name_ref(ref)[0] catch let b:man_sect = '' endtry @@ -391,7 +442,7 @@ function! man#init_pager() abort endfunction function! man#goto_tag(pattern, flags, info) abort - let [l:sect, l:name] = man#extract_sect_and_name_ref(a:pattern) + let [l:sect, l:name] = s:extract_sect_and_name_ref(a:pattern) let l:paths = s:get_paths(l:sect, l:name, v:true) let l:structured = [] @@ -401,9 +452,6 @@ function! man#goto_tag(pattern, flags, info) abort 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 }) - if &cscopetag " return only a single entry so we work well with :cstag (#11675) let l:structured = l:structured[:0] diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 1c1baa943a..f9a5d36205 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2019,11 +2019,12 @@ argidx() Number current index in the argument list arglistid([{winnr} [, {tabnr}]]) Number argument list id argv({nr} [, {winid}]) String {nr} entry of the argument list argv([-1, {winid}]) List the argument list +asin({expr}) Float arc sine of {expr} assert_beeps({cmd}) Number assert {cmd} causes a beep assert_equal({exp}, {act} [, {msg}]) Number assert {exp} is equal to {act} -assert_equalfile({fname-one}, {fname-two}) - Number assert file contents is equal +assert_equalfile({fname-one}, {fname-two} [, {msg}]) + Number assert file contents are equal assert_exception({error} [, {msg}]) Number assert {error} is in v:exception assert_fails({cmd} [, {error}]) Number assert {cmd} fails @@ -2039,7 +2040,6 @@ assert_notmatch({pat}, {text} [, {msg}]) Number assert {pat} not matches {text} assert_report({msg}) Number report a test failure assert_true({actual} [, {msg}]) Number assert {actual} is true -asin({expr}) Float arc sine of {expr} atan({expr}) Float arc tangent of {expr} atan2({expr}, {expr}) Float arc tangent of {expr1} / {expr2} browse({save}, {title}, {initdir}, {default}) @@ -2630,7 +2630,7 @@ assert_equal({expected}, {actual}, [, {msg}]) test.vim line 12: Expected 'foo' but got 'bar' ~ *assert_equalfile()* -assert_equalfile({fname-one}, {fname-two}) +assert_equalfile({fname-one}, {fname-two} [, {msg}]) When the files {fname-one} and {fname-two} do not contain exactly the same text an error message is added to |v:errors|. Also see |assert-return|. diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 15587955de..b934d2dfa0 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -758,13 +758,14 @@ code_action({context}) *vim.lsp.buf.code_action()* TODO: Documentation completion({context}) *vim.lsp.buf.completion()* - TODO: Documentation + Retrieves the completion items at the current cursor position. + Can only be called in Insert mode. declaration() *vim.lsp.buf.declaration()* - TODO: Documentation + Jumps to the declaration of the symbol under the cursor. definition() *vim.lsp.buf.definition()* - TODO: Documentation + Jumps to the definition of the symbol under the cursor. document_highlight() *vim.lsp.buf.document_highlight()* Send request to server to resolve document highlights for the @@ -777,13 +778,18 @@ document_highlight() *vim.lsp.buf.document_highlight()* < document_symbol() *vim.lsp.buf.document_symbol()* - TODO: Documentation + Lists all symbols in the current buffer in the quickfix + window. execute_command({command}) *vim.lsp.buf.execute_command()* TODO: Documentation formatting({options}) *vim.lsp.buf.formatting()* - TODO: Documentation + Formats the current buffer. + + The optional {options} table can be used to specify + FormattingOptions, a list of which is available at https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting . Some unspecified options will be automatically derived from + the current Neovim options. *vim.lsp.buf.formatting_sync()* formatting_sync({options}, {timeout_ms}) @@ -794,10 +800,13 @@ formatting_sync({options}, {timeout_ms}) |vim.lsp.buf_request_sync()|. hover() *vim.lsp.buf.hover()* - TODO: Documentation + Displays hover information about the symbol under the cursor + in a floating window. Calling the function twice will jump + into the floating window. implementation() *vim.lsp.buf.implementation()* - TODO: Documentation + Lists all the implementations for the symbol under the cursor + in the quickfix window. npcall({fn}, {...}) *vim.lsp.buf.npcall()* TODO: Documentation @@ -810,10 +819,13 @@ range_formatting({options}, {start_pos}, {end_pos}) TODO: Documentation references({context}) *vim.lsp.buf.references()* - TODO: Documentation + Lists all the references to the symbol under the cursor in the + quickfix window. rename({new_name}) *vim.lsp.buf.rename()* - TODO: Documentation + Renames all references to the symbol under the cursor. If + {new_name} is not provided, the user will be prompted for a + new name using |input()|. request({method}, {params}, {callback}) *vim.lsp.buf.request()* TODO: Documentation @@ -823,10 +835,12 @@ server_ready() *vim.lsp.buf.server_ready()* `true` if server responds. signature_help() *vim.lsp.buf.signature_help()* - TODO: Documentation + Displays signature information about the symbol under the + cursor in a floating window. type_definition() *vim.lsp.buf.type_definition()* - TODO: Documentation + Jumps to the definition of the type of the symbol under the + cursor. workspace_symbol({query}) *vim.lsp.buf.workspace_symbol()* Lists all symbols in the current workspace in the quickfix @@ -837,6 +851,16 @@ workspace_symbol({query}) *vim.lsp.buf.workspace_symbol()* enter a string on the command line. An empty string means no filtering is done. +incoming_calls() *vim.lsp.buf.incoming_calls()* + Lists all the call sites of the symbol under the cursor in the + |quickfix| window. If the symbol can resolve to multiple + items, the user can pick one in the |inputlist|. + +outgoing_calls() *vim.lsp.buf.outgoing_calls()* + Lists all the items that are called by the symbol under the + cursor in the |quickfix| window. If the symbol can resolve to + multiple items, the user can pick one in the |inputlist|. + ============================================================================== Lua module: vim.lsp.callbacks *lsp-callbacks* diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim index 081181cfe9..0416e41368 100644 --- a/runtime/ftplugin/man.vim +++ b/runtime/ftplugin/man.vim @@ -6,7 +6,7 @@ if exists('b:did_ftplugin') || &filetype !=# 'man' endif let b:did_ftplugin = 1 -let s:pager = get(s:, 'pager', 0) || !exists('b:man_sect') +let s:pager = !exists('b:man_sect') if s:pager call man#init_pager() @@ -26,7 +26,7 @@ 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> - if 1 == bufnr('%') || s:pager + if s:pager nnoremap <silent> <buffer> <nowait> q :lclose<CR>:q<CR> else nnoremap <silent> <buffer> <nowait> q :lclose<CR><C-W>c diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 7442f0c0b5..6fe1d15b7e 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -511,6 +511,7 @@ function lsp.start_client(config) or (not client.resolved_capabilities.type_definition and method == 'textDocument/typeDefinition') or (not client.resolved_capabilities.document_symbol and method == 'textDocument/documentSymbol') or (not client.resolved_capabilities.workspace_symbol and method == 'textDocument/workspaceSymbol') + or (not client.resolved_capabilities.call_hierarchy and method == 'textDocument/prepareCallHierarchy') then callback(unsupported_method(method), method, nil, client_id, bufnr) return diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 839e00c67d..2e27617997 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -30,43 +30,63 @@ function M.server_ready() return not not vim.lsp.buf_notify(0, "window/progress", {}) end +--- Displays hover information about the symbol under the cursor in a floating +--- window. Calling the function twice will jump into the floating window. function M.hover() local params = util.make_position_params() request('textDocument/hover', params) end +--- Jumps to the declaration of the symbol under the cursor. +--- function M.declaration() local params = util.make_position_params() request('textDocument/declaration', params) end +--- Jumps to the definition of the symbol under the cursor. +--- function M.definition() local params = util.make_position_params() request('textDocument/definition', params) end +--- Jumps to the definition of the type of the symbol under the cursor. +--- function M.type_definition() local params = util.make_position_params() request('textDocument/typeDefinition', params) end +--- Lists all the implementations for the symbol under the cursor in the +--- quickfix window. function M.implementation() local params = util.make_position_params() request('textDocument/implementation', params) end +--- Displays signature information about the symbol under the cursor in a +--- floating window. function M.signature_help() local params = util.make_position_params() request('textDocument/signatureHelp', params) end --- TODO(ashkan) ? +--- Retrieves the completion items at the current cursor position. Can only be +--- called in Insert mode. function M.completion(context) local params = util.make_position_params() params.context = context return request('textDocument/completion', params) end +--- Formats the current buffer. +--- +--- The optional {options} table can be used to specify FormattingOptions, a +--- list of which is available at +--- https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting. +--- Some unspecified options will be automatically derived from the current +--- Neovim options. function M.formatting(options) local params = util.make_formatting_params(options) return request('textDocument/formatting', params) @@ -118,6 +138,8 @@ function M.range_formatting(options, start_pos, end_pos) return request('textDocument/rangeFormatting', params) end +--- Renames all references to the symbol under the cursor. If {new_name} is not +--- provided, the user will be prompted for a new name using |input()|. 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. @@ -128,6 +150,8 @@ function M.rename(new_name) request('textDocument/rename', params) end +--- Lists all the references to the symbol under the cursor in the quickfix window. +--- function M.references(context) validate { context = { context, 't', true } } local params = util.make_position_params() @@ -138,11 +162,45 @@ function M.references(context) request('textDocument/references', params) end +--- Lists all symbols in the current buffer in the quickfix window. +--- function M.document_symbol() local params = { textDocument = util.make_text_document_params() } request('textDocument/documentSymbol', params) end +local function pick_call_hierarchy_item(call_hierarchy_items) + if not call_hierarchy_items then return end + if #call_hierarchy_items == 1 then + return call_hierarchy_items[1] + end + local items = {} + for i, item in ipairs(call_hierarchy_items) do + local entry = item.detail or item.name + table.insert(items, string.format("%d. %s", i, entry)) + end + local choice = vim.fn.inputlist(items) + if choice < 1 or choice > #items then + return + end + return choice +end + +function M.incoming_calls() + local params = util.make_position_params() + request('textDocument/prepareCallHierarchy', params, function(_, _, result) + local call_hierarchy_item = pick_call_hierarchy_item(result) + vim.lsp.buf_request(0, 'callHierarchy/incomingCalls', { item = call_hierarchy_item }) + end) +end + +function M.outgoing_calls() + local params = util.make_position_params() + request('textDocument/prepareCallHierarchy', params, function(_, _, result) + local call_hierarchy_item = pick_call_hierarchy_item(result) + vim.lsp.buf_request(0, 'callHierarchy/outgoingCalls', { item = call_hierarchy_item }) + end) +end --- Lists all symbols in the current workspace in the quickfix window. --- diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 4b14f0132d..1ed58995d0 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -214,6 +214,33 @@ M['textDocument/documentHighlight'] = function(_, _, result, _) util.buf_highlight_references(bufnr, result) end +-- direction is "from" for incoming calls and "to" for outgoing calls +local make_call_hierarchy_callback = function(direction) + -- result is a CallHierarchy{Incoming,Outgoing}Call[] + return function(_, _, result) + if not result then return end + local items = {} + for _, call_hierarchy_call in pairs(result) do + local call_hierarchy_item = call_hierarchy_call[direction] + for _, range in pairs(call_hierarchy_call.fromRanges) do + table.insert(items, { + filename = assert(vim.uri_to_fname(call_hierarchy_item.uri)), + text = call_hierarchy_item.name, + lnum = range.start.line + 1, + col = range.start.character + 1, + }) + end + end + util.set_qflist(items) + api.nvim_command("copen") + api.nvim_command("wincmd p") + end +end + +M['callHierarchy/incomingCalls'] = make_call_hierarchy_callback('from') + +M['callHierarchy/outgoingCalls'] = make_call_hierarchy_callback('to') + M['window/logMessage'] = function(_, _, result, client_id) local message_type = result.type local message = result.message diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 4fded1961d..ef5e08680e 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -713,6 +713,9 @@ function protocol.make_client_capabilities() }; applyEdit = true; }; + callHierarchy = { + dynamicRegistration = false; + }; experimental = nil; } end @@ -912,6 +915,7 @@ function protocol.resolve_capabilities(server_capabilities) general_properties.workspace_symbol = server_capabilities.workspaceSymbolProvider or false general_properties.document_formatting = server_capabilities.documentFormattingProvider or false general_properties.document_range_formatting = server_capabilities.documentRangeFormattingProvider or false + general_properties.call_hierarchy = server_capabilities.callHierarchyProvider or false if server_capabilities.codeActionProvider == nil then general_properties.code_action = false |