aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp/_watchfiles.lua
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim/lsp/_watchfiles.lua')
-rw-r--r--runtime/lua/vim/lsp/_watchfiles.lua118
1 files changed, 24 insertions, 94 deletions
diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua
index 1fd112631d..49328fbe9b 100644
--- a/runtime/lua/vim/lsp/_watchfiles.lua
+++ b/runtime/lua/vim/lsp/_watchfiles.lua
@@ -1,95 +1,20 @@
local bit = require('bit')
-local watch = require('vim._watch')
+local glob = vim.glob
+local watch = vim._watch
local protocol = require('vim.lsp.protocol')
local ms = protocol.Methods
local lpeg = vim.lpeg
local M = {}
---- Parses the raw pattern into an |lpeg| pattern. LPeg patterns natively support the "this" or "that"
---- alternative constructions described in the LSP spec that cannot be expressed in a standard Lua pattern.
----
----@param pattern string The raw glob pattern
----@return vim.lpeg.Pattern? pattern An |lpeg| representation of the pattern, or nil if the pattern is invalid.
-local function parse(pattern)
- local l = lpeg
-
- local P, S, V = lpeg.P, lpeg.S, lpeg.V
- local C, Cc, Ct, Cf = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cf
-
- local pathsep = '/'
-
- local function class(inv, ranges)
- for i, r in ipairs(ranges) do
- ranges[i] = r[1] .. r[2]
- end
- local patt = l.R(unpack(ranges))
- if inv == '!' then
- patt = P(1) - patt
- end
- return patt
- end
-
- local function add(acc, a)
- return acc + a
- end
-
- local function mul(acc, m)
- return acc * m
- end
-
- local function star(stars, after)
- return (-after * (l.P(1) - pathsep)) ^ #stars * after
- end
-
- local function dstar(after)
- return (-after * l.P(1)) ^ 0 * after
- end
-
- local p = P({
- 'Pattern',
- Pattern = V('Elem') ^ -1 * V('End'),
- Elem = Cf(
- (V('DStar') + V('Star') + V('Ques') + V('Class') + V('CondList') + V('Literal'))
- * (V('Elem') + V('End')),
- mul
- ),
- DStar = P('**') * (P(pathsep) * (V('Elem') + V('End')) + V('End')) / dstar,
- Star = C(P('*') ^ 1) * (V('Elem') + V('End')) / star,
- Ques = P('?') * Cc(l.P(1) - pathsep),
- Class = P('[') * C(P('!') ^ -1) * Ct(Ct(C(1) * '-' * C(P(1) - ']')) ^ 1 * ']') / class,
- CondList = P('{') * Cf(V('Cond') * (P(',') * V('Cond')) ^ 0, add) * '}',
- -- TODO: '*' inside a {} condition is interpreted literally but should probably have the same
- -- wildcard semantics it usually has.
- -- Fixing this is non-trivial because '*' should match non-greedily up to "the rest of the
- -- pattern" which in all other cases is the entire succeeding part of the pattern, but at the end of a {}
- -- condition means "everything after the {}" where several other options separated by ',' may
- -- exist in between that should not be matched by '*'.
- Cond = Cf((V('Ques') + V('Class') + V('CondList') + (V('Literal') - S(',}'))) ^ 1, mul)
- + Cc(l.P(0)),
- Literal = P(1) / l.P,
- End = P(-1) * Cc(l.P(-1)),
- })
-
- return p:match(pattern) --[[@as vim.lpeg.Pattern?]]
-end
-
----@private
---- Implementation of LSP 3.17.0's pattern matching: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#pattern
----
----@param pattern string|vim.lpeg.Pattern 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
- local p = assert(parse(pattern))
- return p:match(s) ~= nil
- end
- return pattern:match(s) ~= nil
+if vim.fn.has('win32') == 1 or vim.fn.has('mac') == 1 then
+ M._watchfunc = watch.watch
+elseif vim.fn.executable('fswatch') == 1 then
+ M._watchfunc = watch.fswatch
+else
+ M._watchfunc = watch.watchdirs
end
-M._watchfunc = (vim.fn.has('win32') == 1 or vim.fn.has('mac') == 1) and watch.watch or watch.poll
-
---@type table<integer, table<string, function[]>> client id -> registration id -> cancel function
local cancels = vim.defaulttable()
@@ -112,9 +37,9 @@ local to_lsp_change_type = {
--- 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 vim.lpeg.Pattern parsed Lpeg pattern
-M._poll_exclude_pattern = parse('**/.git/{objects,subtree-cache}/**')
- + parse('**/node_modules/*/**')
- + parse('**/.hg/store/**')
+M._poll_exclude_pattern = glob.to_lpeg('**/.git/{objects,subtree-cache}/**')
+ + glob.to_lpeg('**/node_modules/*/**')
+ + glob.to_lpeg('**/.hg/store/**')
--- Registers the workspace/didChangeWatchedFiles capability dynamically.
---
@@ -125,12 +50,8 @@ function M.register(reg, ctx)
local client = assert(vim.lsp.get_client_by_id(client_id), 'Client must be running')
-- Ill-behaved servers may not honor the client capability and try to register
-- anyway, so ignore requests when the user has opted out of the feature.
- local has_capability = vim.tbl_get(
- client.config.capabilities or {},
- 'workspace',
- 'didChangeWatchedFiles',
- 'dynamicRegistration'
- )
+ local has_capability =
+ vim.tbl_get(client.capabilities, 'workspace', 'didChangeWatchedFiles', 'dynamicRegistration')
if not has_capability or not client.workspace_folders then
return
end
@@ -143,7 +64,7 @@ function M.register(reg, ctx)
local glob_pattern = w.globPattern
if type(glob_pattern) == 'string' then
- local pattern = parse(glob_pattern)
+ local pattern = glob.to_lpeg(glob_pattern)
if not pattern then
error('Cannot parse pattern: ' .. glob_pattern)
end
@@ -155,7 +76,7 @@ function M.register(reg, ctx)
local base_uri = glob_pattern.baseUri
local uri = type(base_uri) == 'string' and base_uri or base_uri.uri
local base_dir = vim.uri_to_fname(uri)
- local pattern = parse(glob_pattern.pattern)
+ local pattern = glob.to_lpeg(glob_pattern.pattern)
if not pattern then
error('Cannot parse pattern: ' .. glob_pattern.pattern)
end
@@ -248,4 +169,13 @@ function M.unregister(unreg, ctx)
end
end
+--- @param client_id integer
+function M.cancel(client_id)
+ for _, reg_cancels in pairs(cancels[client_id]) do
+ for _, cancel in pairs(reg_cancels) do
+ cancel()
+ end
+ end
+end
+
return M