Commit Graph

2226 Commits

Author SHA1 Message Date
cyphercodes
7012f8bea3 Fix nested brace space trimming
Delay restoring protected brace spaces until recursive brace
expansion is complete, so nested brace items can strip boundary
spaces consistently.

Fixes #12794.

Closes #12806
Closes #12813
2026-06-18 14:30:21 +08:00
Johannes Altmanninger
c8d7476576 history: don't ignore re-added history items after vacuum
As root-caused in
https://github.com/fish-shell/fish-shell/issues/10300#issuecomment-4674848354,
we sometimes temporarily forget about history items:

Steps:

	1. start fish1
	2. fish1: run "echo remember me"
	3. start fish2
	4. fish1: run "echo something else"
	5. fish1: run "echo remember me"
	6. fish1: run up to VACUUM_FREQUENCY commands, until vacuum happens
	   (can watch ~/.config/fish/${fish_history:-fish}_history)
	7. fish2: run "echo reload"

Now fish2 can't recall "echo remember me" anymore.

This is because when re-running that command, fish1 updates the
command's history entry's "when" timestamp to something younger
than fish2.  When fish2 reloads the history, it ignores history
entries younger than itself (except for the entries created by itself).

Fix this by introducing a second timestamp called "added_when",
the immutable creation time.  Use this for determining the cutoff,
fixing the above scenario.

Keep the "when" key for forward compatibility.  Keep its semantics
(update it whenever we re-run a command; used for sorting); if we'd
instead let "when" be the creation time, then old fish will sort
wrongly commands that are run again in concurrent new fish. (If new
fish vacuums again, it will correct the ordering automatically.)

Alternatively, we could replace "added_when" with another
monotonically increasing number, maybe a integer sequence, with the
per-history next number stored on disk.  We only use it to compare
against the boundary timestamp in "offset_of_next_item_fish_2_0()"
and "rewrite_to_temporary_file()".  I haven't explored this yet because
"SystemTime" is easy and already used for "last added time".

Assisted-by: Theo Beers
Assisted-by: Daniel Rainer

Closes #10300
Closes #12817
2026-06-18 14:09:43 +08:00
Johannes Altmanninger
784c53eb32 Remove attempt to recover history files corrupted by fish 1.x
Simplifies the following commit.
2026-06-18 14:09:43 +08:00
Daniel Rainer
dfa14b90d6 l10n: add system tests for Fluent
Part of #11928
2026-06-18 14:09:43 +08:00
Daniel Rainer
88acc9cfda l10n: add first Fluent message
This migrates the fish version info message from gettext to Fluent. It
can be used to see Fluent-based localization in action.

Because this commit adds new FTL files, these languages show up in the
Fluent language precedence, requiring an update to the corresponding
tests.

Part of #11928
2026-06-18 14:09:43 +08:00
Daniel Rainer
8816960b87 l10n: add Fluent localization system
Add an implementation allowing to use Fluent for localization in Rust.

Fluent is significantly more expressive than gettext. It uses message
IDs which, unlike in gettext, are not necessarily the default message
string. This allows for proper support of messages which happen to be
identical in English, but not in other languages. In gettext, this could
be solved to some extent with contexts, but our gettext implementation
does not support that. In Fluent, arguments to the message are specified
as key-value pairs, which gives translators more semantic information
and allows reordering the arguments in the translation, which is
impossible with gettext. Fluent also allows for more complex grammatical
features, such as different plural forms, grammatical cases, and
adapting phrases to the correct gender.

This commit only introduces the infrastructure for using Fluent instead
of gettext, with the goal of eventually replacing gettext for
localization in Rust. Making use of the new infrastructure is left to
follow-up commits.

To localize a message with Fluent, the new `localize!` macro should be
used. Its arguments are key-value pairs. The first pair must consist of
two string literals. The key is a Fluent message ID and the value the
corresponding definition of the message in English. It must be valid
Fluent syntax for a message definition. The remaining key-value pairs
are used for Fluent variables which appear in the message definition.
Each key must match the name of a variable present in the message
definition, and the value is some Rust value which can be displayed by
Fluent. The variable in the Fluent message definition will be replaced
by that value. Example syntax:

