Neovim offers many functions to perform LSP actions, like vim.lsp.buf.definition()
, but I don’t get how to customize those actions.
For example, is there a way to call vim.lsp.buf.definition()
, but opening the definition in a new split?
Generally, this is the way it works:
- You, or an automatic trigger (like changing text), induces a request from client to server. In the case of vim.lsp.buf.definition() this triggers a json.rpc request from client to server (manually) here.
- Neovim receives a response from the server, which is matched against the list of handlers here
- In this case, there is a tiny bit of indirection from the definition handler to the location_handler function, which calls jump_to_location, the function responsible for opening and moving to the new file/buffer.
If you want to override this functionality, you can do something like this in your init.vim (in a lua heredoc) or init.lua. Keep in mind, you have to bring all methods you want to use in scope, so the following won’t run. You’ll need to replace util.jump_to_location with something that handles opening the result in a new split.
vim.lsp.handlers["textDocument/publishDiagnostics"] = function(_, method, result)
if result == nil or vim.tbl_isempty(result) then
local _ = log.info() and log.info(method, 'No location found')
return nil
end
-- textDocument/definition can return Location or Location[]
-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition
if vim.tbl_islist(result) then
util.jump_to_location(result[1])
if #result > 1 then
util.set_qflist(util.locations_to_items(result))
api.nvim_command("copen")
api.nvim_command("wincmd p")
end
else
util.jump_to_location(result)
end
end
I already do that you check my code
function M.goto_definition(opts)
opts = opts or {}
local params = vim.lsp.util.make_position_params()
-- need to process when it have multiple result and multiple language server
local results_lsp = vim.lsp.buf_request_sync(0, "textDocument/definition", params, opts.timeout or 10000)
if not results_lsp or vim.tbl_isempty(results_lsp) then
print("No results from textDocument/definition")
return
end
for _, lsp_data in pairs(results_lsp) do
if lsp_data ~= nil and lsp_data.result ~= nil and not vim.tbl_isempty(lsp_data.result) then
for _, value in pairs(lsp_data.result) do
local range = value.range or value.targetRange
if range ~= nil then
local line = range.start.line
local character = range.start.character
local file = value.uri or value.targetUri
-- skip node module
-- if file ~=nil and not string.match(file,'node_modules') then
if file ~=nil then
-- mark current cursor open to jumplist
vim.api.nvim_command[[execute "normal! m` "]]
my_open({file , line + 1 , character + 1})
vim.api.nvim_feedkeys('zz','n',true)
return
end
end
end
end
end
-- try to call default lsp function
vim.lsp.buf.definition()
end
thanks @mjlbach and @windwp for your responses, knowing better how nvim lsp works I’ll probably adapt @windwp code to have my custom solution.
In any case, it makes sense to me to have some helper functions on nvim or nvim-lsp to customize how to open the definition, type definition, etc. I seems a very common operation, and I guess more people would like to open the definition in a split
Looks like there is already some WIP to make the jump-to-location configurable: