aboutsummaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/autoload/health/provider.vim7
-rw-r--r--runtime/autoload/man.vim100
-rw-r--r--runtime/doc/eval.txt8
-rw-r--r--runtime/doc/lsp.txt46
-rw-r--r--runtime/ftplugin/man.vim4
-rw-r--r--runtime/lua/vim/lsp.lua1
-rw-r--r--runtime/lua/vim/lsp/buf.lua60
-rw-r--r--runtime/lua/vim/lsp/callbacks.lua27
-rw-r--r--runtime/lua/vim/lsp/protocol.lua4
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