```rust
localize!(
    "fish-version" = "{ $package_name }, version { $version }",
    package_name = "fish",
    version = crate::BUILD_VERSION,
)
````

If a message should be available in more than one place, define a
function containing the `localize!` macro and call that function from
the different locations needing access to the message, instead of
putting multiple `localize!` macros with the same message ID into the
code. The `localize_fn!` macro can help with that.

By having the message definition in the Rust sources, we have all the
relevant information for showing English messages in Rust, without
needing to rely on external sources. However, the Fluent library expects
data for all languages in the FTL format, so we have tooling to
automatically generate `en.ftl` from the Rust sources. Having this file
is also useful for other Fluent tooling, as well as being able to track
modifications to messages, which we also have tooling for.

To add translations for a new language, translators can create a new FTL
file in the `localization/fluent` directory with the appropriate file
name. No additional setup is needed. Translations for an individual
message can be added by copying the message from `en.ftl` and adjusting
the definition for the respective language.

These FTL files are included via `rust-embed` and parsed on demand at
runtime, ensuring that only languages specified in the user's language
settings are considered, saving the effort or parsing unneeded files.
`en.ftl` is always parsed, as it implicitly is the last fallback
option, when no other language in the user's precedence list has a
translation for a message ID. We know that `en.ftl` contains a
translation for all message IDs we use because message IDs can only be
used by putting them into the `localize!` macro with a corresponding
English message definition, and `en.ftl` is auto-generated from these
definitions. This generation happens as a 2-step process: First, we
compile with the `fluent-extract` feature, with the
`FISH_FLUENT_EXTRACTION_DIR` environment variable set to point to an
empty directory. This will result in the `localize!` macro passing the
message ID, definition, and variable names specified in subsequent
key-value pairs, to a proc macro which performs some checks on the
provided data and writes the message definition in the FTL format to a
fresh file in `FISH_FLUENT_EXTRACTION_DIR`. This results in one file per
`localize!` macro invocation. In the second step, the data from all
these files is combined into a single file, `en.ftl`, which is
automatically formatted. Before overwriting the old `en.ftl`, checks are
performed to look for changes to message definitions. If any change is
detected, translations of the message might have to be updated.
Some changes, like removal of a message, can be handled automatically,
by deleting all translations. For changes which cannot be handled fully
automatically, the affected translations are marked with an annotation,
which causes our checks to fail while it is present. It is the
responsibility of the developer who changed the message definition to
handle updates to its translations. This can be done manually, by
editing the affected translations and removing the annotation, which
should be the preferred option when translations need to be modified and
the developer knows the language. Otherwise, there is automation in the
form of `cargo xtask fluent resolve-outdated`, which allows specifying
what should happen to some or all translations of a message, with the
available option being:
- delete the translation, used when the meaning of the English message
  changed so much that keeping the old translations would be misleading
- delete the annotation, used when changes to the English message have
  no impact on translations, e.g. for typo fixes
- change the annotation to indicate that translator review is desired,
  used when the meaning or wording of the English message changed
  slightly, but not enough to invalidate the translations

There are several tools available as subcommands of `cargo xtask
fluent`:
- `check`: Checks the FTL files, ensuring that they can be parsed
  without errors, that no duplicate IDs are specified, that they are
  formatted correctly, and that there are no extra IDs, i.e. IDs not
  present in `en.ftl`, which is expected to be complete. More rigorous
  checks could be added, such as checking whether the same set of
  variables are used for a certain ID in all languages. The complexity
  of Fluent's syntax makes this non-trivial, which is the reason it's
  not already implemented.
- `format`: Formats the specified FTL files (or all by default). Also
  has a mode suitable for editor integration to format files from the
  editor. Examples for setting that up in Vim are provided in the
  `CONTRIBUTING.rst` docs.
- `rename`: Renames IDs or associated variables across all FTL files.
- `resolve-outdated`: Described above.
- `show-missing`: Shows which IDs don't have a translation yet.
- `update`: Runs the generation pipeline for `en.ftl` and potential
  translation updates/annotations.

There is one additional tool, which is designed to help with porting
existing messages localized via gettext to the new Fluent
implementation. This is intentionally not added to fish, because it
is only useful for the transition. Once we have ported all messages to
Fluent we won't have a use for it anymore. If you are interested in
using it to port messages, it's the `po-convert` binary in the
`fluent-ftl-tools` package. The CLI is somewhat convoluted, but can be
simplified by wrapping it with a script which hard-codes the path to the
relevant PO and FTL file directories. Then, the remaining information
which needs to be specified is:
- a line number in a PO file to identify the message to be ported
- the new message ID
- the name of each variable, in the order the formatting specifiers
  appear in the gettext msgid.
Specifying the line number and invoking the wrapper script can be
partially automated by using a custom editor shortcut.
The tool will port the msgstr for each language which has one defined,
and always generate and entry for `en.po` based on the msgid.
The tool does not edit Rust code, but suggests a Rust code snippet on
stdout based on the specified message ID and variable names.

Some of our tooling relies on features of the `fluent` package which
are not exported by default, so we use a fork which changes that until
our PR for adding it upstream is accepted.

Part of #11928
2026-06-18 14:09:43 +08:00
Johannes Altmanninger
3968025421 Don't try to download vermin during test execution
The "vermin" test fails when offline; fix that by adding "vermin"
to the virtulenv.
2026-06-18 14:09:43 +08:00
Peter Ammon
d13ab22a27 Cleanup and add tests for cd builtin
Add a test for the following behavior: if we try to `cd` to a directory,
and it fails, and so we call realpath and discover that our $PWD is stale,
and correct it and then try to `cd` again, and it STILL fails, then:

1. We report the error for the second (better) failure.
2. $PWD is not modified.
2026-06-11 22:48:52 -07:00
Vishrut Sachan
411a43254b env: fix stale $PWD after mv $PWD elsewhere
Fixes #12700
2026-06-11 21:17:44 -07:00
Johannes Altmanninger
de0e519b22 test unreadable-config-paths: skip on windows
See 17ef326c8f.
2026-06-10 00:44:54 +09:00
Johannes Altmanninger
654041895b Stop using universal __fish_initialized to detect first launch
As of 7640e95bd7 (Create user config file/directories only on
first startup again, 2025-12-29) we interpret absence of the
__fish_initialized variable as "this is the first run, so create
~/.config/fish/conf.d etc.".

As reported in #11226, the presence of this universal variable causes
friction to users who track ~/.config/fish/fish_variables in version
control.

Also, __fish_initialized is the only universal variable we create
by default.

Use another way to detect the first run: since we already create
~/.config/fish on every run, assume that a successful mkdir() means
we should also create the subdirectories.

This has false negatives (if the user already created the directory)
and false positives (if the user doesn't want ~/.config/fish to exist)
but at least the latter should not really matter because historically
we always created it, at least for ~/.config/fish/fish_variables.

Alternatively, we could create a file at ~/.cache/fish/first-run-done.
But let's not add more state unless there's a good reason.
2026-06-09 22:31:14 +09:00
Johannes Altmanninger
974aa160f3 fish_update_completions: pass explicit directory argument
Gets rid of another use of XDG_CACHE_HOME; this gets us closer to
a single source of truth. Not super important right now, but will
be helpful if we allow users to override XDG_CACHE_HOME in future.
I guess we could also remove it from create_manpage_completions.py,
that's unnecessary breakage but probably fine since this is not
public-facing.
2026-06-09 22:24:07 +09:00
Johannes Altmanninger
b6e5e5196d path_emit_config_directory_messages: also print error about ~/.cache 2026-06-09 22:24:07 +09:00
Johannes Altmanninger
e861c7f923 path_emit_config_directory_messages: fix condition for inaccessible $__fish_config_dir
Commit 77471c500e (path: use `std::io::Error` instead of raw ints,
2026-01-21) inadvertently prints the "Unable to locate config directory"
error when the *data* dir is inaccessible, rather than when the *config*
dir is inaccessible.

Fix that. While at it, rename variables to avoid this issue.
2026-06-09 22:24:07 +09:00
Johannes Altmanninger
616ff5c5e9 config.fish: lazy load job control functions
These don't really belong in config.fish.  Put them in function files,
like similar wrappers.  The redundancy is ugly but if it's a problem
we can use code generation to fix that.
2026-06-09 22:24:07 +09:00
David Adam
af09b60aeb fix cd symlink resolution tests on Cygwin 2026-06-02 13:43:40 +08:00
Asuka Minato
0eb17fa58f Implement cd -L and cd -P
Closes #7206
2026-06-02 13:43:32 +08:00
Wes Higbee
4b2aba31ee complete: tab-complete anywhere-position abbrs in non-command position
Make abbreviations with `--position anywhere` appear in the completion
menu when tabbing in argument position (e.g. `cat foo.log pg<TAB>`
shows pgr, pgrv, pjq, etc.), not just in command position.

Lift "do_file" into an enum, so we don't have to check "is_redirection"
twice, and can express more accurately the reason for not completing
abbreviations after redirection.

Co-authored-by: Johannes Altmanninger <aclopte@gmail.com>

Closes #12764
2026-06-02 11:38:29 +08:00
Johannes Altmanninger
f717c5e2a0 Fix "bind -M somemode" crash for empty bind modes
This loop iterates over all bindings and crashes if a mode filter
argument is given for a mode that has no binding.  Fix this by
continuing the loop earlier.

Fixes 11d6e92cb5 (Show file location when querying bindings with bind, 2025-12-28)

Fixes #12798
2026-06-02 08:44:27 +08:00
ritoban23
11d6e92cb5 Show file location when querying bindings with bind
Closes #12215
Closes #12504
Closes #12221
2026-05-31 07:43:32 +08:00
Sean Reifschneider
eb535322d0 Fix cW Vi mode regression deleting trailing space
Missed in 02c0455 (Fix Vi mode cw deleting trailing whitespace, 2026-02-12),

Closes #12790
Closes #12791
2026-05-31 07:38:44 +08:00
Fabian Boehm
25cc01845a string: Fix swapped arguments in error message
Really this could do with a bespoke error, but this'll do for now.
2026-05-29 10:23:59 +02:00
Nahor
240647327a init-unreadable-cwd.fish: replace custom check with __fish_is_cygwin 2026-05-28 11:10:14 -07:00
Johannes Altmanninger
7b358d2122 Fix init-unreadable-cwd test on Windows 2026-05-28 21:10:20 +08:00
Johannes Altmanninger
17ef326c8f Disable cwd-error-reporting test on Windows
On Windows, "chmod 000 .; cd ." doesn't fail, so disable this
test case on Windows.  On the Windows CI runner, "uname" prints
"MINGW64_NT-10.0-26100" so we can't use "__fish_is_cygwin".
2026-05-28 16:40:30 +08:00
Peter Ammon
821efeb99c Justify the continued presence of cwd_fd 2026-05-25 22:25:30 -07:00
Peter Ammon
17fdd2f153 Clean up "strip control characters" changes
Simplify these and move the sanitization closer to the point of output.

Stop worrying about control characters beyond what PCRE2 reports in
"[[:cntrl:]]" for clarity.
2026-05-25 16:48:30 -07:00
saku0512
cbc3cd21f7 prompts: strip control characters from VCS state and PWD
Strip control characters from VCS branch and state strings before writing them in prompts.

Also route the informative and minimalist sample prompts through prompt_pwd instead of printing PWD directly.
2026-05-25 15:51:41 -07:00
Nahor
954c68c0b1 Fix comment typo (CLO_EXEC -> CLOEXEC)
Closes #12774
2026-05-23 00:59:56 +08:00
Johannes Altmanninger
1f18b9715f Fix repeated tab causing repeated smartcase completion insertion
Doing

	firefox --pro TAB TAB TAB

results in

	firefox --profile --ProfileManager

git-bisect points to 3546ffa3ef (reader
handle_completions(): remove dead filtering code, 2026-01-02)
but that regression has already been fixed by 85e76ba356
(Fix option substr completions not being filtered out, 2026-04-16).

However in between those two commits, the above case has also been
broken by 2f6b1eaaf9 (reader handle_completions(): don't consider
odd replacing completions for common prefix, 2026-01-02)

The first TAB inserts "--profile ", including the trailing space.
However it also shows the completion pager, which means that subsequent
TABs will insert after the space.

The trailing space does not make sense unless we navigate the pager.
Remove it in all cases, to fix this smartcase scenario.

Alternatively, we could start navigating the pager in this case
(and keep the trailing space), but that's probably too inconsistent.
2026-05-14 16:17:41 +08:00
June Kim
f69f074f66 Add failing test for git prompt rename miscount (issue #11296)
git status --porcelain -z outputs the rename source filename as a
separate NUL-delimited entry after the status line. When the source
filename starts with [ACDMRTU], the informative prompt miscounts it
as an additional staged change.

Part of #12754
2026-05-13 19:58:52 +08:00
Johannes Altmanninger
c003ec3795 vi mode: fix "x" command in builtin read
When we push a new reader for builtin read, we use the default
CursorSelectionMode::Exclusive, which is wrong in Vi mode.
Add a haphazard fix for that.
This is very ugly, we should improve this.

Closes #12724
2026-05-13 19:58:52 +08:00
Nahor
01b9fd9e31 complete: remove unescaping of -c/-p values
This removes the need to double-escape the values on the command line
(once for the command line parser, another for the option handling)

This also brings it in line with the implicit case (`complete cmd ...`)

Fixes #12712

Closes #12718
2026-05-13 18:01:48 +08:00
Johannes Altmanninger
30976d8970 uvar migration: recogize more historical defaults as such
During our one-time migration away from universal variables,
we create ~/.config/fish/conf.d/fish_frozen_theme.fish
if we think that the current theme is different from the default.

The default uvar-backed theme had changed over time, but existing
installations would not be upgraded.  Because of this, we have
a heuristic that assumes that values coinciding with historical
default values also stem from a default.  Some historical values are
missing. Add them.

There are more left, see 03b23dd1b6 (Update default colors,
2022-01-27).

Fixes #12725
2026-05-11 10:41:14 +08:00
Johannes Altmanninger
3dc36f74bc Don't print greetings in interactive read in fake-interactive shell
If a user passes "-i" when running a script, they ought to expect
weird behavior i.e. fish might run the user's interactive-only
configuration which might print things to TTY etc.  But at least
for our part of the configuration, we can avoid depending on the
user-settable interactive bit.

__fish_config_interactive is already only called when we paint the
first prompt, either for a prompt (which implies we're an interactive
shell) or for builtin read (which does not imply anything about the
interactivity of the shell).

Only print greetings when not in interactive read. Notably, "status
is-interactive-read" is not overridable by the user.

This helps us get rid of more "status is-interactive" switches.
2026-05-11 10:21:56 +08:00
Fabian Boehm
5c6acdee09 string shorten: Don't produce output with --quiet
This would've printed stuff in case it didn't have to shorten (which
means it "failed" because the logic is "did something").

Fixes #12732
2026-05-10 11:16:41 +02:00
Peter Ammon
aa5ecd0efa Make the tmux-history-search2 test more reliable 2026-05-09 18:37:07 -07:00
Nahor
ed6fe3f315 tmux-history-search2: fix test on WSL or in console sessions
Those two do not use the "return symbol"

Closes #12704
2026-05-05 14:57:42 +08:00
Nahor
4f539dffaf cd: fix path when trying to cd out of the root directory
Part of #12704
2026-05-05 14:57:42 +08:00
Peter Ammon
b638aa198f Make the tmux-history-search2.fish test pass reliably on macOS 2026-05-03 19:50:26 -07:00
Johannes Altmanninger
71d6ec4ab9 wcsfilecmp: make sure trailing slashes sort first
This command

	cd $(mktemp -d)
	mkdir a "a b"
	complete -C": "

prints

	a b/
	a/

which is wrong ordering.
Usually the trailing slash should not be compared.

Fix this by always sorting slashes first.  Not sure if this is correct
for middle slashes but I couldn't find a case where it matters.

Closes #12695
2026-05-03 20:04:21 +08:00
Daniel Rainer
4e3898d0d7 feat: xtask gettext
Rewrite the PO file handling logic in Rust and make it available via an
xtask. Replaces the
`build_tools/{update_translations,fish_xgettext}.fish` scripts.

Main benefits:
- Better ergonomics
- Better error handling
- Eliminates the need for a fish executable for updating PO files,
  which is particularly useful in CI
- Improved performance, mainly due to concurrent threads working on the
  PO files in parallel

The behavior is mostly unchanged, with the minor exception that section
headers for empty sections are now omitted in PO files.
The interface for invoking the tooling is quite different. Instead of
working with flags, `cargo xtask gettext` has 3 subcommands:
- `update` modifies the PO files to match the current sources
- `check` is like update, but instead of modifying the PO files, it
  shows diffs between the current version of the PO files and what they
  would look like after updating. When there is a difference, the xtask
  exits non-zero, making it useful for checks to detect outdated PO
  files.
- `new` creates a new PO file for the given language.

Both the `update` and `check` command take any number of file paths to
specify the PO files to consider. If none are specified, all files in
`localization/po/` are considered.

Extracting gettext messages from Rust still requires compiling with the
`gettext-extract` feature active. In situations where compilation is
needed for other purposes as well, it can make sense to only build once
and then tell the gettext xtask about the directory into which the
messages have been extracted. This can be done via the
`--rust-extraction-dir` flag. If we stop having gettext messages in
Rust, this logic can be removed.

Closes #12676
2026-04-30 17:31:03 +00:00
Johannes Altmanninger
281399561b Distinguish builtin read history session ID from private mode
Fixes #12662
2026-04-29 01:55:32 +08:00
Johannes Altmanninger
1034945690 Fix regression causing "nosuchcommand || hello" to short-circuit
Commit 3534c07584 (Adopt the new AST in parse_execution, 2020-07-03)
added to parse_execution_context_t::run_job_conjunction an early
return when any job in a job conjunction fails to launch.  This causes
"nosuchcommand || echo hello" to not execute the continuation.

Fix this by restoring the previous behavior.

Fixes #12654
2026-04-29 01:55:32 +08:00
Johannes Altmanninger
e2b18fc5b6 config.fish: don't load default theme in noninteractive shells
We define colors in noninteractive shells for historical reasons
(because colors used to be universal variables).

The other potential reason is to get regular syntax highlighting for
commands like:

	fish -c 'read --shell'

but if anyone actually uses that they can probably load a theme
explicitly.

Stop defining colors in noninteractive shells.  It's usually not
a good idea to make them behave differently from interactive ones,
but color seems only relevant for interactive shells?

Let's see if anyone complains.. we may end up reverting this if people
want to use noninteractive fish to query colors..  but I'm not sure
why that would be necessary.

Closes #12673
2026-04-29 01:48:47 +08:00
Johannes Altmanninger
f2b0706494 reader: repaint commands to not disable "last_cmd"-based UI states (pager etc.)
"commandline -f repaint" might be triggered for various reasons;
since this sets "last_cmd", it will reset some UI states, notably
pager selection:

1. press tab
2. trigger repaint
3. press tab

The repaint prevents us from selecting the first candidate.

Work around this by ignoring repaint events for the last_cmd logic.

Fixes #12683
2026-04-28 14:43:55 +08:00
Nahor
27fb4d6731 Always heightenize file descriptors
Fixes #12618

Closes #12681
2026-04-28 13:51:24 +08:00
Johannes Altmanninger
006fa86ef4 tests/checks/tmux-source.fish: reduce flakiness
As seen in
https://github.com/fish-shell/fish-shell/actions/runs/24944417077/job/73043241890?pr=12171

	Failure:

	  The CHECK on line 12 wants:
	    prompt 1> source -

	  which failed to match line stdout:3:
	    source -

	  Context:
	    prompt 0> source
	    source: missing filename argument or input redirection
	    source - <= no check matches this, previous check on line 11
	    prompt 1> source -
	    prompt 1>
2026-04-26 13:15:23 +08:00
Johannes Altmanninger
923d0b7974 config: use default XDG_DATA_DIRS when unset or empty
Installing a program like sway to /usr/local installs fish
completions to /usr/local/share/fish/vendor_completions.d/sway.fish.
When $XDG_DATA_DIRS is empty, these will typically not
be picked up.

(Since "__extra_completionsdir" is usually
"/usr/share/fish/vendor_completions.d/", this issue typically only
affects "/usr/share", not "/usr".)

Fix this by using the correct fallback value for XDG_DATA_DIRS.

Fixes #11349

Closes #12656
2026-04-22 14:21:09 +08:00
Johannes Altmanninger
fa33f6f0e0 tests/checks/disown.fish: improve test robustness
If the job never gets into stopped state, it will keep running forever.
Narrow the wait condition, to prevent a timeout in failure scenarios.
2026-04-20 17:03:09 +08:00