diff options
author | Mathias Fussenegger <f.mathias@zignar.net> | 2023-02-25 11:17:28 +0100 |
---|---|---|
committer | Mathias Fussenegger <f.mathias@zignar.net> | 2023-02-25 11:17:28 +0100 |
commit | f0f27e9aef7c237dd55fbb5c2cd47c2f42d01742 (patch) | |
tree | 378d8accb3f5a49d41e54bd7529a6cfdeef6bb8e /runtime/lua/vim/lsp/_watchfiles.lua | |
parent | 5732aa706c639b3d775573d91d1139f24624629c (diff) | |
download | rneovim-f0f27e9aef7c237dd55fbb5c2cd47c2f42d01742.tar.gz rneovim-f0f27e9aef7c237dd55fbb5c2cd47c2f42d01742.tar.bz2 rneovim-f0f27e9aef7c237dd55fbb5c2cd47c2f42d01742.zip |
Revert "feat(lsp): implement workspace/didChangeWatchedFiles (#21293)"
This reverts commit 5732aa706c639b3d775573d91d1139f24624629c.
Causes editor to freeze in projects with many watcher registrations
Diffstat (limited to 'runtime/lua/vim/lsp/_watchfiles.lua')
-rw-r--r-- | runtime/lua/vim/lsp/_watchfiles.lua | 274 |
1 files changed, 0 insertions, 274 deletions
diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua deleted file mode 100644 index b9268b963c..0000000000 --- a/runtime/lua/vim/lsp/_watchfiles.lua +++ /dev/null @@ -1,274 +0,0 @@ -local bit = require('bit') -local watch = require('vim._watch') -local protocol = require('vim.lsp.protocol') - -local M = {} - ----@private ----Parses the raw pattern into a number of Lua-native patterns. ---- ----@param pattern string The raw glob pattern ----@return table A list of Lua patterns. A match with any of them matches the input glob pattern. -local function parse(pattern) - local patterns = { '' } - - local path_sep = '[/\\]' - local non_path_sep = '[^/\\]' - - local function append(chunks) - local new_patterns = {} - for _, p in ipairs(patterns) do - for _, chunk in ipairs(chunks) do - table.insert(new_patterns, p .. chunk) - end - end - patterns = new_patterns - end - - local function split(s, sep) - local segments = {} - local segment = '' - local in_braces = false - local in_brackets = false - for i = 1, #s do - local c = string.sub(s, i, i) - if c == sep and not in_braces and not in_brackets then - table.insert(segments, segment) - segment = '' - else - if c == '{' then - in_braces = true - elseif c == '}' then - in_braces = false - elseif c == '[' then - in_brackets = true - elseif c == ']' then - in_brackets = false - end - segment = segment .. c - end - end - if segment ~= '' then - table.insert(segments, segment) - end - return segments - end - - local function escape(c) - if - c == '?' - or c == '.' - or c == '(' - or c == ')' - or c == '%' - or c == '[' - or c == ']' - or c == '*' - or c == '+' - or c == '-' - then - return '%' .. c - end - return c - end - - local segments = split(pattern, '/') - for i, segment in ipairs(segments) do - local last_seg = i == #segments - if segment == '**' then - local chunks = { - path_sep .. '-', - '.-' .. path_sep, - } - if last_seg then - chunks = { '.-' } - end - append(chunks) - else - local in_braces = false - local brace_val = '' - local in_brackets = false - local bracket_val = '' - for j = 1, #segment do - local char = string.sub(segment, j, j) - if char ~= '}' and in_braces then - brace_val = brace_val .. char - else - if in_brackets and (char ~= ']' or bracket_val == '') then - local res - if char == '-' then - res = char - elseif bracket_val == '' and char == '!' then - res = '^' - elseif char == '/' then - res = '' - else - res = escape(char) - end - bracket_val = bracket_val .. res - else - if char == '{' then - in_braces = true - elseif char == '[' then - in_brackets = true - elseif char == '}' then - local choices = split(brace_val, ',') - local parsed_choices = {} - for _, choice in ipairs(choices) do - table.insert(parsed_choices, parse(choice)) - end - append(vim.tbl_flatten(parsed_choices)) - in_braces = false - brace_val = '' - elseif char == ']' then - append({ '[' .. bracket_val .. ']' }) - in_brackets = false - bracket_val = '' - elseif char == '?' then - append({ non_path_sep }) - elseif char == '*' then - append({ non_path_sep .. '-' }) - else - append({ escape(char) }) - end - end - end - end - - if not last_seg and (segments[i + 1] ~= '**' or i + 1 < #segments) then - append({ path_sep }) - end - end - end - - return patterns -end - ----@private ---- Implementation of LSP 3.17.0's pattern matching: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#pattern ---- Modeled after VSCode's implementation: https://github.com/microsoft/vscode/blob/0319eed971719ad48e9093daba9d65a5013ec5ab/src/vs/base/common/glob.ts#L509 ---- ----@param pattern string|table The glob pattern (raw or parsed) to match. ----@param s string The string to match against pattern. ----@return boolean Whether or not pattern matches s. -function M._match(pattern, s) - if type(pattern) == 'string' then - pattern = parse(pattern) - end - -- Since Lua's built-in string pattern matching does not have an alternate - -- operator like '|', `parse` will construct one pattern for each possible - -- alternative. Any pattern that matches thus matches the glob. - for _, p in ipairs(pattern) do - if s:match('^' .. p .. '$') then - return true - end - end - return false -end - -M._watchfunc = (vim.fn.has('win32') == 1 or vim.fn.has('mac') == 1) and watch.watch or watch.poll - ----@type table<number, table<number, function()>> client id -> registration id -> cancel function -local cancels = vim.defaulttable() - -local queue_timeout_ms = 100 ----@type table<number, uv_timer_t> client id -> libuv timer which will send queued changes at its timeout -local queue_timers = {} ----@type table<number, lsp.FileEvent[]> client id -> set of queued changes to send in a single LSP notification -local change_queues = {} ----@type table<number, table<string, lsp.FileChangeType>> client id -> URI -> last type of change processed ---- Used to prune consecutive events of the same type for the same file -local change_cache = vim.defaulttable() - -local to_lsp_change_type = { - [watch.FileChangeType.Created] = protocol.FileChangeType.Created, - [watch.FileChangeType.Changed] = protocol.FileChangeType.Changed, - [watch.FileChangeType.Deleted] = protocol.FileChangeType.Deleted, -} - ---- Registers the workspace/didChangeWatchedFiles capability dynamically. ---- ----@param reg table LSP Registration object. ----@param ctx table Context from the |lsp-handler|. -function M.register(reg, ctx) - local client_id = ctx.client_id - local client = vim.lsp.get_client_by_id(client_id) - for _, w in ipairs(reg.registerOptions.watchers) do - local glob_patterns = {} - if type(w.globPattern) == 'string' then - for _, folder in ipairs(client.workspace_folders) do - table.insert(glob_patterns, { baseUri = folder.uri, pattern = w.globPattern }) - end - else - table.insert(glob_patterns, w.globPattern) - end - for _, glob_pattern in ipairs(glob_patterns) do - local pattern = parse(glob_pattern.pattern) - local base_dir = nil - if type(glob_pattern.baseUri) == 'string' then - base_dir = glob_pattern.baseUri - elseif type(glob_pattern.baseUri) == 'table' then - base_dir = glob_pattern.baseUri.uri - end - assert(base_dir, "couldn't identify root of watch") - base_dir = vim.uri_to_fname(base_dir) - local kind = w.kind - or protocol.WatchKind.Create + protocol.WatchKind.Change + protocol.WatchKind.Delete - - table.insert( - cancels[client_id][reg.id], - M._watchfunc(base_dir, { uvflags = { recursive = true } }, function(fullpath, change_type) - change_type = to_lsp_change_type[change_type] - -- e.g. match kind with Delete bit (0b0100) to Delete change_type (3) - local kind_mask = bit.lshift(1, change_type - 1) - local change_type_match = bit.band(kind, kind_mask) == kind_mask - if not M._match(pattern, fullpath) or not change_type_match then - return - end - - local change = { - uri = vim.uri_from_fname(fullpath), - type = change_type, - } - - local last_type = change_cache[client_id][change.uri] - if last_type ~= change.type then - change_queues[client_id] = change_queues[client_id] or {} - table.insert(change_queues[client_id], change) - change_cache[client_id][change.uri] = change.type - end - - if not queue_timers[client_id] then - queue_timers[client_id] = vim.defer_fn(function() - client.notify('workspace/didChangeWatchedFiles', { - changes = change_queues[client_id], - }) - queue_timers[client_id] = nil - change_queues[client_id] = nil - change_cache[client_id] = nil - end, queue_timeout_ms) - end - end) - ) - end - end -end - ---- Unregisters the workspace/didChangeWatchedFiles capability dynamically. ---- ----@param unreg table LSP Unregistration object. ----@param ctx table Context from the |lsp-handler|. -function M.unregister(unreg, ctx) - local client_id = ctx.client_id - local client_cancels = cancels[client_id] - local reg_cancels = client_cancels[unreg.id] - while #reg_cancels > 0 do - table.remove(reg_cancels)() - end - client_cancels[unreg.id] = nil - if not next(cancels[client_id]) then - cancels[client_id] = nil - end -end - -return M |