mirror of
https://github.com/neovim/neovim.git
synced 2026-09-08 23:18:59 +00:00
Problem: LuaLS struggles with the generics used in Nvim's runtime, requiring broad diagnostic suppressions. Indexing is also slow. Solution: Use EmmyLua for type checks in the build and CI. It offers more sophisticated type checking, substantially better support for generics, and much better flow analysis. Correct the affected annotations. Use `@internal`, supported directly by EmmyLua, instead of `@nodoc` for shared internal declarations, and support it in the help parser. AI-assisted
123 lines
3.2 KiB
Lua
123 lines
3.2 KiB
Lua
local async = require('vim.async._core')
|
|
local runtime = require('vim.async._runtime')
|
|
|
|
--- An event can be used to notify multiple tasks that some event has
|
|
--- happened. An Event object manages an internal flag that can be set to true
|
|
--- with the `set()` method and reset to `false` with the `clear()` method.
|
|
--- The `wait()` method blocks until the flag is set to `true`. The flag is
|
|
--- set to `false` initially.
|
|
--- @class vim.async.Event
|
|
--- @field private _is_set boolean
|
|
--- @field private _waiters (function|false)[]
|
|
local Event = {}
|
|
Event.__index = Event
|
|
|
|
--- @param waiters (function|false)[]
|
|
--- @return boolean
|
|
local function has_waiters(waiters)
|
|
for _, waiter in ipairs(waiters) do
|
|
if waiter then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
--- Set the event.
|
|
---
|
|
--- All tasks waiting for event to be set will be awakened on a later event-loop
|
|
--- turn.
|
|
---
|
|
--- If `max_woken` is provided, only up to `max_woken` waiters will be woken.
|
|
--- If waiters are woken this way, the event is reset because the signal is
|
|
--- reserved for those waiters.
|
|
--- @param max_woken? integer
|
|
function Event:set(max_woken)
|
|
if self._is_set then
|
|
return
|
|
end
|
|
|
|
local limited = max_woken ~= nil
|
|
if not has_waiters(self._waiters) then
|
|
self._is_set = true
|
|
return
|
|
end
|
|
|
|
self._is_set = true
|
|
if limited then
|
|
-- The signal is reserved for existing waiters and will be assigned on the
|
|
-- scheduled turn. New waiters must not consume it first.
|
|
self._is_set = false
|
|
end
|
|
|
|
runtime.schedule(function()
|
|
local waiters = self._waiters
|
|
local waiters_to_notify = {} --- @type function[]
|
|
local limit = max_woken or math.huge
|
|
while #waiters > 0 and #waiters_to_notify < limit do
|
|
local waiter = table.remove(waiters, 1)
|
|
if waiter then
|
|
waiters_to_notify[#waiters_to_notify + 1] = waiter
|
|
end
|
|
end
|
|
|
|
if limited and #waiters_to_notify == 0 and not has_waiters(waiters) then
|
|
self._is_set = true
|
|
end
|
|
|
|
for _, waiter in ipairs(waiters_to_notify) do
|
|
waiter()
|
|
end
|
|
end)
|
|
end
|
|
|
|
--- Wait until the event is set.
|
|
---
|
|
--- If the event is set, return immediately. Otherwise block until another
|
|
--- task calls set().
|
|
--- @async
|
|
function Event:wait()
|
|
async.await(function(callback)
|
|
if self._is_set then
|
|
callback()
|
|
else
|
|
table.insert(self._waiters, callback)
|
|
return {
|
|
close = function(_, on_close)
|
|
-- set() compacts the waiter list, so cancellation cannot rely on the
|
|
-- original insertion index still pointing at this callback.
|
|
for i, waiter in ipairs(self._waiters) do
|
|
if waiter == callback then
|
|
self._waiters[i] = false
|
|
break
|
|
end
|
|
end
|
|
if on_close then
|
|
on_close()
|
|
end
|
|
end,
|
|
}
|
|
end
|
|
end)
|
|
end
|
|
|
|
--- Clear (unset) the event.
|
|
---
|
|
--- Tasks awaiting on wait() will now block until the set() method is called
|
|
--- again.
|
|
function Event:clear()
|
|
self._is_set = false
|
|
end
|
|
|
|
--- Create a new event.
|
|
---
|
|
--- An event can signal to multiple listeners to resume execution.
|
|
--- The event can be set from a non-async context.
|
|
--- @return vim.async.Event
|
|
return function()
|
|
return setmetatable({
|
|
_waiters = {},
|
|
_is_set = false,
|
|
}, Event)
|
|
end
|