-
-
Notifications
You must be signed in to change notification settings - Fork 629
fix(diagnostics): throttle sequential CocDiagnostics updates, make debouncer safe and sequential #1430
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(diagnostics): throttle sequential CocDiagnostics updates, make debouncer safe and sequential #1430
Changes from all commits
c38cc7b
fd66fcf
6f174c5
835a66d
9d04802
3cc9b92
57f2f0a
be18bf8
386c6f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,8 +29,8 @@ function M.reload_project(project_root, path) | |
return | ||
end | ||
|
||
if path and not path:match("^" .. project_root) then | ||
path = nil | ||
if path and path:find(project_root, 1, true) ~= 1 then | ||
return | ||
end | ||
|
||
local git_status = Runner.run { | ||
|
@@ -43,7 +43,7 @@ function M.reload_project(project_root, path) | |
|
||
if path then | ||
for p in pairs(project.files) do | ||
if p:match("^" .. path) then | ||
if p:find(path, 1, true) == 1 then | ||
project.files[p] = nil | ||
end | ||
end | ||
|
@@ -138,10 +138,6 @@ function M.load_project_status(cwd) | |
reload_tree_at(opts.project_root) | ||
end) | ||
end, | ||
on_event0 = function() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I must have left this in when testing. |
||
log.line("watcher", "git event") | ||
M.reload_tree_at(project_root) | ||
end, | ||
} | ||
end | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -307,25 +307,57 @@ function M.key_by(tbl, key) | |
return keyed | ||
end | ||
|
||
---Execute callback timeout ms after the lastest invocation with context. Waiting invocations for that context will be discarded. Caller should this ensure that callback performs the same or functionally equivalent actions. | ||
local function timer_stop_close(timer) | ||
if timer:is_active() then | ||
timer:stop() | ||
end | ||
if not timer:is_closing() then | ||
timer:close() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Manual reference count handling was not necessary; libuv will clear the last reference on handle close. |
||
end | ||
end | ||
|
||
---Execute callback timeout ms after the lastest invocation with context. | ||
---Waiting invocations for that context will be discarded. | ||
---Invocation will be rescheduled while a callback is being executed. | ||
---Caller must ensure that callback performs the same or functionally equivalent actions. | ||
--- | ||
---@param context string identifies the callback to debounce | ||
---@param timeout number ms to wait | ||
---@param callback function to execute on completion | ||
function M.debounce(context, timeout, callback) | ||
if M.debouncers[context] then | ||
pcall(uv.close, M.debouncers[context]) | ||
-- all execution here is done in a synchronous context; no thread safety required | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tested exhaustively using |
||
|
||
M.debouncers[context] = M.debouncers[context] or {} | ||
local debouncer = M.debouncers[context] | ||
|
||
-- cancel waiting or executing timer | ||
if debouncer.timer then | ||
timer_stop_close(debouncer.timer) | ||
end | ||
|
||
M.debouncers[context] = uv.new_timer() | ||
M.debouncers[context]:start( | ||
timeout, | ||
0, | ||
vim.schedule_wrap(function() | ||
M.debouncers[context]:close() | ||
M.debouncers[context] = nil | ||
local timer = uv.new_timer() | ||
debouncer.timer = timer | ||
timer:start(timeout, 0, function() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the key change: perform all our control operations in the direct callback instead of during scheduled execution. |
||
timer_stop_close(timer) | ||
|
||
-- reschedule when callback is running | ||
if debouncer.executing then | ||
M.debounce(context, timeout, callback) | ||
return | ||
end | ||
|
||
-- call back at a safe time | ||
debouncer.executing = true | ||
vim.schedule(function() | ||
callback() | ||
debouncer.executing = false | ||
|
||
-- no other timer waiting | ||
if debouncer.timer == timer then | ||
M.debouncers[context] = nil | ||
end | ||
end) | ||
) | ||
end) | ||
end | ||
|
||
function M.focus_file(path) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I keep needing a "clean" log context that doesn't get removed by
validate_options
. Feel free to remove.