Commit Graph

313 Commits

Author SHA1 Message Date
Johannes Altmanninger
f821d6dd7f Feature flag for turning off TTY querying
Experience with OSC 133 and kitty keyboard protocol enabling sequences
has shown that a lot of users are still on incompatible terminals.
It's not always easy to fix those terminals straight away. There
are probably some more environments where primary device attribute
queries are not answered.

Add a feature flag (similar to keyboard-protocols and mark-prompt)
to allow users to turn this off.

When the terminal fails to respond to primary device attribute, we
already print an error pointing to "help terminal-compatibility".
Inside that document, inside the "primary device attribute" section,
point out this new feature flag.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
c31e769f7d Use XTVERSION for terminal-specific workarounds
As mentioned in earlier commit
("Query terminal before reading config").

Closes #11812
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
99854c107a Set kitty keyboard capability only at startup 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
5e317497ef Query terminal before reading config
We still have terminal-specific workarounds based on TERM_PROGRAM and
others, see test/test_driver.py.  In future we should get rid of them.

They are also unreliable, potentially missing inside SSH/containers,
incorrect if a terminal was started from another terminal (#11812);
also TERM can be incorrect for many reasons.

The better criterion for terminal-specific workarounds is XTVERSION,
which has none of the above disadvantages.

Since some of the workarounds (tmux, iTerm2) need to be applied before
we draw the first prompt. This also means: before we read any config
because config may call builtin "read".

Do startup queries before reading config.

Some changes implied by this:
1. Remove a call to init_input() which is already done by env_init()
2. call initialize_tty_metadata() only after queries have returned
3. Since we call initialize_tty_metadata() before the first
   call to tty.enable_tty_protocols() in Reader::readline(),
   we can remove the redundant call from reader_interactive_init().
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
96f63159b5 Be consistent about how we handle bad stdin FD
When poll() or read() on stdin fails, fish's interactive reader
pretends it has received SIGHUP, and subsequently exits.

I don't know if this is the right thing to do, or how to reproduce
this in a realistic scenario.

Unlike fish, fish_key_reader seems to ignore these failures, meaning
it will retry poll()/read() immediately.  This seems less correct,
so use fish's behavior for now.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
6fdce59572 Extract function for setting shell modes
We do the same thing in several places, with lots of small differences.
Extract the most reasonable behavior and use it everywhere.  Note that
we had an explictly-motivated ENOTTY check; the motivating issues
doesn't seem to reproduce anymore here though I did not bisect yet.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
21150726d3 Remove confused redundant mutex lock 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
37a0919fee Also check isatty(0) before querying
A process like "fish -i <somefile ..."  probably shouldn't query
because it's not gonna work.

In future we could enable this by sending/receiving queries to/from
/dev/tty rather than stdout/stdin.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
7a14dd0b7f Be more consistent about when to query the TTY
We are more conservative with querying on startup than we are with
querying for cursor position.

Part of this is oversight (if startup querying is not done, we
basically never get into a position where we query for cursor position,
outside extreme edge cases).

Also, some specific scenarios where we query for cursor position
inside, say, Midnight Commander, are not actually broken, because that
only happens when Midnight Commander gives us control.  But let's
favor consistency for now; the Midnight Commander issue should be
fixed soon anyway.

Use the same logic in both cases.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
fb0e17d6ea tty_handoff: extract Midnight Commander workaround
A following commit wants to run the full initialize_tty_metadata()
only after querying XTVERSION.

But MC_TMPDIR needs to be checked before querying for XTVERSION.

Remove this cyclic dependency by extracting the MC_TMPDIR check.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
a6a38b78d6 Don't initialize interactive reader redundantly
Commands like

	fish -C 'read'

create two top-level readers one after the other.  The second one is
the fish REPL.

Both run some initialization of globals and parser variables.  This is
weird; it should not be necessary.

Let's call reader_interactive_init() only once.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
90124c7889 Remove workaround for dvtm/abduco
dvtm and abduco are two terminal session managers with the same
original.

Among other issues, they fail to reply to primary device
attribute.  We have added a workaround for that based on
TERM=dvtm-256color (unreliable). A patch has been submitted for dvtm
https://lists.suckless.org/hackers/2502/19264.html

I don't know of a maintained fork (the original ones have had no
commit in 5 years) and there are better alternatives available
(shpool, tmux).  They have other VT100 compatibility issues as well
(accidental DECRST; something like "ls" Tab Tab Tab causes spurious
bold and underline markup).

