Skip to content

Commit

Permalink
scanning.lua: add type annotations for all functions
Browse files Browse the repository at this point in the history
  • Loading branch information
CogentRedTester committed Feb 3, 2025
1 parent 1c5a6f9 commit 5da0208
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 21 deletions.
8 changes: 5 additions & 3 deletions modules/apis/parse-state.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ local parse_state_API = {}

---A wrapper around coroutine.yield that aborts the coroutine if
--the parse request was cancelled by the user.
--the coroutine is
--the coroutine is
---@async
---@param self ParseState
---@param ... any
---@return any ...
---@return unknown ...
function parse_state_API:yield(...)
local co = coroutine.running()
local is_browser = co == g.state.co
Expand All @@ -28,7 +29,8 @@ function parse_state_API:yield(...)
return unpack(result, 1, result.n)
end

--checks if the current coroutine is the one handling the browser's request
---Checks if the current coroutine is the one handling the browser's request.
---@return boolean
function parse_state_API:is_coroutine_current()
return coroutine.running() == g.state.co
end
Expand Down
23 changes: 15 additions & 8 deletions modules/defs/parser.lua
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---@meta ParserDefs
---@meta _

---A ParserConfig object returned by addons
---@class ParserConfig
---@field priority number
---@field api_version string The minimum API version the string requires.
---@field version string? The minimum API version the string requires. @deprecated.
---
---@field can_parse (fun(self: Parser, directory: string, parse_state: ParseState): boolean)?
---@field parse (fun(self: Parser, directory: string, parse_state: ParseState): List?, Opts?)?
---@field can_parse (async fun(self: Parser, directory: string, parse_state: ParseState): boolean)?
---@field parse (async fun(self: Parser, directory: string, parse_state: ParseState): List?, Opts?)?
---@field setup fun(self: Parser)?
---
---@field name string?
Expand All @@ -18,13 +18,20 @@
---The parser object used by file-browser once the parsers have been loaded and initialised.
---@class Parser: ParserConfig, ParserAPI
---@field name string
---@field can_parse fun(self: Parser, directory: string, parse_state: ParseState): boolean
---@field parse fun(self: Parser, directory: string, parse_state: ParseState): List?, Opts?
---@field can_parse async fun(self: Parser, directory: string, parse_state: ParseState): boolean
---@field parse async fun(self: Parser, directory: string, parse_state: ParseState): List?, Opts?


---@alias ParseStateSource 'browser'|'loadlist'|'script-message'|'addon'|string


---The ParseStateTemplate object passed to the parse functions
---@class ParseStateTemplate: table
---@field source ParseStateSource?


---The Parse State object passed to the can_parse and parse methods
---@class ParseState: ParseStateAPI
---@field source 'browser'|'loadlist'|'script-message'|'addon'|string
---@class ParseState: ParseStateAPI,ParseStateTemplate
---@field source ParseStateSource
---@field directory string
---@field already_deferred boolean?
---@field yield fun(self: ParseState, ...:unknown): ...
41 changes: 32 additions & 9 deletions modules/navigation/scanning.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ local function clear_non_adjacent_state()
cache:clear_traversal_stack()
end

--parses the given directory or defers to the next parser if nil is returned
---parses the given directory or defers to the next parser if nil is returned
---@async
---@param directory string
---@param index number
---@return List?
---@return Opts?
local function choose_and_parse(directory, index)
msg.debug(("finding parser for %q"):format(directory))
local parser, list, opts
Expand All @@ -36,7 +41,12 @@ local function choose_and_parse(directory, index)
return list, opts
end

--sets up the parse_state table and runs the parse operation
---Sets up the parse_state table and runs the parse operation.
---@async
---@param directory string
---@param parse_state ParseState
---@return List|nil
---@return Opts
local function run_parse(directory, parse_state)
msg.verbose(("scanning files in %q"):format(directory))
parse_state.directory = directory
Expand All @@ -46,23 +56,30 @@ local function run_parse(directory, parse_state)

