aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua')
-rw-r--r--runtime/lua/vim/lsp.lua6
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua4
-rw-r--r--runtime/lua/vim/lsp/handlers.lua44
-rw-r--r--runtime/lua/vim/lsp/protocol.lua3
-rw-r--r--runtime/lua/vim/lsp/util.lua57
-rw-r--r--runtime/lua/vim/treesitter.lua2
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua10
-rw-r--r--runtime/lua/vim/treesitter/query.lua132
8 files changed, 228 insertions, 30 deletions
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index ed31572abb..0326550245 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -569,6 +569,8 @@ function lsp.start_client(config)
-- TODO(remove-callbacks)
callbacks = handlers;
handlers = handlers;
+ -- for $/progress report
+ messages = { name = name, messages = {}, progress = {}, status = {} }
}
-- Store the uninitialized_clients for cleanup in case we exit before initialize finishes.
@@ -881,8 +883,8 @@ function lsp._text_document_did_save_handler(bufnr)
client.notify('textDocument/didSave', {
textDocument = {
uri = uri;
- text = included_text;
- }
+ };
+ text = included_text;
})
end
end)
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index efca5b53af..072349b226 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -1044,6 +1044,8 @@ function M.display(diagnostics, bufnr, client_id, config)
diagnostics = diagnostics or M.get(bufnr, client_id)
+ vim.api.nvim_command("doautocmd <nomodeline> User LspDiagnosticsChanged")
+
if not diagnostics or vim.tbl_isempty(diagnostics) then
return
end
@@ -1062,8 +1064,6 @@ function M.display(diagnostics, bufnr, client_id, config)
if signs_opts then
M.set_signs(diagnostics, bufnr, client_id, nil, signs_opts)
end
-
- vim.api.nvim_command("doautocmd User LspDiagnosticsChanged")
end
-- }}}
-- Diagnostic User Functions {{{
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index e034923afb..359573beb1 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -24,6 +24,47 @@ M['workspace/executeCommand'] = function(err, _)
end
end
+-- @msg of type ProgressParams
+-- Basically a token of type number/string
+local function progress_callback(_, _, params, client_id)
+ local client = vim.lsp.get_client_by_id(client_id)
+ if not client then
+ err_message("LSP[", client_id, "] client has shut down after sending the message")
+ end
+ local val = params.value -- unspecified yet
+ local token = params.token -- string or number
+
+
+ if val.kind then
+ if val.kind == 'begin' then
+ client.messages.progress[token] = {
+ title = val.title,
+ message = val.message,
+ percentage = val.percentage,
+ }
+ elseif val.kind == 'report' then
+ client.messages.progress[token] = {
+ message = val.message,
+ percentage = val.percentage,
+ }
+ elseif val.kind == 'end' then
+ if client.messages.progress[token] == nil then
+ err_message(
+ 'echom "[lsp-status] Received `end` message with no corresponding `begin` from "')
+ else
+ client.messages.progress[token].message = val.message
+ client.messages.progress[token].done = true
+ end
+ end
+ else
+ table.insert(client.messages, {content = val, show_once = true, shown = 0})
+ end
+
+ vim.api.nvim_command("doautocmd <nomodeline> User LspProgressUpdate")
+end
+
+M['$/progress'] = progress_callback
+
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
M['textDocument/codeAction'] = function(_, _, actions)
if actions == nil or vim.tbl_isempty(actions) then
@@ -212,9 +253,8 @@ M['textDocument/signatureHelp'] = function(_, method, result)
end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight
-M['textDocument/documentHighlight'] = function(_, _, result, _)
+M['textDocument/documentHighlight'] = function(_, _, result, _, bufnr, _)
if not result then return end
- local bufnr = api.nvim_get_current_buf()
util.buf_highlight_references(bufnr, result)
end
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index fd86d8bb97..b785d2f586 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -728,6 +728,9 @@ function protocol.make_client_capabilities()
dynamicRegistration = false;
};
experimental = nil;
+ window = {
+ workDoneProgress = true;
+ }
}
end
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 3da4dd6219..0972ea83c4 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -120,6 +120,63 @@ local function get_line_byte_from_position(bufnr, position)
return col
end
+--- Process and return progress reports from lsp server
+function M.get_progress_messages()
+
+ local new_messages = {}
+ local msg_remove = {}
+ local progress_remove = {}
+
+ for _, client in ipairs(vim.lsp.get_active_clients()) do
+ local messages = client.messages
+ local data = messages
+ for token, ctx in pairs(data.progress) do
+
+ local new_report = {
+ name = data.name,
+ title = ctx.title or "empty title",
+ message = ctx.message,
+ percentage = ctx.percentage,
+ progress = true,
+ }
+ table.insert(new_messages, new_report)
+
+ if ctx.done then
+ table.insert(progress_remove, {client = client, token = token})
+ end
+ end
+
+ for i, msg in ipairs(data.messages) do
+ if msg.show_once then
+ msg.shown = msg.shown + 1
+ if msg.shown > 1 then
+ table.insert(msg_remove, {client = client, idx = i})
+ end
+ end
+
+ table.insert(new_messages, {name = data.name, content = msg.content})
+ end
+
+ if next(data.status) ~= nil then
+ table.insert(new_messages, {
+ name = data.name,
+ content = data.status.content,
+ uri = data.status.uri,
+ status = true
+ })
+ end
+ for _, item in ipairs(msg_remove) do
+ table.remove(client.messages, item.idx)
+ end
+
+ for _, item in ipairs(progress_remove) do
+ client.messages.progress[item.token] = nil
+ end
+ end
+
+ return new_messages
+end
+
--- Applies a list of text edits to a buffer.
--@param text_edits (table) list of `TextEdit` objects
--@param buf_nr (number) Buffer id
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index 6886f0c178..79dcf77f9e 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -50,7 +50,7 @@ function M._create_parser(bufnr, lang, opts)
end
end
- a.nvim_buf_attach(self.bufnr, false, {on_bytes=bytes_cb, on_detach=detach_cb})
+ a.nvim_buf_attach(self.bufnr, false, {on_bytes=bytes_cb, on_detach=detach_cb, preview=true})
self:parse()
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua
index 04b5fee256..9c620c422c 100644
--- a/runtime/lua/vim/treesitter/languagetree.lua
+++ b/runtime/lua/vim/treesitter/languagetree.lua
@@ -289,7 +289,7 @@ function LanguageTree:_get_injections()
local root_node = tree:root()
local start_line, _, end_line, _ = root_node:range()
- for pattern, match in self._injection_query:iter_matches(root_node, self._source, start_line, end_line+1) do
+ for pattern, match, metadata in self._injection_query:iter_matches(root_node, self._source, start_line, end_line+1) do
local lang = nil
local injection_node = nil
local combined = false
@@ -298,9 +298,9 @@ function LanguageTree:_get_injections()
-- using a tag with the language, for example
-- @javascript
for id, node in pairs(match) do
+ local data = metadata[id]
local name = self._injection_query.captures[id]
- -- TODO add a way to offset the content passed to the parser.
- -- Needed to shave off leading quotes and things of that nature.
+ local offset_range = data and data.offset
-- Lang should override any other language tag
if name == "language" then
@@ -308,7 +308,7 @@ function LanguageTree:_get_injections()
elseif name == "combined" then
combined = true
elseif name == "content" then
- injection_node = node
+ injection_node = offset_range or node
-- Ignore any tags that start with "_"
-- Allows for other tags to be used in matches
elseif string.sub(name, 1, 1) ~= "_" then
@@ -317,7 +317,7 @@ function LanguageTree:_get_injections()
end
if not injection_node then
- injection_node = node
+ injection_node = offset_range or node
end
end
end
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 3537ba78f5..682f981fbc 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -92,6 +92,17 @@ local function read_query_files(filenames)
return table.concat(contents, '\n')
end
+local match_metatable = {
+ __index = function(tbl, key)
+ rawset(tbl, key, {})
+ return tbl[key]
+ end
+}
+
+local function new_match_metadata()
+ return setmetatable({}, match_metatable)
+end
+
--- Returns the runtime query {query_name} for {lang}.
--
-- @param lang The language to use for the query
@@ -222,6 +233,44 @@ local predicate_handlers = {
-- As we provide lua-match? also expose vim-match?
predicate_handlers["vim-match?"] = predicate_handlers["match?"]
+
+-- Directives store metadata or perform side effects against a match.
+-- Directives should always end with a `!`.
+-- Directive handler receive the following arguments
+-- (match, pattern, bufnr, predicate)
+local directive_handlers = {
+ ["set!"] = function(_, _, _, pred, metadata)
+ if #pred == 4 then
+ -- (set! @capture "key" "value")
+ metadata[pred[2]][pred[3]] = pred[4]
+ else
+ -- (set! "key" "value")
+ metadata[pred[2]] = pred[3]
+ end
+ end,
+ -- Shifts the range of a node.
+ -- Example: (#offset! @_node 0 1 0 -1)
+ ["offset!"] = function(match, _, _, pred, metadata)
+ local offset_node = match[pred[2]]
+ local range = {offset_node:range()}
+ local start_row_offset = pred[3] or 0
+ local start_col_offset = pred[4] or 0
+ local end_row_offset = pred[5] or 0
+ local end_col_offset = pred[6] or 0
+ local key = pred[7] or "offset"
+
+ range[1] = range[1] + start_row_offset
+ range[2] = range[2] + start_col_offset
+ range[3] = range[3] + end_row_offset
+ range[4] = range[4] + end_col_offset
+
+ -- If this produces an invalid range, we just skip it.
+ if range[1] < range[3] or (range[1] == range[3] and range[2] <= range[4]) then
+ metadata[pred[2]][key] = range
+ end
+ end
+}
+
--- Adds a new predicates to be used in queries
--
-- @param name the name of the predicate, without leading #
@@ -229,12 +278,25 @@ predicate_handlers["vim-match?"] = predicate_handlers["match?"]
-- signature will be (match, pattern, bufnr, predicate)
function M.add_predicate(name, handler, force)
if predicate_handlers[name] and not force then
- a.nvim_err_writeln(string.format("Overriding %s", name))
+ error(string.format("Overriding %s", name))
end
predicate_handlers[name] = handler
end
+--- Adds a new directive to be used in queries
+--
+-- @param name the name of the directive, without leading #
+-- @param handler the handler function to be used
+-- signature will be (match, pattern, bufnr, predicate)
+function M.add_directive(name, handler, force)
+ if directive_handlers[name] and not force then
+ error(string.format("Overriding %s", name))
+ end
+
+ directive_handlers[name] = handler
+end
+
--- Returns the list of currently supported predicates
function M.list_predicates()
return vim.tbl_keys(predicate_handlers)
@@ -244,6 +306,10 @@ local function xor(x, y)
return (x or y) and not (x and y)
end
+local function is_directive(name)
+ return string.sub(name, -1) == "!"
+end
+
function Query:match_preds(match, pattern, source)
local preds = self.info.patterns[pattern]
@@ -254,30 +320,52 @@ function Query:match_preds(match, pattern, source)
-- Also, tree-sitter strips the leading # from predicates for us.
local pred_name
local is_not
- if string.sub(pred[1], 1, 4) == "not-" then
- pred_name = string.sub(pred[1], 5)
- is_not = true
- else
- pred_name = pred[1]
- is_not = false
- end
- local handler = predicate_handlers[pred_name]
+ -- Skip over directives... they will get processed after all the predicates.
+ if not is_directive(pred[1]) then
+ if string.sub(pred[1], 1, 4) == "not-" then
+ pred_name = string.sub(pred[1], 5)
+ is_not = true
+ else
+ pred_name = pred[1]
+ is_not = false
+ end
+
+ local handler = predicate_handlers[pred_name]
- if not handler then
- a.nvim_err_writeln(string.format("No handler for %s", pred[1]))
- return false
- end
+ if not handler then
+ error(string.format("No handler for %s", pred[1]))
+ return false
+ end
- local pred_matches = handler(match, pattern, source, pred)
+ local pred_matches = handler(match, pattern, source, pred)
- if not xor(is_not, pred_matches) then
- return false
+ if not xor(is_not, pred_matches) then
+ return false
+ end
end
end
return true
end
+--- Applies directives against a match and pattern.
+function Query:apply_directives(match, pattern, source, metadata)
+ local preds = self.info.patterns[pattern]
+
+ for _, pred in pairs(preds or {}) do
+ if is_directive(pred[1]) then
+ local handler = directive_handlers[pred[1]]
+
+ if not handler then
+ error(string.format("No handler for %s", pred[1]))
+ return
+ end
+
+ handler(match, pattern, source, pred, metadata)
+ end
+ end
+end
+
--- Iterates of the captures of self on a given range.
--
-- @param node The node under witch the search will occur
@@ -294,14 +382,18 @@ function Query:iter_captures(node, source, start, stop)
local raw_iter = node:_rawquery(self.query, true, start, stop)
local function iter()
local capture, captured_node, match = raw_iter()
+ local metadata = new_match_metadata()
+
if match ~= nil then
local active = self:match_preds(match, match.pattern, source)
match.active = active
if not active then
return iter() -- tail call: try next match
end
+
+ self:apply_directives(match, match.pattern, source, metadata)
end
- return capture, captured_node
+ return capture, captured_node, metadata
end
return iter
end
@@ -322,13 +414,17 @@ function Query:iter_matches(node, source, start, stop)
local raw_iter = node:_rawquery(self.query, false, start, stop)
local function iter()
local pattern, match = raw_iter()
+ local metadata = new_match_metadata()
+
if match ~= nil then
local active = self:match_preds(match, pattern, source)
if not active then
return iter() -- tail call: try next match
end
+
+ self:apply_directives(match, pattern, source, metadata)
end
- return pattern, match
+ return pattern, match, metadata
end
return iter
end