From 54d9cdc328ec6f06a043b0136dc625a391372bdb Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 2 Jul 2026 22:09:15 -0700 Subject: [PATCH] Collapse keymap resolution and prune keybindings menu The keymap cache parsed xkbcli compile-keymap into a bash associative array, serialized that array back into a string, and re-parsed it inside a second awk's BEGIN block. One awk now reads the compiled keymap directly, seeded with the fallback table so xkbcli failure still degrades gracefully. (The xkbcli child must read /dev/null or it consumes the binding records arriving on stdin.) Also gone: the never-called lookup_keycode_cached, the DEBUG timing scaffold, the redundant explicit noops in the Lua mock that the metatable already provides, the walker-era XML entity escaping that nothing renders as markup anymore, and the monitor-height computation in favor of a fixed height like every other menu. The cache key drops the double sha256 and now includes the active keymap, so switching XKB layout invalidates cached symbol names instead of serving stale ones. Binding records are byte-identical before and after: 215 of 215. 686 lines to 573. Co-Authored-By: Claude Fable 5 --- bin/omarchy-menu-keybindings | 177 +++++++---------------------------- 1 file changed, 32 insertions(+), 145 deletions(-) diff --git a/bin/omarchy-menu-keybindings b/bin/omarchy-menu-keybindings index 88a538d2..80e42442 100755 --- a/bin/omarchy-menu-keybindings +++ b/bin/omarchy-menu-keybindings @@ -2,27 +2,6 @@ # omarchy:summary=Display Hyprland keybindings defined in your configuration using an interactive search menu. -declare -A KEYCODE_SYM_MAP -# Hyprland reports XKB keycodes for code: bindings. Keep a small fallback for -# common keys so the menu remains readable if xkbcli cannot resolve a symbol. -declare -A FALLBACK_KEYCODE_SYM_MAP=( - [10]="1" - [11]="2" - [12]="3" - [13]="4" - [14]="5" - [15]="6" - [16]="7" - [17]="8" - [18]="9" - [19]="0" - [20]="MINUS" - [21]="EQUAL" - [59]="COMMA" - [60]="PERIOD" - [61]="SLASH" -) - # Hyprland's Lua config provider currently reports Lua binds as dispatcher # __lua and code:... binds from hl.bind() as key="" and keycode=0 in # `hyprctl -j binds`. Keep a lightweight source-derived cache so the menu can @@ -31,74 +10,33 @@ declare -A LUA_BIND_KEY_MAP declare -A LUA_BIND_DISPATCHER_MAP declare -A LUA_BIND_ARG_MAP -build_keymap_cache() { - local keymap - keymap="$(xkbcli compile-keymap)" || { - echo "Failed to compile keymap" >&2 - return 1 - } - - while IFS=, read -r code sym; do - [[ -z $code || -z $sym ]] && continue - KEYCODE_SYM_MAP["$code"]="$sym" - done < <( - awk ' - BEGIN { sec = "" } - /xkb_keycodes/ { sec = "codes"; next } - /xkb_symbols/ { sec = "syms"; next } - sec == "codes" { - if (match($0, /<([A-Za-z0-9_]+)>\s*=\s*([0-9]+)\s*;/, m)) code_by_name[m[1]] = m[2] - } - sec == "syms" { - if (match($0, /key\s*<([A-Za-z0-9_]+)>\s*\{\s*\[\s*([^, \]]+)/, m)) sym_by_name[m[1]] = m[2] - } - END { - for (k in code_by_name) { - c = code_by_name[k] - s = sym_by_name[k] - if (c != "" && s != "" && s != "NoSymbol") print c "," s - } - } - ' <<<"$keymap" - ) -} - -lookup_keycode_cached() { - local symbol - symbol="${KEYCODE_SYM_MAP[$1]}" - - if [[ -z $symbol ]]; then - symbol="${FALLBACK_KEYCODE_SYM_MAP[$1]}" - fi - - if [[ -z $symbol ]]; then - symbol="code:$1" - fi - - printf '%s\n' "${symbol^^}" -} - +# Hyprland reports XKB keycodes for code: bindings. Resolve them to symbols +# via the compiled keymap, with a small fallback for common keys so the menu +# remains readable if xkbcli cannot resolve a symbol. parse_keycodes() { - local start end elapsed - [[ ${DEBUG:-0} == "1" ]] && start=$(date +%s.%N) - - awk -v keymap="$( - for code in "${!KEYCODE_SYM_MAP[@]}"; do - printf '%s=%s\n' "$code" "${KEYCODE_SYM_MAP[$code]}" - done - for code in "${!FALLBACK_KEYCODE_SYM_MAP[@]}"; do - printf '%s=%s\n' "$code" "${FALLBACK_KEYCODE_SYM_MAP[$code]}" - done - )" ' + awk ' BEGIN { - split(keymap, entries, "\n") - for (entry_index in entries) { - if (entries[entry_index] == "") continue - separator = index(entries[entry_index], "=") - if (separator == 0) continue - code = substr(entries[entry_index], 1, separator - 1) - symbol = substr(entries[entry_index], separator + 1) - if (!(code in keycode_symbol)) keycode_symbol[code] = toupper(symbol) + split("10=1 11=2 12=3 13=4 14=5 15=6 16=7 17=8 18=9 19=0 20=MINUS 21=EQUAL 59=COMMA 60=PERIOD 61=SLASH", fallbacks, " ") + for (i in fallbacks) { + separator = index(fallbacks[i], "=") + keycode_symbol[substr(fallbacks[i], 1, separator - 1)] = substr(fallbacks[i], separator + 1) + } + + # 0) { + if (line ~ /xkb_keycodes/) { section = "codes"; continue } + if (line ~ /xkb_symbols/) { section = "syms"; continue } + if (section == "codes" && match(line, /<([A-Za-z0-9_]+)>\s*=\s*([0-9]+)\s*;/, m)) code_by_name[m[1]] = m[2] + if (section == "syms" && match(line, /key\s*<([A-Za-z0-9_]+)>\s*\{\s*\[\s*([^, \]]+)/, m)) sym_by_name[m[1]] = m[2] + } + close(keymap_cmd) + + for (name in code_by_name) { + code = code_by_name[name] + symbol = sym_by_name[name] + if (code != "" && symbol != "" && symbol != "NoSymbol") keycode_symbol[code] = toupper(symbol) } mouse_symbol["272"] = "LEFT MOUSE BUTTON" @@ -125,17 +63,6 @@ parse_keycodes() { print } ' - - if [[ $DEBUG == "1" ]]; then - end=$(date +%s.%N) - # fall back to awk if bc is missing - if command -v bc >/dev/null 2>&1; then - elapsed=$(echo "$end - $start" | bc) - else - elapsed=$(awk -v s="$start" -v e="$end" 'BEGIN{printf "%.6f", (e - s)}') - fi - echo "[DEBUG] parse_keycodes elapsed: ${elapsed}s" >&2 - fi } # Supplement `hyprctl -j binds` for Lua-only binds that Hyprland currently @@ -266,10 +193,6 @@ noop = setmetatable({}, { end, }) -local function noop_fn() - return noop -end - hl = setmetatable({ dsp = dsp_proxy("hl.dsp"), bind = function(keys, bind_dispatcher, opts) @@ -293,20 +216,6 @@ hl = setmetatable({ return noop end, - unbind = noop_fn, - config = noop_fn, - env = noop_fn, - monitor = noop_fn, - window_rule = noop_fn, - workspace_rule = noop_fn, - layer_rule = noop_fn, - gesture = noop_fn, - animation = noop_fn, - curve = noop_fn, - exec_cmd = noop_fn, - dispatch = noop_fn, - on = noop_fn, - timer = noop_fn, get_config = function() return nil end, @@ -441,13 +350,6 @@ parse_binding_records() { gsub(/(^|[[:space:]])uwsm(-app| app)[[:space:]]+--[[:space:]]+/, "", action); gsub(/^[ \t]+|[ \t]+$/, "", action); gsub(/[ \t]+/, " ", key_combo); # Collapse multiple spaces to one - - # Escape XML entities - gsub(/&/, "\\&", action); - gsub(//, "\\>", action); - gsub(/"/, "\\"", action); - gsub(/'"'"'/, "\\'", action); } if (action != "") { @@ -518,7 +420,6 @@ prioritize_entries() { } output_binding_records_uncached() { - build_keymap_cache build_lua_bind_cache { @@ -533,8 +434,9 @@ output_binding_records_uncached() { keybindings_cache_key() { { - printf 'v3\n' - hyprctl -j binds 2>/dev/null | sha256sum | awk '{ print $1 }' + printf 'v4\n' + hyprctl -j devices 2>/dev/null | jq -r '.keyboards[].active_keymap' 2>/dev/null + hyprctl -j binds 2>/dev/null } | sha256sum | awk '{ print $1 }' } @@ -556,28 +458,15 @@ refresh_keybindings_cache() { } output_binding_records() { - local cache_dir cache_file cache_key cache_name - - cache_key=$(keybindings_cache_key) || { - output_binding_records_uncached - return - } + local cache_dir cache_file cache_name cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/omarchy" - cache_name="keybindings-${cache_key}.records" + cache_name="keybindings-$(keybindings_cache_key).records" cache_file="$cache_dir/$cache_name" if [[ -s $cache_file ]]; then cat "$cache_file" - return - fi - - mkdir -p "$cache_dir" || { - output_binding_records_uncached - return - } - - if refresh_keybindings_cache "$cache_dir" "$cache_file" "$cache_name"; then + elif mkdir -p "$cache_dir" 2>/dev/null && refresh_keybindings_cache "$cache_dir" "$cache_file" "$cache_name"; then cat "$cache_file" else output_binding_records_uncached @@ -671,10 +560,8 @@ if [[ $1 == "--print" || $1 == "-p" ]]; then output_keybindings else records=$(output_binding_records) - monitor_height=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .height') - menu_height=$((monitor_height * 40 / 100)) selection=$(cut -f1 <<<"$records" | - omarchy-menu-select 'Keybindings' -- --width 800 --height "$menu_height") + omarchy-menu-select 'Keybindings' -- --width 800 --height 500) if [[ -n $selection ]]; then record=$(awk -F '\t' -v selection="$selection" '$1 == selection { print; exit }' <<<"$records")