mirror of
https://github.com/neovim/neovim.git
synced 2025-10-22 17:11:49 +00:00
lsp: add 'textDocument/documentSymbol’ callback
Spec: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
This commit is contained in:
@@ -48,6 +48,7 @@ go-to-definition, hover, etc. Example config: >
|
|||||||
nnoremap <silent> <c-k> <cmd>lua vim.lsp.buf.signature_help()<CR>
|
nnoremap <silent> <c-k> <cmd>lua vim.lsp.buf.signature_help()<CR>
|
||||||
nnoremap <silent> 1gD <cmd>lua vim.lsp.buf.type_definition()<CR>
|
nnoremap <silent> 1gD <cmd>lua vim.lsp.buf.type_definition()<CR>
|
||||||
nnoremap <silent> gr <cmd>lua vim.lsp.buf.references()<CR>
|
nnoremap <silent> gr <cmd>lua vim.lsp.buf.references()<CR>
|
||||||
|
nnoremap <silent> g0 <cmd>lua vim.lsp.buf.document_symbol()<CR>
|
||||||
|
|
||||||
Nvim provides the |vim.lsp.omnifunc| 'omnifunc' handler which allows
|
Nvim provides the |vim.lsp.omnifunc| 'omnifunc' handler which allows
|
||||||
|i_CTRL-X_CTRL-O| to consume LSP completion. Example config (note the use of
|
|i_CTRL-X_CTRL-O| to consume LSP completion. Example config (note the use of
|
||||||
@@ -733,6 +734,9 @@ definition() *vim.lsp.buf.definition()*
|
|||||||
document_highlight() *vim.lsp.buf.document_highlight()*
|
document_highlight() *vim.lsp.buf.document_highlight()*
|
||||||
TODO: Documentation
|
TODO: Documentation
|
||||||
|
|
||||||
|
document_symbol() *vim.lsp.buf.document_symbol()*
|
||||||
|
TODO: Documentation
|
||||||
|
|
||||||
formatting({options}) *vim.lsp.buf.formatting()*
|
formatting({options}) *vim.lsp.buf.formatting()*
|
||||||
TODO: Documentation
|
TODO: Documentation
|
||||||
|
|
||||||
|
@@ -525,14 +525,15 @@ function lsp.start_client(config)
|
|||||||
callback = resolve_callback(method)
|
callback = resolve_callback(method)
|
||||||
or error("not found: request callback for client "..client.name)
|
or error("not found: request callback for client "..client.name)
|
||||||
end
|
end
|
||||||
local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback)
|
local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback, bufnr)
|
||||||
-- TODO keep these checks or just let it go anyway?
|
-- TODO keep these checks or just let it go anyway?
|
||||||
if (not client.resolved_capabilities.hover and method == 'textDocument/hover')
|
if (not client.resolved_capabilities.hover and method == 'textDocument/hover')
|
||||||
or (not client.resolved_capabilities.signature_help and method == 'textDocument/signatureHelp')
|
or (not client.resolved_capabilities.signature_help and method == 'textDocument/signatureHelp')
|
||||||
or (not client.resolved_capabilities.goto_definition and method == 'textDocument/definition')
|
or (not client.resolved_capabilities.goto_definition and method == 'textDocument/definition')
|
||||||
or (not client.resolved_capabilities.implementation and method == 'textDocument/implementation')
|
or (not client.resolved_capabilities.implementation and method == 'textDocument/implementation')
|
||||||
|
or (not client.resolved_capabilities.document_symbol and method == 'textDocument/documentSymbol')
|
||||||
then
|
then
|
||||||
callback(unsupported_method(method), method, nil, client_id)
|
callback(unsupported_method(method), method, nil, client_id, bufnr)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
return rpc.request(method, params, function(err, result)
|
return rpc.request(method, params, function(err, result)
|
||||||
@@ -874,7 +875,7 @@ end
|
|||||||
function lsp.buf_request_sync(bufnr, method, params, timeout_ms)
|
function lsp.buf_request_sync(bufnr, method, params, timeout_ms)
|
||||||
local request_results = {}
|
local request_results = {}
|
||||||
local result_count = 0
|
local result_count = 0
|
||||||
local function _callback(err, _method, result, client_id, bufnr)
|
local function _callback(err, _method, result, client_id)
|
||||||
request_results[client_id] = { error = err, result = result }
|
request_results[client_id] = { error = err, result = result }
|
||||||
result_count = result_count + 1
|
result_count = result_count + 1
|
||||||
end
|
end
|
||||||
@@ -905,7 +906,7 @@ function lsp.buf_notify(bufnr, method, params)
|
|||||||
method = { method, 's' };
|
method = { method, 's' };
|
||||||
}
|
}
|
||||||
local resp = false
|
local resp = false
|
||||||
for_each_buffer_client(bufnr, function(client, _client_id)
|
for_each_buffer_client(bufnr, function(client, _client_id, _resolved_bufnr)
|
||||||
if client.rpc.notify(method, params) then resp = true end
|
if client.rpc.notify(method, params) then resp = true end
|
||||||
end)
|
end)
|
||||||
return resp
|
return resp
|
||||||
|
@@ -38,7 +38,13 @@ end
|
|||||||
|
|
||||||
M['textDocument/references'] = function(_, _, result)
|
M['textDocument/references'] = function(_, _, result)
|
||||||
if not result then return end
|
if not result then return end
|
||||||
util.set_qflist(result)
|
util.set_qflist(util.locations_to_items(result))
|
||||||
|
end
|
||||||
|
|
||||||
|
M['textDocument/documentSymbol'] = function(_, _, result, _, bufnr)
|
||||||
|
if not result or vim.tbl_isempty(result) then return end
|
||||||
|
|
||||||
|
util.set_qflist(util.symbols_to_items(result, bufnr))
|
||||||
api.nvim_command("copen")
|
api.nvim_command("copen")
|
||||||
api.nvim_command("wincmd p")
|
api.nvim_command("wincmd p")
|
||||||
end
|
end
|
||||||
@@ -97,7 +103,7 @@ local function location_callback(_, method, result)
|
|||||||
end
|
end
|
||||||
util.jump_to_location(result[1])
|
util.jump_to_location(result[1])
|
||||||
if #result > 1 then
|
if #result > 1 then
|
||||||
util.set_qflist(result)
|
util.set_qflist(util.locations_to_items(result))
|
||||||
api.nvim_command("copen")
|
api.nvim_command("copen")
|
||||||
api.nvim_command("wincmd p")
|
api.nvim_command("wincmd p")
|
||||||
end
|
end
|
||||||
|
@@ -663,19 +663,19 @@ function protocol.make_client_capabilities()
|
|||||||
documentHighlight = {
|
documentHighlight = {
|
||||||
dynamicRegistration = false
|
dynamicRegistration = false
|
||||||
};
|
};
|
||||||
-- documentSymbol = {
|
documentSymbol = {
|
||||||
-- dynamicRegistration = false;
|
dynamicRegistration = false;
|
||||||
-- symbolKind = {
|
symbolKind = {
|
||||||
-- valueSet = (function()
|
valueSet = (function()
|
||||||
-- local res = {}
|
local res = {}
|
||||||
-- for k in pairs(protocol.SymbolKind) do
|
for k in pairs(protocol.SymbolKind) do
|
||||||
-- if type(k) == 'string' then table.insert(res, k) end
|
if type(k) == 'number' then table.insert(res, k) end
|
||||||
-- end
|
end
|
||||||
-- return res
|
return res
|
||||||
-- end)();
|
end)();
|
||||||
-- };
|
};
|
||||||
-- hierarchicalDocumentSymbolSupport = false;
|
hierarchicalDocumentSymbolSupport = true;
|
||||||
-- };
|
};
|
||||||
};
|
};
|
||||||
workspace = nil;
|
workspace = nil;
|
||||||
experimental = nil;
|
experimental = nil;
|
||||||
|
@@ -834,23 +834,60 @@ function M.locations_to_items(locations)
|
|||||||
return items
|
return items
|
||||||
end
|
end
|
||||||
|
|
||||||
-- locations is Location[]
|
function M.set_loclist(items)
|
||||||
-- Only sets for the current window.
|
|
||||||
function M.set_loclist(locations)
|
|
||||||
vim.fn.setloclist(0, {}, ' ', {
|
vim.fn.setloclist(0, {}, ' ', {
|
||||||
title = 'Language Server';
|
title = 'Language Server';
|
||||||
items = M.locations_to_items(locations);
|
items = items;
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- locations is Location[]
|
function M.set_qflist(items)
|
||||||
function M.set_qflist(locations)
|
|
||||||
vim.fn.setqflist({}, ' ', {
|
vim.fn.setqflist({}, ' ', {
|
||||||
title = 'Language Server';
|
title = 'Language Server';
|
||||||
items = M.locations_to_items(locations);
|
items = items;
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Convert symbols to quickfix list items
|
||||||
|
---
|
||||||
|
--@symbols DocumentSymbol[] or SymbolInformation[]
|
||||||
|
function M.symbols_to_items(symbols, bufnr)
|
||||||
|
local function _symbols_to_items(_symbols, _items, _bufnr)
|
||||||
|
for _, symbol in ipairs(_symbols) do
|
||||||
|
if symbol.location then -- SymbolInformation type
|
||||||
|
local range = symbol.location.range
|
||||||
|
local kind = protocol.SymbolKind[symbol.kind]
|
||||||
|
table.insert(_items, {
|
||||||
|
filename = vim.uri_to_fname(symbol.location.uri),
|
||||||
|
lnum = range.start.line + 1,
|
||||||
|
col = range.start.character + 1,
|
||||||
|
kind = kind,
|
||||||
|
text = '['..kind..'] '..symbol.name,
|
||||||
|
})
|
||||||
|
elseif symbol.range then -- DocumentSymbole type
|
||||||
|
local kind = protocol.SymbolKind[symbol.kind]
|
||||||
|
table.insert(_items, {
|
||||||
|
-- bufnr = _bufnr,
|
||||||
|
filename = vim.api.nvim_buf_get_name(_bufnr),
|
||||||
|
lnum = symbol.range.start.line + 1,
|
||||||
|
col = symbol.range.start.character + 1,
|
||||||
|
kind = kind,
|
||||||
|
text = '['..kind..'] '..symbol.name
|
||||||
|
})
|
||||||
|
if symbol.children then
|
||||||
|
for _, child in ipairs(symbol) do
|
||||||
|
for _, v in ipairs(_symbols_to_items(child, _items, _bufnr)) do
|
||||||
|
vim.list_extend(_items, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return _items
|
||||||
|
end
|
||||||
|
return _symbols_to_items(symbols, {}, bufnr)
|
||||||
|
end
|
||||||
|
|
||||||
-- Remove empty lines from the beginning and end.
|
-- Remove empty lines from the beginning and end.
|
||||||
function M.trim_empty_lines(lines)
|
function M.trim_empty_lines(lines)
|
||||||
local start = 1
|
local start = 1
|
||||||
@@ -903,11 +940,15 @@ function M.make_position_params()
|
|||||||
local line = api.nvim_buf_get_lines(0, row, row+1, true)[1]
|
local line = api.nvim_buf_get_lines(0, row, row+1, true)[1]
|
||||||
col = str_utfindex(line, col)
|
col = str_utfindex(line, col)
|
||||||
return {
|
return {
|
||||||
textDocument = { uri = vim.uri_from_bufnr(0) };
|
textDocument = M.make_text_document_params();
|
||||||
position = { line = row; character = col; }
|
position = { line = row; character = col; }
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function M.make_text_document_params()
|
||||||
|
return { uri = vim.uri_from_bufnr(0) }
|
||||||
|
end
|
||||||
|
|
||||||
-- @param buf buffer handle or 0 for current.
|
-- @param buf buffer handle or 0 for current.
|
||||||
-- @param row 0-indexed line
|
-- @param row 0-indexed line
|
||||||
-- @param col 0-indexed byte offset in line
|
-- @param col 0-indexed byte offset in line
|
||||||
|
@@ -241,7 +241,7 @@ describe('LSP', function()
|
|||||||
|
|
||||||
it('should succeed with manual shutdown', function()
|
it('should succeed with manual shutdown', function()
|
||||||
local expected_callbacks = {
|
local expected_callbacks = {
|
||||||
{NIL, "shutdown", {}, 1};
|
{NIL, "shutdown", {}, 1, NIL};
|
||||||
{NIL, "test", {}, 1};
|
{NIL, "test", {}, 1};
|
||||||
}
|
}
|
||||||
test_rpc_server {
|
test_rpc_server {
|
||||||
|
Reference in New Issue
Block a user