Some terminals such as conhost and putty cannot parse DCS commands,
and will echo them back.
Work around this by making sure that this echoed text will not
be visible.
Do so by temporarily enabling the alternative screen buffer when
sending DCS queries (in this case only XTGETTCAP). The alternative
screen buffer feature seems widely supported, and easier to get right
than trying to clear individual lines etc.
The alternative screen may still be visible for a
short time. Luckily we can use [Synchronized Output](
https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036)
to make sure the screen change is never visible to the user.
Querying support for that is deemed safe since it only requires a
CSI command.
Note that it seems that every terminal that supports Synchronized
Output also parses DCS commands successfully. This means that we
could get away without the alternative screen buffer in practice.
Not sure yet.
The implementation is slightly more complex than necessary in that it
defines a redundant ImplicitEvent. This is for two reasons: 1. I have
a pending change that wants to use it, so this removes diff noise and
2. we historically have sc/input_common.rs not depend on src/output.rs.
I dont' think any are strong reasons though.
If I run "sleep 3", type a command and hit enter, then there is no
obvious way to cancel or edit the imminent command other than ctrl-c
but that also cancels sleep, and doesn't allow editing. (ctrl-z sort
of works, but also doesn't allow editing).
Let's try to limit ourselves to inserting the buffered command
(translating enter to a newline), and only execute once the user
actually presses enter after the previous command is done.
Hide it behind a new feature flag for now.
By making things less scary, this might be more user-friendly, at
the risk of breaking expectations in some cases.
This also fixes a class of security issues where a command like
`cat malicious-file.txt` might output escape sequences, causing
the terminal to echo back a malicious command; such files can still
insert into the command line but at least not execute it directly.
(Since it's only fixed partially I'm not really sure if the security
issue is a good enough motivation for this particular change.)
Note that bracketed paste probably has similar motivation as this feature.
Part of #10987Closes#10991
Some terminals like the Linux console don't support indn (scroll
forward). Let's query for the presence of these features, and fall
back to the traditional behavior if absent.
For now, break with the tradition of using the terminfo database that
we read ourselves. Instead ask the terminal directly via XTGETTCAP.
This is a fairly young feature implemented by terminals like xterm,
foot and kitty, however xterm doesn't expose these capabilities at
this point.
This is a good opportunity to try XTGETTCAP, since these are
capabilities we haven't used before. Advantages of XTGETTCAP are that
it works across SSH and is independent of $TERM (of course ignoring
$TERM may also be breaking to some users). Let's see if it sees
adoption in practice.
Tested to work on foot and kitty, allowing the default ctrl-l binding
to work without erasing any screen content.
See #11003
The new ctrl-l implementation relies on Cursor Position Reporting (CPR)
This may not work on exotic terminals that don't support CSI 6n yet
As a workaround, probe for this feature by sending a CSI 6n (CPR)
on startup. Until the terminal responds, have scrollback-push fall
back to clear-screen.
The theoretical problem here is that we might handle scrollback-push
before we have handled the response to our feature probe. That seems
fairly unlikely; also e49dde87cc has the same characteristics.
This could query a capability instead (via XTGETTCAP or otherwise)
but I haven't found one; and this seems at least as reliable.
While at it, change the naming a bit.
See #11003
After we query kitty keyboard protocol support,
we send CSI 5n, to also receive a response if
the protocol is not supported.
However we don't bother to wait for the response, so this extra
message is not really useful (only to get better logs). Remove it.
With tmux 3.0 (from 2019) inside SSH, the CSI 5n response is echoed.
I guess with all other terminals we were just lucky. Move it to
right after where we disable ECHO I guess.
In general, asynchronous requests create a lot of potential for error,
we should try to get away from them.
These aren't typically used in the terminal but they are present on
many keyboards.
Also reorganize the named key constants a bit. Between F500 and
ENCODE_DIRECT_BASE (F600) we have space for 256 named keys.
The FdReadableSet api was always intended to be converted to use Duration
instead of usec/msec once the ffi was removed. This lets us be explicit about
forever/infinite timeouts and removes the (small) chance of a collision between
u64::MAX and INFINITE.
I tried this out with `type Timeout = Option<Duration>` (only without the alias)
but was unhappy with easy it is to accidentally use `None` when you meant a
timeout of zero.
Commit 1c4e5cadf2 (Autosuggestions in multi-line
command lines, 2024-12-15) accidentally passed an empty
"commandline_before_suggestion" to compute_layout() when there is
no autosuggestion.
Closes#10996
As reported in
https://github.com/fish-shell/fish-shell/issues/10992#issuecomment-2568954940,
the user may reset the terminal and run scrollback-push without
repainting in between. This means that the terminal will report
the cursor position y=0 x=0 which doesn't match what fish renders.
Fortunately, y=0 is a safe fallback value for the scrollback-push
use case.
While at it, fix an off-by-one error in a log.
Before 1c4e5cadf2 (Autosuggestions in multi-line command lines,
2024-12-15), the completion code path in the autosuggestion performer
used to do something weird: it used to request completions for the
entire command line (with the implied cursor at end) but try to apply
the same completion at the actual cursor.
That commit changed this to request completions only up to the cursor
position, which could in theory make us produce valid completions even
if the cursor is not at end of the line. However, that doesn't really
work since autosuggestions can only be rendered at the end of the line.
And the worst of it, that commit tries to compute
line_at_cursor(&full_line, search_string_range.end)
which crashes as out-of-bounds if the completion needs to replace the token
(like a case-correcting completion does).
Let's apply completions to the end, matching how autosuggestions work
in general.
I believe it's possible that the cursor position reported by the
terminal does not match fish's cursor. In that case, overflow. Fix
that since we should not trust the terminal.
Also rename a confusingly named variable.
Mouse-click handling has a similar issue, fix that too.
FWIW, tmux always reports cursor position zero (\x1b[1;1R) when
querying from fish (but not when querying with printf).
Will investigate that next, see the linked issue.
Fixes#10992
Added in libc 0.2.163.
The constants for _CS_PATH are not implemented for some of the BSDs yet
(rust-lang/cmake#3612), so we need to keep our linking of this via the C
compiler for now.
If I run
$ command A
$ command B
$ command C
and find myself wanting to re-run the same sequence of commands
multiple times, I like to join them into a single command:
$ command A &&
command B &&
command C
When composing this mega-commandline, history search can recall the
first one; the others I usually inserted with a combination of ctrl-k,
ctrl-x or the ctrl-r (since 232483d89a (History pager to only operate
on the line at cursor, 2024-03-22), which is motivated by exactly
this use case).
It's irritating that autosuggestions are missing, so try adding them.
Today, only single-line commands from history are suggested. In
future, we should perhaps also suggest any line from a multi-line
command from history.
If I type something that invalidates the autosuggestion, the
autosuggestion is still kept around in memory. This is used if
1. there is no valid autosuggestion for the new commandline
2. the user types something like "backspace backspace a"
that both makes the cached autosuggestion valid again, and does
not trigger autosuggestion suppression (hence backspace alone is
not anough)
The fact that an autosuggestion might not match the current command
line makes it more difficult to implement autosuggestions on multiline
command lines.
For now let's invalidate autosuggestions eagerly, to enable the
next commit. This heuristic invalidates too much but I don't think
that matters. We'll simply recompute the autosuggestion in those few
cases which.
As soon as we start processing a scrollback-push readline command, we
pause execution of all other readline commands until scrollback-push
retires. This means that we never get into a situation with two
active scrollback-push commands -- unless we are executing readline
commands via a script running "commandline -f":
since the first part of scrollback-push handling returns immediately,
the script will proceed before scrollback-push retires.
A second scrollback-push fails an assertion. Work around that for now.
In future, scrollback-push should block when invoked by such a script,
just like it does when invoked from bindings.
Commit 83b0294fc9 (ctrl-l to scroll content instead of erasing screen,
2024-12-21) broke tests like tests/checks/tmux-autosuggestion.fish
on macOS CI.
I didn't get to the bottom of this but it's probably because terminfo
is broken on that CI system.
A (related?) failure mode can be observed using
TERM=linux-m ssh my-mac tmux
ctrl-l moves the cursor but fails to scroll the text.
The only reason for using terminfo here was to be consistent with
the rest of the code base. Let's use a hardcoded value instead;
I don't see why any terminal would deviate from xterm here.
This fixes macOS CI and the TERM=linux-m "misconfiguration".
It is possible that we should be using a different escape sequence
here; I'm not sure.
On ctrl-l we send `\e[2J` (Erase in Display). Some terminals interpret
this to scroll the screen content instead of clearing it. This happens
on VTE-based terminals like gnome-terminal for example.
The traditional behavior of ctrl-l erasing the screen (but not the
rest of the scrollback) is weird because:
1. `ctrl-l` is the easiest and most portable way to push the prompt
to the top (and repaint after glitches I guess). But it's also a
destructive action, truncating scrollback. I use it for scrolling
and am frequently surprised when my scroll back is missing
information.
2. the amount of lines erased depends on the window size.
It would be more intuitive to erase by prompts, or erase the text
in the terminal selection.
Let's use scrolling behavior on all terminals.
The new command could also be named "push-to-scrollback", for
consistency with others. But if we anticipate a want to add other
scrollback-related commands, "scrollback-push" is better.
This causes tests/checks/tmux-history-search.fish to fail; that test
seems pretty broken; M-d (alt-d) is supposed to delete the current
search match but there is a rogue "echo" that is supposed to invalidate
the search match. I'm not sure how that ever worked.
Also, pexepect doesn't seem to support cursor position reporting,
so work around that.
Ref: https://codeberg.org/dnkl/foot/wiki#how-do-i-make-ctrl-l-scroll-the-content-instead-of-erasing-it
as of wiki commit b57489e298f95d037fdf34da00ea60a5e8eafd6d
Closes#10934
We parse "\e\e[x" as alt-modified "Invalid" key. Due to this extra
modifier, we accidentally add it to the input queue, instead of
dropping this invalid key.
We don't really want to try to extract some valid keys from this
invalid sequence, see also the parent commit.
This allows us to remove misplaced validation that was added by
e8e91c97a6 (fish_key_reader: ignore sentinel key, 2024-04-02) but
later obsoleted by 66c6e89f98 (Don't add collateral sentinel key to
input queue, 2024-04-03).
This situation can be triggered in practice inside a terminal like tmux
3.5 by running
tmux new-session fish -C 'sleep 2' -d reader -o log-file
and typing "alt-escape x"
The log shows that we drop treat this as alt-[ and drop the x on the floor.
reader: Read char alt-\[ -- Key { modifiers: Modifiers { ctrl: false,
alt: true, shift: false }, codepoint: '[' } -- [27, 91, 120]
This input ("\e[x") is ambiguous.
It looks like it could mean "alt-[,x". However that conflicts with a
potential future CSI code, so it makes no sense to try to support this.
Returning "None" from parse_csi() causes this weird behavior of
returning "alt-[" and dropping the rest of the parsed sequence.
This is too easy; it has even crept into a bunch of places
where the input sequence is actually valid like "VT200 button released"
but where we definitely don't want to report any key.
Fix the default: report no key for all unknown sequences and
intentionally-suppressed sequences. Treat it at "alt-[" only when
there is no input byte available, which is more or less unambiguous,
hence a strong enough signal that this is a actually "alt-[".