-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
1,163 additions
and
189 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
--- @brief actions.lua | ||
--- | ||
--- Define common actions that can be taken on different LSP structures. | ||
--- | ||
--- Module structure is: | ||
--- | ||
--- actions.<lsp_structure_name>.<action_name> | ||
--- | ||
--- Where: | ||
--- lsp_structure_name: | ||
--- | ||
--- Name of the structure as defined in the LSP specification. | ||
--- For example: Location, LocationLink, Hover, etc. | ||
--- | ||
--- All structures are assumed to be (wherever possible) either a singular or list of those items. | ||
--- The LSP spec often has something return either `Location` or `Location[]`. | ||
--- All callbacks here should be able to handle either of them. | ||
--- | ||
--- action_name: | ||
--- Name of what the user can expect to happen when this function is called. | ||
--- | ||
--- Example: | ||
--- actions.Location.jump_first will jump to the singular Location passed or the first Location in a Location[]. | ||
--- | ||
--- In general, actions should handle a singular or list of objects where it makes sense (as in the above). | ||
--- | ||
--- All actions should return a function that returns a function of that has the structure of: > | ||
--- function(err, method, params, client_id, bufnr) | ||
--- < | ||
--- | ||
--- which is the same interface as those called by the client after an asynchronous request. | ||
--- | ||
--- All builtin actions are wrapped in a metatable that allows two conveniences: | ||
--- 1. When calling the `action` directly, it will perform the default action. | ||
--- You can write the code directly as a call (through the use of `__call` metamethod): | ||
--- <pre> | ||
--- Location.jump_first(_, method, result) | ||
--- </pre> | ||
--- | ||
--- 2. Allows for storing a callback with different configuration by using the `with` key: | ||
--- <pre> | ||
--- local my_highlight = Location.highlight.with { higroup = 'Substitute', timeout = 100 } | ||
--- </pre> | ||
--- | ||
--- Don't make this a local variable if you want to use it in a mapping, it needs to be global to be accessed easily. | ||
--- The convention is to prefix with `on` to differentiate from other Location objects- from other namespaces. | ||
--- | ||
--- <pre> | ||
--- onLocation = require('vim.lsp.actions').Location | ||
--- | ||
--- -- only `jump` to definition | ||
--- vim.lsp.buf.definition { callbacks = onLocation.jump_first } | ||
--- | ||
--- -- `jump` to definition, and then `highlight` the item | ||
--- vim.lsp.buf.definition { callbacks = { onLocation.jump_first, onLocation.highlight } } | ||
--- | ||
--- -- `jump to definition, and then perform a `highlight` action with `higroup` set to 'Substitute' and timeout set to 2s | ||
--- vim.lsp.buf.definition { | ||
--- callbacks = { | ||
--- onLocation.jump_first, onLocation.highlight.with { higroup = 'Substitute', timeout = 2000 } | ||
--- } | ||
--- } | ||
--- </pre> | ||
|
||
local api = vim.api | ||
|
||
local log = require('vim.lsp.log') | ||
local structures = require('vim.lsp.structures') | ||
local util = require('vim.lsp.util') | ||
|
||
local actions = {} | ||
|
||
--- Diagnostic Actions | ||
actions.Diagnostic = {} | ||
|
||
--- Handle the caching and display of diagnostics from the `textDocument/publishDiagnostics` notification. | ||
-- | ||
--@param args: Config table: ~ | ||
-- should_underline (bool): Should underlines be displayed | ||
-- update_in_insert (bool): Should diagnostic displays be updated while in insert mode | ||
actions.Diagnostic.handle_publish_diagnostics = function(args) | ||
-- TODO(tjdevries): This should be tied somehow with the function that exists in structures.Diagnostic... | ||
args = vim.tbl_extend("force", { | ||
should_underline = true, | ||
update_in_insert = true, | ||
}, args or {}) | ||
|
||
return function(_, method, notification, client_id) | ||
local uri = notification.uri | ||
local bufnr = vim.uri_to_bufnr(uri) | ||
if not bufnr then | ||
return | ||
end | ||
|
||
-- Unloaded buffers should not handle diagnostics. | ||
-- When the buffer is loaded, we'll call on_attach, which sends textDocument/didOpen. | ||
-- This should trigger another publish of the diagnostics. | ||
-- | ||
-- In particular, this stops a ton of spam when first starting a server for current | ||
-- unloaded buffers. | ||
if not api.nvim_buf_is_loaded(bufnr) then | ||
return | ||
end | ||
|
||
local diagnostics = notification.diagnostics | ||
|
||
-- util.buf_diagnostics_save_positions(bufnr, notification.diagnostics) | ||
structures.Diagnostic.save_buf_diagnostics(diagnostics, bufnr, client_id) | ||
|
||
if args.update_in_insert then | ||
structures.Diagnostic.buf_remove_schedule_display_on_insert_leave(bufnr, client_id) | ||
else | ||
local mode = vim.api.nvim_get_mode() | ||
|
||
if string.sub(mode.mode, 1, 1) == 'i' then | ||
structures.Diagnostic.buf_schedule_display_on_insert_leave(bufnr, client_id, args) | ||
return | ||
end | ||
end | ||
|
||
structures.Diagnostic.display(diagnostics, bufnr, client_id, args) | ||
end | ||
end | ||
|
||
--------------------------------------------------- | ||
-- All built-in LSP structures should be of the right type. | ||
--------------------------------------------------- | ||
local action_mt = { | ||
__call = function(t, err, method, result, client_id, bufnr) | ||
return t.default(err, method, result, client_id, bufnr) | ||
end, | ||
|
||
__index = function(t, k) | ||
if k == 'with' then | ||
return rawget(t, 'generator') | ||
end | ||
|
||
if k == 'default' then | ||
rawset(t, 'default', t.generator()) | ||
return rawget(t, 'default') | ||
end | ||
|
||
error('Unsupported key: ', k) | ||
end, | ||
} | ||
|
||
--- Create generator for nicely configurable callbacks with __call semantics. | ||
-- | ||
-- Useful for creating your own generator functions to be used in configurable | ||
-- callbacks for requests. | ||
local wrap_generator = function(generator) | ||
vim.validate { generator = { generator, 'c' } } | ||
|
||
return setmetatable({ | ||
generator = generator, | ||
_is_generator = true | ||
}, action_mt) | ||
end | ||
|
||
for lsp_structure, action_set in pairs(actions) do | ||
for action_name, generator in pairs(action_set) do | ||
actions[lsp_structure][action_name] = wrap_generator(generator) | ||
end | ||
end | ||
|
||
actions._wrap_generator = wrap_generator | ||
|
||
return actions |
Oops, something went wrong.