Skip to content

Commit

Permalink
lsp: Multiple diagnostic updates
Browse files Browse the repository at this point in the history
  • Loading branch information
tjdevries committed Sep 9, 2020
1 parent aa45579 commit 3a09fae
Show file tree
Hide file tree
Showing 7 changed files with 1,163 additions and 189 deletions.
5 changes: 5 additions & 0 deletions runtime/lua/vim/lsp.lua
Expand Up @@ -1182,6 +1182,11 @@ function lsp.get_log_path()
end
-- Defines the LspDiagnostics signs if they're not defined already.
function lsp.for_each_buffer_client(...)
return for_each_buffer_client(...)
end
-- Define the LspDiagnostics signs if they're not defined already.
do
--@private
--- Defines a sign if it isn't already defined.
Expand Down
168 changes: 168 additions & 0 deletions runtime/lua/vim/lsp/actions.lua
@@ -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

0 comments on commit 3a09fae

Please sign in to comment.