Skip to content

Commit

Permalink
keybinds.lua: add type annotations for all functions
Browse files Browse the repository at this point in the history
The typing for these keybinds is a bit of a mess, and really should
be simplified, but that is something that can be done after this PR is
merged. The static typing done here will enable a refactor in the
future.
  • Loading branch information
CogentRedTester committed Feb 3, 2025
1 parent e70a3a3 commit 4a24709
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 23 deletions.
37 changes: 35 additions & 2 deletions modules/defs/keybind.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
---@meta KeybindDefs
---@meta _

---@class Keybind
---@class KeybindFlags
---@field repeatable boolean?
---@field scalable boolean?
---@field complex boolean?


---@class KeybindCommandTable


---@class Keybind
---@field key string
---@field command KeybindCommand
---@field api_version string?
---
---@field name string?
---@field condition string?
---@field flags KeybindFlags?
---@field filter ('file'|'dir')?
---@field parser string?
---@field multiselect boolean?
---@field multi-type ('repeat'|'concat')?
---@field delay number?
---@field concat-string string?
---@field passthrough boolean?
---
---@field prev_key Keybind? The keybind that was previously set to the same key.
---@field codes Set<string>? Any substituation codes used by the command table.
---@field condition_codes Set<string>? Any substitution codes used by the condition string.
---@field addon boolean? Whether the keybind was created by an addon.


---@alias KeybindCommand function|KeybindCommandTable[]
---@alias KeybindTuple [string,string,table|function,KeybindFlags?]
---@alias KeybindList (Keybind|KeybindTuple)[]
80 changes: 59 additions & 21 deletions modules/keybinds.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,17 @@ g.state.keybinds = {
{'Ctrl+a', 'select_all', cursor.select_all}
}

--a map of key-keybinds - only saves the latest keybind if multiple have the same key code
---a map of key-keybinds - only saves the latest keybind if multiple have the same key code
---@type KeybindList
local top_level_keys = {}

--format the item string for either single or multiple items
---Format the item string for either single or multiple items.
---@param base_code_fn Replacer
---@param items Item[]
---@param state State
---@param cmd Keybind
---@param quoted? boolean
---@return string|nil
local function create_item_string(base_code_fn, items, state, cmd, quoted)
if not items[1] then return end
local func = quoted and function(...) return ("%q"):format(base_code_fn(...)) end or base_code_fn
Expand All @@ -58,7 +65,12 @@ end
local KEYBIND_CODE_PATTERN = fb_utils.get_code_pattern(fb_utils.code_fns)
local item_specific_codes = 'fnij'

--substitutes the key codes for the
---Replaces codes in the given string using the replacers.
---@param str string
---@param cmd Keybind
---@param items Item[]
---@param state State
---@return string
local function substitute_codes(str, cmd, items, state)
local overrides = {}

Expand All @@ -70,25 +82,35 @@ local function substitute_codes(str, cmd, items, state)
return fb_utils.substitute_codes(str, overrides, items[1], state)
end

--iterates through the command table and substitutes special
--character codes for the correct strings used for custom functions
---Iterates through the command table and substitutes special
---character codes for the correct strings used for custom functions.
---@param cmd Keybind
---@param items Item[]
---@param state State
---@return KeybindCommand
local function format_command_table(cmd, items, state)
local command = cmd.command
if type(command) == 'function' then return command end
local copy = {}
for i = 1, #cmd.command do
for i = 1, #command do
copy[i] = {}

for j = 1, #cmd.command[i] do
for j = 1, #command[i] do
copy[i][j] = substitute_codes(cmd.command[i][j], cmd, items, state)
end
end
return copy
end

--runs all of the commands in the command table
--key.command must be an array of command tables compatible with mp.command_native
--items must be an array of multiple items (when multi-type ~= concat the array will be 1 long)
---Runs all of the commands in the command table.
---@param cmd Keybind key.command must be an array of command tables compatible with mp.command_native
---@param items Item[] must be an array of multiple items (when multi-type ~= concat the array will be 1 long).
---@param state State
local function run_custom_command(cmd, items, state)
local custom_cmds = cmd.codes and format_command_table(cmd, items, state) or cmd.command
if type(custom_cmds) == 'function' then
error(('attempting to run a function keybind as a command table keybind\n%s'):format(utils.to_string(cmd)))
end

for _, custom_cmd in ipairs(custom_cmds) do
msg.debug("running command:", utils.to_string(custom_cmd))
Expand All @@ -104,7 +126,12 @@ local function has_item_codes(codes)
return false
end

--runs one of the custom commands
---Runs one of the custom commands.
---@async
---@param cmd Keybind
---@param state State
---@param co thread
---@return boolean|nil
local function run_custom_keybind(cmd, state, co)
--evaluates a condition and passes through the correct values
local function evaluate_condition(condition, items)
Expand Down Expand Up @@ -178,8 +205,12 @@ local function run_custom_keybind(cmd, state, co)
return #selection == num_selection
end

--recursively runs the keybind functions, passing down through the chain
--of keybinds with the same key value
---Recursively runs the keybind functions, passing down through the chain
---of keybinds with the same key value.
---@async
---@param keybind Keybind
---@param state State
---@param co thread
local function run_keybind_recursive(keybind, state, co)
msg.trace("Attempting custom command:", utils.to_string(keybind))

Expand All @@ -195,7 +226,8 @@ local function run_keybind_recursive(keybind, state, co)
end
end

--a wrapper to run a custom keybind as a lua coroutine
---A wrapper to run a custom keybind as a lua coroutine.
---@param key Keybind
local function run_keybind_coroutine(key)
msg.debug("Received custom keybind "..key.key)
local co = coroutine.create(run_keybind_recursive)
Expand All @@ -215,7 +247,10 @@ local function run_keybind_coroutine(key)
end
end

--scans the given command table to identify if they contain any custom keybind codes
---Scans the given command table to identify if they contain any custom keybind codes.
---@param command_table function|KeybindCommandTable[]
---@param codes Set<string>
---@return Set<string>
local function scan_for_codes(command_table, codes)
if type(command_table) ~= "table" then return codes end
for _, value in pairs(command_table) do
Expand All @@ -231,16 +266,19 @@ local function scan_for_codes(command_table, codes)
return codes
end

--inserting the custom keybind into the keybind array for declaration when file-browser is opened
--custom keybinds with matching names will overwrite eachother
---Inserting the custom keybind into the keybind array for declaration when file-browser is opened.
---Custom keybinds with matching names will overwrite eachother.
---@param keybind Keybind
local function insert_custom_keybind(keybind)
-- api checking for the keybinds is optional, so set to a valid version if it does not exist
keybind.api_version = keybind.api_version or '1.0.0'
if not addons.check_api_version(keybind, 'keybind '..keybind.name) then return end

local command = keybind.command

--we'll always save the keybinds as either an array of command arrays or a function
if type(keybind.command) == "table" and type(keybind.command[1]) ~= "table" then
keybind.command = {keybind.command}
if type(command) == "table" and type(command[1]) ~= "table" then
keybind.command = {command}
end

keybind.codes = scan_for_codes(keybind.command, {})
Expand All @@ -256,8 +294,8 @@ local function insert_custom_keybind(keybind)
top_level_keys[keybind.key] = keybind
end

--loading the custom keybinds
--can either load keybinds from the config file, from addons, or from both
---Loading the custom keybinds.
---Can either load keybinds from the config file, from addons, or from both.
local function setup_keybinds()
if not o.custom_keybinds and not o.addons then return end

Expand Down

0 comments on commit 4a24709

Please sign in to comment.