Also, as of the parent commit, failure to respond to primary DA is
no longer catastrophic. So let's remove the workaround.  This means
that fish inside dvtm/abduco will pause for 2 seconds on startup and
print a warning (unless interrupted by ctrl-c).
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
7ef4e7dfe7 Time out terminal queries after a while
Add a timeout of 2 seconds queries; if any query takes longer, warn
about that and reduce the timeout  so we stop blocking the UI.  This 2
second delay could also happen when network latency is momentarily
really high, so we might want relax this in future.

Note that this timeout is only triggered by a single uninterrupted
poll() (and measured from the start of poll(), which should happen
shortly after sending the query). Any polls interrupted by signals
or uvars/IO port before the timeout would be hit do not matter.
We could change this in future.

Closes #11108
Closes #11117
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
06ede39ec9 Degrade gracefully when failing to receive cursor position report
Follow up the cursor position report query with a primary device
attribute one.  When that one arrives, any cursor position response
must have arrived too. This allows us to prevent a hang on terminals
that only support primary device attribute.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
1612576d1c Restructure query response handling logic
Instead of switching only on the response type, switch on the
combination of that and the expected response.  This helps the
following commits, which add more combinations (due to following up
cursor position report with a primary DA, and adding a timeout). No
behavior change here.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
2260465ed7 Rename enum variants to remove a name clash 2025-09-24 15:51:32 +02:00
The0x539
b0565edf85 reader: add case-insensitive history autosuggest
Resolves issue #3126

To match what I've been able to figure out about the existing design
philosophy, case-sensitive matches still always take priority,
but case-insensitive history suggestions precede case-insensitive
completion suggestions.
2025-09-08 22:26:02 -05:00
Johannes Altmanninger
eaa837effa Refresh TTY timestamps again in most cases
See commit 081c3282b7 (Refresh TTY timestamps also in some rare cases,
2025-01-15) and others.
Fixes d27f5a5293 (Adopt TtyHandoff in remaining places, 2025-06-21)
Fixes #11671
2025-07-25 10:23:48 +02:00
Peter Ammon
d27f5a5293 Adopt TtyHandoff in remaining places
This adopts the tty handoff in remaining places. The idea is to rationalize
when we enable and disable tty protocols (such as CSI-U).

In particular this removes the tty protocol disabling in Parser::eval_node
- that is intended to execute pure fish script and should not be talking to
the tty.
2025-07-19 16:04:13 -07:00
Peter Ammon
f0e007c439 Relocate tty metadata and protocols and clean it up
fish-shell attempts to set up certain terminal protocols (bracketed paste,
CSI-U) while it is in control of the tty, and disable these when passing
off the tty to other processes. These terminal protocols are enabled or
disabled by emitting certain control sequences to the tty.

Today fish-shell does this in a somewhat haphazard way, tracking whether
the protocols are enabled or disabled. Functions like `Parser::exec_node`
then just toggle these, causing data to be written to the terminal in
unexpected places. In particular this is very bad for concurrent execution:
we don't want random threads talking to the tty.

Fortunately we have a controlled place where we can muck with the tty:
`TtyTransfer` which controls handoff of ownership to child processes (via
`tcsetpgrp`). Let's centralize logic around enabling and disabling terminal
protocols there. Put it in a new module and rename it to `TtyHandoff` which is a
little nicer.

This commit moves code around and does some cleanup; it doesn't actually
pull the trigger on centralizing the logic though. Next commit will do that.
2025-07-19 16:04:13 -07:00
Johannes Altmanninger
1605d8d6ce Merge pull request #11644 2025-07-11 12:10:49 +02:00
Daniel Rainer
1e981a9827 Support upper/lower casing selection
Fixes #11639.
2025-07-10 16:57:17 +02:00
Johannes Altmanninger
b5bb50d742 builtin commandline: apply commandline+cursor to first top-level reader
Historically, `fish -C "commandline echo"` was silently ignored.  Make it do
the expected thing.  This won't affect subsequent readers because we only do
it for top-level ones, and reader_pop() will clear the commandline state again.