local list, opts = choose_and_parse(directory, 1)

if list == nil then return msg.debug("no successful parsers found") end
if list == nil then return msg.debug("no successful parsers found"), {} end
opts = opts or {}
opts.parser = g.parsers[opts.id]

if not opts.filtered then fb_utils.filter(list) end
if not opts.sorted then fb_utils.sort(list) end
return list, opts
end

--returns the contents of the given directory using the given parse state
--if a coroutine has already been used for a parse then create a new coroutine so that
--the every parse operation has a unique thread ID
---Returns the contents of the given directory using the given parse state.
---If a coroutine has already been used for a parse then create a new coroutine so that
---the every parse operation has a unique thread ID.
---@async
---@param directory string
---@param parse_state ParseStateTemplate
---@return List|nil
---@return Opts
local function parse_directory(directory, parse_state)
local co = fb_utils.coroutine.assert("scan_directory must be executed from within a coroutine - aborting scan "..utils.to_string(parse_state))
if not g.parse_states[co] then return run_parse(directory, parse_state) end

--if this coroutine is already is use by another parse operation then we create a new
--one and hand execution over to that
---@async
local new_co = coroutine.create(function()
fb_utils.coroutine.resume_err(co, run_parse(directory, parse_state))
end)
Expand All @@ -78,7 +95,9 @@ local function parse_directory(directory, parse_state)
return g.parse_states[co]:yield()
end

--sends update requests to the different parsers
---Sends update requests to the different parsers.
---@async
---@param moving_adjacent? number|boolean
local function update_list(moving_adjacent)
msg.verbose('opening directory: ' .. g.state.directory)

Expand Down Expand Up @@ -115,6 +134,8 @@ local function update_list(moving_adjacent)
msg.warn("could not read directory", g.state.directory, "redirecting to root")
list, opts = parse_directory("", { source = "browser" })

if not list then error(('fatal error - failed to read the root directory')) end

-- sets the directory redirect flag
opts.directory = ''
end
Expand Down Expand Up @@ -145,8 +166,9 @@ local function update_list(moving_adjacent)
g.state.prev_directory = g.state.directory
end

--rescans the folder and updates the list
--returns the coroutine for the new parse operation
---rescans the folder and updates the list.
---@param moving_adjacent? number|boolean
---@return thread? # The coroutine for the triggered parse operation. May be aborted early if directory is in the cache.
local function rescan(moving_adjacent)
if moving_adjacent == nil then moving_adjacent = 0 end

Expand All @@ -160,6 +182,7 @@ local function rescan(moving_adjacent)

--the directory is always handled within a coroutine to allow addons to
--pause execution for asynchronous operations
---@async
g.state.co = fb_utils.coroutine.queue(function()
update_list(moving_adjacent)
if g.state.empty_text == "~" then g.state.empty_text = "empty directory" end
Expand Down
4 changes: 3 additions & 1 deletion modules/playlist.lua
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ local concurrent_loadlist_wrapper

---This function recursively loads directories concurrently in separate coroutines.
---Results are saved in a tree of tables that allows asynchronous access.
---@async
---@param directory string
---@param load_opts LoadOpts
---@param prev_dirs Set<string>
Expand All @@ -98,7 +99,7 @@ local function concurrent_loadlist_parse(directory, load_opts, prev_dirs, item_t
if prev_dirs[directory] then return end
prev_dirs[directory] = true

local list, list_opts = scanning.scan_directory(directory, { source = "loadlist" })
local list, list_opts = scanning.scan_directory(directory, { source = 'loadlist' })
if list == g.root then return end

--if we can't parse the directory then append it and hope mpv fares better
Expand Down Expand Up @@ -174,6 +175,7 @@ end

---Recursive function to load directories serially.
---Returns true if any items were appended to the playlist.
---@async
---@param directory string
---@param load_opts LoadOpts
---@param prev_dirs Set<string>
Expand Down

0 comments on commit 5da0208

Please sign in to comment.