diff --git a/data/core/command.lua b/data/core/command.lua index faf872e2..2b28f63b 100644 --- a/data/core/command.lua +++ b/data/core/command.lua @@ -1,25 +1,55 @@ local core = require "core" local command = {} +---A predicate function accepts arguments from `command.perform()` and evaluates to a boolean.
+---If the function returns true, then the function associated with the command is executed. +--- +---The predicate function can also return other values after the boolean, which will +---be passed into the function associated with the command. +---@alias core.command.predicate_function fun(...: any): boolean, ... + +---A predicate is a string, an Object or a function, that is used to determine +---whether a command should be executed. +--- +---If the predicate is a string, it is resolved into an `Object` via `require()` +---and checked against the active view with `Object:extends()`.
+---For example, `"core.docview"` will match any view that inherits from `DocView`.
+---A `!` can be appended to the predicate to strictly match the current view via `Object:is()`, +---instead of matching any view that inherits the predicate. +--- +---If the predicate is a table, it is checked against the active view with `Object:extends()`. +---Strict matching via `Object:is()` is not available. +--- +---If the predicate is a function, it must behave like a predicate function. +---@see core.command.predicate_function +---@alias core.command.predicate string|core.object|core.command.predicate_function + +---A command is identified by a command name. +---The command name contains a category and the name itself, separated by a colon (':'). +--- +---All commands should be in lowercase and should not contain whitespaces; instead +---they should be replaced by a dash ('-'). +---@alias core.command.command_name string + +---The predicate and its associated function. +---@class core.command.command +---@field predicate core.command.predicate_function +---@field perform fun(...: any) + +---@type { [string]: core.command.command } command.map = {} +---@type core.command.predicate_function local always_true = function() return true end ----Used iternally by command.add, statusview, and contextmenu to generate a ----function with a condition to evaluate returning the boolean result of this ----evaluation. +---This function takes in a predicate and produces a predicate function +---that is internally used to dispatch and execute commands. --- ----If a string predicate is given it is treated as a require import that should ----return a valid object which is checked against the current active view, ----eg: "core.docview" will match any view that inherits from DocView. Appending ----a `!` at the end of the string means we want to match the given object ----from the import strcitly eg: "core.docview!" only DocView is matched. ----A function that returns a boolean can be used instead to perform a custom ----evaluation, setting to nil means always evaluates to true. ---- ----@param predicate string | table | function ----@return function +---This function should not be called manually. +---@see core.command.predicate +---@param predicate core.command.predicate|nil If nil, the predicate always evaluates to true. +---@return core.command.predicate_function function command.generate_predicate(predicate) predicate = predicate or always_true local strict = false @@ -38,10 +68,20 @@ function command.generate_predicate(predicate) predicate = function(...) return core.active_view:is(class), core.active_view, ... end end end + ---@cast predicate core.command.predicate_function return predicate end +---Adds commands to the map. +--- +---The function accepts a table containing a list of commands +---and their functions.
+---If a command already exists, it will be replaced. +---@see core.command.predicate +---@see core.command.command_name +---@param predicate core.command.predicate +---@param map { [core.command.command_name]: fun(...: any) } function command.add(predicate, map) predicate = command.generate_predicate(predicate) for name, fn in pairs(map) do @@ -57,11 +97,21 @@ local function capitalize_first(str) return str:sub(1, 1):upper() .. str:sub(2) end +---Prettifies the command name. +--- +---This function adds a space between the colon and the command name, +---replaces dashes with spaces and capitalizes the command appropriately. +---@see core.command.command_name +---@param name core.command.command_name +---@return string function command.prettify_name(name) + ---@diagnostic disable-next-line: redundant-return-value return name:gsub(":", ": "):gsub("-", " "):gsub("%S+", capitalize_first) end +---Returns all the commands that can be executed (their predicates evaluate to true). +---@return core.command.command_name[] function command.get_all_valid() local res = {} local memoized_predicates = {} @@ -76,6 +126,10 @@ function command.get_all_valid() return res end +---Checks whether a command can be executed (its predicate evaluates to true). +---@param name core.command.command_name +---@param ... any +---@return boolean function command.is_valid(name, ...) return command.map[name] and command.map[name].predicate(...) end @@ -98,12 +152,26 @@ local function perform(name, ...) end -function command.perform(...) - local ok, res = core.try(perform, ...) +---Performs a command. +--- +---The arguments passed into this function are forwarded to the predicate function.
+---If the predicate function returns more than 1 value, the other values are passed +---to the command. +--- +---Otherwise, the arguments passed into this function are passed directly +---to the command. +---@see core.command.predicate +---@see core.command.predicate_function +---@param name core.command.command_name +---@param ... any +---@return boolean # true if the command is performed successfully. +function command.perform(name, ...) + local ok, res = core.try(perform, name, ...) return not ok or res end +---Inserts the default commands for Lite XL into the map. function command.add_defaults() local reg = { "core", "root", "command", "doc", "findreplace",