This improves consistency with the parent commit.  We probably don't want to
support arbitrary readline commands before the first reader is initialized,
but setting the initial commandline seems useful: first, it would have helped
me in the past for debugging fish.  Second, it would allow one to rewrite
an application launcher:

	 foot --app-id my-foot-launcher -e fish -C '
	 	set fish_history launcher
	 	bind escape exit
	 	bind ctrl-\[ exit
	-	function fish_should_add_to_history
	-		false
	-	end
	-	for enter in enter ctrl-j
	-		bind $enter '\''
	-			history append -- "$(commandline)"
	-			commandline "setsid $(commandline) </dev/null >/dev/null 2>&1 & disown && exit"
	-			commandline -f execute
	-		'\''
	-	end
	+	commandline "setsid  </dev/null >/dev/null 2>&1 & disown && exit"
	+	commandline --cursor $(string length "setsid ")
	 '

which is probably not desirable today because it will disable autosuggestions.
Though that could be fixed eventually by making autosuggestions smarter.

If we find a generally-useful use case, we should mention this in the changelog.

Ref: https://github.com/fish-shell/fish-shell/pull/11570#discussion_r2144544053
2025-07-03 14:38:42 +02:00
Daniel Müller
32c36aa5f8 builtins commandline/complete: allow handling commandline before reader initialization
Commands like "commandline foo" silently fail, and "complete -C" fails with
a weird "option requires an argument" error.

I think at least the first one can be useful in edge cases, e.g. to test
code that does not separate the `commandline` input and output (#11570),
and to set fish's initial commandline, see the next commit.

I don't think there are super strong reasons to allow these, but if the
existing state is merely due to "no one has ever thought of doing this",
then we should try changing it.

For consistency, also allow "complete -C". I guess an argument for that is
that it's weird to make a command behave differently in non-interactive shells.

For now, keep the historical behavior of disabling access to the command
line in non-interactive shells. If we find a good reason for allowing that
(which seems unlikely), we can.

Ref: https://github.com/fish-shell/fish-shell/pull/11570#discussion_r2144544053

Co-authored-by: Johannes Altmanninger <aclopte@gmail.com>
2025-07-03 14:16:18 +02:00
Johannes Altmanninger
1d893b77d3 Also flag MSRV rustc/clippy warnings in CI 2025-06-28 11:04:29 +02:00
Johannes Altmanninger
5e12d4e99c Use sync::OnceCell for terminal modes, fixing memory leak
My

    $ sudo docker/docker_run_tests.sh --shell-after docker/jammy-asan.Dockerfile

shows a lot of complaints about

    Direct leak of 60 byte(s) in 1 object(s) allocated from:

because some unit tests call reader_init() and reader_deinit().  Work around
this by initializing this value only once.  AFAICT, OnceCell is async-signal
safe (unlike Mutex), although I don't think documentation promises that.

It doesn't feel great to change implementation code to accomodate tests but
I think for this specific issue that's what we usually do.  Alternatively,
we could add to lsan_suppressions.txt.
2025-06-24 12:52:35 +02:00
Johannes Altmanninger
7e03bebf97 Merge pull request #11582 2025-06-16 12:23:21 +02:00
Peter Ammon
941701da3d Restore some async-signal discipline to SIGTERM
The changes to enable terminal protocols (CSI-U, etc) also attempts to
re-disable these when fish exits. In particular it attempts to disable these
from a SIGTERM handler.

Unfortunately none of that machinery is async-signal safe. Indeed our SIGTERM
handler has gotten rather sketchy, with taking a mutex and some other stuff.

Remove the async-signal-unsafe stuff and make SIGTERM manifestly safe.
Unfortunately this means that terminal protocols will remain set after SIGTERM
but that's probably unavoidable.
2025-06-15 19:06:47 -07:00
Peter Ammon
eb211e1d10 Bravely remove calls to redirect_tty_output
Calls to redirect_tty_output were added in many places when certain tty-syscalls
returned EIO. See commit 396bf1235d

This was intended to work around a glibc bug in wide character output, but it
was never really justifiable or tested, and we no longer use glibc wide
character output.

Bravely remove these, except in the case where we got SIGHUP and we don't want
to trigger SIGTTIN or SIGTTOU.
2025-06-15 18:09:53 -07:00
Daniel Rainer
413ce9fdb3 Call cursor_position() lazily
This function does not need to run when the pager is not focused. Calling it
lazily eliminates the overhead of calling it when it is not needed.
This code runs on each keypress when entering a command, so it makes sense to
keep it as lean as possible. (At the very least it avoids spam when trying to
debug/analyze gettext behavior.)
2025-06-15 23:16:22 +02:00
Peter Ammon
00c528c13f Remove parser.assert_can_execute
We don't need this; Rust's Send safety means that a Parser never changes its
thread.
2025-06-14 16:05:54 -07:00
Peter Ammon
2104f8a18a Remove Rc from Parser's vars
This will help enable Parser to be Send, which will be important for
concurrent execution.
2025-06-14 13:59:17 -07:00
cyclopentane
383d2aa3e9 Fix behaviour upon repeating a vi-mode t/T jump 2025-06-08 01:03:06 +02:00
Johannes Altmanninger
3951a858dd reader: do not send queries if stdout is not a terminal
Interactive fish with output redirected ("fish >/dev/null")
is not a common use case but it is valid.

Perhaps surprisingly, "fish >some-file" *does* print terminal escape codes
(colors, cursor movement etc.) even if terminal output is not a TTY.
This is typically harmless, and potentially useful for debugging.

We also send blocking terminal queries but those are not harmless in this case.
Since no terminal will receive the queries, we hang; indefinitely as of today,
but we should give up after a timeout and print an error.  Either way that
seems needlessly surprising. Suppress queries in this case.

In future, we should probably do something similar if stdin is not a terminal;
though in that case we're even less likely to be interactive (only "-i"
I think).
2025-05-29 17:57:38 +02:00
Johannes Altmanninger
c7a19a00ab reader: minor simplification 2025-05-29 17:57:38 +02:00
Johannes Altmanninger
2071df126c Idiomatic type for reader_readline nchars argument 2025-05-29 17:57:38 +02:00
Johannes Altmanninger
b3dbdb90c2 On enter, insert any failed search into the command line
This might help with use cases such as https://github.com/fish-shell/fish-shell/pull/11450.
Not sure.
2025-05-29 16:39:52 +02:00
Johannes Altmanninger
dc129add9e Assert that autosuggestions are always valid
This documents an invariant established by 532abaddae (Invalidate stale
autosuggestions eagerly, 2024-12-25). It was initially broken but fixed in
ba4ead6ead (Stop saving autosuggestions that we can't restore, 2025-01-17).
2025-05-29 16:39:52 +02:00
王宇逸
7c2c7f5874 Use uninit instead of zeroed 2025-05-19 20:54:12 +08:00
Yuyi Wang
c2eaef7273 Update nix to 0.30.1 (#11458)
After nix updated to 0.30, all functions related to file descriptor accepts impl AsFd, e.g., BorrowedFd. This PR is a minimal update. It tries to use impl AsFd as long as possible, but uses BorrowedFd in some places. Yes it introduces unsafe, but doesn't introduce new unsafe code.
2025-05-06 10:52:54 +02:00
Peter Ammon
ccfe949514 ast: make Ast generic
We can parse two different things via Ast:

1. A regular job list
2. A freestanding argument list, as used in `complete --arguments ...`

This second case is specific to one use.

Prior to this commit, we parsed the Ast and then "forgot" what we parsed,
storing a &dyn Node. Then we had to cast it to the right type, and assert,
and etc.

Make Ast generic over the Node type it parsed, and default the Node type to
JobList. This simplifies call sites.
2025-05-04 18:59:13 -07:00
Peter Ammon
dfac66082a ast: adopt Kind in yet more places 2025-05-04 18:59:05 -07:00
Peter Ammon
ee9cf33689 ast: Remove Node::category
This is now unused.
2025-05-04 18:59:05 -07:00
Johannes Altmanninger
094e8b29c2 Cleanup some import statements 2025-04-29 13:51:18 +02:00
Johannes Altmanninger
f8699d2b7e Move mouse click handling from input decoding to the input queue consumer
The input queue doesn't want to be blocked, so let's decide later what to
do with a mouse click.  This fixes the potential problem where a mouse click
is ignored if it is received while we're waiting for the terminal to repond
to a query.
2025-04-29 13:51:18 +02:00
Johannes Altmanninger
168c1a50a4 Don't route query responses until we pop them from the input queue
This makes more sense, see also the next commit.
2025-04-29 13:51:18 +02:00
Johannes Altmanninger
5897d13333 Dedicated input event type for query responses
These code paths have a tiny bit of logic in common, share that.

Maybe this should be nested in ImplicitEvent, and maybe Eof and CheckExit
shouldn't be..
2025-04-29 13:51:18 +02:00
Johannes Altmanninger
d18cb6ce27 Use OnceCell for terminal query state initialization
This object is initialized once just before we start reading from the terminal.
Once seems to be the appropriate type for this.  This gets rid of an awkward
enum variant.
2025-04-29 13:51:18 +02:00
Johannes Altmanninger
9b44a59b80 Rename terminal-query-state data structure
While at it, extract a function for initialization.  This looks pretty ugly
but it will get better with the next commit.
2025-04-29 13:51:18 +02:00
Johannes Altmanninger
788eddd0e8 Make terminal query data state a RefCell instead of mutex
We never need to access this from other threads, so a Mutex is overkill.
Leave behind stale variable names like "wait_guard" to be cleaned up by the
next commit.

Since TestInputEventQueuer is used concurrently in tests,
give it its own private object, to avoid borrowing conflicts.

Same for fish_key_reader; this fixes the issue that fish_key_reader potentially
reads keyboard input before a query is finished.
2025-04-29 13:50:15 +02:00