aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r--runtime/lua/vim/lsp.lua88
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua6
-rw-r--r--runtime/lua/vim/lsp/handlers.lua6
-rw-r--r--runtime/lua/vim/lsp/protocol.lua13
-rw-r--r--runtime/lua/vim/lsp/util.lua255
-rw-r--r--runtime/lua/vim/shared.lua14
-rw-r--r--runtime/lua/vim/treesitter.lua6
-rw-r--r--runtime/lua/vim/treesitter/query.lua15
8 files changed, 341 insertions, 62 deletions
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 841c365cbe..4c453df3f6 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -42,6 +42,8 @@ lsp._request_name_to_capability = {
['textDocument/prepareCallHierarchy'] = 'call_hierarchy';
['textDocument/rename'] = 'rename';
['textDocument/codeAction'] = 'code_action';
+ ['textDocument/codeLens'] = 'code_lens';
+ ['codeLens/resolve'] = 'code_lens_resolve';
['workspace/executeCommand'] = 'execute_command';
['textDocument/references'] = 'find_references';
['textDocument/rangeFormatting'] = 'document_range_formatting';
@@ -228,6 +230,7 @@ local function validate_client_config(config)
before_init = { config.before_init, "f", true };
offset_encoding = { config.offset_encoding, "s", true };
flags = { config.flags, "t", true };
+ get_language_id = { config.get_language_id, "f", true };
}
local cmd, cmd_args = lsp._cmd_parts(config.cmd)
@@ -262,23 +265,47 @@ end
--@param bufnr (Number) Number of the buffer, or 0 for current
--@param client Client object
local function text_document_did_open_handler(bufnr, client)
+ local use_incremental_sync = (
+ if_nil(client.config.flags.allow_incremental_sync, true)
+ and client.resolved_capabilities.text_document_did_change == protocol.TextDocumentSyncKind.Incremental
+ )
+ if use_incremental_sync then
+ if not client._cached_buffers then
+ client._cached_buffers = {}
+ end
+ client._cached_buffers[bufnr] = nvim_buf_get_lines(bufnr, 0, -1, true)
+ end
if not client.resolved_capabilities.text_document_open_close then
return
end
if not vim.api.nvim_buf_is_loaded(bufnr) then
return
end
+ local filetype = nvim_buf_get_option(bufnr, 'filetype')
+
local params = {
textDocument = {
version = 0;
uri = vim.uri_from_bufnr(bufnr);
- -- TODO make sure our filetypes are compatible with languageId names.
- languageId = nvim_buf_get_option(bufnr, 'filetype');
+ languageId = client.config.get_language_id(bufnr, filetype);
text = buf_get_full_text(bufnr);
}
}
client.notify('textDocument/didOpen', params)
util.buf_versions[bufnr] = params.textDocument.version
+
+ -- Next chance we get, we should re-do the diagnostics
+ vim.schedule(function()
+ vim.lsp.handlers["textDocument/publishDiagnostics"](
+ nil,
+ "textDocument/publishDiagnostics",
+ {
+ diagnostics = vim.lsp.diagnostic.get(bufnr, client.id),
+ uri = vim.uri_from_bufnr(bufnr),
+ },
+ client.id
+ )
+ end)
end
-- FIXME: DOC: Shouldn't need to use a dummy function
@@ -400,6 +427,9 @@ end
---
--@param name (string, default=client-id) Name in log messages.
---
+--@param get_language_id function(bufnr, filetype) -> language ID as string.
+--- Defaults to the filetype.
+---
--@param offset_encoding (default="utf-16") One of "utf-8", "utf-16",
--- or "utf-32" which is the encoding that the LSP server expects. Client does
--- not verify this is correct.
@@ -438,16 +468,7 @@ end
--@param trace: "off" | "messages" | "verbose" | nil passed directly to the language
--- server in the initialize request. Invalid/empty values will default to "off"
--@param flags: A table with flags for the client. The current (experimental) flags are:
---- - allow_incremental_sync (bool, default false): Allow using on_line callbacks for lsp
----
---- <pre>
---- -- In attach function for the client, you can do:
---- local custom_attach = function(client)
---- if client.config.flags then
---- client.config.flags.allow_incremental_sync = true
---- end
---- end
---- </pre>
+--- - allow_incremental_sync (bool, default true): Allow using incremental sync for buffer edits
---
--@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be
--- fully initialized. Use `on_init` to do any actions once
@@ -459,6 +480,11 @@ function lsp.start_client(config)
config.flags = config.flags or {}
config.settings = config.settings or {}
+ -- By default, get_language_id just returns the exact filetype it is passed.
+ -- It is possible to pass in something that will calculate a different filetype,
+ -- to be sent by the client.
+ config.get_language_id = config.get_language_id or function(_, filetype) return filetype end
+
local client_id = next_client_id()
local handlers = config.handlers or {}
@@ -808,7 +834,6 @@ end
--- Notify all attached clients that a buffer has changed.
local text_document_did_change_handler
do
- local encoding_index = { ["utf-8"] = 1; ["utf-16"] = 2; ["utf-32"] = 3; }
text_document_did_change_handler = function(_, bufnr, changedtick,
firstline, lastline, new_lastline, old_byte_size, old_utf32_size,
old_utf16_size)
@@ -827,23 +852,12 @@ do
util.buf_versions[bufnr] = changedtick
-- Lazy initialize these because clients may not even need them.
local incremental_changes = once(function(client)
- local size_index = encoding_index[client.offset_encoding]
- local length = select(size_index, old_byte_size, old_utf16_size, old_utf32_size)
- local lines = nvim_buf_get_lines(bufnr, firstline, new_lastline, true)
-
- -- This is necessary because we are specifying the full line including the
- -- newline in range. Therefore, we must replace the newline as well.
- if #lines > 0 then
- table.insert(lines, '')
- end
- return {
- range = {
- start = { line = firstline, character = 0 };
- ["end"] = { line = lastline, character = 0 };
- };
- rangeLength = length;
- text = table.concat(lines, '\n');
- };
+ local lines = nvim_buf_get_lines(bufnr, 0, -1, true)
+ local startline = math.min(firstline + 1, math.min(#client._cached_buffers[bufnr], #lines))
+ local endline = math.min(-(#lines - new_lastline), -1)
+ local incremental_change = vim.lsp.util.compute_diff(client._cached_buffers[bufnr], lines, startline, endline)
+ client._cached_buffers[bufnr] = lines
+ return incremental_change
end)
local full_changes = once(function()
return {
@@ -851,19 +865,12 @@ do
};
end)
local uri = vim.uri_from_bufnr(bufnr)
- for_each_buffer_client(bufnr, function(client, _client_id)
- local allow_incremental_sync = if_nil(client.config.flags.allow_incremental_sync, false)
-
+ for_each_buffer_client(bufnr, function(client)
+ local allow_incremental_sync = if_nil(client.config.flags.allow_incremental_sync, true)
local text_document_did_change = client.resolved_capabilities.text_document_did_change
local changes
if text_document_did_change == protocol.TextDocumentSyncKind.None then
return
- --[=[ TODO(ashkan) there seem to be problems with the byte_sizes sent by
- -- neovim right now so only send the full content for now. In general, we
- -- can assume that servers *will* support both versions anyway, as there
- -- is no way to specify the sync capability by the client.
- -- See https://github.com/palantir/python-language-server/commit/cfd6675bc10d5e8dbc50fc50f90e4a37b7178821#diff-f68667852a14e9f761f6ebf07ba02fc8 for an example of pyls handling both.
- --]=]
elseif not allow_incremental_sync or text_document_did_change == protocol.TextDocumentSyncKind.Full then
changes = full_changes(client)
elseif text_document_did_change == protocol.TextDocumentSyncKind.Incremental then
@@ -931,6 +938,9 @@ function lsp.buf_attach_client(bufnr, client_id)
if client.resolved_capabilities.text_document_open_close then
client.notify('textDocument/didClose', params)
end
+ if client._cached_buffers then
+ client._cached_buffers[bufnr] = nil
+ end
end)
util.buf_versions[bufnr] = nil
all_buffer_active_clients[bufnr] = nil
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index a1f24706c0..4e82c46fef 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -270,8 +270,10 @@ local function set_diagnostic_cache(diagnostics, bufnr, client_id)
diagnostic.severity = DiagnosticSeverity.Error
end
-- Account for servers that place diagnostics on terminating newline
- local start = diagnostic.range.start
- start.line = math.min(start.line, buf_line_count - 1)
+ if buf_line_count > 0 then
+ local start = diagnostic.range.start
+ start.line = math.min(start.line, buf_line_count - 1)
+ end
end
diagnostic_cache[bufnr][client_id] = diagnostics
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 0cf80e1443..eacbd90077 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -304,7 +304,7 @@ M['textDocument/typeDefinition'] = location_handler
M['textDocument/implementation'] = location_handler
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp
-M['textDocument/signatureHelp'] = function(_, method, result)
+M['textDocument/signatureHelp'] = function(_, method, result, _, bufnr)
-- When use `autocmd CompleteDone <silent><buffer> lua vim.lsp.buf.signature_help()` to call signatureHelp handler
-- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `<silent>` to ignore
if not (result and result.signatures and result.signatures[1]) then
@@ -317,9 +317,11 @@ M['textDocument/signatureHelp'] = function(_, method, result)
print('No signature help available')
return
end
- util.focusable_preview(method, function()
+ local syntax = api.nvim_buf_get_option(bufnr, 'syntax')
+ local p_bufnr, _ = util.focusable_preview(method, function()
return lines, util.try_trim_markdown_code_blocks(lines)
end)
+ api.nvim_buf_set_option(p_bufnr, 'syntax', syntax)
end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 388f65c180..7e43eb84de 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -749,6 +749,9 @@ function protocol.make_client_capabilities()
};
workspaceFolders = true;
applyEdit = true;
+ workspaceEdit = {
+ resourceOperations = {'rename', 'create', 'delete',},
+ };
};
callHierarchy = {
dynamicRegistration = false;
@@ -975,6 +978,16 @@ function protocol.resolve_capabilities(server_capabilities)
general_properties.rename = true
end
+ if server_capabilities.codeLensProvider == nil then
+ general_properties.code_lens = false
+ general_properties.code_lens_resolve = false
+ elseif type(server_capabilities.codeLensProvider) == 'table' then
+ general_properties.code_lens = true
+ general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider or false
+ else
+ error("The server sent invalid codeLensProvider")
+ end
+
if server_capabilities.codeActionProvider == nil then
general_properties.code_action = false
elseif type(server_capabilities.codeActionProvider) == 'boolean'
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 6fb9f09c99..412be68396 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -226,6 +226,168 @@ end
-- function M.glob_to_regex(glob)
-- end
+--@private
+--- Finds the first line and column of the difference between old and new lines
+--@param old_lines table list of lines
+--@param new_lines table list of lines
+--@returns (int, int) start_line_idx and start_col_idx of range
+local function first_difference(old_lines, new_lines, start_line_idx)
+ local line_count = math.min(#old_lines, #new_lines)
+ if line_count == 0 then return 1, 1 end
+ if not start_line_idx then
+ for i = 1, line_count do
+ start_line_idx = i
+ if old_lines[start_line_idx] ~= new_lines[start_line_idx] then
+ break
+ end
+ end
+ end
+ local old_line = old_lines[start_line_idx]
+ local new_line = new_lines[start_line_idx]
+ local length = math.min(#old_line, #new_line)
+ local start_col_idx = 1
+ while start_col_idx <= length do
+ if string.sub(old_line, start_col_idx, start_col_idx) ~= string.sub(new_line, start_col_idx, start_col_idx) then
+ break
+ end
+ start_col_idx = start_col_idx + 1
+ end
+ return start_line_idx, start_col_idx
+end
+
+
+--@private
+--- Finds the last line and column of the differences between old and new lines
+--@param old_lines table list of lines
+--@param new_lines table list of lines
+--@param start_char integer First different character idx of range
+--@returns (int, int) end_line_idx and end_col_idx of range
+local function last_difference(old_lines, new_lines, start_char, end_line_idx)
+ local line_count = math.min(#old_lines, #new_lines)
+ if line_count == 0 then return 0,0 end
+ if not end_line_idx then
+ end_line_idx = -1
+ end
+ for i = end_line_idx, -line_count, -1 do
+ if old_lines[#old_lines + i + 1] ~= new_lines[#new_lines + i + 1] then
+ end_line_idx = i
+ break
+ end
+ end
+ local old_line
+ local new_line
+ if end_line_idx <= -line_count then
+ end_line_idx = -line_count
+ old_line = string.sub(old_lines[#old_lines + end_line_idx + 1], start_char)
+ new_line = string.sub(new_lines[#new_lines + end_line_idx + 1], start_char)
+ else
+ old_line = old_lines[#old_lines + end_line_idx + 1]
+ new_line = new_lines[#new_lines + end_line_idx + 1]
+ end
+ local old_line_length = #old_line
+ local new_line_length = #new_line
+ local length = math.min(old_line_length, new_line_length)
+ local end_col_idx = -1
+ while end_col_idx >= -length do
+ local old_char = string.sub(old_line, old_line_length + end_col_idx + 1, old_line_length + end_col_idx + 1)
+ local new_char = string.sub(new_line, new_line_length + end_col_idx + 1, new_line_length + end_col_idx + 1)
+ if old_char ~= new_char then
+ break
+ end
+ end_col_idx = end_col_idx - 1
+ end
+ return end_line_idx, end_col_idx
+
+end
+
+--@private
+--- Get the text of the range defined by start and end line/column
+--@param lines table list of lines
+--@param start_char integer First different character idx of range
+--@param end_char integer Last different character idx of range
+--@param start_line integer First different line idx of range
+--@param end_line integer Last different line idx of range
+--@returns string text extracted from defined region
+local function extract_text(lines, start_line, start_char, end_line, end_char)
+ if start_line == #lines + end_line + 1 then
+ if end_line == 0 then return '' end
+ local line = lines[start_line]
+ local length = #line + end_char - start_char
+ return string.sub(line, start_char, start_char + length + 1)
+ end
+ local result = string.sub(lines[start_line], start_char) .. '\n'
+ for line_idx = start_line + 1, #lines + end_line do
+ result = result .. lines[line_idx] .. '\n'
+ end
+ if end_line ~= 0 then
+ local line = lines[#lines + end_line + 1]
+ local length = #line + end_char + 1
+ result = result .. string.sub(line, 1, length)
+ end
+ return result
+end
+
+--@private
+--- Compute the length of the substituted range
+--@param lines table list of lines
+--@param start_char integer First different character idx of range
+--@param end_char integer Last different character idx of range
+--@param start_line integer First different line idx of range
+--@param end_line integer Last different line idx of range
+--@returns (int, int) end_line_idx and end_col_idx of range
+local function compute_length(lines, start_line, start_char, end_line, end_char)
+ local adj_end_line = #lines + end_line + 1
+ local adj_end_char
+ if adj_end_line > #lines then
+ adj_end_char = end_char - 1
+ else
+ adj_end_char = #lines[adj_end_line] + end_char
+ end
+ if start_line == adj_end_line then
+ return adj_end_char - start_char + 1
+ end
+ local result = #lines[start_line] - start_char + 1
+ for line = start_line + 1, adj_end_line -1 do
+ result = result + #lines[line] + 1
+ end
+ result = result + adj_end_char + 1
+ return result
+end
+
+--- Returns the range table for the difference between old and new lines
+--@param old_lines table list of lines
+--@param new_lines table list of lines
+--@returns table start_line_idx and start_col_idx of range
+function M.compute_diff(old_lines, new_lines, start_line_idx, end_line_idx)
+ local start_line, start_char = first_difference(old_lines, new_lines, start_line_idx)
+ local end_line, end_char = last_difference(vim.list_slice(old_lines, start_line, #old_lines),
+ vim.list_slice(new_lines, start_line, #new_lines), start_char, end_line_idx)
+ local text = extract_text(new_lines, start_line, start_char, end_line, end_char)
+ local length = compute_length(old_lines, start_line, start_char, end_line, end_char)
+
+ local adj_end_line = #old_lines + end_line
+ local adj_end_char
+ if end_line == 0 then
+ adj_end_char = 0
+ else
+ adj_end_char = #old_lines[#old_lines + end_line + 1] + end_char + 1
+ end
+
+ local _, utf16_start_char = vim.str_utfindex(old_lines[start_line], start_char - 1)
+ local _, utf16_end_char = vim.str_utfindex(old_lines[#old_lines + end_line + 1], adj_end_char)
+
+ local result = {
+ range = {
+ start = { line = start_line - 1, character = utf16_start_char},
+ ["end"] = { line = adj_end_line, character = utf16_end_char}
+ },
+ text = text,
+ rangeLength = length + 1,
+ }
+
+ return result
+end
+
--- Can be used to extract the completion items from a
--- `textDocument/completion` request, which may return one of
--- `CompletionItem[]`, `CompletionList` or null.
@@ -359,13 +521,13 @@ end
--- precedence is as follows: textEdit.newText > insertText > label
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
local function get_completion_word(item)
- if item.textEdit ~= nil and item.textEdit.newText ~= nil then
+ if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= "" then
if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then
return item.textEdit.newText
else
return M.parse_snippet(item.textEdit.newText)
end
- elseif item.insertText ~= nil then
+ elseif item.insertText ~= nil and item.insertText ~= "" then
if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then
return item.insertText
else
@@ -453,6 +615,62 @@ function M.text_document_completion_list_to_complete_items(result, prefix)
return matches
end
+
+--- Rename old_fname to new_fname
+--
+--@param opts (table)
+-- overwrite? bool
+-- ignoreIfExists? bool
+function M.rename(old_fname, new_fname, opts)
+ opts = opts or {}
+ local bufnr = vim.fn.bufadd(old_fname)
+ vim.fn.bufload(bufnr)
+ local target_exists = vim.loop.fs_stat(new_fname) ~= nil
+ if target_exists and not opts.overwrite or opts.ignoreIfExists then
+ vim.notify('Rename target already exists. Skipping rename.')
+ return
+ end
+ local ok, err = os.rename(old_fname, new_fname)
+ assert(ok, err)
+ api.nvim_buf_call(bufnr, function()
+ vim.cmd('saveas! ' .. vim.fn.fnameescape(new_fname))
+ end)
+end
+
+
+local function create_file(change)
+ local opts = change.options or {}
+ -- from spec: Overwrite wins over `ignoreIfExists`
+ local fname = vim.uri_to_fname(change.uri)
+ if not opts.ignoreIfExists or opts.overwrite then
+ local file = io.open(fname, 'w')
+ file:close()
+ end
+ vim.fn.bufadd(fname)
+end
+
+
+local function delete_file(change)
+ local opts = change.options or {}
+ local fname = vim.uri_to_fname(change.uri)
+ local stat = vim.loop.fs_stat(fname)
+ if opts.ignoreIfNotExists and not stat then
+ return
+ end
+ assert(stat, "Cannot delete not existing file or folder " .. fname)
+ local flags
+ if stat and stat.type == 'directory' then
+ flags = opts.recursive and 'rf' or 'd'
+ else
+ flags = ''
+ end
+ local bufnr = vim.fn.bufadd(fname)
+ local result = tonumber(vim.fn.delete(fname, flags))
+ assert(result == 0, 'Could not delete file: ' .. fname .. ', stat: ' .. vim.inspect(stat))
+ api.nvim_buf_delete(bufnr, { force = true })
+end
+
+
--- Applies a `WorkspaceEdit`.
---
--@param workspace_edit (table) `WorkspaceEdit`
@@ -460,8 +678,17 @@ end
function M.apply_workspace_edit(workspace_edit)
if workspace_edit.documentChanges then
for idx, change in ipairs(workspace_edit.documentChanges) do
- if change.kind then
- -- TODO(ashkan) handle CreateFile/RenameFile/DeleteFile
+ if change.kind == "rename" then
+ M.rename(
+ vim.uri_to_fname(change.oldUri),
+ vim.uri_to_fname(change.newUri),
+ change.options
+ )
+ elseif change.kind == 'create' then
+ create_file(change)
+ elseif change.kind == 'delete' then
+ delete_file(change)
+ elseif change.kind then
error(string.format("Unsupported change: %q", vim.inspect(change)))
else
M.apply_text_document_edit(change, idx)
@@ -687,8 +914,8 @@ function M.preview_location(location)
end
local range = location.targetRange or location.range
local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line+1, false)
- local filetype = api.nvim_buf_get_option(bufnr, 'filetype')
- return M.open_floating_preview(contents, filetype)
+ local syntax = api.nvim_buf_get_option(bufnr, 'syntax')
+ return M.open_floating_preview(contents, syntax)
end
--@private
@@ -873,8 +1100,10 @@ function M.fancy_floating_markdown(contents, opts)
-- This is because the syntax command doesn't accept a target.
local cwin = vim.api.nvim_get_current_win()
vim.api.nvim_set_current_win(winnr)
+ api.nvim_win_set_option(winnr, 'conceallevel', 2)
+ api.nvim_win_set_option(winnr, 'concealcursor', 'n')
- vim.cmd("ownsyntax markdown")
+ vim.cmd("ownsyntax lsp_markdown")
local idx = 1
--@private
local function apply_syntax_to_region(ft, start, finish)
@@ -975,7 +1204,7 @@ end
--- Shows contents in a floating window.
---
--@param contents table of lines to show in window
---@param filetype string of filetype to set for opened buffer
+--@param syntax string of syntax to set for opened buffer
--@param opts dictionary with optional fields
-- - height of floating window
-- - width of floating window
@@ -988,10 +1217,10 @@ end
-- - pad_bottom number of lines to pad contents at bottom
--@returns bufnr,winnr buffer and window number of the newly created floating
---preview window
-function M.open_floating_preview(contents, filetype, opts)
+function M.open_floating_preview(contents, syntax, opts)
validate {
contents = { contents, 't' };
- filetype = { filetype, 's', true };
+ syntax = { syntax, 's', true };
opts = { opts, 't', true };
}
opts = opts or {}
@@ -1004,12 +1233,12 @@ function M.open_floating_preview(contents, filetype, opts)
local width, height = M._make_floating_popup_size(contents, opts)
local floating_bufnr = api.nvim_create_buf(false, true)
- if filetype then
- api.nvim_buf_set_option(floating_bufnr, 'filetype', filetype)
+ if syntax then
+ api.nvim_buf_set_option(floating_bufnr, 'syntax', syntax)
end
local float_option = M.make_floating_popup_options(width, height, opts)
local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option)
- if filetype == 'markdown' then
+ if syntax == 'markdown' then
api.nvim_win_set_option(floating_winnr, 'conceallevel', 2)
end
api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents)
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index 998e04f568..0a663628a5 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -400,6 +400,20 @@ function vim.tbl_count(t)
return count
end
+--- Creates a copy of a table containing only elements from start to end (inclusive)
+---
+--@param list table table
+--@param start integer Start range of slice
+--@param finish integer End range of slice
+--@returns Copy of table sliced from start to finish (inclusive)
+function vim.list_slice(list, start, finish)
+ local new_list = {}
+ for i = start or 1, finish or #list do
+ new_list[#new_list+1] = list[i]
+ end
+ return new_list
+end
+
--- Trim whitespace (Lua pattern "%s") from both sides of a string.
---
--@see https://www.lua.org/pil/20.2.html
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index 3af66b134c..64a5ba1fd8 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -12,11 +12,7 @@ local M = vim.tbl_extend("error", query, language)
setmetatable(M, {
__index = function (t, k)
- if k == "TSHighlighter" then
- a.nvim_err_writeln("vim.TSHighlighter is deprecated, please use vim.treesitter.highlighter")
- t[k] = require'vim.treesitter.highlighter'
- return t[k]
- elseif k == "highlighter" then
+ if k == "highlighter" then
t[k] = require'vim.treesitter.highlighter'
return t[k]
end
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 8b94348994..1b29618997 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -8,10 +8,23 @@ Query.__index = Query
local M = {}
+local function dedupe_files(files)
+ local result = {}
+ local seen = {}
+
+ for _, path in ipairs(files) do
+ if not seen[path] then
+ table.insert(result, path)
+ seen[path] = true
+ end
+ end
+
+ return result
+end
function M.get_query_files(lang, query_name, is_included)
local query_path = string.format('queries/%s/%s.scm', lang, query_name)
- local lang_files = a.nvim_get_runtime_file(query_path, true)
+ local lang_files = dedupe_files(a.nvim_get_runtime_file(query_path, true))
if #lang_files == 0 then return {} end