aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp/_watchfiles.lua
diff options
context:
space:
mode:
authorJon Huhn <nojnhuh@users.noreply.github.com>2023-06-14 05:40:11 -0500
committerGitHub <noreply@github.com>2023-06-14 12:40:11 +0200
commit79a5b89d66db74560e751561542064674e980146 (patch)
tree1354780c69deb6cdff85c724e3a7f8bc3079459d /runtime/lua/vim/lsp/_watchfiles.lua
parent0ce065a332cbd3874bd68a86fa9eda20c5467170 (diff)
downloadrneovim-79a5b89d66db74560e751561542064674e980146.tar.gz
rneovim-79a5b89d66db74560e751561542064674e980146.tar.bz2
rneovim-79a5b89d66db74560e751561542064674e980146.zip
perf(lsp): reduce polling handles for workspace/didChangeWatchedFiles (#23500)
Co-authored-by: Lewis Russell <lewis6991@gmail.com>
Diffstat (limited to 'runtime/lua/vim/lsp/_watchfiles.lua')
-rw-r--r--runtime/lua/vim/lsp/_watchfiles.lua52
1 files changed, 35 insertions, 17 deletions
diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua
index 14e5dc6cf8..87938fe4d5 100644
--- a/runtime/lua/vim/lsp/_watchfiles.lua
+++ b/runtime/lua/vim/lsp/_watchfiles.lua
@@ -1,7 +1,7 @@
local bit = require('bit')
-local lpeg = require('lpeg')
local watch = require('vim._watch')
local protocol = require('vim.lsp.protocol')
+local lpeg = vim.lpeg
local M = {}
@@ -107,6 +107,13 @@ local to_lsp_change_type = {
[watch.FileChangeType.Deleted] = protocol.FileChangeType.Deleted,
}
+--- Default excludes the same as VSCode's `files.watcherExclude` setting.
+--- https://github.com/microsoft/vscode/blob/eef30e7165e19b33daa1e15e92fa34ff4a5df0d3/src/vs/workbench/contrib/files/browser/files.contribution.ts#L261
+---@type Lpeg pattern
+M._poll_exclude_pattern = parse('**/.git/{objects,subtree-cache}/**')
+ + parse('**/node_modules/*/**')
+ + parse('**/.hg/store/**')
+
--- Registers the workspace/didChangeWatchedFiles capability dynamically.
---
---@param reg table LSP Registration object.
@@ -122,10 +129,10 @@ function M.register(reg, ctx)
then
return
end
- local watch_regs = {}
+ local watch_regs = {} --- @type table<string,{pattern:userdata,kind:integer}>
for _, w in ipairs(reg.registerOptions.watchers) do
local relative_pattern = false
- local glob_patterns = {}
+ local glob_patterns = {} --- @type {baseUri:string, pattern: string}[]
if type(w.globPattern) == 'string' then
for _, folder in ipairs(client.workspace_folders) do
table.insert(glob_patterns, { baseUri = folder.uri, pattern = w.globPattern })
@@ -135,7 +142,7 @@ function M.register(reg, ctx)
table.insert(glob_patterns, w.globPattern)
end
for _, glob_pattern in ipairs(glob_patterns) do
- local base_dir = nil
+ local base_dir = nil ---@type string?
if type(glob_pattern.baseUri) == 'string' then
base_dir = glob_pattern.baseUri
elseif type(glob_pattern.baseUri) == 'table' then
@@ -144,6 +151,7 @@ function M.register(reg, ctx)
assert(base_dir, "couldn't identify root of watch")
base_dir = vim.uri_to_fname(base_dir)
+ ---@type integer
local kind = w.kind
or protocol.WatchKind.Create + protocol.WatchKind.Change + protocol.WatchKind.Delete
@@ -153,8 +161,8 @@ function M.register(reg, ctx)
pattern = lpeg.P(base_dir .. '/') * pattern
end
- table.insert(watch_regs, {
- base_dir = base_dir,
+ watch_regs[base_dir] = watch_regs[base_dir] or {}
+ table.insert(watch_regs[base_dir], {
pattern = pattern,
kind = kind,
})
@@ -163,12 +171,12 @@ function M.register(reg, ctx)
local callback = function(base_dir)
return function(fullpath, change_type)
- for _, w in ipairs(watch_regs) do
+ for _, w in ipairs(watch_regs[base_dir]) do
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(w.kind, kind_mask) == kind_mask
- if base_dir == w.base_dir and M._match(w.pattern, fullpath) and change_type_match then
+ if M._match(w.pattern, fullpath) and change_type_match then
local change = {
uri = vim.uri_from_fname(fullpath),
type = change_type,
@@ -198,15 +206,25 @@ function M.register(reg, ctx)
end
end
- local watching = {}
- for _, w in ipairs(watch_regs) do
- if not watching[w.base_dir] then
- watching[w.base_dir] = true
- table.insert(
- cancels[client_id][reg.id],
- M._watchfunc(w.base_dir, { uvflags = { recursive = true } }, callback(w.base_dir))
- )
- end
+ for base_dir, watches in pairs(watch_regs) do
+ local include_pattern = vim.iter(watches):fold(lpeg.P(false), function(acc, w)
+ return acc + w.pattern
+ end)
+
+ table.insert(
+ cancels[client_id][reg.id],
+ M._watchfunc(base_dir, {
+ uvflags = {
+ recursive = true,
+ },
+ -- include_pattern will ensure the pattern from *any* watcher definition for the
+ -- base_dir matches. This first pass prevents polling for changes to files that
+ -- will never be sent to the LSP server. A second pass in the callback is still necessary to
+ -- match a *particular* pattern+kind pair.
+ include_pattern = include_pattern,
+ exclude_pattern = M._poll_exclude_pattern,
+ }, callback(base_dir))
+ )
end
end