aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp/_watchfiles.lua
diff options
context:
space:
mode:
authorMathias Fussenegger <f.mathias@zignar.net>2023-02-25 11:17:28 +0100
committerMathias Fussenegger <f.mathias@zignar.net>2023-02-25 11:17:28 +0100
commitf0f27e9aef7c237dd55fbb5c2cd47c2f42d01742 (patch)
tree378d8accb3f5a49d41e54bd7529a6cfdeef6bb8e /runtime/lua/vim/lsp/_watchfiles.lua
parent5732aa706c639b3d775573d91d1139f24624629c (diff)
downloadrneovim-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.lua274
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