fix(config-toggle): replace read -k 3 with read -k 1 + ESC sequence detection
fish's read -k N waits for exactly N bytes; single-char keys ('q', 'j',
Space, Tab) were blocking indefinitely waiting for 2 more bytes, causing
the input loop to appear unresponsive. Random 3-key bundles were then
processed as unrecognised input, triggering infinite redraws.
Switch to read -k 1 (one raw byte per call). Arrow keys still work: the
terminal sends ESC+[+A/B as a burst, so after reading ESC the two
continuation bytes are already in the TTY buffer and the follow-up reads
return immediately. Bare ESC now passes the next keypress through as the
effective key rather than exiting (q/Q remain the exit keys).
This commit is contained in:
@@ -46,7 +46,7 @@ function config-toggle --description 'Interactive TUI for toggling opinionated c
|
|||||||
echo " $c_flag↑ ↓$c_reset or $c_flag""j k$c_reset Move cursor up / down"
|
echo " $c_flag↑ ↓$c_reset or $c_flag""j k$c_reset Move cursor up / down"
|
||||||
echo " $c_flag""Space$c_reset Cycle: ON → OFF → DEFAULT → ON"
|
echo " $c_flag""Space$c_reset Cycle: ON → OFF → DEFAULT → ON"
|
||||||
echo " $c_flag""Tab$c_reset Switch scope (Universal ↔ Session)"
|
echo " $c_flag""Tab$c_reset Switch scope (Universal ↔ Session)"
|
||||||
echo " $c_flag""q$c_reset / $c_flag""Escape$c_reset Exit"
|
echo " $c_flag""q$c_reset / $c_flag""Q$c_reset Exit"
|
||||||
echo
|
echo
|
||||||
echo "$c_head""Scopes:$c_reset"
|
echo "$c_head""Scopes:$c_reset"
|
||||||
echo " $c_flag""Universal$c_reset Persistent across all sessions ($c_dim""set -U$c_reset)"
|
echo " $c_flag""Universal$c_reset Persistent across all sessions ($c_dim""set -U$c_reset)"
|
||||||
@@ -81,10 +81,10 @@ function config-toggle --description 'Interactive TUI for toggling opinionated c
|
|||||||
__config_toggle_draw $cur_row $cur_scope $vars
|
__config_toggle_draw $cur_row $cur_scope $vars
|
||||||
|
|
||||||
# ── Event loop ────────────────────────────────────────
|
# ── Event loop ────────────────────────────────────────
|
||||||
# read -k 3 reads UP TO 3 chars and returns immediately when input is
|
# read -k 1 reads exactly one raw byte. Arrow keys send ESC+[+letter as a
|
||||||
# exhausted — it does not block waiting for exactly 3 chars. A bare ESC
|
# 3-byte burst — after reading the ESC we immediately read 2 more bytes
|
||||||
# sends 1 byte and read -k 3 returns at once with a 1-char string.
|
# which are already in the terminal buffer. Single-char keys ('j','k',' ',
|
||||||
# Arrow keys send the 3-byte sequence ESC [ A/B, matched as \e\[A / \e\[B.
|
# 'q', Tab) are returned right away with no extra blocking.
|
||||||
while true
|
while true
|
||||||
# Check for Ctrl-C signal (trap sets this flag)
|
# Check for Ctrl-C signal (trap sets this flag)
|
||||||
if set -q __config_toggle_exit
|
if set -q __config_toggle_exit
|
||||||
@@ -92,15 +92,25 @@ function config-toggle --description 'Interactive TUI for toggling opinionated c
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
read -k 3 -l key
|
read -k 1 -l key
|
||||||
|
|
||||||
|
# Detect CSI escape sequences (arrow keys: ESC [ A/B)
|
||||||
|
if test "$key" = \e
|
||||||
|
read -k 1 -l key2
|
||||||
|
if test "$key2" = "["
|
||||||
|
read -k 1 -l key3
|
||||||
|
set key "$key$key2$key3" # \e[A or \e[B
|
||||||
|
else
|
||||||
|
# ESC + non-bracket (ALT+key or plain ESC): treat key2 as key
|
||||||
|
set key $key2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
switch $key
|
switch $key
|
||||||
case \e\[A # Up arrow (ESC [ A)
|
case \e\[A # Up arrow
|
||||||
set cur_row (math "max(0, $cur_row - 1)")
|
set cur_row (math "max(0, $cur_row - 1)")
|
||||||
case \e\[B # Down arrow (ESC [ B)
|
case \e\[B # Down arrow
|
||||||
set cur_row (math "min(6, $cur_row + 1)")
|
set cur_row (math "min(6, $cur_row + 1)")
|
||||||
case \e # Bare Escape (1 byte) — exit
|
|
||||||
break
|
|
||||||
case k
|
case k
|
||||||
set cur_row (math "max(0, $cur_row - 1)")
|
set cur_row (math "max(0, $cur_row - 1)")
|
||||||
case j
|
case j
|
||||||
|
|||||||
Reference in New Issue
Block a user