Skip to content
Closed
Show file tree
Hide file tree
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
10 changes: 9 additions & 1 deletion bin/omarchy-menu-keybindings
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,17 @@ local function dsp_proxy(path)
})
end

-- Unknown hl.* accessors fall back to a noop so simple config calls parse, but
-- a noop must read as an empty sequence: the scan is reading real user configs
-- (e.g. ones that call hl.get_loaded_plugins and iterate the result), and a
-- metatable that yields a fresh value for every numeric index makes ipairs()
-- and # spin forever. Return nil for numbers so such loops terminate.
local noop
noop = setmetatable({}, {
__index = function()
__index = function(_, key)
if type(key) == "number" then
return nil
end
return noop
end,
__call = function()
Expand Down
41 changes: 41 additions & 0 deletions test/shell.d/keybindings-menu-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,44 @@ for action in "${expected_alternatives[@]}"; do
fail "every action named as having an alternative is bound twice" "$action"
done
pass "every action named as having an alternative is bound twice"

# The menu parses real user configs under a stubbed `hl`. A config that calls a
# list accessor and walks the result (such as a border-fx style file doing
# ipairs(hl.get_loaded_plugins())) must leave the scan, not hang on a noop
# that answers a value for every numeric index. Hyprland reports the bind below
# without its key, so a rendered "SUPER + P" is the scan reaching past the loop
# rather than terminating with nothing left to say.
stub_hyprctl <<BINDS
$(lua_bind 64 "" "Spin probe")
BINDS

spin_home="$tmpdir/spin-home"
mkdir -p "$spin_home/.config/hypr"

cat >"$spin_home/.config/hypr/hyprland.lua" <<'LUA'
local function border_fx_loaded()
for _, plugin in ipairs(hl.get_loaded_plugins()) do
if plugin.name == "hypr-shiny-border" then
return true
end
end

return false
end

border_fx_loaded()

hl.bind("SUPER + P", hl.dsp.exec_cmd("true"), { description = "Spin probe" })
LUA

# A cache of its own: the records cache is keyed on the stubbed hyprctl alone,
# so sharing one with an earlier block answers from the cache and never reads
# the config this is about.
spin_rendered=$(env -i PATH="$stub_bin:$ROOT/bin:$PATH" HOME="$spin_home" \
XDG_CACHE_HOME="$tmpdir/spin-cache" OMARCHY_PATH="$ROOT" \
timeout 20 bash "$ROOT/bin/omarchy-menu-keybindings" --print)
[[ $? == 0 ]] ||
fail "a config iterating a stubbed hl accessor completes the scan" "$spin_rendered"
grep -q 'SUPER + P *→ Spin probe' <<<"$spin_rendered" ||
fail "a config iterating a stubbed hl accessor completes the scan" "$spin_rendered"
pass "a config iterating a stubbed hl accessor completes the scan"