Skip to content
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: defer execution to avoid segfaults and fix throttler #526

Merged
merged 2 commits into from
Dec 4, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 39 additions & 14 deletions lua/treesitter-context.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,51 @@ local all_contexts = {}
--- @param ms? number
--- @return F
local function throttle_by_id(f, ms)
ms = ms or 200
ms = ms or 150
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is have decreased throttle timeout a little to compensate previous incorrect behavior.


local timers = {} --- @type table<any,uv.uv_timer_t>
local state = {} --- @type table<any,string>
local waiting = {} --- @type table<any,boolean>
return function(id)

local schedule = function(id, wrapper)
state[id] = "scheduled"
vim.schedule_wrap(wrapper)(id, wrapper)
end

local on_throttle_finish = function(id, wrapper)
assert(state[id] == "throttled")
if waiting[id] == nil then
timers[id] = nil
state[id] = nil
return
end
waiting[id] = nil
schedule(id, wrapper)
end

local wrapper = function(id, wrapper)
assert(state[id] == "scheduled")
state[id] = "running"
f(id)
assert(state[id] == "running")
state[id] = "throttled"

if timers[id] == nil then
timers[id] = assert(vim.loop.new_timer())
else
waiting[id] = true
return
end
timers[id]:start(ms, 0, function() on_throttle_finish(id, wrapper) end)
end

f(id) -- first call, execute immediately
timers[id]:start(ms, 0, function()
if waiting[id] then
vim.schedule(function() f(id) end) -- only execute if there are calls waiting
end
waiting[id] = nil
timers[id] = nil
end)
return function(id)
if state[id] == nil then
schedule(id, wrapper)
return
end
-- Don't set 'waiting' for 'scheduled' state since the callback is about to start.
-- Consequently, there is no need to run it again after throttling is completed.
if state[id] ~= "scheduled" then
waiting[id] = true
end
end
end

Expand Down Expand Up @@ -173,7 +199,6 @@ function M.enable()

if config.multiwindow then
table.insert(update_events, 'WinResized')
table.insert(update_events, 'WinLeave')
Copy link
Contributor Author

@apollo1321 apollo1321 Nov 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't recall why I used this event for multi-window support. It appears to be unnecessary because it triggers for the window being left (nvim_get_current_win() returns the window that the user is leaving).

end

autocmd(update_events, update)
Expand Down
Loading