fzf consumed only the part of a sequence it recognized, and the rest was
typed into the query. CTRL-A sent as \e[97;5u became "97;5u", and CTRL-F
sent as \e[70;5u fired Home before typing ";5u". A BEL-terminated OSC
reply also aborted fzf, because the BEL that followed the typed payload
was read as CTRL-G.
Frame CSI and SS3 by their parameter and final byte ranges, OSC, DCS and
APC by their terminator. Drop the whole frame when the parser does not
recognize it, or recognizes only a prefix of it, and never consume past
it: the parser could count a key typed after it. rxvt ends keys with $,
as in \e[7$ and \e[23$, so $ after plain digits is a final byte. Elsewhere
it stays intermediate, as in DECRPM replies.
Take the second-chance read only when the sequence at the start of the
buffer is the only one and is unfinished. Otherwise it blocked until the
next key, holding back what followed, CTRL-G included. That read has no
time limit, so what it returns may be the next key, typed any time later.
It is never dropped with the frame.
Telling a terminal's sequence from an ALT key is the hard part, so these
keep what the parser made of them:
- An unterminated sequence, since that is how ALT-[, ALT-O, ALT-], ALT-P
and ALT-_ arrive
- A CSI or SS3 of four bytes or fewer. It could be an ALT key and typed
text, and rxvt sends keys of that size fzf does not know, such as \e[3^
- A string sequence whose payload does not start like a reply: digits and
';' for OSC, '>|', '!|', '[01]$r' or '[01]+r' for DCS, 'G' and a key
for APC
- SOS and PM, which nothing sends
Within a string, an ESC ends it and introduces a sequence of its own, and
only OSC ends with BEL. The read loop does not wait for a string
terminator: that held ALT-], ALT-P and ALT-_ for ESCDELAY and let typed
bytes pile up until they looked like a sequence.
- When the cursor is not at column 0, Init() makes space so fzf draws on
the next line and the prompt line is left intact
- Close() undid that: it moved up onto the prompt line before ESC[J, so
the erase wiped a line fzf never wrote on
- Shells repaint afterwards, so it showed as a flicker of the last prompt
line. Reported on fish, also seen on bash and nushell. zsh erases the
same line but repaints fast enough to hide it
- Erase our own area first, then step back and restore the column, so
nothing on that line is touched, RPROMPT included
Fix#4913
- getch() charged EINTR against a 100 iteration limit meant for read
attempts, and the count only reset once a byte was read
- So signals arriving during an idle prompt drained it, and running out
returned getCharError, which became Event{Fatal} and exit status 2
- Only the cancellable path is affected, hence only --listen
- select() is not restarted by SA_RESTART, unlike the blocking read on
the non-cancellable path, so it is the only one that sees EINTR
- waitReadable() already retries EINTR without a limit
Fix#4917
ESCDELAY=0 makes the retry count zero, so the wait added in d0377ed had
no effect and the reply still leaked. Past the introducer these bytes
cannot be the ESC key, so waiting for them adds no Escape latency.
- Wait at least defaultEscDelay, honour a larger ESCDELAY
- Lone ESC still honours ESCDELAY=0, measured 3ms to exit either way
Fix#4899
Read loop dropped its escDelay retry budget after every successful byte,
so a sequence split across reads reached the parser as a fragment, parsed
as ALT-[ with the remainder left behind as query text.
- fzf queries DECRQM at startup since dab626b, so a terminal answering
late leaked "?2004;2$y" into the query
- Same split leaked modified keys and mouse sequences: CTRL-UP left "5A",
SGR mouse left "0;1;1M"
- Bound unchanged, a stall longer than escDelay still falls back to ALT
Fix#4899
The file is opened under t.TempDir() and never closed, so the cleanup
that TempDir registers cannot remove it on Windows, where an open file
cannot be unlinked. The test itself passes; it is the cleanup that fails
the run. history_test.go and options_test.go already close theirs.
Ask the terminal whether the mode is already on (DECRQM) and put it back
that way on exit. Forcing it off broke pasting in shells that run fzf
from a line editor widget, which enable the mode only when the editor
starts. Terminals that do not answer fall back to disabling it.
- Startup queries go out in one write, cursor position last. Every
terminal answers DSR, so its reply bounds the wait: a paste reply
still missing by then means the terminal does not know the query.
- Bound the first read with select(2). Terminals that never answer
escape sequences, such as FreeBSD virtual terminals, blocked startup
until a key was pressed, and that keystroke was then discarded.
Fix#4887Fix#2860Fix#976
- Fires like result, but only after the input stream closes
- Use for one-shot per-query actions that would otherwise re-fire on
every intermediate snapshot during loading
Close#4835
- every(N) fires every N seconds (fractional, floored to 0.01s)
- Encoded as tui.Every with duration in Char as milliseconds, so
every(1) and every(2) coexist as distinct keymap entries
- FZF_IDLE_TIME exposes whole seconds since the last user activity
(keystroke or mouse event); pair with every() for idle-based
patterns like auto-accept/auto-quit
Close#1211
The display sanitizer already stripped raw 8-bit C1 bytes (0x80-0x9F)
because they decode to RuneError as standalone bytes. Their valid UTF-8
encodings (0xC2 0x80 .. 0xC2 0x9F) decode to the same code points but
were passed through, allowing a filename or input line containing CSI
(U+009B), OSC (U+009D), or DCS (U+0090) to inject terminal control
sequences when rendered.
New --border=dashed / --list-border=dashed / --header-border=dashed etc.
Uses U+2576 (╶) for horizontal edges and U+2506 (┆) for verticals, with
rounded corners (╭╮╰╯) and sharp T-junction mids (├┤). Terminal cells
are taller than wide (~2:1), so horizontals use a sparse stub per cell
while verticals need more dashes per cell to look evenly dashed.
Works with inline sections.
New BorderShape that embeds the section inside the --list-border
frame, joined to the list content by a horizontal separator with
T-junctions where the list shape has side borders. Requires a list
border with both top and bottom segments; falls back to 'line'
otherwise. Stacks when multiple sections are inline.
Sections inherit --color list-border by default and are colored as a
uniform block via their own --color *-border and *-bg.
Incompatible with --header-first. --header-border=inline requires
--header-lines-border to be inline or unset.
* Fix nth attr merge order to respect precedence hierarchy
nth attrs were merged ON TOP of current-fg/selected-fg attrs, so
nth:regular would clear attrs like underline from current-fg. Fix the
merge chain to apply nth BEFORE the line-type overlay:
fg < nth < selected-fg < current-fg < hl < selected-hl < current-hl
Store raw current-fg and selected-fg attrs in ColorTheme before they
get merged with fg/ListFg, then pass them as nthOverlay through
printHighlighted to colorOffsets where the correct merge chain is
computed: fgAttr.Merge(nthAttr).Merge(nthOverlay).
Fix#4687
* Make current-fg inherit from list-fg instead of fg
current-fg was inheriting from fg, not list-fg, so setting list-fg
had no effect on the current line. e.g.
fzf --color fg:dim,list-fg:underline,nth:regular -d / --nth -1
The non-nth part of the current line was dim (from fg) instead of
underline (from list-fg). Resolve ListFg/ListBg earlier in InitTheme
so Current can inherit from them.
* Make selected-fg inherit from list-fg via merge instead of override
selected-fg used o() which replaces the attr from list-fg entirely.
e.g. with fg:dim,selected-fg:italic, the dim was lost on selected
lines because o() replaced dim with italic instead of merging them.
Use ColorAttr.Merge() so attrs are combined additively, consistent
with how current-fg inherits from list-fg.
* Apply selected-fg attrs on current line when item is selected
When an item is both current and selected, the current-line rendering
ignored selected-fg attrs entirely. e.g.
echo foo | fzf --color fg:dim,nth:regular,current-fg:underline,selected-fg:italic --bind result:select --multi
The italic from selected-fg was lost. Merge NthSelectedAttr into the
overlay and rebuild colBase attr with the correct precedence chain:
fg < selected-fg < current-fg.
Support double, curly, dotted, and dashed underline styles via --color
(e.g. underline-curly) and ANSI passthrough (SGR 4:N, 58, 59) with --ansi.
Close#4633Close#4678
Thanks to @shtse8 for the test cases.
Fix#4652
Code was reorganized so that it doesn't delete the current line if the
cursor is not at the front. This should be acceptable in almost all
cases.
Thanks to @phanen for reporting the issue and suggesting the fix.
Since you can actually distinguish between Ctrl-H and Ctrl-Backspace in Windows we need to reintroduce the tui.CtrlH constant. On *nix systems we map all Ctrl(-Alt)-h to Ctrl(-Alt)-Backspace internally, but you can use either in --bind.
Windows sends different key events and modifier combinations to theFullscreenRenderer than a tcell FullscreenRenderer on Linux (-tags tcell).
This led to Ctrl+H being misinterpreted (and therefore unbindable) on some Windows builds.
Basically reverts changes to `src/tui/tcell.go` introduced by `a0cabe0`.
- Fix parsing in escSequence so 7-byte CSI forms (e.g. ESC [ 5 ; 10 ~) set *sz = 7 and the entire sequence is consumed.
- Prevents trailing bytes (like 10~) from remaining in the input buffer and being printed as stray characters.
Motivation:
`--color base16` can be a better default than `dark` or `light`, since it uses
the colors defined by the current theme. This usually blends in more
naturally and works well in both light and dark modes.
However, some elements were previously hard-coded with white or black
foreground colors, which can cause rendering issues in certain terminal
themes.