diff options
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r-- | runtime/lua/vim/lsp.lua | 88 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/diagnostic.lua | 6 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/handlers.lua | 6 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/protocol.lua | 13 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/util.lua | 255 | ||||
-rw-r--r-- | runtime/lua/vim/shared.lua | 14 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter.lua | 6 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/query.lua | 15 |
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 |