Compare commits

...

854 Commits

Author SHA1 Message Date
David Adam
8dbbe71bc6 disable Linux development builds for now
I'll add the rest of the infrastructure later.
2026-04-13 21:12:51 +08:00
David Adam
d9d9eced98 workflow: build development builds on master branch 2026-04-12 21:11:26 +08:00
David Adam
64a829f0df add workflow to create development build source packages 2026-04-12 18:50:56 +08:00
Nahor
440e7fcbc1 Fix failing system tests on Cygwin
The main changes are:
- disabling some checks related to POSIX file permissions when a filesystem is
mounted with "noacl" (default on MSYS2)
- disabling some checks related to symlinks when using fake ones (file copy)

Windows with acl hasn't been tested because 1) Cygwin itself does not have any
Rust package yet to compile fish, and 2) MSYS2 defaults to `noacl`

Part of #12171
2026-04-11 18:55:00 +08:00
Nahor
52495c8124 tests: make realpath tests easier to debug
- Use the different strings for different checks to more easily narrow down
where a failure happens
- Move CHECK comments outside a `if...else...end` to avoid giving the impression
that the check only runs in the `if` case.

Part of #12171
2026-04-11 18:44:22 +08:00
Vishrut Sachan
467b03d715 git completions: prioritize recent commits for rebase --interactive
Fixes #12537

Closes #12619
2026-04-11 18:33:05 +08:00
Johannes Altmanninger
b5c40478f6 Fix typo, closes #12586, closes #12577 2026-04-11 18:33:05 +08:00
Johannes Altmanninger
1286745e78 Remove bits for async-signal-safety of old SIGTERM handler
This is implied by the parent commit.

To enable this, stop trying to run cleanup in panic handlers if we
panic on a background thread.
2026-04-11 18:25:26 +08:00
Yakov Till
b99ae291d6 Save history on SIGTERM and SIGHUP before exit
Previously, SIGTERM immediately re-raised with SIG_DFL, killing
fish without saving history. SIGHUP deferred via a flag but never
re-raised, so the parent saw a normal exit instead of signal death.

Unify both signals: the handler stores the signal number in a single
AtomicI32, the reader loop exits normally, throwing_main() saves
history and re-raises with SIG_DFL so the parent sees WIFSIGNALED.

Fixes #10300

Closes #12615
2026-04-11 18:06:02 +08:00
Daniel Rainer
8ae71c80f4 refactor: extract string escape and unescape funcs
Move the functions for escaping and unescaping strings from
`src/common.rs` into `fish_common`. It might make sense to move them
into a dedicated crate at some point, but for now just move them to the
preexisting crate to unblock other extraction.

Closes #12625
2026-04-11 17:49:50 +08:00
Daniel Rainer
cf6170200c refactor: move const to fish_widestring
Another step to eliminate dependency cycles between `src/expand.rs` and
`src/common.rs`.

Part of #12625
2026-04-11 17:49:49 +08:00
Daniel Rainer
c13038b968 refactor: move move char consts to widestring
This time, move char constants from `src/expand.rs` to
`fish_widestring`, which resolves a dependency cycle between
`src/expand.rs` and `src/common.rs`.

Part of #12625
2026-04-11 17:49:48 +08:00
Daniel Rainer
816077281d refactor: move encoding functions to widestring
The decoding functions for our widestrings are already in the
`fish_widestring` crate, so by symmetry, it makes sense to put the
encoding functions there as well. This also makes it easier to depend on
these functions, giving more options when it comes to further code
extraction.

Part of #12625
2026-04-11 17:49:47 +08:00
Daniel Rainer
78ea24a262 refactor: move char definitions into widestring
Use `fish_widestring` as the place where char definitions live. This has
the advantage that all our code can depend on `fish_widestring` without
introducing dependency cycles. Having a common place for character
definitions also makes it easier to see which chars have a special
meaning assigned to them.

This change also unblocks some follow-up refactoring by removing a
dependency cycle between `src/common.rs` and `src/wildcard.rs`.

Part of #12625
2026-04-11 17:49:46 +08:00
Daniel Rainer
6a5b9bcde1 refactor: move PUA-decoding function to widestring
These functions don't depend on `wcstringutil` functionality, so there
is no need for them to be there. The advantage of putting them into our
`widestring` crate is that quite a lot of code depends on it, and
extracting some of that code would result in crate dependency cycles if
the functions stayed in the `wcstringutil` crate. Our `widestring` crate
does not depend on any of our other crates, so there won't be any cyclic
dependency issues with code in it.

Part of #12625
2026-04-11 17:49:45 +08:00
Daniel Rainer
b7b786aabf cleanup: move escape_string_with_quote
It makes a lot more sense to have this function in the same module as
the other escaping functions. There was no usage of this function in
`parse_util` except for the test, so it makes little sense to keep the
function there. Moving it also eliminates a pointless cyclic dependency
between `common` and `parse_util`.

Part of #12625
2026-04-11 17:49:43 +08:00
Daniel Rainer
dc63b7bb20 cleanup: don't export write_loop twice
Exporting it as both `safe_write_loop` and `write_loop` is redundant and
causes inconsistencies. Remove the `pub use` and use `write_loop` for
the function name. It is shorter, and in Rust the default assumption is
that code is safe unless otherwise indicated, so there is no need to be
explicit about it.

Part of #12625
2026-04-11 17:49:42 +08:00
Daniel Rainer
9c819c020e refactor: don't reexport fish_common in common
Not reexporting means that imports have to change to directly import
from `fish_common`. This makes it easier to see which dependencies on
`src/common.rs` actually remain, which helps with identifying candidates
for extraction.

While at it, group some imports.

Part of #12625
2026-04-11 17:49:41 +08:00
Daniel Rainer
dc9b1141c8 refactor: extract fish_reserved_codepoint
Part of #12625
2026-04-11 17:49:40 +08:00
Daniel Rainer
3d364478ee refactor: remove dep on key mod from common
Removing this dependency allows extracting the `fish_reserved_codepoint`
function, and other code depending on it in subsequent commits.

Part of #12625
2026-04-11 17:49:39 +08:00
Daniel Rainer
faf331fdad refactor: use macro for special key char def
Reduce verbosity of const definitions. Define a dedicated const for the
base of the special key encoding range. This range is 256 bytes wide, so
by defining consts via an u8 offset from the base, we can guarantee that
the consts fall into the allocated range. Ideally, we would also check
for collisions, but Rust's const capabilities don't allow for that as
far as I'm aware.

Having `SPECIAL_KEY_ENCODE_BASE` in the `rust-widestring` crate allows
getting rid of the dependency on `key::Backspace` in the
`fish_reserved_codepoint` function, which unblocks code extraction.

Part of #12625
2026-04-11 17:49:37 +08:00
Daniel Rainer
47b6c0aec2 cleanup: rename and document UTF-8 decoding function
While the function is only used to decode single codepoints, nothing in
its implementation limits it to single codepoints, so the name
`decode_one_codepoint_utf8` is misleading. Change it to the simpler and
more accurate `decode_utf8`. Add a doc comment to describe the
function's behavior.

Part of #12625
2026-04-11 17:49:36 +08:00
Daniel Rainer
eb478bfc3e refactor: remove syntactic deps on main crate
Part of #12625
2026-04-11 17:49:35 +08:00
Johannes Altmanninger
63cf79f5f6 Reuse function for creating sighupint topic monitor 2026-04-11 17:27:25 +08:00
Johannes Altmanninger
ff284d642e tests/checks/tmux-abbr.fish: fix on BusyBox less (alpine CI) 2026-04-11 17:03:46 +08:00
Johannes Altmanninger
cc64da62a9 fish_color_valid_path: also apply bg and underline colors
Closes #12622
2026-04-11 17:03:46 +08:00
Johannes Altmanninger
a974fe990f fish_color_valid_path: respect explicit normal foreground 2026-04-11 17:03:46 +08:00
David Adam
39239724ec make_vendor_tarball: drop unused tar search 2026-04-10 05:56:00 +08:00
Daniel Rainer
524a7bac6e l10n: restore translations from error rewrite
Most translations were adjusted correctly, but a few were missed, so
restore them here.

Closes #12623
2026-04-09 01:36:53 +08:00
Nahor
d649c2aab4 string: remove StringError::NotANumber
Use the more generic `StringError::InvalidArgs` instead

Closes #12556
2026-04-08 14:11:31 +08:00
Nahor
30e6aa85e2 error rewrite: use new Error to report errors
To homogenize error reporting format, use a new Error struct. Currently this
is used for builtins and ensuring a common cmd/subcmd prefix.

Part of #12556
2026-04-08 14:11:31 +08:00
Nahor
abd7442521 printf: de-duplicate code
Part of #12556
2026-04-08 14:05:05 +08:00
Johannes Altmanninger
f5c48038b5 Skip tmux-abbr's "pipe builtin into less" test on BusyBox less
Fails on Alpine CI.
2026-04-08 14:04:15 +08:00
Daniel Rainer
895a6e7034 l10n(fr): use non-breaking space before colon
Closes #12617
2026-04-08 13:54:04 +08:00
Johannes Altmanninger
2193e88423 Pass job group down to builtin_print_help
Repro:

	$ abbr -h | less
	fish: Job 1, 'abbr --help | less' has stopped

Bisects to 5ece9bec6c (__fish_print_help: use man as-is, 2025-08-29).

Root cause is similar to
https://github.com/fish-shell/fish-shell/issues/8699#issuecomment-1030762748
except it's specific to how __fish_print_help is invoked, since
"__fish_print_help abbr | less" works fine.

Fix this by letting __fish_print_help access the terminal.

Fixes #12612

Solved by Claude in a single prompt:

---

 ▐▛███▜▌   Claude Code v2.1.92
▝▜█████▛▘  Sonnet 4.6
  ▘▘ ▝▝    ~/git/fish-shell

❯ "abbr -h | less" gets a signal, resulting in the message "fish: Job 1, 'abbr -h | less' has stopped"
whereas "__fish_print_help abbr | less" works just fine, even though "abbr -h" should run exactly that.
Why this difference?

[...]

● Now I can give you a clear answer.

  ---
  The difference comes down to job group inheritance when man is
  spawned.

  The deferred execution order

  In abbr -h | less, fish uses the "deferred process" optimization:
  since abbr is a non-external process piping into an external one,
  fish launches less first,
   transfers the terminal to less's process group, then runs the
   deferred abbr builtin. By the time abbr's help function spawns man,
   the terminal already
  belongs to less's pgroup.

  Same thing happens with __fish_print_help abbr | less — the outer
  less is launched first and gets the terminal.

  Where the paths diverge

  Function case (__fish_print_help abbr | less):
  get_performer_for_function (exec.rs:1050) captures job.group.clone()
  and passes it to eval_node. So when command man runs inside the
  function, it's given the outer pipeline's job group — the same
  one that owns the terminal. man's inner less pager joins that group
  and can read from the terminal freely. ✓

  Builtin case (abbr -h | less): builtin_print_help calls
  parser.eval(&cmd, streams.io_chain) (shared.rs:652). parser.eval
  is defined as:

  pub fn eval(&self, cmd: &wstr, io: &IoChain) -> EvalRes {
      self.eval_with(cmd, io, None, BlockType::top, false)
      //                      ^^^^ job_group = None
  }

  job_group = None means man creates a brand-new job group with no
  terminal access. When man spawns its less pager, the pager tries
  to read keyboard input from
   the terminal, but the terminal belongs to the outer less's process
   group. The pager receives SIGTTIN → stops → the whole job is
   reported as stopped.

  The fix

  builtin_print_help should pass streams.job_group to the eval call
  (which is already populated at exec.rs:1192). Changing line 652 from:

  let res = parser.eval(&cmd, streams.io_chain);

  to:

  let res = parser.eval_with(&cmd, streams.io_chain, streams.job_group.as_ref(), BlockType::top, false);

  would give man the outer job's job group, letting its pager access
  the terminal just like the function case.

✻ Sautéed for 13m 0s
2026-04-07 20:21:38 +08:00
Kayce Basques
86c3778c2a docs: Format path as inline code
Closes #12609
2026-04-07 18:52:36 +08:00
Nahor
d2653b7cac tmux-set.fish: fix spurious CI failure
Part of #12556
2026-04-07 17:49:57 +08:00
Johannes Altmanninger
cf16949ce7 contrib/debian/control: remove insufficient mdoc dependency
On Debian, mandoc provides "/usr/bin/mman", not "/usr/bin/man", so that
package alone is not enough.  Users that want to use mandoc could use a
package that "Provides: man", for example by creating symlink to "mman".

See https://github.com/fish-shell/fish-shell/issues/12596#issuecomment-4188332803
2026-04-07 17:49:57 +08:00
Daniel Rainer
85311546de refactor: extract fish-feature-flags crate
Another step in splitting up the main library crate.

Note that this change requires removing the `#[cfg(test)]` annotations
around the `LOCAL_OVERRIDE_STACK` code, because otherwise the code would
be removed in test builds for other packages, making the `#[cfg(test)]`
functions unusable from other packages, and functions with such feature
gates in their body would have the code guarded by these gates removed
in test builds for tests in other packages.

Closes #12494
2026-04-07 17:49:57 +08:00
Daniel Rainer
c44aa32a15 cleanup: remove syntactic dependency on main crate
This is done in preparation for extracting this file into its own crate.

Part of #12494
2026-04-07 17:49:57 +08:00
Daniel Rainer
65bc9b9e3e refactor: stop aliasing feature_test function
Having a public function named `test` is quite unspecific. Exporting it
both as `test` and `feature_test` results in inconsistent usage. Fix
this by renaming the function to `feature_test` and removing the alias.

Part of #12494
2026-04-07 17:49:57 +08:00
Daniel Rainer
8125f78a84 refactor: use override stack for feature tests
Several features of fish can be toggled at runtime (in practice at
startup). To keep track of the active features, `FEATURES`, an array of
`AtomicBool` is used. This can safely be shared across threads without
requiring locks.

Some of our tests override certain features to test behavior with a
specific value of the feature. Prior to this commit, they did this by
using thread-local versions of `FEATURES` instead of the process-wide
version used in non-test builds. This approach has two downsides:
- It does not allow nested overrides.
- It prevents using the code across package boundaries.
The former is a fairly minor issue, since I don't think we need nested
overrides. The latter prevents splitting up our large library crate,
since `#[cfg(test)]`-guarded code can only be used within a single
package.

To resolve these issues, a new approach to feature overrides in
tests is introduced in this commit: Instead of having a thread-local
version of `FEATURES`, all code, whether test or not, uses the
process-wide `FEATURES`. For non-test code, there is no change. For test
code, `FEATURES` is now also used. To override features in tests, a new
`with_overridden_feature` function is added, which replaces
`scoped_test` and `set`. It works by maintaining a thread-local stack of
feature overrides (`LOCAL_OVERRIDE_STACK`). The overridden `FeatureFlag`
and its new value are pushed to the stack, then the code for which the
override should be active is run, and finally the stack is popped again.
Feature tests now have to scan the stack for the first appearance of the
`FeatureFlag`, or use the value in `FEATURES` if the stack does not
contain the `FeatureFlag`. In most cases, the stack will be empty or
contain very few elements, so scanning it should not take long. For now,
it's only active in test code, so non-test code is unaffected. The plan
is to change this when the feature flag code is extracted from the main
library crate. This would slightly slow down feature tests in non-test
code, but there the stack will always be empty, since we only override
features in tests.

Part of #12494
2026-04-07 17:49:57 +08:00
Daniel Rainer
f0f48b4859 cleanup: stop needlessly exporting struct fields
Part of #12494
2026-04-07 17:49:57 +08:00
David Adam
a009f87630 build_tools: add sh script to build linux packages 2026-04-07 10:57:41 +08:00
David Adam
edb66d4d4e remove dput_cf_gen, not actually helpful 2026-04-07 10:57:41 +08:00
Nahor
f3f675b4cc Standardized error messages: constant names
Part of #12556
2026-04-05 13:15:47 +08:00
Nahor
434610494f argparse: fix error status code
To homogenize error reporting format, use a new Error struct. Currently this
is used for builtins and ensuring a common cmd/subcmd prefix.

Part of #12556
2026-04-05 13:15:47 +08:00
Dennis Yildirim
3cce1f3f4c Added completions for git verify-commit and verify-tag
Closes #12607
2026-04-05 13:14:31 +08:00
Johannes Altmanninger
a5bde7954e Update changelog 2026-04-05 13:07:29 +08:00
Nahor
99d63c21f1 Add tests to exercise all builtin error messages
With a few exceptions, only one test is added for a given message, even
when there are multiple ways to trigger the same message (e.g. different
invalid option combinations, or triggered in shared functions such as
`builtin_unknown_option`)

Includes a few very minor fixes, such as missing a newline, or using the
wrong var name.

Closes #12603
2026-04-05 00:22:42 +08:00
Nahor
c3e3658157 ulimit: remove unreachable error message
When there is no limit value, ulimit will have printed the current one
and exited

Part of #12603
2026-04-05 00:22:41 +08:00
Nahor
8d92016e72 string: error messages fixes
- fix wrong pattern used in `string replace` error message
- replace unreachable error with `unreachable!` in `string`
- fix cmd being used in place of subcmd 

Part of #12603
2026-04-05 00:22:41 +08:00
Nahor
f6a72b4e19 status: replace unreachable code with an assert!
Part of #12603
2026-04-05 00:22:40 +08:00
Nahor
2f9c2df10d set: report an error when called with -a,-p and no NAME
Previously executing `set -a` or `set -p` would just list all the
variables, which does not make sense since the user specifically ask
for an action (append/prepend).

Update the help page synopsis

Part of #12603
2026-04-05 00:22:40 +08:00
Johannes Altmanninger
0367aaea7d Disable relocatable tree logic when DATADIR or SYSCONFDIR are non-default
If all of

	$PREFIX/bin/fish
	$PREFIX/share/fish
	$PREFIX/etc/fish

exist, then fish assumes it's in a relocatable directory tree.
This is used by homebrew (PREFIX=/usr/local) and maybe also nix(?).

Other Linux distros prefer to use /etc/fish instead of $PREFIX/etc/fish
[1].  To do so, they need to pass -DCMAKE_INSTALL_SYSCONFDIR=/etc.
The relocatable tree logic assumes default data and sysconf dirs
(relative to a potentially relocatable prefix). If the user changes
any of those, and the relocatable tree logic happens to kick in,
that'd overrule user preference, which is surprising.

So a non-default data or sysconf path is a strong enough signal that
we want to disable the relocatable tree logic. Do that.

Closes #10748

[1]: ff2f69cd56/PKGBUILD (L43)
2026-04-04 01:22:54 +08:00
Johannes Altmanninger
e25b4b6f05 tests/checks/realpath.fish: fix on macOS 2026-04-03 23:26:37 +08:00
Yakov Till
90cbfd288e Fix test_history_path_detection panic: call test_init()
test_history_path_detection calls add_pending_with_file_detection(),
which spawns a thread pool task via ThreadPool::perform(). This
requires threads::init() to have been called, otherwise
assert_is_background_thread() panics.

Add the missing test_init() call, matching other tests that use
subsystems requiring initialization.

Closes #12604
2026-04-03 14:48:53 +08:00
Nahor
68453843d4 set: fix unreachable error messages
- Remove unreachable error message in `handle_env_return()`
While we could have put an empty block in `handle_env_return()` and
removed the condition on `NotFound` in `erase()`, we prefered to use
`unreachable!` in case `handle_env_return()` gets called in new scenarios
in the future

- Make reachable the error message when asking to show a slice

Part of #12603
2026-04-03 13:53:42 +08:00
Nahor
fb57f95391 realpath: fix random error message
With empty argument, `realpath` skips all processing, so the error
message, based on `errno`, was unrelated and changed depending on what
failed before. E.g:

```
$ builtin realpath "" /tmp "" /no-exist ""
builtin realpath: : Resource temporarily unavailable
/tmp
builtin realpath: : Invalid argument
/dont-exist
builtin realpath: : No such file or directory
```

Part of #12603
2026-04-03 13:53:41 +08:00
Nahor
1ed276292b read: remove unnecessary code
`to_stdout` is set to `true` if and only if `argv` is not empty.
- `argv` length and `to_stdout` are redundant, so we can remove `to_stdout`
- some tests in `validate_read_args` are necessarily false

Part of #12603
2026-04-03 13:53:41 +08:00
Branch Vincent
09e46b00cc completions: update ngrok
Closes #12598
2026-04-03 13:53:41 +08:00
Johannes Altmanninger
695bc293a9 contrib/debian/control: allow any "man" virtual pkg (man-db/mandoc)
We support multiple "man" implementations; at least man-db's and
mandoc's.

So we can relax the mandoc dependency to a dependency on the virtual
package providing "man". Note that as of today, "mandoc" fails to
have a "Provides: man".

However since Debian policy says in
https://www.debian.org/doc/debian-policy/ch-relationships.html

> To specify which of a set of real packages should be the default
> to satisfy a particular dependency on a virtual package, list the
> real package as an alternative before the virtual one.

we want to list possible real packages anyway, so do that.

Closes #12596
2026-04-03 12:20:08 +08:00
Nahor
344ff7be88 printf: remove unreachable code
Remove an unreachable, yet translated, error string and make the code
more idiomatic

Closes #12594
2026-04-01 09:37:57 +08:00
Nahor
014e3b3aff math: fix error message
Fix badly formatted error message, and make it translatable

Part of #12594
2026-04-01 09:37:57 +08:00
Nahor
68c7baff90 read: remove deprecation error for -i
`-i` has been an error and undocumented for 8 years now (86362e7) but
still requires some translating today. Time to retire it fully.

Part of #12594
2026-04-01 09:37:56 +08:00
Johannes Altmanninger
6eaad2cd80 Remove some redundant translation sources 2026-03-31 14:55:04 +08:00
Johannes Altmanninger
b321e38f5a psub: add missing line endings to error messages
Fixes #12593
2026-03-31 14:43:34 +08:00
Daniel Rainer
a32dd63163 fix: simplify and correct trimming of features
The previous handling was unnecessarily complex and had a bug introduced
by porting from C++ to Rust: The substrings `\0x0B` and `\0x0C` in Rust
mean `\0` (the NUL character) followed by the regular characters `0B`
and `0C`, respectively, so feature names starting or ending with these
characters would have these characters stripped away.

Replace this handling by built-in functionality, and simplify some
syntax. We now trim all whitespace, instead of just certain ASCII
characters, but I think there is no reason to limit trimming to ASCII.

Closes #12592
2026-03-31 14:43:34 +08:00
Bacal Mesfin
ef90afa5b9 feat: add completions for dnf copr
Closes #12585
2026-03-31 14:43:34 +08:00
r-vdp
7bd37dfe55 create_manpage_completions: handle groff \X'...' device control escapes
help2man 1.50 added \X'tty: link URL' hyperlink escapes to generated
man pages. coreutils 9.10 is the first widely-deployed package to ship
these, and it broke completion generation for most of its commands
(only 17/106 man pages parsed successfully).

The escape wraps option text like this:

  \X'tty: link https://example.com/a'\fB\-a, \-\-all\fP\X'tty: link'

Two places needed fixing:

- remove_groff_formatting() didn't strip \X'...', so Type1-4 parsers
  extracted garbage option names like "--all\X'tty"

- Deroffer.esc_char_backslash() didn't recognize \X, falling through
  to the generic single-char escape which stripped only the \, leaving
  "X'tty: link ...'" as literal text. Option lines then started with
  X instead of -, so TypeDeroffManParser's is_option() check failed.

Also handle \Z'...' (zero-width string) which has identical syntax.

Closes #12578
2026-03-31 13:15:52 +08:00
Johannes Altmanninger
14ce56d2a5 Remove unused fish_wcwidth wrapper 2026-03-30 13:57:27 +08:00
Johannes Altmanninger
01ee6f968d Support backward-word-end when cursor is past end
Closes #12581
2026-03-30 13:57:27 +08:00
Johannes Altmanninger
7f6dcde5e0 Fix backward-delete-char not stopping after control characters
Fixes 146384abc6 (Stop using wcwidth entirely, 2026-03-15)

Fixes #12583
2026-03-30 13:57:27 +08:00
Johannes Altmanninger
34fc573668 Modernize wcwidth API
Return None rather than -1 for nonprintables.  We probably still
differ from wcwidth which is bad (given we use the same name), but
hopefully not in a way that matters.

Fixes 146384abc6 (Stop using wcwidth entirely, 2026-03-15).
2026-03-30 13:57:10 +08:00
Johannes Altmanninger
93cbf2a0e8 Reuse wcswidth logic for rendered characters 2026-03-29 17:04:14 +08:00
Nahor
8194c6eb79 cd: replace unreachable code with assert
Closes #12584
2026-03-29 16:49:24 +08:00
Nahor
3194572156 bind: replace fake enum (c_int) with a real Rust enum
Part of #12584
2026-03-29 16:49:24 +08:00
Johannes Altmanninger
e635816b7f Fix Vi mode dl deleting from wrong character
Fixes b9b32ad157 (Fix vi mode dl and dh regressions, 2026-02-25).

Closes #12580
(which describes only the issue already fixed by b9b32ad157).
2026-03-28 21:45:33 +08:00
Johannes Altmanninger
2b3ecf22da start new cycle
Created by ./build_tools/release.sh 4.6.0
2026-03-28 13:16:34 +08:00
Johannes Altmanninger
c7ecc3bd78 Release 4.6.0
Created by ./build_tools/release.sh 4.6.0
2026-03-28 12:56:37 +08:00
Johannes Altmanninger
828a20ef30 Use cfg!(apple) 2026-03-28 12:41:58 +08:00
Fabian Boehm
7f5692dfd3 Skip ttyname on apple systems
Not relevant there because they don't have a "console session" as
such, and the ttyname call may hang.

Fixes #12506
2026-03-27 17:36:08 +01:00
Fabian Boehm
454939d5ab Update docs for fish_emoji_width defaulting to 2 2026-03-27 16:23:30 +01:00
Johannes Altmanninger
8756bc3afb Update changelog 2026-03-27 22:24:03 +08:00
Johannes Altmanninger
272f5dda83 Revert "CI: disable failing ubuntu-asan job"
This reverts commit 23ce9de1c3.

The compilation failure on Rust nightly was fixed in rust-shellexpand
commit b6173f0 (Rename WstrExt and WstrRefExt methods, 2026-02-23).
2026-03-27 22:23:08 +08:00
Johannes Altmanninger
dde33bab7e Clean up word-end special case handling in forward-word 2026-03-27 22:22:29 +08:00
David Adam
63f642c9dd CHANGELOG: log #12562 2026-03-27 22:21:08 +08:00
Fabian Boehm
146384abc6 Stop using wcwidth entirely
wcwidth isn't a great idea - it returns "-1" for anything it doesn't
know and non-printables, which can easily break text.

It is also unclear that it would be accurate to the system console,
and that's a minority use-case over using ssh to access older systems.

Additionally, it means we use one less function from libc and
simplifies the code.

Closes #12562
2026-03-27 21:31:19 +08:00
Fabian Boehm
8561008513 Unconditionally default emoji width to 2
"Emoji width" refers to the width of emoji codepoints. Since Unicode
9, they're classified as "wide" according to
TR11 (https://www.unicode.org/reports/tr11/).

Unicode 9 was released in 2016, and this slowly percolated into C
libraries and terminals. Glibc updated its default in 2.26, released
in August 2017.

Until now, we'd guess support for unicode 9 by checking the system
wcwidth function for an emoji - if it returned 2, we'd set our emoji
width to 2 as well.

However, that's a problem in the common case of using ssh to connect
to an old server - modern desktop OS, old server LTS OS, boom.

So now we instead just figure you've got a system that's *displaying*
the emoji that has been updated in the last 9 years.

In effect we're putting the burden on those who run old RHEL et al as
their client OS. They need to set $fish_emoji_width to 1.

Fixes #12500

Part of #12562
2026-03-27 21:31:19 +08:00
Remo Senekowitsch
88d01f7eb8 completions/cargo: avoid auto-installing toolchain via rustup
When cargo is installed via rustup, running cargo actually goes through
a proxy managed by rustup. This proxy determines the actual toolchain
to use, depending on environment variables, directory overrides etc. In
some cases, the proxy may automatically install the selected toolchain
if it's not yet installed, for example when first working on a project
that pins its rust toolchain via a `rust-toolchain.toml` file. In that
case, running cargo in the completion script can block the prompt for
a very long time. To avoid this, we instruct the rustup proxy not to
auto-install any toolchain with an environment variable.

Closes #12575
2026-03-27 20:11:22 +08:00
rohan436
4467822f6e docs: fix typo in MANPATH guidance string
Closes #12574
2026-03-27 18:55:38 +08:00
Johannes Altmanninger
5fe1cfb895 Bump initial Primary DA query timeout
Commit 7ef4e7dfe7 (Time out terminal queries after a while,
2025-09-21) though that "2 seconds ought to be enough for anyone".
But that's not true in practice: when rebooting a macOS system, it
can take longer. Let's see if 10 seconds is enough.  It should be fine
to have such a high timeout since this shouldn't happen in other cases.

Closes #12571
2026-03-26 16:31:24 +08:00
Daan De Meyer
484032fa9e Support SHELL_PROMPT_PREFIX, SHELL_PROMPT_SUFFIX, and SHELL_WELCOME
Add support for the SHELL_PROMPT_PREFIX, SHELL_PROMPT_SUFFIX, and
SHELL_WELCOME environment variables as standardized by systemd v257.

SHELL_PROMPT_PREFIX and SHELL_PROMPT_SUFFIX are automatically prepended
and appended to the left prompt at the shell level, so all prompts
(default, custom, and sample) pick them up without modification.

SHELL_WELCOME is displayed after the greeting when an interactive shell
starts.

These variables provide a standard interface for tools like systemd's
run0 to communicate session context to the shell.

Fixes https://github.com/fish-shell/fish-shell/issues/10924

Closes #12570
2026-03-26 15:45:50 +08:00
Johannes Altmanninger
fdd10ba9b2 Fix forward-word regression on single-char words followed by punctuation
The `forward-word` readline command on "a-a-a" is wrong (jumps to
"a"); on "aa-aa-aa" it's right (jumps to "-"); that's a regression
from bbb2f0de8d (feat(vi-mode): make word movements vi-compliant,
2026-01-10).

The is_word_end check for ForwardWordEmacs only tests for blank
(whitespace) after the current char. In the Punctuation style, words
also end before punctuation.

Fix this.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

See https://github.com/fish-shell/fish-shell/issues/12543#issuecomment-4125223455
2026-03-26 15:31:00 +08:00
Nahor
8679464689 color: prefer set_color --reset over set_color normal
`set_color normal` is too ambiguous and easily misinterpreted since
it actually reset all colors and modes instead of resetting just
the foreground color as one without prior knowledge might expect.

Closes #12548
2026-03-25 21:53:05 +08:00
Rodolfo Gatti
9ea760c401 Accept |& as an alias for &|
Closes #12565
2026-03-25 21:53:05 +08:00
Steffen Winter
d36c53c6e2 functions: dynamically query gentoo system paths
Use portageq to retrieve system paths instead of hardcoding them in.
This helps especially in Gentoo Prefix, where the installation is not
in / but rather offset inside a subdirectory (usually a users home
directory).

This only affects the "slow" path. When eix is installed it will be used
instead. It already accounts for Prefix installations.

Closes #12552
2026-03-25 21:53:05 +08:00
Steffen Winter
b047450cd0 functions: enable eix fast path for gentoo packages
The idea is taken from the deprecated/unused __fish_print_portage_packages.

Part of #12552
2026-03-25 21:53:05 +08:00
Johannes Altmanninger
0b93989080 fix ProcStatus::status_value in pipeline after ctrl-z
Repro (with default prompt):

	$ HOME=$PWD target/debug/fish -C '
	  function sleep_func; sleep 1; false; end
	  commandline "sleep 2 | sleep 3 | sleep 4 | sleep_func"
	  '
	Welcome to fish, the friendly interactive shell
	Type help for instructions on how to use fish
	johannes@e15 ~> sleep 2 | sleep 3 | sleep 4 | sleep_func
	^Zfish: Job 1, 'sleep 2 | sleep 3 | sleep 4 | s…' has stopped
	johannes@e15 ~ [0|SIGTSTP|SIGTSTP|1]> 

I'm not sure why the first sleep is not reported as stopped.

Co-authored-by: Lieuwe Rooijakkers <lieuwerooijakkers@gmail.com>

Fixes issue #12301

Closes #12550
2026-03-25 21:53:05 +08:00
rohan436
6b4ab05cbc docs: fix duplicated word in language docs
Closes #12554
2026-03-25 17:36:37 +08:00
Johannes Altmanninger
b5c367f8bf Bounds check backward-word-end
While at it, narrow the bounds check for forward movements,
no need to check for impossible cases.

Fixes #12555
2026-03-25 17:36:21 +08:00
Fabian Boehm
a6959aba2a Delete AGENTS.md again
This was an attempt at making vibecoded PRs slightly more palatable,
but it is itself an unpopular move.

Fixes #12526.
2026-03-19 18:41:36 +01:00
Noah Hellman
0a07e8dbdf pager: set col width only once
a bit hidden when column width is overwritten afterwards

Closes #12546
2026-03-16 16:13:21 +08:00
Noah Hellman
b2f350d235 pager: left-align descriptions
left aligned columns are usually easier to read in most cases (except for
e.g. numbers which descriptions are usually not)

Part of #12546
2026-03-16 16:13:21 +08:00
Noah Hellman
d1407cfde6 pager: add Column struct
to group column info, will add more

Part of #12546
2026-03-16 16:06:21 +08:00
Noah Hellman
f076aa1637 pager: take iterator for print_max
avoid unnecessary allocations just to create temporary wstrs

Part of #12546
2026-03-16 16:06:21 +08:00
Noah Hellman
96602f5096 pager tests: add multi-column tests
check alignment of descriptions

Part of #12546
2026-03-16 15:37:38 +08:00
Noah Hellman
a36fc0b316 pager tests: add completions helper
will be especially helpful for multi-completion tests

Part of #12546
2026-03-16 15:37:37 +08:00
Noah Hellman
d7f413bee9 add contrib/shell.nix
Closes #12544
2026-03-15 17:46:24 +08:00
Noah Hellman
81e9e0bd5c tests: use /bin/sh instead of /bin/ls, /bin/echo
/bin/ls and /bin/echo do not necessarily exist on all systems, e.g.
nixos.

/bin/sh should at least exist on more systems than /bin/ls and /bin/echo

Part of #12544
2026-03-15 17:46:24 +08:00
Noah Hellman
5c042575b0 highlight test: use /usr/bin/env instead of /bin/cat
/bin/cat doesn't exist on e.g. nixos, which only has /bin/sh and
/usr/bin/env.

/usr/bin/env should at least exist on more systems than /bin/cat

Part of #12544
2026-03-15 17:46:24 +08:00
Noah Hellman
a0d8d27f45 tests: use command instead of absolute paths
These binaries are not guaranteed to exist at /bin/X on all systems,
e.g. nixos does not place binaries there, but as long as they are in the
PATH we can find them with command.

Part of #12544
2026-03-15 17:46:24 +08:00
Johannes Altmanninger
d1d4cbf3f8 build_tools/update_translations.fish 2026-03-15 17:46:24 +08:00
Steffen Winter
7f41c8ba1f completions/coredumpctl: update for systemd 259
Closes #12545
2026-03-15 16:17:32 +08:00
Steffen Winter
c8989e1849 completions/run0: update for systemd 259
Part of #12545
2026-03-15 16:17:32 +08:00
Helen Chong
ae4e258884 chore(update-dependencies.sh): pin Catppuccin theme repository commit for fetching
See also https://github.com/catppuccin/fish/pull/41

Closes #12525
2026-03-15 16:14:39 +08:00
Nahor
3f0b4d38ff set_color: use only on/off for boolean options
This is done partly for consistency `underline` where we still need
`off` but where true/false doesn't make sense, and partly to simplify
user choices and the code.

See #12507

Closes #12541
2026-03-15 16:12:40 +08:00
t4k44
e9379904fb l10n: add Japanese translation
Closes #12499
2026-03-14 18:30:17 +01:00
Helen Chong
116671c577 docs(changelog): fix typo for Catppuccin color themes
Closes #12524
2026-03-13 15:04:05 +08:00
xtqqczze
53e1718a68 fix: pointer and signal handler casts
- avoid UB from `usize` → pointer cast
- use correct type for `sa_sigaction`

Closes #12488
2026-03-13 15:04:05 +08:00
Nahor
eab84c896e completions/docker: don't load if docker is not started
In WSL, if Docker is not started, the `docker` command is a script
that prints an error message to stdout instead of a valid script.

`docker.exe` is available and can return the completion script. However
any completion will end up calling that `docker` script anyway,
resulting further errors due to the unexpected output.

Closes #12538
2026-03-13 14:37:50 +08:00
JANMESH ARUN SHEWALE
ddf99b7063 Fix vi mode x and X keys to populate kill-ring
Closes #12420
Closes #12536
2026-03-13 14:29:06 +08:00
JANMESH ARUN SHEWALE
bcda4c5c4d fish_indent: preserve comments before brace blocks
Closes #12523
Closes #12505
2026-03-13 14:22:21 +08:00
rohan436
1244a47d86 docs: fix separator spelling in argparse docs
Closes #12533
2026-03-13 14:22:21 +08:00
Julien Gautier
4279a4f879 Update 'mount' completion script with the generated one, and update_translations
Closes #12531
2026-03-13 14:22:21 +08:00
Daniel Danner
efebb7bcdb docs/set: Fix mention of --export
Closes #12530
2026-03-13 14:22:21 +08:00
Daniel Rainer
226e818c25 resource usage: print KiB, not kb
The value shown is in KiB (2^{10} bytes), according to
`man 2 getrusage`, not kb (10^3 bits), so reflect this in the variable
name and output.

Closes #12529
2026-03-13 14:22:21 +08:00
Johannes Altmanninger
888e6d97f9 Address code review comments
See #12502
2026-03-13 14:22:21 +08:00
Nahor
eb7ea0ef9b set_color: add --foreground and --reset options
`--foreground` has two purposes:
- allow resetting the foreground color to its default, without also
resetting the other colors and modes
- improve readibility and unify the `set_color` arguments

`--reset` also has two purposes:
- provide a more intuitive way to reset the text formatting
- allow setting the colors and modes from a clean state without
requiring two calls to `set_color`

Part 3/3 of #12495

Closes #12507
2026-03-13 14:22:21 +08:00
Nahor
4e41d142fd set_color: allow resetting the underline style
Part 2/3 of #12495

Part of #12507
2026-03-13 14:22:00 +08:00
Nahor
a893dd10f4 set_color: allow resetting specific attributes
Add an optional `on`/`off`` value to italics/reverse/striketrough
to allow turning of the attribute without having to use the `normal`
color, i.e. reset the whole style

Part 1/3 of #12495

Part of #12507
2026-03-13 14:22:00 +08:00
Johannes Altmanninger
cba82a3c64 Remove code duplication 2026-03-13 14:22:00 +08:00
Nahor
6e8c32eb12 Remove the Default trait for text face/styling
The notion of default is context dependent: there is the default for
the state (default color, non-bold, no underline, ...) and the default
for a change (no color change, no underline change, ...).

Currently, using a single default works because either the style
attributes cannot be turned off individually (the user is expected
to reset to default then re-set necessary attributes), or the code
has special handling for specific scenarios (e.g. highlighting).

So in preparation for later commits, where attribute can be turned off
individually, make the two defaults explicit.

Part of #12507
2026-03-13 14:06:21 +08:00
Nahor
8ef9864c0c Cleaner fix for #[rustfmt::skip] on expressions
Part of #12507
2026-03-13 12:24:28 +08:00
Nahor
786ac339b8 Remove EnterStandoutMode
Now that terminfo has been removed, EnterStandoutMode is just a duplicate
of EnterReverseMode.

Part of #12507
2026-03-13 12:24:28 +08:00
Johannes Altmanninger
dbb6ae6cf5 Update changelog 2026-03-10 09:56:05 +08:00
Nahor
c4aa03a1fd complete: fix completion of commands starting with -
When trying to complete a command starting with `-`, and more
specifically when trying to get the description of possible commands,
the dash was interpreted as an option for `__fish_describe_command`,
resulting in an "unknown option" most of the time.

This is a regression introduced when adding option parsing to
`__fish_describe_command`

Fixes 7fc27e9e5 (cygwin: improve handling of `.exe` file extension, 2025-11-22)
Fixes #12510

Closes #12522
2026-03-10 09:56:05 +08:00
Nahor
e9340a3c43 CI: rebase MSYS2 dll
This fixes, or should make it less likely, spurious CI failures because
of:
`child_info_fork::abort: address space needed by <DLL> is already occupied`

The issue is that Unix `fork()`, given how it works, preserves
libraries' addresses. Windows does not have such a function, so Cygwin
needs emulate it by moving the libraries in a child process to match
the addresses in its parent. This leads to conflicts if Windows already
loaded something there.

As a workaround, Cygwin has a `rebase` application to assign specific
addresses to each DLL and forcing Windows to use those. This generally
fixes the issue (until a DLL is updated that is, but that's not
a concern for CI since everything is rebuilt from scratch every time).

In the case of #12515 though, the failing DLL is a temporary one built
during the compilation. So a rebase of MSYS2 packages will not quite
fix the problem. However, by moving other DLLs at specific locations,
it reduce the risk of collision to only be between the temporary ones.

Fixes #12515

Closes #12521
2026-03-10 09:56:05 +08:00
Volodymyr Chernetskyi
143a53d9c3 feat: add completions for tflint
Closes #12520
2026-03-10 09:56:05 +08:00
Volodymyr Chernetskyi
a282acf083 feat: add git completions for interpret-trailers
Closes #12519
2026-03-10 09:55:42 +08:00
Mike Yuan
cbd5d7640a functions/history: honor explicitly specified --color=
This partially reverts 324223ddff.

The offending commit broke the ability to set color mode via option
completely in interactive sessions.

Closes #12511
2026-03-09 17:15:55 +11:00
Delapouite
6f262afe8e doc: add link to not command from if command
It's very common to want to express negation in a `if` command.
Therefore a quick way to learn about the `not` command is handy.

Closes #12512
2026-03-09 17:15:55 +11:00
Daniel Rainer
310eba7156 cleanup: use nix version of getrusage
Change the behavior when `getrusage` fails. Previously, failure was
masked by using 0 values for everything. This is misleading. Instead, we
now panic on such failures, because they should never occur with our
usage of the function.

Closes #12502
2026-03-09 17:15:55 +11:00
Daniel Rainer
0223edc639 cleanup: use nix version of getpid
Alternatively, we could also use `nix::unistd::Pid::this()`.

Part of #12502
2026-03-09 16:52:08 +11:00
Daniel Rainer
6d0bb4a6b8 cleanup: replace custom umask wrapper by nix
Part of #12502
2026-03-09 16:52:08 +11:00
Daniel Rainer
7992fda9fe refactor: extract perror
Part of #12502
2026-03-09 16:52:08 +11:00
Daniel Rainer
bf5fa4f681 feat: implement perror_nix
Similar to `perror_io`, we don't need to make a libc call for `nix`
results, since the error variant contains the errno, from which a static
mapping to an error message exists. Avoid using `perror` and instead use
`perror_io` or `perror_nix` as appropriate where possible.

The `perror_io` and `perror_nix` functions could be combined by
implementing `fish_printf::ToArg` for `nix::errno::Errno`, but such a
function would violate type safety, as it would allow passing any
formattable argument, not necessarily limited to functions with a `%s`
formatting.

Part of #12502
2026-03-09 16:52:08 +11:00
Daniel Rainer
735f3ae6ad cleanup: remove obsolete wperror
Part of #12502
2026-03-09 16:52:08 +11:00
Daniel Rainer
131febed2a cleanup: remove unnecessary wstr usage
Part of #12502
2026-03-09 16:52:07 +11:00
Daniel Rainer
61dec20abd cleanup: inline wrename function
This function was only used in a single place and does not do anything
complicated, so inline it.

Part of #12502
2026-03-09 16:52:07 +11:00
Daniel Rainer
c0bb0d6584 refactor: extract write_to_fd into util crate
Part of #12502
2026-03-09 16:52:07 +11:00
Daniel Rainer
c5e4fed021 format: use 4-space indents in more files
Change some files which have lines whose indentation is not a multiple
of the 4 spaces specified in the editorconfig file.

Some of these changes are fixes or clear improvements (e.g. in Rust
macros which rustfmt can't format properly). Other changes don't clearly
improve the code style, and in some cases it might actually get worse.

The goal is to eventually be able to use our editorconfig for automated
style checks, but there are a lot of cases where conforming to the
limited editorconfig style spec does not make sense, so I'm not sure how
useful such automated checks can be.

Closes #12408
2026-03-09 16:52:07 +11:00
Daniel Rainer
1df7a8ba29 cleanup: remove obsolete ellipsis complexity
Previously, we chose the ellipsis character/string based on the locale.
We now assume a UTF-8 locale, and accordingly always use the Unicode
HORIZONTAL ELLIPSIS U+2026 `…`. When this was changed, some of the logic
for handling different ellipsis values was left behind. It no longer
serves a purpose, so remove it.

The functions returning constants are replaced by constants. Since the
ellipsis as a `wstr` is only used in a single file, make it a local
const there and define it via the `ELLIPSIS_CHAR` const.

Put the `ELLIPSIS_CHAR` definition into `fish-widestring`, removing the
dependency of `fish-wcstringutil` on `fish-common`, helping future
extraction efforts.

One localized message contains an ellipsis, which was inserted via a
placeholder, preventing translators from localizing it. Since the
ellipsis is a constant, put it directly into the localized string.

Closes #12493
2026-03-03 15:14:39 +11:00
Daniel Rainer
121b8fffa6 fix: version test on shallow, dirty git repo
In shallow, dirty git repo, the version identifier will look something
like `fish, version 4.5.0-g971e0b7-dirty`, with no commit counter
indicating the commits since the last version. Our regex did not handle
this case.

Make the commit counter optional, which also allows removing the second
alternative in the regex, since it's now subsumed by the first.

Fixes #12497

Closes #12498
2026-03-03 15:14:39 +11:00
Johannes Altmanninger
9c4190e40a Retry writes on signals other than INT/HUP
Fixes #12496
2026-03-03 15:14:39 +11:00
Daniel Rainer
f000149837 refactor: move FilenameRef into fish_common
Closes #12492
2026-03-03 15:14:39 +11:00
Daniel Rainer
f6f50df43d refactor: extract more from src/common.rs
This time, functions for decoding `wstr` into various types and the
`ToCString` trait are extracted.

Part of the wider goal of slimming down the main library to improve
incremental build performance and reduce dependency cycles.

Part of #12492
2026-03-03 15:14:39 +11:00
Daniel Rainer
29160a1592 gettext: support non-ASCII msgids for Rust
The `msguniq` call for deduplicating the msgids originating from Rust
previously did not get a header entry (empty msgid with msgstr
containing metadata). This works fine as long as all msgids are
ASCII-only. But when a non-ASCII character appears in a msgid, `msguniq`
errors out without a header specifying the encoding. To resolve this,
add the header to the input of this `msguniq` invocation and then remove
the header again using sed to prevent duplicating it for the outer
msguniq call at the end of the file.

Closes #12491
2026-03-03 15:14:39 +11:00
Julio Napurí
fcdcae72c5 l10n: add spanish translation
Closes #12489
2026-03-03 15:14:38 +11:00
Daniel Rainer
971e0b7d37 l10n: create common function for finding l10n/po dir
Reduce repetition and make it easier to relocate the directory. This
approach will also be useful for Fluent.

Closes #12484
2026-02-26 14:34:46 +11:00
Daniel Rainer
d6ac8a48c0 xtasks: make files_with_extension more accessible
This function is useful beyond the formatting module, so put it into the
top level of the library.

Closes #12483
2026-02-26 14:34:46 +11:00
Daniel Rainer
2ba031677f xtask: don't panic on failure
Panicking suggests that an assumption of our code was violated.
The current use of panics in xtasks is for expected failures, so it's
better to avoid panicking and instead just print the error message to
stderr and exit 1.

Closes #12482
2026-02-26 14:34:46 +11:00
Johannes Altmanninger
c15ea5d1e6 CI: deny unknown lints on Rust stable
Closes #12334
2026-02-25 18:22:48 +11:00
Johannes Altmanninger
78f3c95641 Reliably suppress warning about unknown Rust lints
Lint table order is unspecified, leading to spurious "unknown
lint" errors which ought to have been suppressed, see
https://github.com/rust-lang/cargo/issues/16518

From https://rust-lang.github.io/rfcs/3389-manifest-lint.html

> lower (particularly negative) numbers ... show up first on the
> command-line to tools like rustc

So we can use the priority property to make sure that unknown lints
are suppressed before rustc processes the other lint specifications.

Part of #12334
2026-02-25 18:22:48 +11:00
Aditya Giri
149fec8f02 string: accept --char alias for pad and shorten
Closes #12460
2026-02-25 18:18:21 +11:00
Daniel Rainer
1b0fa8f804 unicode: use new decoded_width function
For now, only add it in a single place. There are more instances where
width calculation could be improved, but this one has already been
converted to use the `unicode-width` crate before, so conversion is easy
and a strict improvement.

Closes #12457
2026-02-25 18:18:21 +11:00
Daniel Rainer
c38dd1f420 unicode: add function for width computation
Accurately computing the width of arbitrary strings is a non-trivial
problem. We outsource the logic for it to the `unicode-width` crate. But
directly passing our PUA-encoded strings to the crate would give
incorrect results whenever a PUA codepoint is encoded in our string,
since one input PUA codepoint is converted into 3 consecutive codepoints
in our encoding. Therefore, we need to decode before performing width
calculations. Our regular decoding decodes to raw bytes, which is
incompatible with the `unicode-width` crate, since it expects `char`s,
and the decoded bytes could be invalid UTF-8, making their width
undefined. We tackle this problem by building a custom iterator which
does on-the-fly decoding. Encoded PUA codepoints are turned back into
the original codepoints, and any other PUA-encoded bytes are replaced by
one replacement character (U+FFFD) per byte. The latter is not necessary
since PUA codepoints have a defined width of 1, so we could also forward
the PUA-encoded bytes which encode invalid UTF-8 input instead of
inserting the replacement character. The choice to use the replacement
character is made to avoid producing a char sequence where some PUA
codepoints represent themselves, whereas others still encode non-UTF-8
bytes. Such a mix of semantics would be confusing if the char sequence
is ever used for anything else. Replacement characters make it clear
that there are no remaining encoded semantics. Note that using the char
sequences produced in this way for any purpose other than width
computation is not intended. For output, our pre-existing decoding to
bytes should be used, which allows preserving non-UTF-8 bytes.

The implementation of the iterator is not entirely straightforward,
since we need to read up to 3 chars to be able to decide whether we have
an encoded PUA character. Therefore, we need to cache some chars across
invocations of the iterator's `next` and `next_back` invocations. This
is done via a custom buffer struct, which does not require dynamic
allocations.

The tests for the new functionality are only in the main crate because
the encoding function is not available in the `fish-widestring` crate.
Once that is resolved, the tests should be moved.

Part of #12457
2026-02-25 18:18:21 +11:00
Johannes Altmanninger
b9b32ad157 Fix vi mode dl and dh regressions
Also improve AGENTS.md though we should totally point to
CONTRIBUTING.rst instead.

Fixes #12461
2026-02-25 18:18:21 +11:00
Janne Pulkkinen
45ac6472e2 completions/protontricks: update options
New options were added and the help text updated for the old to better
tell them apart.

Closes #12477
2026-02-25 18:18:21 +11:00
Janne Pulkkinen
9235c5de6c completions/protontricks: use new flag for complete
`protontricks -l` will launch a graphical prompt to choose Steam
installation if multiple installations are found. `-L/--list-all`
is a new flag introduced in 1.14.0 that retrieves all games without user
interaction.

Also silence stderr, since it can cause warning messages to be printed.

Part of #12477
2026-02-25 16:47:54 +11:00
Johannes Altmanninger
6c091dbaf4 Remove spurious intermediate ⏎ symbol on prompt redraw
Commit 7ac9ce7ffb (Reduce the number of escape sequences for text
styles, 2026-02-06) includes a bad merge conflict resolution of
a conflict with 38513de954 (Remove duplicated code introduced in
commit 289057f, 2026-02-07). Fix that.

Fixes #12476
2026-02-25 16:47:25 +11:00
Daniel Rainer
3e7e57945c format: replace style.fish by xtask
Replace the `build_tools/style.fish` script by an xtask. This eliminates
the need for a fish binary for performing the formatting/checking. The
`fish_indent` binary is still needed. Eventually, this should be made
available as a library function, so the xtask can use that instead of
requiring a `fish_indent` binary in the `$PATH`.

The new xtask is called `format` rather than `style`, because that's a
more fitting description of what it does (and what the script it
replaces did).

The old script's behavior is not replicated exactly:
- Specifying `--all` and explicit paths is supported within a single
  invocation.
- Explicit arguments no longer have to be files. If a directory is
  specified, all files within it will be considered.
- The git check for un-staged changes is no longer filtered by file
  names, mainly to simplify the implementation.
- A warning is now printed if neither the `--all` flag nor a path are
  provided as arguments. The reason for this is that one might assume
  that omitting these arguments would default to formatting everything
  in the current directory, but instead no formatting will happen in
  this case.
- The wording of some messages is different.

The design of the new code tries to make it easy to add formatters for
additional languages, or change the ones we already have. This is
achieved by separating the code into one function per language, which
can be modified without touching the code for the other languages.
Adding support for a new formatter/language only requires adding a
function which builds the formatter command line based on the arguments
to the xtask, and calling that function from the main `format` function.

Closes #12467
2026-02-24 16:33:04 +11:00
Johannes Altmanninger
23ce9de1c3 CI: disable failing ubuntu-asan job
The rust-shellexpand dependency (via rust-embed)
fails to build on nightly Rust; Fix is at
https://gitlab.com/ijackson/rust-shellexpand/-/merge_requests/19
2026-02-23 15:49:34 +11:00
exploide
6aee7bf378 completions/ip: complete netns for ip l set dev netns, plus some option arguments
Closes #12464
2026-02-23 14:25:09 +11:00
David Adam
5b360238b2 dput_cf_gen: drop -x flag, only used for development 2026-02-21 10:58:12 +08:00
David Adam
5b44c9668f add script to generate a dput.cf for Ubuntu PPA uploads 2026-02-21 10:38:24 +08:00
Peter Ammon
d01a403c65 Cleanup ParserTestErrorBits harder
We don't need this bitwise operator override.
2026-02-17 19:13:13 -08:00
Peter Ammon
b65649725e Cleanup ParserTestErrorBits
This was a weird type that made sense in C++ but not so much in Rust.
Let's clean this up.
2026-02-16 22:29:30 -08:00
Johannes Altmanninger
89c2f1bd6b start new cycle
Created by ./build_tools/release.sh 4.5.0
2026-02-17 11:54:05 +11:00
Johannes Altmanninger
3478f78a05 Release 4.5.0
Created by ./build_tools/release.sh 4.5.0
2026-02-17 11:32:33 +11:00
Johannes Altmanninger
19cedb01bc changelog 2026-02-17 11:31:17 +11:00
xtqqczze
cb24c4a863 rust: use const_locks feature
Closes #12454
2026-02-17 11:28:58 +11:00
xtqqczze
93478e7c51 simplify serialize_with_vars
Closes #12453
2026-02-17 11:28:58 +11:00
xtqqczze
4eac5f4d9d clippy: fix unused_trait_names lint
https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names

Closes #12450
2026-02-17 11:28:58 +11:00
xtqqczze
e76370b3b7 clippy: fix str_to_string lint
https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string

Closes #12449
2026-02-17 11:13:34 +11:00
Daniel Rainer
1e3153c3fb unicode: fix history search cursor positioning
The cursor position calculation did not correctly account for the width
of Unicode text, resulting in the cursor being placed to far left in
scenarios with characters taking up 2 cells, such as in Chinese text.

Fix this by combining the entire line into a string and computing the
length of the resulting string using the `unicode-width` crate.

This is the first code in the main crate making use of `unicode-width`.
Eventually, we'll probably want to use it in more places, to get better
and consistent results.

Fixes #12444

Closes #12446
2026-02-17 11:13:25 +11:00
Peter Ammon
d0a95e4fde Fix some dumb clipply 2026-02-15 11:25:40 -08:00
Peter Ammon
7174ebbb4b Slightly refactor history tests
This introduces a create_test_history helper function; it looks sort of
silly now but will be useful for upcoming history improvements.
2026-02-15 11:09:14 -08:00
Peter Ammon
e81cec1633 Fix kill-word-vi on macOS/BSDs
`seq 0` outputs nothing on Linux but `1 0` on macOS and BSDs, breaking
this word motion. Fix it by not running `seq 0`.
2026-02-14 12:06:51 -08:00
Johannes Altmanninger
02c04550fd Fix Vi mode cw deleting trailing whitespace
Fixes 38e633d49b (fish_vi_key_bindings: add support for count,
2025-12-16).

Fixes #12443
2026-02-12 14:11:26 +11:00
David Adam
b0bfc7758a CHANGELOG: editing 2026-02-11 16:41:00 +08:00
Weixie Cui
cbf21b13d7 fix: unwrap may cause panic
Closes #12441
2026-02-11 14:49:54 +11:00
Aman Verma
14f3c3e13e completions: use upstream completions for dua
Our completions had gone out-of-date. They didn't support invocations
like `dua path/to/folder`, instead requiring a subcommand.

Closes #12438
2026-02-11 14:46:01 +11:00
Johannes Altmanninger
05ea6e9e66 Remove unused "Output" trait
Continue incidental cleanup started in earlier commit (Reduce the
number of escape sequences for text styles, 2026-02-06).
2026-02-11 14:37:38 +11:00
Nahor
74d2e6d5d8 Rip support for terminfo database
The terminfo database hasn't been used by default since 4.1.0
(see #11344, #11345)

Closes #12439
2026-02-11 14:37:38 +11:00
Nahor
1b8f9c2b03 Initialize Features using a loop
This removes the need to update `Features::new()` every time a feature
is added or removed

Part of #12439
2026-02-11 14:37:38 +11:00
Johannes Altmanninger
491158dfad Output reset_text_face: minor simplification 2026-02-11 14:37:38 +11:00
Johannes Altmanninger
7fa08e31c6 Tweak naming for outputter buffer consumer
Rust idiomatic naming is "take" for "move", also Rust might actually
move the object to a different memory adress, which is fine because
all Rust objects are trivially relocatable.
2026-02-11 14:22:14 +11:00
Nahor
7ac9ce7ffb Reduce the number of escape sequences for text styles
When setting graphics attributes (SGR), combine them into a single
escape sequence to reduce the length of the string and make it slightly
easier to read by people when needed.

Some terminal/parser[^1] may have a cap on the number of parameters, so
we limit the number to 16.

[^1]: https://vt100.net/emu/dec_ansi_parser: "There is no limit to the
number of characters in a parameter string, although a maximum of 16
parameters need be stored. If more than 16 parameters arrive, all
the extra parameters are silently ignored.""

Closes #12429
2026-02-11 14:22:14 +11:00
Johannes Altmanninger
197779697d Remove unused Output implementations
I think the OutputStream was used only in old fish_indent code.
The BufferedOutputter seems obsolete, maybe because DerefMut covers
this.
2026-02-11 14:22:14 +11:00
Weixie Cui
96fabe4b29 fix: update 2026-02-10 14:21:04 +01:00
David Adam
4a4d35b625 completions/dput: add new completions 2026-02-10 16:02:31 +08:00
David Adam
92739a06e2 completions/dscacheutil: correct description 2026-02-10 15:46:53 +08:00
David Adam
244e9586ca completions/chsh: remove stray whitespace 2026-02-10 15:44:29 +08:00
David Adam
13a2ccae66 release.sh: add a couple of explanatory comments 2026-02-10 14:29:16 +08:00
David Adam
ed34845b10 Debian packaging: don't link the .cargo directory from vendor tarball
The main tarball did not contain a .cargo directory, but it does now,
and this symlink ends up in the wrong place and is not needed.
2026-02-10 13:07:01 +08:00
Daniel Rainer
74b104c9f6 cleanup: delete unmaintained Dockerfile
This Dockerfile has been broken for quite a while now, at least since
Rust is required for building fish. No one seems to have complained
about it being broken, so there is no point in keeping it around. The
`docker` directory contains several Dockerfiles which could be used
instead.

https://github.com/fish-shell/fish-shell/pull/12408#discussion_r2770432433

Closes #12435
2026-02-09 12:16:52 +11:00
Francisco Giordano
1f8cdf85b6 Fix ctrl-l interference with history search
Closes #12436
2026-02-09 12:16:33 +11:00
David Adam
dc02a8ce35 CI: run apt-get update before any apt install
Try to avoid surprises down the track by doing this across the board.
2026-02-09 00:04:40 +08:00
Pothi Kalimuthu
65e726324a Fix the link to GitHub repo 2026-02-08 23:38:46 +08:00
Johannes Altmanninger
fe3c42af9e Work around github actions python failure
github actions runners have python 3.12, so the upgrade to debian
stable's 3.13 broke things:

	+ env UV_PYTHON=python uv --no-managed-python lock --check --exclude-newer=2026-02-01T13:00:00Z
	Using CPython 3.12.3 interpreter at: /usr/bin/python
	error: The requested interpreter resolved to Python 3.12.3, which is incompatible with the project's Python requirement: `>=3.13` (from `project.requires-python`)
	Error: Process completed with exit code 2.

Steps:
1. edit pyproject.toml and
2. uv lock --upgrade --exclude-newer="$(awk -F'"' <uv.lock '/^exclude-newer[[:space:]]*=/ {print $2}')"

In future we should maybe use managed python?
2026-02-08 14:03:53 +11:00
Johannes Altmanninger
28c7e7173f Fix fish_indent_interrupt CI workaround 2026-02-08 14:01:45 +11:00
Johannes Altmanninger
a897a26daa Test Vi mode dfX
Tests d25965afba (Vi mode: hack in support for df and friends,
2026-02-06).
2026-02-08 13:16:07 +11:00
Johannes Altmanninger
ffce214362 Test fish_vi_key_bindings argument
Tests 6d01138797 (Fix vi key bindings regression when called with
argument, 2026-02-04).
2026-02-08 13:16:07 +11:00
Liam Snow
bdb8e4847f dir_iter: handle missing d_type on illumos
Closes #12410
Fixes #11099
2026-02-08 13:16:07 +11:00
Liam Snow
9750d8c3ad path: return Unknown for remoteness check on illumos
Part of #12410
2026-02-08 13:12:41 +11:00
Johannes Altmanninger
dadec16661 path_remoteness: use cfg_if 2026-02-08 13:12:41 +11:00
Liam Snow
b5cd2763db spawn: POSIX_SPAWN flags are not i32 on illumos
Part of #12410
2026-02-08 13:12:41 +11:00
Liam Snow
a08dd70354 env: define _CS_PATH const for illumos
Part of #12410
2026-02-08 13:12:41 +11:00
Liam Snow
5b39d1fc6a ulimit: add illumos to platforms missing MEMLOCK/NPROC limits
Part of #12410
2026-02-08 13:12:41 +11:00
Nahor
38513de954 Remove duplicated code introduced in commit 289057f
Closes #12432
2026-02-08 13:12:41 +11:00
Johannes Altmanninger
cc1ae25c3a Fix kill-a-word kill-inner-word bounds checks if at end of command line
These commands are meant to be used in Vi mode when the cursor is on
a valid character, so there's not much reason to try to make them do
something when the cursor is past-end.  Do nothing, like we already
do for the empty commandline.

Reported in #12430
2026-02-08 13:12:41 +11:00
Johannes Altmanninger
002fa0e791 Fix cursor position after accepting Vi mode autosuggestion
Closes #12430
2026-02-08 13:12:41 +11:00
Jack Pickle
0589de7523 fix git stash completions not working after flags
update __fish_git_stash_not_using_subcommand check for actual subcommands
instead of treating any word after 'stash' as a subcommand.

stay dry by adding__fish_git_stash_is_push helper that matches both implicit and explicit push.

fixes #11307

Closes #12421
2026-02-08 13:12:41 +11:00
Johannes Altmanninger
4e7e0139fd Update to sphinx 9.1
sphinx==9.1.0 depends on Python>=3.12,
so change our pinning policy to fit.
Note we still support Python 3.9 in user-facing code.

Steps:
1. edit updatecli.d/python.yml
2. remove bad "uv lock" from build_tools/update-dependencies.sh
   (didn't respect exclude-newer)
3. updatecli apply --config updatecli.d/python.yml
4. uv lock --upgrade --exclude-newer="$(date --date='7 days ago' --iso-8601)"
2026-02-08 13:12:41 +11:00
Johannes Altmanninger
c044e1d433 update-dependencies.sh: update and pin 3rd party github workflows 2026-02-08 13:12:41 +11:00
Johannes Altmanninger
fb0edf564e Add AGENTS.md
LLM-generated contributions tend to produce too many redundant
comments. Fix that.  This doesn't work OOTB for Claude, but it's easy
to tell it to respect AGENTS.md..
2026-02-08 13:12:41 +11:00
Salman Muin Kayser Chishti
969ce68d5d Upgrade GitHub Actions for Node 24 compatibility
Signed-off-by: Salman Muin Kayser Chishti <13schishti@gmail.com>

Closes #12422
2026-02-08 12:59:26 +11:00
Salman Muin Kayser Chishti
6ef2e04518 Upgrade GitHub Actions to latest versions
Signed-off-by: Salman Muin Kayser Chishti <13schishti@gmail.com>

Closes #12423
2026-02-08 12:59:26 +11:00
Johannes Altmanninger
bc84fe9407 tests/pexpects/fish_indent_interrupt: skip in CI
This fails intermittently in CI. Disable it.  We disable a lot of
other tests as well which is why we run tests on developer machines
before pushing to master.

See #12351
2026-02-08 12:59:26 +11:00
Peter Ammon
38e8416da5 test_history_path_detection to adopt custom history directories
Simplifies this test by making it no longer serial.
2026-02-07 12:29:54 -08:00
Peter Ammon
cf0e07aecd test_history_formats to adopt custom history directories
Simplifies this test by making it no longer serial.
2026-02-07 12:23:13 -08:00
Peter Ammon
8e014bbf97 test_history_formats to adopt custom history directories
Simplifies this test by making it no longer serial.
2026-02-07 12:20:11 -08:00
Peter Ammon
f9013ad7a6 Allow histories to be created outside of the default path
Currently history files are written to the "data directory"
(XDG_DATA_HOME). This is awkward in testing since we have to put files
into this directory.

Allow histories to have their own directory, so that they don't
interfere with other files. This will help simplify some tests.

Adopt this in some (but not all) history tests.
2026-02-07 12:20:09 -08:00
Johannes Altmanninger
8d2d50573a changelog 2026-02-06 15:38:50 +11:00
Johannes Altmanninger
d25965afba Vi mode: hack in support for df and friends
Commit 38e633d49b (fish_vi_key_bindings: add support for count,
2025-12-16) introduced an operator mode which kind of makes a lot of
sense for today's fish.  If we end up needing more flexibility and
tighter integration, we might want to move some of this into core.

Unfortunately the change is at odds with our cursed forward-jump
implementation.  The forward-jump special input function works by
magically reading the next key from stdin, which causes problems when
we are executing a script:

	commandline -f begin-selection
	commandline -f forward-jump
	commandline -f end-selection

here end-selection will be executed immediately
and forward-jump fails to wait for a keystroke.

We should get rid of forward-jump implementation.

For now, replace only the broken thing with a dedicated bind mode
for each of f/F/t/T.

Fixes #12417
2026-02-06 15:38:50 +11:00
Johannes Altmanninger
6f895935a9 bind.rst: update meaning of --silent option
We can probably even remove this, since the remaining behavior doesn't
seem useful.
2026-02-06 15:19:49 +11:00
Johannes Altmanninger
70ebc969f9 fish_vi_key_bindings: remove unused "bind -s" option
Commit 46d1334f95 (Silence bind errors in default key bindings,
2017-10-03) worked around errors arising from "bind -k".
We no longer use that, so remove that.
2026-02-06 15:19:49 +11:00
Simon Olofsson
6d01138797 Fix vi key bindings regression when called with argument
Commit bbb2f0de8d added a ctrl-right binding to override the shared
binding with forward-word-vi for vi-compliance. However, it incorrectly
passed $argv which caused the error:
  "bind: cannot parse key 'default'"

when calling fish_vi_key_bindings with a mode argument like:
  fish_vi_key_bindings "default"

Fix that.

Co-Authored-By: Johannes Altmanninger <aclopte@gmail.com>

Closes #12413
2026-02-06 12:40:53 +11:00
Daniel Rainer
779f1371a1 l10n: localize colon followed by content
Some languages have different conventions regarding colons. In order to
handle this better in cases with non-constant strings, as is the case in
`describe_with_prefix`, use localization to figure out how colons should
be localized.

This approach fixes the extra whitespace inserted after Chinese colons.
See #12405.

Closes #12414
2026-02-06 12:05:03 +11:00
Daniel Rainer
5d24a846e3 cleanup: simplify pushing to WString
Part of #12414
2026-02-06 12:05:02 +11:00
madblobfish
c4f1c25a87 completion/rfkill: add completions for toggle command
"toggle" command was not considered in the completions

Closes #12412
2026-02-06 12:05:02 +11:00
Daniel Rainer
7d754b2865 ci: update before apt install
GitHub does not consistently provide images with up-to-date package
lists, causing `apt install` failures.
Work around this by updating the package list.
https://github.com/actions/runner-images/issues/13636
https://github.com/actions/runner-images/issues/12599

Closes #12418
2026-02-06 11:42:30 +11:00
Johannes Altmanninger
232e38f105 Fix rst syntax in changelog 2026-02-06 11:41:23 +11:00
Tristan Partin
725afc71fd Fix fish_mode_prompt formatting error
"variable" was in the formatted portion of the text when it should not
have been.

Signed-off-by: Tristan Partin <tristan@partin.io>
2026-02-05 07:14:21 +01:00
David Adam
6d60eaf04e CMake: use cargo variable to call cargo
Fixes the build where a custom cargo binary is passed in.
2026-02-03 11:10:34 +08:00
Johannes Altmanninger
7c1d8cf4f1 start new cycle
Created by ./build_tools/release.sh 4.4.0
2026-02-03 12:39:46 +11:00
Johannes Altmanninger
e1a0df68fc Release 4.4.0
Created by ./build_tools/release.sh 4.4.0
2026-02-03 12:11:51 +11:00
Johannes Altmanninger
14832117b6 release.sh: regression fixes
The error check in make_tarball.sh gives a false negative when building
from a release tag.
2026-02-03 12:10:40 +11:00
Johannes Altmanninger
56deaafb34 Remove extra newline in error log
Introduced in 149594f974 (Initial revision, 2005-09-20).
2026-02-03 11:58:52 +11:00
Johannes Altmanninger
b8b36f3293 de.po: fix typo 2026-02-03 11:58:52 +11:00
Daniel Rainer
a9ab708494 gettext: enforce trimmed msgids
Now that we have trimmed our msgids, add an assertion to ensure that
they stay trimmed. Note that we don't check msgstrs. We could do so when
building the maps which get put into the executable, but there we also
include messages originating from fish scripts, and there we don't
enforce trimmed messages, so limiting the checks to only messages
originating from the Rust code there would not be trivial.

Closes #12405
2026-02-03 11:58:52 +11:00
Daniel Rainer
a33363902c gettext: don't put tabs into localizable strings
This allows the strings to be simpler, and keeps
localization-independent formatting out of localizable strings.

Tab-based formatting is brittle, and should probably be reworked.

This is also the final piece to have no more leading or trailing
whitespace in our localizable strings in the Rust code.

Part of #12405
2026-02-03 11:58:52 +11:00
Daniel Rainer
3ca3d10e28 gettext: simplify localizable string
Only the first line needs to be localized, so don't include the rest for
localization.

Part of #12405
2026-02-03 11:58:52 +11:00
Daniel Rainer
05c236481f gettext: remove leading spaces
Now, non of the localizable strings in Rust have any more leading or
trailing spaces, making it easier to use them with Fluent. Again, there
are some slight issues with Chinese translations, which now have
additional whitespace.

Part of #12405
2026-02-03 11:58:52 +11:00
Daniel Rainer
25e2bdb7de gettext: remove trailing spaces
Another step towards trimming the localizable strings. Fix
inconsistencies in some of the translations. Translations for zh_CN are
not entirely consistent between using ASCII colons and `:`. If the
latter is used, which also happens for zh_TW, no trailing space is
present in the translation even if it is present in the msgid. This
means that the new code will show excessive whitespace after these
colons, since a regular space is inserted outside of the localization
code. While this might not be pretty, I don't think it really breaks
anything, and not having to deal with trailing whitespace simplifies
working with Fluent.

Part of #12405
2026-02-03 11:58:52 +11:00
Daniel Rainer
12bd4cf9e9 l10n: don't localize PID
This simplifies our table formatting. Since none of our translations
modify the string `PID`, it seems reasonable to assume that the term
does not benefit from localization.

Part of #12405
2026-02-03 11:58:52 +11:00
Daniel Rainer
0d79681070 gettext: remove remaining trailing newlines
Complete the work started in
e78e3f16e (gettext: remove trailing newlines, 2026-01-30)

Now, there are no remaining trailing newlines in the localizable strings
in our Rust sources. A bit more work is still needed to get rid of a few
leading and trailing spaces, the goal being that for all localizable
strings `s` in our Rust sources, `s == s.trim()`.

Includes a bit of drive-by refactoring.

Part of #12405
2026-02-03 11:58:52 +11:00
Daniel Rainer
47d7f52149 editorconfig: use indent_size=2 for Vagrantfiles
This matches the existing formatting of the files.

Part of #12408
2026-02-03 11:26:59 +11:00
Daniel Rainer
000a5cc80a format: add final newlines to files
Part of #12408
2026-02-03 11:26:59 +11:00
Daniel Rainer
66f51f5166 editorconfig: remove special handling for *.in
The file we have whose names end in `.in` are not Makefiles, so there is
no reason to deviate from our default formatting for them.

Part of #12408
2026-02-03 11:26:59 +11:00
Daniel Rainer
7360deee74 format: remove trailing whitespace
Part of #12408
2026-02-03 11:26:59 +11:00
Daniel Rainer
e0916e793b format: don't use tabs for indentation
This is done in accordance with our editorconfig file.

Part of #12408
2026-02-03 11:26:59 +11:00
Daniel Rainer
f43a79bc13 editorconfig: set indent_style=tab for makefiles
Replace mixed indentation in GNUmakefile used for alignment by a single
tab increase in indentation

Part of #12408
2026-02-03 11:26:59 +11:00
Daniel Rainer
fb56a6a54d editorconfig: set YAML indent_size to 2
This matches what we're already using, except for a few inconsistencies.

Part of #12408
2026-02-03 11:26:59 +11:00
Timen Zandbergen
f6936f222a Add strikethrough TextStyling
Closes #12369
2026-02-03 11:26:59 +11:00
Johannes Altmanninger
553aa40b34 Fix crash in Vi's "dge" due to misplaced cursor
kill-selection does not respect fish_cursor_end_mode; fix that.
2026-02-02 12:49:39 +11:00
Johannes Altmanninger
ac9c339e5a Vi bindings: remove unused special input functions
These were only needed before the new approach from 38e633d49b
(fish_vi_key_bindings: add support for count, 2025-12-16).
2026-02-02 12:33:50 +11:00
Johannes Altmanninger
0ae3b33f2f tarball: remove unused git-archive knob
The problem introduced by 081c469f6f (tarball: include
.cargo/config.toml again, 2026-01-13) seems solved by 09d8570922
(debian packaging: generate patches automatically, 2026-02-01),
so the argument to make_tarball.sh is no longer needed.
2026-02-02 12:22:45 +11:00
Johannes Altmanninger
6dbe5637ef redirect_tty_after_sighup: minor simplification 2026-02-02 11:25:03 +11:00
David Adam
09d8570922 debian packaging: generate patches automatically
The vendor tarball drops a new version of .cargo/config into place,
which the Debian toolchain does not like (as it is an unexpected
modification of the original tarball). Tell dpkg-source to generate a
patch automatically, as trying to do it in fish's packaging scripts is
brittle.
2026-02-01 22:59:12 +08:00
Johannes Altmanninger
4ab433418e Update changelog 2026-02-01 20:13:13 +11:00
Johannes Altmanninger
73e3b47ebd tarball: capture Git version
Commit 92dae88f62 (tarball: remove redundant "version" file,
2026-01-12) committed to using Cargo.toml as single source of truth
for our version string.

This works fine for tarballs built from a tag, but those built from
an arbitrary Git commit will show a misleading version ("4.3.3"
instead of something like "4.3.3-190-g460081725b5-dirty").

Fix this by copying the Git version to the tarball's Cargo.{toml,lock}.

It's not clear if we really need this (it's only for nightly builds)
so we might end up reverting this.

Ref: https://matrix.to/#/!YLTeaulxSDauOOxBoR:matrix.org/$BdRagDGCV-8yVjBs0i3QyWUdBK820vTmjuSBqgpsuJY

Note that OBS builds are probably still broken from 081c469f6f
(tarball: include .cargo/config.toml again, 2026-01-13); see
https://github.com/fish-shell/fish-shell/pull/12292#discussion_r2694000477
2026-02-01 20:13:13 +11:00
Johannes Altmanninger
bb7dce941a termios: use nix wrapper 2026-02-01 20:13:13 +11:00
Johannes Altmanninger
55e9255264 termios: remove repeated assignment to global 2026-02-01 19:58:51 +11:00
Johannes Altmanninger
cf53ef02c4 termios: simplify set-operations for toggling flow control 2026-02-01 19:58:23 +11:00
Johannes Altmanninger
c8ea418154 Update Cargo dependencies 2026-02-01 12:14:36 +11:00
Peter Ammon
44920b4be8 Pre-allocate certain string capacities
Reduce some allocation churn.
2026-01-31 10:51:33 -08:00
Tiago de Paula
b980e50acf __fish_complete_suffix: Remove –foo= from token
Otherwise we get --foo=--foo= suggested.

Fixes #12374. Related to #9538.

Closes #12400
2026-01-31 14:12:50 +11:00
Daniel Rainer
e78e3f16e1 gettext: remove trailing newlines
Remove trailing newlines from msgids. Newlines do not need to be
localized, so translators should not have to care about them.

In addition to simplifying the jobs of translators using gettext, not
having trailing newlines also makes it easier to port localizations to
Fluent.

Closes #12399
2026-01-31 14:12:50 +11:00
Johannes Altmanninger
714c2f7373 builtin commandline: minor refactoring 2026-01-31 14:12:50 +11:00
Johannes Altmanninger
f1036beb1f Use std::iter::once 2026-01-31 14:12:50 +11:00
Daniel Rainer
bd4e55b149 io: delete unused append_narrow function
Closes #12396
2026-01-31 14:12:50 +11:00
Daniel Rainer
22e5e63a65 io: remove append_char
Its functionality is subsumed by `append`. In some cases, the call can
be omitted and replaced by an `appendln`.

Part of #12396
2026-01-31 14:12:49 +11:00
Daniel Rainer
414dba994f io: accept intoCharIter in more functions
This makes them more general than the previous versions which expected
`&wstr`. It comes at the cost of additional eager calls to `chars()`.

To implement `appendln` without having to call `append` twice, implement
`IntoCharIter` for chained iterators whose elements are both the kind of
iterator specified by `IntoCharIter`.

Because `IntoCharIter` is not implemented for owned types to avoid
allocations, some call sites which used to pass `WString` need to be
updated to pass references instead, which constitutes the bulk of the
changes in this commit.

Part of #12396
2026-01-31 14:12:49 +11:00
xtqqczze
934def3b75 clippy fix semicolon_if_nothing_returned lint
https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned

Closes #12395
2026-01-31 14:12:49 +11:00
JANMESH ARUN SHEWALE
74c2837d87 dnf: support subcommand aliases in completions
Closes #12393
2026-01-31 14:12:49 +11:00
xtqqczze
40c480c2cf clippy: fix nightly while_let_loop lint
https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop

Closes #12391
2026-01-31 14:12:49 +11:00
Johannes Altmanninger
ad82c7ebf1 fish_update_completions: fix escaping of | and ~
Fixes #12345
2026-01-31 14:12:49 +11:00
xtqqczze
86b126628c fix: update SourceLineCache to use NonNull for last_source pointer
Closes #12401
2026-01-31 14:12:49 +11:00
Johannes Altmanninger
24ee3afdae Address MSRV clippy lint 2026-01-31 14:12:49 +11:00
Peter Ammon
08a9f5e683 Remove LineCounter
LineCounter was a funny type which aided in eagerly computing line
numbers. It's no longer needed as we lazily compute lines, so remove it.
2026-01-30 09:30:57 -08:00
Peter Ammon
ffb09a429d Lazily compute line numbers
Prior to this commit, the line number of each block (say, begin, or
function call, etc) was computed eagerly and stored in the block. However
this line number is only used in rare cases, when printing backtraces.

Instead of storing the line number, store the node in the abstract syntax
tree along with the (shared) source text, and only compute the line numbers
when a backtrace is required.

This is about a ~4% improvement on seq_echo benchmark.
2026-01-30 09:27:17 -08:00
Johannes Altmanninger
87048330b7 Update to Rust 1.93 2026-01-30 15:34:43 +11:00
andreacfromtheapp
c2f7c5fbeb feat: add catpuccin themes
Apologies about the unsolicited PR. I hope this helps.

Adding Catppuccin themes from: https://github.com/catppuccin/fish

They note:

Q: Where's the Latte theme?
A: All three themes contain Latte as the light variant. Install any of
them, and then set your system or terminal theme to light mode.

Not sure about Fish Shell policy to keep them up to date here, I hope
this is not a nuissance.

Closes #12299
2026-01-30 15:34:43 +11:00
Tiago de Paula
4fde7e4f2f completion/quilt: list predefined spec filters
Closes #12370
2026-01-30 15:34:43 +11:00
Tiago de Paula
3369cf9a70 completion/quilt: complete grep
Part of #12370
2026-01-30 15:34:43 +11:00
Tiago de Paula
ff5e83cd42 completion/quilt: complete diff context sizes
Part of #12370
2026-01-30 15:34:43 +11:00
Tiago de Paula
193a564c8f completion/quilt: add patch name suggestions
Part of #12370
2026-01-30 15:34:43 +11:00
Tiago de Paula
296e2369d7 completion/quilt: handle refresh -z[name]
Avoid suggestions like `-zh`, `-z=[name]` and `-z [name]`.

Part of #12370
2026-01-30 15:34:43 +11:00
Tiago de Paula
16cb7cc550 completion/quilt: add switches for 0.69
Add missing switch options for quilt, synced with the latest version.

Part of #12370
2026-01-30 15:34:43 +11:00
Tiago de Paula
c86b8c8130 completion/quilt: make subcommands exclusive
Includes the missing `revert` command.

Part of #12370
2026-01-30 15:34:43 +11:00
Tiago de Paula
1b1badfd02 completion/quilt: split commands
Part of #12370
2026-01-30 15:34:43 +11:00
Tiago de Paula
b7f990fe4a completion/quilt: update descriptions
Part of #12370
2026-01-30 15:34:43 +11:00
Razzi Abuissa
e639efb77c completions/git: add some completions for git rev-parse
I took the verbiage mostly from `man git-rev-parse` (slightly shortened
since space is at a premium for completions):

       --is-inside-git-dir
           When the current working directory is below the repository directory print "true", otherwise "false".

       --is-inside-work-tree
           When the current working directory is inside the work tree of the repository print "true", otherwise "false".

Closes #12382
2026-01-30 15:34:43 +11:00
Luc J. Bourhis
41c2f8b9e0 completions/pandoc: deprecate --self-contained, add replacements
Closes #12385
2026-01-30 15:34:43 +11:00
Johannes Altmanninger
78b33eb47b changelog 2026-01-30 15:34:43 +11:00
xtqqczze
7e1164762c clippy fix nightly map_unwrap_or lint
https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or

Closes #12376
2026-01-30 15:34:43 +11:00
Tiago de Paula
8a9203e8e3 completion/pacman: suggest absolute paths for -F
Closes #12347
2026-01-30 15:34:43 +11:00
Tiago de Paula
99cae405ca completion/pacman: completions for -S $repo/$package
Part of #12347
2026-01-30 15:34:43 +11:00
Tiago de Paula
f11c9c5105 completion/pacman: fix argument order
- fixed argument order for `-Sg`, `-Sl`, `-Qg`, `-Qp` and `-Qo`
- removed completions for `-Dk`
- added "Package Group" completions for `-Q` and `-R`
- remove package completions for `-F `

Part of #12347
2026-01-30 15:34:43 +11:00
Tiago de Paula
f8f98d2581 completion/pacman: dump options for --arch and -U
Add lists with most known archictures for Arch Linux Ports and
all possible options for PKGEXT. Also simplified option list
for `--color` and improved completions for `--config`.

Part of #12347
2026-01-30 15:34:43 +11:00
Tiago de Paula
e7648662ae completion/pacman: list only installed groups for -Qg
Part of #12347
2026-01-30 15:34:43 +11:00
Tiago de Paula
69c4021646 completion/pacman: update descriptions
Some description were slightly off from what they currently do.
Also added `disable-sandbox*` options for Pacman 7.

Part of #12347
2026-01-30 15:34:43 +11:00
Tiago de Paula
d0084cb9df completion/pacman: enforce consistent style
Keep the same argument order and string quoting across the file.
Also avoid variables for simple function calls.

Part of #12347
2026-01-30 15:34:43 +11:00
Tiago de Paula
5f59582e63 completion/pacman: use function to check main operation
Should be simpler and safer than double expansion of a loop variable.

Part of #12347
2026-01-30 15:34:43 +11:00
Tiago de Paula
c89c516467 completion/pacman: sort options like the man page
Should hopefully be easier to update options it in the future.

Part of #12347
2026-01-30 15:34:43 +11:00
Johannes Altmanninger
40bf0e0ce9 tests/man: skip if documentation has not been built
Closes #12291
2026-01-30 15:34:43 +11:00
xtqqczze
07e3e924c3 build(deps): bump actions/checkout to 6 2026-01-30 08:06:16 +08:00
Johannes Altmanninger
f2bb5b5d7f Work around intermittent test_complete failure
test_complete and test_history_races both have the #[serial]
annotations.  Still, "cargo test" sometimes fails with

   thread 'complete::tests::test_complete' (370134) panicked at             │
   src/complete.rs:2814:13:                                                 │
   assertion `left == right` failed                                         │
     left: [("TTestWithColon", false), ("history-races-test-balloon",       │
   true), ("test/", true)]                                                  │
    right: [("TTestWithColon", false), ("test/", true)]                     │

I don't understand why this happens (filesystem race condition?)
but let's fix it by having "test_complete" ignore files from other
tests.

Fixes #12184
2026-01-27 22:10:05 +11:00
Johannes Altmanninger
3bd6771ae7 screen: fix test name 2026-01-27 22:06:52 +11:00
Johannes Altmanninger
527176ca97 test_driver: reduce default concurrency to reduce flakiness
Unlimited concurrency frequently makes system tests fail when when run
as "tests/test_driver.py" or "cargo xtask check".  Add a workaround
until we fix them.

Use $(nproc), which is already the default for RUST_TEST_THREADS
and CARGO_BUILD_JOBS, and it works pretty reliably on my laptop.
It's possible that we can increase it a bit to make it faster,
but until the tests are improved, we can't increase it much without
risking failures.

Ref: https://github.com/fish-shell/fish-shell/pull/12292#discussion_r2713370474

Tracking issue: #11815
2026-01-27 22:06:52 +11:00
Daniel Rainer
0cd8d64607 nix: get user info without direct libc calls
The `get_home` function has no users, so delete it instead of rewriting
it.

Closes #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
68e408b930 nix: use gethostname
Part of #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
74b03de449 nix: use killpg
Part of #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
ac1c77d499 wutil: use std file utils instead of libc in test
Part of #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
c7aaf4249a wutil: use std::fs::rename
Part of #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
11dda63f7b wutil: use std::fs::read_link
Part of #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
04b799c0c5 nix: use access
Part of #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
44f9363e39 wutil: libc::unlink -> std::fs::remove_file
Part of #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
43513d3fca sleep: use stdlib instead of libc::usleep
Part of #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
e41f2f6588 nix: use fchdir
Part of #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
17d870a842 nix: allow converting fish Pid to nix Pid
Part of #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
ab02558181 nix: use waitpid for disowned pids
Use the `nix::sys::wait::waitpid` function instead of libc's `waitpid`
for reaping disowned PIDs.
Note that this commit changes behavior. Previously, any updates received
for the PID were interpreted as the process having exited. However,
`waitpid` also indicates that processes were stopped or continued, or,
on Linux, that a ptrace event occurred. If the process has not exited,
it cannot be reaped, so I think it should be kept in the list of
disowned PIDs.

Part of #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
fa1a128aeb nix: use getpgrp wrapper
Part of #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
1f0a7e7697 nix: use wrappers for geteuid and getegid
The nix crate offers thin wrappers around these functions which allow us
to get rid of our own libc wrappers, reducing the amount of code marked
`unsafe`.

Part of #12380
2026-01-27 21:50:54 +11:00
Daniel Rainer
cce788388f encoding: remove or annotate WString::from_str
We want to discourage direct conversion from regular Rust strings to
`WString`, since our `WString`s are assumed to use the PUA encoding
scheme. If the input string contains certain PUA codepoints and the
resulting `WString` is decoded, it would not result in the same bytes as
the UTF-8 encoding of the input string. To avoid this, use
`str2wcstring`.

There are two remaining usages of `WString::from_str` which have been
annotated to indicate why they are there.
2026-01-27 11:14:04 +01:00
Daniel Rainer
a93c24b084 refactor: improve widestring conversion utils
- Don't use `WString::from_str` for `str`s which are available at
  compile-time. Use `L!(input).to_owned()` instead. The reason for this
  is that `WString::from_str` can cause problems if the input contains
  PUA bytes which we use for our custom encoding scheme. In such cases,
  `bytes2wcstring` should be used, to avoid problems when decoding the
  `WString`. Removing harmless usages of `WString::from_str` allows us
  to focus on the potentially dangerous ones which don't convert
  `str`'s that are compiled into the binary.
- Make `cstr2wcstring` actually take `CStr` as its input. The former
  version was only used in one place, and the conversion to `CStr`
  should happen there, where it can be checked that the conversion makes
  sense and is safe. The new version is used in
  `src/env/environmant.rs`, to avoid `to_bytes()` calls cluttering the
  code there.
- Add `osstr2wcstring` function. This function also works for `Path`.
  Now, these types can be converted to widestrings with much less
  syntactic clutter.
2026-01-27 11:14:04 +01:00
Johannes Altmanninger
6a2f531f9b try_apply_edit_to_autosuggestion: update icase matched codepoint counter
This function mutates the autosuggestion's search_string_range without
updating the number of matched codepoints accordingly, fix that.
Fixes 78f4541116 (reader: fix try_apply_edit_to_autosuggestion false
positive, 2026-01-22).

Fixes #12377
2026-01-26 02:22:48 +11:00
Johannes Altmanninger
eda1694581 try_apply_edit_to_autosuggestion: new test 2026-01-26 02:22:48 +11:00
Johannes Altmanninger
1854d03b95 wildcard: remove unneeded lock for local variable 2026-01-26 02:22:48 +11:00
xtqqczze
2180777f73 use nix::sys::signal::SigSet
Closes #12335
2026-01-25 03:40:12 +01:00
xtqqczze
f2fc779c94 Update feature(let_chains) TODO
Closes #12318
2026-01-25 03:40:12 +01:00
xtqqczze
3a024b6d8f Inline LazyLock helpers
Closes #12333
2026-01-25 03:40:12 +01:00
Next Alone
33d9957bcb completions: add claude command completions
Add tab completion support for claude CLI tool, including:
  - Top-level commands (doctor, install, mcp, plugin, setup-token, update)
  - Global options for model, agent, system prompt configuration
  - Tool and permission management options
  - MCP server configuration
  - IDE and Chrome integration settings
  - Output format and session management options
  - Sub-command specific help

Closes #12361
2026-01-25 03:40:12 +01:00
Daniel Rainer
28c0a8bc81 path: de-duplicate getter logic
Also remove comments which were already obsolete before these changes.

Closes #12365
2026-01-25 03:40:12 +01:00
Daniel Rainer
77471c500e path: use std::io::Error instead of raw ints
The functions we use already give us errors from `std`, so we might as
well use them, instead of converting them to raw libc errors.

Part of #12365
2026-01-25 03:40:12 +01:00
Johannes Altmanninger
98eaef2778 reader: remove obsolete workaround
We no longer use libc's locale-aware tolower etc.
2026-01-25 03:40:12 +01:00
Johannes Altmanninger
78f4541116 reader: fix try_apply_edit_to_autosuggestion false positive
Given command line ": i" and suggestion ": İnstall" whose lowercase
mapping is ": i\u{307}nstall", most of our code assumes that typing
"n" invalidates the autosuggestion.

This doesn't happen because try_apply_edit_to_autosuggestion thinks
that "i" has fully matched the suggestion's "İ".

Fix this inconsistency by recording the exact number of lowercase
characters already matched in the suggestion; then we only need to
compare the rest.

This allows us to restore an important invariant; this reverts
1d2a5997cc (Remove broken assert, 2026-01-21).
2026-01-25 03:40:12 +01:00
Johannes Altmanninger
2a3fe73a6d reader: unit-test try_apply_edit_to_autosuggestion 2026-01-25 03:38:11 +01:00
Johannes Altmanninger
9c3cb154d3 Revert "reader: Remove two more case-insensitive asserts"
See following commits.
This reverts commit 0778919e7f.
2026-01-25 03:38:11 +01:00
Johannes Altmanninger
4e68a36141 terminal: name some enum fields 2026-01-25 03:19:22 +01:00
Daniel Rainer
b62fa06753 asan: remove redundant handling
The special exit handling when running with address sanitization no
longer seems necessary. Our tests all pass without it.

Similarly, the leak sanitizer suppression is no longer needed, since we
don't get any warnings when running our checks without it.

Because our Rust code no longer has any ASAN-specific behavior, we don't
need the `asan` feature anymore.

Closes #12366
2026-01-25 03:19:22 +01:00
xtqqczze
17129f64db assertions: use assert_matches!
Closes #12348
2026-01-25 03:19:22 +01:00
Johannes Altmanninger
524c73d8b7 history search: remove unused "match everything" search type
The "match everything" search type is weird because it implicitly
overrules SearchFlags::IGNORE_CASE and the search string.

Remove it.
2026-01-25 03:19:22 +01:00
Johannes Altmanninger
78e91e6bc4 history search: prune call graph 2026-01-25 03:15:33 +01:00
Johannes Altmanninger
9d260cac0b reader autosuggestion: doc comments 2026-01-25 03:14:43 +01:00
Johannes Altmanninger
01d91dd65c history: unexport test-only constructors 2026-01-25 03:14:43 +01:00
xtqqczze
69d96e1e66 build.rs: inline fn canonicalize 2026-01-24 16:05:07 -08:00
Peter Ammon
1ab12dadac Remove "parse_util" prefix from functions in that module
This was a naming holdover from C++
2026-01-24 14:35:46 -08:00
Peter Ammon
a7dc701f26 Make ParseTreeFlags an ordinary struct instead of bitflags
Simplifies some code.
2026-01-24 14:05:34 -08:00
Peter Ammon
590ad9cd93 Rename var_err_len into VAR_ERR_LEN 2026-01-24 14:05:34 -08:00
Peter Ammon
cbca5177ea Adopt get_or_insert_with_default()
Minor cleanup of path.rs
2026-01-24 12:21:03 -08:00
Peter Ammon
e3105bee39 Fix out-of-tree builds
This fixes cmake builds outside of the fish-shell source tree.
2026-01-24 12:05:14 -08:00
Fabian Boehm
0778919e7f reader: Remove two more case-insensitive asserts
See #12326. Turns out there wasn't just one assert, it was three.

These can be triggered by entering (interactively) "flatpak in"
after having "flatpak İnstall" in your history so it's autosuggested.

Removing the asserts it generally *works* okay, and there's absolutely
no good reason for turning this into a crash.
2026-01-24 13:41:46 +01:00
Fabian Boehm
e36684786f completions/systemctl: List disabled units for start as well
I'm pretty sure this used to list "dead", now it shows "disabled".

Fixes #12368
2026-01-22 06:52:22 +01:00
Fabian Boehm
8fcd6524db Remove unused variable 2026-01-21 17:47:43 +01:00
Daniel Rainer
23fb01f921 cmake: don't build fish_test_helper
Our test driver (`tests/test_driver.py`) already builds this
unconditionally on each run, so there is no point in having CMake build
the binary as well. If we want to reduce the effort of rebuilding the
test helper on each invocation of the test driver, we could consider
some other caching approach, but it should work for non-CMake builds as
well. I consider this a low priority, since building the executable only
takes a few 10s of milliseconds on relatively modern hardware.

Closes #12364
2026-01-21 17:43:26 +01:00
Daniel Rainer
14915108d1 xtask: add man-pages task
Use the new xtask in CMake builds.

Closes #12292
2026-01-21 17:43:26 +01:00
Daniel Rainer
20cc07c5cd xtask: add html-docs task
This task is a bit annoying to implement because `sphinx-build` depends
on `fish_indent`, so that needs to be built, and the binary location
added to `PATH`.

Building `fish_indent` can be avoided by setting the `--fish-indent`
argument to the path to an existing `fish_indent` executable.

Use the new xtask in CMake builds. To do so without having to add
configuration options about where to put the docs, or having to copy
them from the default location to `build/user_doc`, we instead get rid of
the `user_doc` directory and change the code which accesses the docs to
read them from the non-configurable default output location in Cargo's
target directory.

Part of #12292
2026-01-21 17:43:26 +01:00
Daniel Rainer
2f74785620 xtask: add initial setup
This is an initial implementation of the cargo xtask approach
https://github.com/matklad/cargo-xtask

For now, the only xtask is "check", which can be triggered by running
`cargo xtask check`. It is a thin wrapper around `build_tools/check.sh`.

Part of #12292
2026-01-21 17:43:26 +01:00
Daniel Rainer
03894fea3c cmake: remove unnecessary dir nesting
The `cargo` directory in the CMake build directory is only used to store
Cargo's build output, as would be done by `target` when building without
CMake. But instead of putting the build output directly into the `cargo`
directory, a nested `build` directory was used. There is no point in
this nesting, so remove it.

Closes #12352
2026-01-21 17:43:26 +01:00
Daniel Rainer
f407ca18a4 docs: fix doc dir computation
`FISH_CMAKE_BINARY_DIR` is the top-level CMake output directory, not its
subdirectory used as the target directory by Cargo. So far, this has not
caused issues because CMake builds explicitly call `sphinx-build` to
build the man pages, instead of using the Rust crate for embedding them.

Closes #12354
2026-01-21 17:43:26 +01:00
Daniel Rainer
07394a1621 style.fish: canonicalize workspace root
Use a canonicalized absolute path for the workspace root to make errors
containing paths easier to read.

Closes #12359
2026-01-21 17:43:26 +01:00
Fabian Boehm
8bf416cd64 completions/flatpak: Fix broken cache usage
This can't work because __fish_cached calls out to /bin/sh for fairly dubious reasons,
and it tries to get it to run a function.

So just run the *command* that the function runs and do the added filtering outside.
2026-01-21 17:31:20 +01:00
Fabian Boehm
1d2a5997cc Remove broken assert
See #12326

I have been able to trigger this pretty reliably, and the simplest fix is
to... just not assert out when we would return anyway.

It doesn't reproduce with `commandline` because it needs the
suggestion to exist, it'll happen when you enter the "n" of "install"
if the suggestion is "İnstall" (i.e. uppercase turkish dotted i)

In general asserts in the reader make for a terrible experience.
2026-01-21 17:28:14 +01:00
Daniel Rainer
53e6758cc3 cmake: use variable for sphinx output dir
This clarifies the usage of the directory, simplifies usages, and makes
it easier to move the directory.

Closes #12353
2026-01-20 16:24:56 +00:00
Fabian Boehm
2df1fa90f1 completions/cargo: Uncouple from rustup completions
We could technically extract this into a function, but it's a trivial
one-liner.

This allows rustup completions to be independently overridden.

Fixes #12357
2026-01-20 16:43:54 +01:00
neglu
a454d53c28 realpath: allow multiple arguments
Closes #12344
2026-01-18 13:25:00 +01:00
xtqqczze
6d5948d814 update sphinx-doc url
Closes #12346
2026-01-18 13:25:00 +01:00
xtqqczze
18061ad177 refactor list_available_languages
remove the unnecessary temporary HashSet

Closes #12343
2026-01-18 13:25:00 +01:00
xtqqczze
8eb59bc500 clippy: fix stable_sort_primitive lint
https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive

Part of #12343
2026-01-18 13:25:00 +01:00
xtqqczze
ddb3046ccd clippy: fix clippy::manual_assert lint
https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert

Closes #12341
2026-01-18 13:25:00 +01:00
xtqqczze
5e3bc86268 clippy: fix explicit_into_iter_loop lint
https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop

Closes #12340
2026-01-18 13:25:00 +01:00
xtqqczze
9dfb5705c5 Remove unnecessary qualification from size_of
Closes #12339
2026-01-18 13:25:00 +01:00
xtqqczze
66ea0f78be Use inline const expressions
Inline const expressions were stablised in Rust 1.79

Part of #12339
2026-01-18 13:25:00 +01:00
xtqqczze
c99c84558f clippy: fix format_push_string lint
https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string

Closes #12338
2026-01-18 13:25:00 +01:00
Daniel Rainer
d35edcf5fa assertions: use assert_ne! where possible
Closes #12336
2026-01-18 13:25:00 +01:00
Daniel Rainer
8718d62b11 assertions: use assert_eq! where possible
Using `assert_eq!` instead of `assert!` has the advantage that when the
assertion fails, the debug representation of both sides will be shown,
which can provide more information about the failure than only seeing
that the assertion failed.

Part of #12336
2026-01-18 13:25:00 +01:00
Fabian Boehm
939fa25a5b Disable fossil prompt by default
Apparently `fossil changes --differ` is slow.

Fixes #12342
2026-01-17 15:53:41 +01:00
Daniel Rainer
93eb9ee4d8 wgetopt: extract into new crate
Move `src/wgetopt.rs` into a new dedicated crate.

Closes #12317
2026-01-16 16:22:34 +01:00
Daniel Rainer
154c095e66 wgetopt: remove dependencies on main lib
Only items from `fish_widestring`'s prelude are used, so depend on that
directly, to prepare for extraction.

Part of #12317
2026-01-16 16:22:33 +01:00
Johannes Altmanninger
50a97856dc Update rust-embed 2026-01-16 16:09:13 +01:00
Daniel Rainer
02cb93311a docs: use shared output dir for all docs
Prepare for having HTML docs in the build output as well as man pages.
To avoid cluttering the top-level build dir, introduce a new `fish-docs`
directory, and put directories for the different types of docs in it.

The doctrees (cache files for sphinx) will be put parallel to the output
directories, to have a clear separation between desired output and cache
files. Note that we use separate cache directories for the different
builders, since sphinx does not handle shared caches well.

Part of #12292
2026-01-16 11:40:47 +01:00
xtqqczze
49b3721b75 clippy: fix unnecessary_semicolon lint
https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_semicolon

Closes #12328
2026-01-16 11:40:47 +01:00
xtqqczze
db18515aed clippy: fix range_plus_one lint
https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one

Closes #12329
2026-01-16 11:40:44 +01:00
xtqqczze
2708191d3c clippy: fix ref_option lint
https://rust-lang.github.io/rust-clippy/master/index.html#ref_option

Closes #12327
2026-01-16 11:39:22 +01:00
xtqqczze
16612c6e49 Use Rust new_uninit feature
new_uninit was stabilised in Rust 1.82

Closes #12323
2026-01-16 11:39:22 +01:00
xtqqczze
40d45ee21e clippy: fix ptr_offset_by_literal lint
https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_by_literal

Closes #12322
2026-01-16 11:39:22 +01:00
xtqqczze
3df3b52b18 clippy: fix mut_mut lint
https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut

Closes #12321
2026-01-16 11:39:22 +01:00
xtqqczze
efbf8b0203 Inline have_proc_stat
Closes #12319
2026-01-16 11:39:22 +01:00
Johannes Altmanninger
c84c006f42 util: remove code clone
Not in the right module yet but it's a start.
2026-01-16 11:39:22 +01:00
Daniel Rainer
97e0eda477 util: extract into new crate
Move `src/util.rs` into a new dedicated crate.

Closes #12316
2026-01-15 17:26:29 +01:00
Daniel Rainer
f1d78103e4 util: remove dependencies on main crate
Only items from `fish_widestring`'s prelude are used, so depend on that
directly, to prepare for extraction.

Part of #12316
2026-01-15 17:22:08 +01:00
Daniel Rainer
7072eec225 wcstringutil: extract into new crate
This moves the code from `src/wcstringutil.rs` into a new dedicated
crate.

Closes #12315
2026-01-15 17:20:07 +01:00
Daniel Rainer
9b1cda9025 wcstringutil: remove dependencies on main lib
This is done in preparation for extraction into its own crate.

Part of #12315
2026-01-15 17:19:00 +01:00
Daniel Rainer
2c06731a14 color: extract into new crate
This moves the code from `src/color.rs` into a new dedicated crate.

Closes #12311
2026-01-15 17:17:40 +01:00
Daniel Rainer
b14692cb95 color: be explicit about widestring usage
This eliminates the dependency on the main library crate, preparing
`src/color.rs` for extraction into a dedicated crate.

Part of #12311
2026-01-15 17:17:40 +01:00
Daniel Rainer
253f6726c0 widestring: rename wchar -> widestring
This is a more descriptive name for the crate.

https://github.com/fish-shell/fish-shell/pull/12311#discussion_r2681191208

Closes #12313
2026-01-15 17:17:40 +01:00
Daniel Rainer
36db3b7f3f wchar: remove dependency on fish_common
Now, the `fish_wchar` crate does not have any local dependencies, making
it easy to depend on it in other crates without worrying about cyclic
dependencies.

Additionally, remove the (non-fish) `widestring` crate as a direct
dependency of the main crate.
Now, only the `fish_wchar` and `fish_printf` crates directly depend on
`widestring`. `fish_printf` could also depend on `fish_wchar`, but I
left that as is since `fish_printf` was published, so depending on a
crate which is not published to crates.io does not seem like a good
idea.

Part of #12313
2026-01-15 17:17:20 +01:00
Daniel Rainer
385cdef89b wchar: don't wrap utf32str macro
There is no need to wrap this macro. We can just introduce `L` as an
alias.

https://github.com/fish-shell/fish-shell/pull/12311#discussion_r2681174391

Closes #12312
2026-01-15 17:07:13 +01:00
Johannes Altmanninger
72870d8331 forward-char: fix buffer overflow
Fixes 701c5da823 (forward-char: respect fish_cursor_end_mode again,
2026-01-12).

Fixes #12325
2026-01-14 18:28:04 +01:00
Johannes Altmanninger
081c469f6f tarball: include .cargo/config.toml again
The problem worked around by commit e4674cd7b5 (.cargo/config.toml:
exclude from tarball, 2025-01-12) is as follows:
our OBS packages are built by doing something like

	tar xf $tarball && tar xf $vendor_tarball

except that the tool apparently break when a file is present in
both tarballs.

Our workaround is to not include .cargo/config.toml in the tarball.

The workaround seems too broad. It need not affect all tarballs
but only the ones used by OBS. We want to add xtask aliases to
.cargo/config.toml. They will be used by CMake targets (sphinx-doc),
so they should be available to tarball consumers.

Restrict the scope of the workaround: add back .cargo/config.toml
to the export, and allow opting in to this behavioer by passing a
negative pathspec

	build_tools/make_tarball.sh :/!.cargo/config.toml
2026-01-14 18:28:04 +01:00
phanium
1affa7a16e bind: nextd-or-forward-word/prevd-or-backward-word don't emit nextd/prevd
Fixes bbb2f0de8d (feat(vi-mode): make word movements
vi-compliant,2026-01-10)

Closes #12320
2026-01-13 11:59:48 +01:00
Johannes Altmanninger
701c5da823 forward-char: respect fish_cursor_end_mode again
Fixes bbb2f0de8d (feat(vi-mode): make word movements vi-compliant,
2026-01-10).

When setting cursor pos, we need to make sure to call update_buff_pos,
which knows whether the one-past-last character ought to be selectable.
2026-01-13 11:59:48 +01:00
Daniel Rainer
860f75ee97 tarballs: append to cargo config
This allows putting other configuration into the config file without it
being deleted for tarballs.

https://github.com/fish-shell/fish-shell/pull/12292#discussion_r2678697643
2026-01-12 19:49:32 +01:00
Daniel Rainer
334710ebf7 cargo: remove obsolete config
The settings we had in `.cargo/config.toml` correspond to the defaults
in Rust 2024, so there is no point in setting them explicitly.

https://github.com/fish-shell/fish-shell/pull/12292#discussion_r2678697643
2026-01-12 19:49:32 +01:00
phanium
b15da3e118 bind: forward-word to go to end of line again
Fixes bbb2f0de8d (feat(vi-mode): make word movements vi-compliant,
2026-01-10)

Closes #12314
2026-01-12 19:40:45 +01:00
David Adam
f1a9b9bf43 CHANGELOG: editing for clarity and formatting 2026-01-12 23:10:01 +08:00
David Adam
abffe983f8 Debian packaging: correct sphinx dependency name 2026-01-12 22:19:42 +08:00
Johannes Altmanninger
b363e4afe7 tests/checks/version: support shallow clones 2026-01-12 13:04:56 +01:00
Johannes Altmanninger
b60582ff75 build.rs: remove code duplication in version computation
get_version() in build.rs duplicates some logic in
build_tools/git_version_gen.sh. There are some differences

1. When computing the Git hash, get_version() falls back to reading
  .git/HEAD directly if "git describe" fails
1.1. Git is not installed
     Not sure if this is a good strong reason. If you don't have
     Git installed, how would you have created ".git"? If the exact
     Git SHA is important, maybe we should use something like gitoxide
     for this rather than implementing our own.
1.2. there is a permission problem
     The case mentiond in 0083192fcb (Read git SHA ourselves if it
     is unavailable, 2024-12-09) doesn't seem to happen with current
     versions of Git: "sudo git describe" works fine.  Something like
     "sudo -u postgres git describe" doesn't. We could support that
     but let's wait until we know of a use case.
1.3 there are no tags
    (when doing "cargo install --git", as mentioned in 0dfc490721
    (build.rs: Use Cargo_PKG_VERSION if no version could be found,
    2024-06-10)).
    Missing tags are no longer a problem because we read the version
    from Cargo.toml now. Tweak the script to make sure that the
    version is 4.3.3-g${git_sha} instead of just ${git_sha}.

2. get_version() falls back to jj too.
   That was added for jj workspaces that don't have a Git worktree;
   but those should be one their way out; when using jj's Git backend,
   all workspaces will get an associated worktre.
2026-01-12 12:17:48 +01:00
Johannes Altmanninger
92dae88f62 tarball: remove redundant "version" file
Use the version in Cargo.toml instead.

Print a warning if the Cargo.toml version is not a prefix of the Git
version.  This can happen legit scenarios, see 0dfc490721 (build.rs:
Use Cargo_PKG_VERSION if no version could be found, 2024-06-10)
but the next commit will fix that.

Also remove stale comments in git_version_gen.sh.
2026-01-12 12:17:48 +01:00
Johannes Altmanninger
f7d9c92820 git_version_gen: don't silence stderr
This should never fail, and error output doesn't hurt here.
2026-01-12 12:17:48 +01:00
Daniel Rainer
860bba759d git_version_gen: always output to stdout
Now that the script no longer writes to files, we can remove the
`--stdout` flag and instead output to stdout by default.

Closes #12307
2026-01-12 12:17:27 +01:00
Daniel Rainer
564ef66665 git_version_gen: remove obsolete code
The `git_version_gen.sh` script is no longer used to write any files, so
remove the logic for it.

This code included handling for permission problems when running `git
describe`. It was introduced by
15f1b5f368, but unfortunately without
mentioning which CVE caused the change in git. I found CVE-2022-24765,
which was published not long before the commit was made, and its
description looks like it's fitting. On a recent version of git
(2.52.0), I had no problems running `make && sudo make install`, so it
seems like this issue might no longer be relevant. One suboptimal thing
to note is that `sudo make install` currently builds `fish_indent` even
if it was built via `make` before, which is not great, but unrelated to
these changes.

Part of #12307
2026-01-12 12:17:27 +01:00
Daniel Rainer
c8642efeb0 cmake: remove version file handling
This version file handling is no longer in use. Version strings are
generated on-demand using either `build_tools/git_version_gen.sh` or the
Rust implementation in `build.rs`. This makes the CMake code for version
handling obsolete.

Note that the current handling for version strings in Rust builds is
imperfect. If none of the build inputs change, the Rust code will not be
rebuilt, meaning the version strings in the executables are not updated.
This has been the case for a while and is not caused by this patch
series. This trade-off has been deemed worthwhile, because it simplifies
the implementation and eliminates the need for rebuilds when only the
version string changed. Because any changes to the actual input files
will trigger rebuilds, the version string will reference a commit which
is close enough to the actual version that it should not cause problems.

Part of #12307
2026-01-12 12:17:27 +01:00
Daniel Rainer
309bb778af cmake: use script for setting version in fish.pc
The `build_tools/git_version_gen.sh` script can be used to determine the
appropriate version string. With this change, the convoluted version
file generation logic in CMake is no longer used and can subsequently be
removed.

Part of #12307
2026-01-12 12:17:27 +01:00
Daniel Rainer
36c340b40f cmake: remove obsolete version env var
The `FISH_BUILD_VERSION_FILE` variable was only read in
`doc_src/conf.py`. There, it can be replaced by the
`build_tools/git_version_gen.sh` script, which takes the version from a
file called `version` at the workspace root if one exists, and otherwise
from git. This should cover all cases where the docs are built, so there
is no need to keep using the `FISH_BUILD_VERSION_FILE` variable.

Part of #12307
2026-01-12 12:17:27 +01:00
Johannes Altmanninger
a79c54be66 shell_modes: clear the FLUSHO flag
On macOS, pressing ctrl-o (VDISCARD) before starting fish will discard
all terminal output, from shell or its child processes. This breaks
querying and seems like something we don't want to support, so maybe
disable it?

Not sure if term_fix_external_modes needs it too, add it I guess.

Fixes #12304
2026-01-12 12:17:27 +01:00
Johannes Altmanninger
143c8c4ffd termios: slim down reference graph 2026-01-12 09:44:42 +01:00
Daniel Rainer
116bcdac28 common: extract more code into separate crate
This extracts the remaining code from `src/common.rs` which does not
depend on other parts of the main library crate. No functional changes.

Closes #12310
2026-01-12 09:44:42 +01:00
Daniel Rainer
4762d6a0a7 common: extract constants into crate
Part of #12310
2026-01-12 07:37:23 +01:00
Daniel Rainer
6e00deffd0 common: remove unused constant
There was only a single usage of `EMPTY_STRING`, and there it was
immediately dereferenced, so use an empty static `&wstr` instead and
remove the `WString` constant.

Closes #12309
2026-01-12 07:34:23 +01:00
Johannes Altmanninger
34f3e5ef23 __fish_change_key_bindings: remove dead code
We embed share/functions/*.fish now, so we should be able to assume
that "set --no-event" works.
2026-01-11 21:12:40 +01:00
Johannes Altmanninger
b9dfbcee13 Fix spurious status=1 from calling binding function from config
A command like "fish -C fish_default_key_bindings" shows a exit status
of 1 in the default prompt. Fix that.
2026-01-11 21:12:40 +01:00
Johannes Altmanninger
de2ac37c92 Extract typedef for AtomicU64 2026-01-11 21:12:40 +01:00
Daniel Rainer
a194a557c5 cmake: remove obsolete doc setup
The `conf.py` file that was copied is not used for building. It seems
that it was used as a mechanism for triggering rebuilds on changes to
the original file, but in the current setup, `sphinx-build` is called
unconditionally when the relevant targets are built, so there is no
point in keeping on copying the `conf.py` file.

Closes #12303
2026-01-11 21:12:40 +01:00
iksuddle
8e34dc4cdb bind: show all modes by default
Ensure `bind` builtin lists binds for all modes if `--mode` is not
given.

- The `get` function in `src/input.rs` now takes an optional bind
  mode and returns a list of input mappings (binds).
- The `list_one` function in `src/builtins/bind.rs` lists binds in
  the results returned by `get`.
- Creating the output string for a bind has been extracted to its
  own function: `BuiltinBind::generate_output_string`.
- The `bind_mode_given` option has been removed.

Fixes #12214

Closes #12285
2026-01-11 21:12:40 +01:00
SharzyL
bbb2f0de8d feat(vi-mode): make word movements vi-compliant
- The behavior of `{,d}{w,W}`, `{,d}{,g}{e,E}` bindings in vi-mode is
  now more compatible with vim, except that the underscore is not a
  keyword (which can be achieved by setting `set iskeyword-=_` in vim).

- Add commands `{forward,kill}-{word,bigword}-vi`,
  `{forward,backward,kill,backward-kill}-{word,bigword}-end` and
  `kill-{a,inner}-{word,bigword}` corresponding to above-mentioned
  bindings.

- Closes #10393.

Closes #12269

Co-authored-by: Johannes Altmanninger <aclopte@gmail.com>
2026-01-11 21:12:40 +01:00
SharzyL
4c3fcc7b16 feat(wchar): add word_char module for vi-mode character classification
Part of #12269
2026-01-11 21:12:40 +01:00
Johannes Altmanninger
f33fef3ca3 word_motion: rewrite state machine logic
The state machine uses polymorphic state stored as a raw u8.  This is
not very idiomatic Rust.

Define proper enum types. This helps the upcoming behavior change.

Ref: https://github.com/fish-shell/fish-shell/pull/12269#discussion_r2679005387

Co-authored-by: SharzyL <me@sharzy.in>
2026-01-11 21:12:40 +01:00
Johannes Altmanninger
10537df82d word_motion: remove unused function 2026-01-11 21:12:40 +01:00
SharzyL
8af6b07d07 word_motion: tighten test assertions 2026-01-11 21:12:40 +01:00
SharzyL
c014c95e1b word_motion: deduplicate type
Co-authored-by: Johannes Altmanninger <aclopte@gmail.com>
2026-01-11 21:12:40 +01:00
Johannes Altmanninger
bd86f53b9f Extract MoveWordStateMachine to a reader submodule
This has a very small dependency on the tokenizer.
2026-01-11 21:12:40 +01:00
SharzyL
042117ee30 refactor(tokenizer): use unqualified names in word motion tests
Add `use Direction::*` and `use MoveWordStyle::*` in tests to reduce
verbosity. Reformat tests to one-line style and reorder by test type.
No behavior change.

Part of #12269
2026-01-11 18:37:14 +01:00
Heitor Augusto
38e633d49b fish_vi_key_bindings: add support for count
Closes #12170
2026-01-11 18:37:14 +01:00
Peter Ammon
ce286010a8 Remove a stale comment 2026-01-10 09:52:18 -08:00
Wang Bing-hua
ef18b6684b history: fix highlighting for inline timestamps
Highlighting the entire record caused custom prefixes to be
parsed as command syntax. For example, using:
`history --show-time="[%Y-%m-%d %H:%M:%S] "`
resulted in the timestamp being colorized as shell code.

Move highlighting inside format_history_record to process the
command string before the timestamp is prepended.

Closes #12300
2026-01-10 13:46:46 +01:00
Ilya Grigoriev
ab984f98ab completions: add completions for ijq
https://codeberg.org/gpanders/ijq
https://github.com/gpanders/ijq

For comparison purposes, here's the output of `ijq --help`
as of ijq 1.2.0:

```
ijq - interactive jq

Usage: ijq [-cnsrRMSV] [-f file] [filter] [files ...]

Options:
  -C	force colorized JSON, even if writing to a pipe or file
  -H string
    	set path to history file. Set to '' to disable history. (default "/Users/ilyagr/Library/Application Support/ijq/history")
  -M	monochrome (don't colorize JSON)
  -R	read raw strings, not JSON texts
  -S	sort keys of objects on output
  -V	print version and exit
  -c	compact instead of pretty-printed output
  -f filename
    	read initial filter from filename
  -hide-input-pane
    	hide input (left) viewing pane
  -jqbin string
    	name of or path to jq binary to use (default "jq")
  -n	use `null` as the single input value
  -r	output raw strings, not JSON texts
  -s	read (slurp) all inputs into an array; apply filter to it
```

Closes #12297
2026-01-10 13:46:46 +01:00
Johannes Altmanninger
cfadb2de36 Temporary hack to restore historical behavior of "read --prompt-str ''"
As mentioned in commit 289057f981 (reset_abandoning_line: actually
clear line on first prompt, 2025-11-11), we want to eventually allow
builtin read with a starting cursor with x>0.  Until then, add a hack
to restore historical behavior in the case that users observed.

See #12296
2026-01-10 13:46:46 +01:00
Johannes Altmanninger
3d0b378c40 test_wwrite_to_fd: remove redundant assertion
Also should have been -1, not 0.
Ref: https://github.com/fish-shell/fish-shell/pull/12199#discussion_r2677040787
2026-01-10 13:46:46 +01:00
Daniel Rainer
aa56359834 gettext: use wgettext_fmt for formatting
Using `wgettext_fmt` instead of `wgettext` + `sprintf` in these cases
allows for proper localization of the colons.
2026-01-10 13:46:46 +01:00
Johannes Altmanninger
a0a60c2d29 completions/git: cover up some plumbing subcommands
Also hide show-branch, it doesn't seem very useful?

See 400d5281f4 (r174249792)
2026-01-10 13:46:46 +01:00
Johannes Altmanninger
324223ddff history: assume pager supports color sequences
Git does the same, try: git -c core.pager='cat -v' show

Closes #12298
2026-01-10 13:46:46 +01:00
Johannes Altmanninger
58e7a50de8 Fix line-wise autosuggestion false positive when line doesn't start command
To reduce the likelihood of false positive line-wise history
autosuggestions, we only them when the cursor's line starts a new
process ("parse_util_process_extent").

There are still false positives. Given

	$ true &&
          somecommand
	$ echo "
	someothercommand
	"

typing "some" suggests "someothercommand" from history even though
that was not actually used as command.

Fix this by using similar rules for suggestion candidates.

Might help #12290
2026-01-10 13:46:46 +01:00
Johannes Altmanninger
739b82c34d Fix line-wise autosuggestion false positive when command doesn't exist
If my history has

	git clean -dxf &&
	./autogen.sh &&
	./configure --prefix=...

then autosuggestions for "./conf" will show the third line, even if
./configure does not exist.

This is because even for line-wise autosuggestions, we only check
validity of the first command ("git"). Fix that by checking
the command from the line that's actually suggested.

The next commit will fix the issue that line-wise autosuggestions
may not actually be commands.

See also #12290
2026-01-10 13:46:46 +01:00
Johannes Altmanninger
b5bf9d17e3 OSC 7: also escape hostname
I think gethostname() is not guaranteed to return only URL-safe
characters, so better safe than sorry.
2026-01-10 13:46:46 +01:00
Johannes Altmanninger
917fb024ea end-of-buffer: accept autosuggestion if already at ned 2026-01-09 10:29:10 +01:00
Johannes Altmanninger
7a05ea0f93 Reapply "cmake: rename WITH_GETTEXT to WITH_MESSAGE_LOCALIZATION"
This reverts commit 94fdb36f6b.
2026-01-07 08:56:03 +01:00
Johannes Altmanninger
5d75c47fcc start new cycle
Created by ./build_tools/release.sh 4.3.3
2026-01-07 08:52:39 +01:00
Johannes Altmanninger
c98fd886fd Release 4.3.3
Created by ./build_tools/release.sh 4.3.3
2026-01-07 08:34:20 +01:00
Johannes Altmanninger
94fdb36f6b Revert "cmake: rename WITH_GETTEXT to WITH_MESSAGE_LOCALIZATION"
This reverts commit 77e1aead40 for the
patch release.
2026-01-07 08:32:33 +01:00
Johannes Altmanninger
d1a40ace7d changelog: fix RST syntax 2026-01-07 08:32:20 +01:00
Johannes Altmanninger
dd4d69a288 cirrus: fix FreeBSD pkg install failure
Multiple PRs fail with

	pkg: Repository FreeBSD-ports cannot be opened. 'pkg update' required
	Updating database digests format: . done
	pkg: No packages available to install matching 'cmake-core' have been found in the repositories
2026-01-07 08:01:21 +01:00
Daniel Rainer
e16ea8df11 sync: once_cell::sync::OnceCell -> std::sync::OnceLock
Rust 1.70 stabilized `std::sync::OnceLock`, which replaces
`once_cell::sync::OnceCell`.

With this, we only have a single remaining direct dependency on
`once_cell`: `VAR_DISPATCH_TABLE` in `src/env_dispatch.rs`, where we use
`Lazy::get`. This can be replaced with `LazyLock::get` once our MSRV
reaches 1.94, where the function is stabilized.

At the moment, `serial_test` depends on `once_cell`, so even if we
eliminate it as a direct dependency, it will remain a transitive
dependency.

Closes #12289
2026-01-07 07:59:24 +01:00
Daniel Rainer
80e1942980 sync: once_cell::sync::Lazy -> std::sync::LazyLock
Rust 1.80 stabilized `std::sync::LazyLock`, which replaces
`once_cell::sync::Lazy`. There is one exception in
`src/env_dispatch.rs`, which still uses the `once_cell` variant, since
the code there relies on `Lazy::get`, which also exists for `LazyLock`,
but will only be stabilized in Rust 1.94, so we can't use it yet.

Part of #12289
2026-01-07 07:59:24 +01:00
Johannes Altmanninger
99109278a6 Enable color theme reporting again on tmux >= 3.7
Color theme reporting has race conditions, so we might want to disable
it until we have fixed those. Not sure.

At least the tmux-specific issue hsa been fixed,
so treat new tmux like other terminals.
See https://github.com/tmux/tmux/issues/4787#issuecomment-3716135010

See #12261
2026-01-07 07:59:24 +01:00
Johannes Altmanninger
f924a880c8 Update changelog 2026-01-07 07:59:23 +01:00
Johannes Altmanninger
5683d26d24 github: update pull request template
Repeat here that 'Fixes #' should go into commit message, and remove
redundant PR description.
2026-01-07 07:35:30 +01:00
Lumynous
740aef06df l10n(zh-TW): Complete untranslated strings
Closes #12288
2026-01-07 07:35:30 +01:00
Daniel Rainer
557f6d1743 check: allow overriding default Rust toolchain
This is useful for running the checks with a toolchain which is
different from the default toolchain, for example to check if everything
works with our MSRV, or on beta/nightly toolchains. Additionally,
providing a way to run using the nightly toolchain allows writing
wrappers around `check.sh` which make use of nightly-only features.

The toolchain could be changed using `rustup toolchain default`, but if
the toolchain should only be used for a specific run, this is
inconvenient, and it does not allow for concurrent builds using
different toolchains.

Closes #12281
2026-01-06 17:54:56 +00:00
Johannes Altmanninger
8d257f5c57 completions/fastboot: one item per line 2026-01-06 14:29:32 +01:00
Johannes Altmanninger
d880a14b1a changelog: add sections 2026-01-06 14:29:32 +01:00
Next Alone
d4fcc00821 completions(fastboot): sync partitions from Xiaomi images
Signed-off-by: Next Alone <12210746+NextAlone@users.noreply.github.com>

Closes #12283
2026-01-06 14:29:32 +01:00
Johannes Altmanninger
6d8bb292ec fish_config theme choose: overwrite color-aware theme's vars also if called from config
Webconfig persists themes to ~/.config/fish/conf.d/fish_frozen_theme.fish
(the name is due to historical reasons).

That file's color variables have no "--theme=foo" annotations, which
means that fish_config can't distinguish them from other "user-set"
values.  We can change this in future, but that doesn't affect the
following fix.

A "fish_config theme choose foo" command is supposed to
overwrite all variables that are defined in "foo.theme".
If the theme is color-theme-aware *and* this command runs before
$fish_terminal_color_theme is initialized, we delay loading of the
theme until that initialization happens.  But the --on-variable
invocation won't have the override bit set, thus it will not touch
variables that don't have "--theme=*" value.  Fix this by clearing
immediately the variables mentioned in the theme.

Fixes #12278

While at it, tweak the error message for this command because it's
not an internal error:

	fish -c 'echo yes | fish_config theme save tomorrow'
2026-01-06 14:29:32 +01:00
Lennard Hofmann
c638401469 Speedup syntax highlighting of redirection targets
Instead of checking twice whether the redirection target is a valid file,
use the return value from test_redirection_target().

Closes #12276
2026-01-06 10:39:58 +01:00
Fabian Boehm
5930574d8a README: Mention cargo
A bit pedantic, we're also not mentioning that you need a linker, but
oh well.

Fixes #12277
2026-01-05 17:16:33 +01:00
Daniel Rainer
fdef7c8689 l10n: add initialize_localization function
This replaces `initialize_gettext`. It is only defined when the
`localize-messages` feature is enabled, to avoid giving the impression
that it does anything useful when the feature is disabled.

With this change, Fluent will be initialized as well once it is added,
without requiring any additional code for initialization.

Closes #12190
2026-01-05 15:12:34 +00:00
Daniel Rainer
5c36a1be1b l10n: create gettext submodule
Put the gettext-specific code into `localization/gettext`.

Part of #12190
2026-01-05 15:12:34 +00:00
Daniel Rainer
14f747019b l10n: create localization/settings
Extract the language selection code from the gettext crate, and to a
lesser extent from `src/localization/mod.rs` and put it into
`src/localization/settings.rs`. No functional changes are intended.

Aside from better separation of concerns, this refactoring makes it
feasible to reuse the language selection logic for Fluent later on.

Part of #12190
2026-01-05 15:12:34 +00:00
Johannes Altmanninger
d7d5d2a9be completions/make: fix on OpenBSD/FreeBSD
Tested with the POSIX Makefile from https://github.com/mawww/kakoune

Closes #12272
2026-01-05 12:52:00 +01:00
Johannes Altmanninger
750955171a __fish_migrate: don't leak standard file descriptors
The __fish_migrate.fish function spawns a "sh -c 'sleep 7' &" child
process that inherits stdin/stdout/stderr file descriptors fish.

This means that if the app running "fish
tests/checks/__fish_migrate.fish" actually waits for fish to close its
standard file descriptors, it will appear to hang for 7 seconds. Fix
that by closing the file descriptors in the background job when
creating it.

Closes #12271
2026-01-05 12:52:00 +01:00
Denys Zhak
36fd93215b fish_indent: Keep braces on same line in if/while conditions
Closes #12270
2026-01-05 12:50:19 +01:00
Lennard Hofmann
6e7353170a Highlight valid paths in redirection targets
Closes #12260
2026-01-05 12:50:19 +01:00
Peter Ammon
62cc117c12 Minor refactoring of add_to_history
Preparation for other refactoring in the future.
2026-01-04 12:33:53 -08:00
Peter Ammon
af00695383 Clean up replace_home_directory_with_tilde
Fix a stale comment and add a test.
2026-01-04 11:08:21 -08:00
Johannes Altmanninger
85ac91eb2b fish_config theme choose: fix Tomorrow always using light version
The backward compat hack canonicalization caused us to always treat
"tomorrow" light theme.

Restrict this hack to the legacy name (Tomorrow); don't do it when
the new canonical name (tomorrow) is used.  The same issue does not
affect other themes because their legacy names always have a "light"
or 'dark' suffix, which means that the canonical name is different,
so the legacy hacks don't affect the canonical name.

Fixes #12266
2026-01-04 13:08:26 +01:00
Johannes Altmanninger
d1ed582919 fish_config theme choose: apply backwards compat hacks only to sample themes
This logic exists to not break user configurations as we renamed
themes.  But user-sourced themes haven't been renamed.

(It's also questionable whether we should really have these compat
hacks; they might cause confusion in the long run).
2026-01-04 13:08:26 +01:00
xtqqczze
06a14c4a76 clippy: fix assigning_clones lint
https://rust-lang.github.io/rust-clippy/master/index.html#assigning_clones

Closes #12267
2026-01-04 13:08:26 +01:00
takeokunn
400d5281f4 feat(git-completion): add missing options and completions for commands
- Add missing options and completions for fetch, show-branch, am,
  checkout, archive, grep, pull, push, revert, rm, config, clean, and
  other commands
- Replace TODO comments with actual option completions for improved
  usability
- Ensure all new options have appropriate descriptions and argument
  handling for fish shell completion

Closes #12263
2026-01-04 13:08:26 +01:00
Johannes Altmanninger
50778670fb Disable color theme reporting in tmux for now
Due to the way tmux implements it, color theme reporting
causes issues when typing commands really quickly (such as
when synthesizing keys).  We're working on fixing this, see
https://github.com/tmux/tmux/issues/4787#issuecomment-3707866550
Disable it for now. AFAIK other terminals are not affected.

Closes #12261
2026-01-04 13:08:26 +01:00
WitherZuo
9037cd779d Optimize functions page style of fish_config.
- Fix the background color of .function-body in dark mode to improve readability.
- Switch to Tomorrow Night Bright color theme for better contrast and readability in dark mode.
- Format all stylesheets of fish_config.

Closes #12257
2026-01-04 13:08:26 +01:00
phanium
c23a4cbd9f Add --color option for some builtins
Fixes #9716

Closes #12252
2026-01-04 13:08:26 +01:00
Johannes Altmanninger
5d8f7801f7 builtin fish_indent/fish_key_reader: call help from existing fish process
Something like

	PATH=mypath builtin fish_indent --help

runs "fish -c '__fish_print_help fish_indent'" internally.  Since we
don't call setenv(), the PATH update doesn't reach the child shell.
Fix this by using what other builtins use if we are one (i.e. if we
have a Parser in context).

Fixes #12229
Maybe also #12085
2026-01-04 13:08:26 +01:00
Johannes Altmanninger
756134cf2b test/tmux-complete3: fail more reliably 2026-01-04 13:08:26 +01:00
Johannes Altmanninger
c16677fd6f tty_handoff: use Drop consistently
We sometimes use explicit reclaim() and sometimes rely on the drop
implementation. This adds an unnecesary step to reading all uses of
this code.  Make this consistent. Use drop everywhere though we could
use explicit reclaim too.
2026-01-04 13:08:26 +01:00
Johannes Altmanninger
13bc514aa6 __fish_complete_directories: remove use of empty variable
Closes #12248
2026-01-04 09:42:25 +01:00
Johannes Altmanninger
1c3403825c completions/signify: consistent style
Also, replace use of "ls" with globbing.
2026-01-04 09:42:25 +01:00
LunarEclipse
6f1ac7c949 Prioritize files with matching extensions for flag arguments in signify completions
Closes #12243
2026-01-04 09:42:25 +01:00
LunarEclipse
f5d3fd8a82 Full completions for openbsd signify
Part of #12243
2026-01-04 09:42:25 +01:00
Johannes Altmanninger
0a23a78523 Soft-wrapped autosuggestion to hide right prompt for now
Prior to f417cbc981 (Show soft-wrapped portions in autosuggestions,
2025-12-11), we'd truncate autosuggestions before the right prompt.
We no longer do that for autosuggestions that soft-wrap, which means
we try to draw both right prompt and suggestion in the same space.

Make suggestion paint over right prompt for now, since this seems to
be a simple and robust solution.  We can revisit this later.

Fixes #12255
2026-01-03 15:54:04 +01:00
xtqqczze
725cf33f1a fix: remove never read collection from parse_cmd_opts
Closes #12251
2026-01-03 15:54:04 +01:00
xtqqczze
2d6db3f980 clippy: fix implicit_clone lint
https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone

Closes #12245
2026-01-03 15:54:04 +01:00
xtqqczze
41b9584bb3 clippy: fix cloned_instead_of_copied lint
https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied

Closes #12244
2026-01-03 15:54:04 +01:00
Tin Lai
c915435417 respect canonical config for untracked files
Signed-off-by: Tin Lai <tin@tinyiu.com>

Closes #11709
2026-01-03 15:54:04 +01:00
Johannes Altmanninger
afcde1222b Update cargo dependencies
cargo update && cargo +nightly -Zunstable-options update --breaking
2026-01-03 15:54:04 +01:00
Benjamin A. Beasley
a3cb512628 Update phf from 0.12 to 0.13
Closes #12222
2026-01-03 15:54:04 +01:00
Johannes Altmanninger
fc71ba07da share: fix typo 2026-01-03 15:54:04 +01:00
Johannes Altmanninger
9c867225ee reader handle_completions(): remove code duplication
We fail to flash the command line if we filter out completions due
to !reader_can_replace. Fix that and de-duplicate the logic
2026-01-03 15:54:04 +01:00
Johannes Altmanninger
972355e2fc reader handle_completions(): remove stale comment
See 656b39a0b3 (Also show case-insensitive prefix matches in completion pager, 2025-11-23).
2026-01-03 15:54:04 +01:00
Johannes Altmanninger
8f4c80699f reader handle_completions(): don't allocate a second completion list 2026-01-03 15:54:04 +01:00
Johannes Altmanninger
e79b00d9d1 reader handle_completions(): also truncate common prefix when replacing
I don't know why we don't apply the common-prefix truncation logic
when all completions are replacing.
Let's do that.
2026-01-03 15:54:04 +01:00
Johannes Altmanninger
2f6b1eaaf9 reader handle_completions(): don't consider odd replacing completions for common prefix
If "will_replace_token" is set, we generally only consider
appending completions.  This changed in commit 656b39a0b3 (Also show
case-insensitive prefix matches in completion pager, 2025-11-23) which
also allowed icase completions as long as they are also prefix matches.

Such replacing completions might cause the common prefix to be empty,
which breaks the appending completions.

Fix this by not considering these replacing completions for the
common-prefix computation. The pager already doesn't show the prefix
for these completions specifically.

Fixes #12249
2026-01-03 15:54:04 +01:00
Johannes Altmanninger
3546ffa3ef reader handle_completions(): remove dead filtering code
We skip completions where "will_replace_token != c.replaces_token()".
This means that
- if will_replace_token, we filter out non-replacing completions.
  But those do not exist because, by definition, will_replace_token
  is true iff there are no non-replacing completions.
- if !will_replace_token, we filter out replacing completions.
  From the definition of will_replace_token follows that there is
  some non-replacing completion, which must be a prefix or exact match.
  Since we've filtered by rank, any replacing ones must have the same rank.
  So the replacement bit must be due to smartcase.  Smartcase
  completions are already passed through explicitly here since
  656b39a0b3 (Also show case-insensitive prefix matches in completion
  pager, 2025-11-23).

So the cases where we 'continue' here can never happen.
Remove this redundant check.
2026-01-03 15:54:04 +01:00
Johannes Altmanninger
30f96860a7 reader handle_completions(): closed form for pager prefix bool 2026-01-03 15:54:04 +01:00
Johannes Altmanninger
41d50f1a71 reader handle_completions(): don't duplicate pager prefix allocation
While at it, use Cow I guess?
2026-01-03 15:54:04 +01:00
Johannes Altmanninger
1e9c80f34c reader handle_completions(): don't allocate common prefix 2026-01-03 15:54:04 +01:00
Johannes Altmanninger
b88d2ed812 reader handle_completions(): don't clone surviving completions 2026-01-03 15:54:04 +01:00
Johannes Altmanninger
92dd37d3c7 reader handle_completions(): remove dead code for skipping to add prefix
The tuple (will_replace_token, all_matches_exact_or_prefix) can never
be (false, false).

Proof by contraction:
1. Goal: show unsatisfiability of: !will_replace_token && !all_matches_exact_or_prefix
2. Substitute defintions: !all(replaces) && !all(is_exact_or_prefix)
3. wlog, !replaces(c1) && !is_exact_or_prefix(c2)
4. since c1 and c2 have same rank we know that !is_exact_or_prefix(c1)
5. !is_exact_or_prefix() implies requires_full_replacement()
6. all callers that create a Completion from StringFuzzyMatch::try_create(),
   set CompleteFlags::REPLACE_TOKEN if requires_full_replacement(),
   so requires_full_replacement() implies replaces()
7. From 4-6 follows: !is_exact_or_prefix(c1) implies replaces(c1), which is a contradiction
2026-01-03 15:54:04 +01:00
Johannes Altmanninger
f24cc6a8fc reader handle_completions(): remove code clone
While at it,
1. add assertions to tighten some screws
2. migrate to closed form / inline computation.
2026-01-03 15:54:04 +01:00
Johannes Altmanninger
3117a488ec complete: reuse replaces_token() 2026-01-03 15:54:04 +01:00
Johannes Altmanninger
185b91de13 reader rls: remove redundant initial value
This initial value is weird and None works the same way so use that.
2026-01-03 15:54:04 +01:00
Johannes Altmanninger
e20024f0f0 reader rls: minimize state for tracking the completion pager 2026-01-03 15:54:04 +01:00
Johannes Altmanninger
501ec1905e Test completion pager invalidation behavior 2026-01-03 15:54:04 +01:00
Johannes Altmanninger
7ebd2011ff update-dependencies.sh: fix uv lock --check command 2026-01-03 15:54:04 +01:00
Daniel Rainer
8004f354aa changelog: put new changes into upcoming release
The update to `WITH_GETTEXT` was not released in 4.3.2, so remove it
from there and put it into the section for the upcoming release.

Closes #12254
2026-01-02 00:11:36 +00:00
Daniel Rainer
77e1aead40 cmake: rename WITH_GETTEXT to WITH_MESSAGE_LOCALIZATION
This change is made to make the option name appropriate for Fluent
localization.

While at it, add a feature summary for this feature.

Closes #12208
2026-01-01 23:46:07 +00:00
Peter Ammon
e48a88a4b3 Minor refactoring of history item deletion
Clean this up.
2026-01-01 12:00:18 -08:00
Peter Ammon
1154d9f663 Correct error reporting when rewriting history files
A recent change attempted this:

    let result: std::io::Result<()> = { code()? }

However this doesn't initialize the Result on error - instead it
returns from the function, meaning that the error would be silently
dropped.

Fix that by reporting the error at the call site instead.
2025-12-31 10:20:31 -08:00
Johannes Altmanninger
810a707069 Fix PROMPT_SP hack regression
Commit fbad0ab50a (reset_abandoning_line: remove redundant
allocations, 2025-11-13) uses byte count of ⏎ (3) instead of char
count (1), thus overestimating the number of spaces this symbol takes.

Fixes #12246
2025-12-31 07:46:50 +01:00
Peter Ammon
7fa9e9bfb9 History: use Rust's buffered writing instead of our own
Simplify some code.
2025-12-30 20:26:07 -08:00
Alan Somers
48b0e7e695 Fix installation of prompts and theme files after 4.3.0
Installation of these files was accidentally broken by d8f1a2a.

Fixes #12241
2025-12-31 11:10:34 +08:00
Peter Ammon
848fa57144 Introduce and adopt BorrowedFdFile
Rust has this annoying design where all of the syscall conveniences on
File assume that it owns its fd; in particular this means that we can't
easily construct File from stdin, a raw file descriptor, etc.

The usual workarounds are to construct a File and then mem::forget it
(this is apparently idiomatic Rust!). But this has problems of its own:
for example it can't easily be used in Drop.

Introduce BorrowedFdFile which wraps File with ManuallyDrop and then
never drops the file (i.e. it's always forgotten). Replace some raw FDs
with BorrowedFdFile.
2025-12-30 14:07:39 -08:00
Peter Ammon
4101e831af Make fish_indent stop panicing on closed stdin
Prior to this commit, this code:

    fish_indent <&-

would panic as we would construct a File with a negative fd.
Check for a closed fd as other builtins do.
2025-12-30 14:07:39 -08:00
Peter Ammon
eb803ba6a7 Minor cleanup of shared::Arguments 2025-12-30 14:07:39 -08:00
Fabian Boehm
248a8e7c54 Fix doc test 2025-12-30 20:40:55 +01:00
Peter Ammon
2fa8c8cd7f Allow ctrl-C to work in fish_indent builtin
Since fish_indent became a builtin, it cannot be canceled with control-C,
because Rust's `read_to_end` retries on EINTR. Add our own function which
propagates EINTR and use it.

Fixes #12238
2025-12-30 10:39:19 -08:00
Johannes Altmanninger
8d5f5586dc start new cycle
Created by ./build_tools/release.sh 4.3.2
2025-12-30 17:43:15 +01:00
Johannes Altmanninger
c5bc7bd5f9 Release 4.3.2
Created by ./build_tools/release.sh 4.3.2
2025-12-30 17:21:04 +01:00
Johannes Altmanninger
2b3bd29588 Fix infinite repaint when setting magic variables in prompt
Commit 7996637db5 (Make fish immediately show color changes again,
2025-12-01) repaints unnecessarily when a local unexported color
variable changes.  Also, it repaints when the change comes from
fish_prompt, causing an easy infinite loop.  Same when changing TERM,
COLORTERM and others.

This feature is relevant when using a color-theme aware theme, so
try to keep it. Repaint only on global/universal changes.
Also ignore changes if already repainting fish prompt.

This change may be at odds with concurrent execution (parser should
not care about whether we are repainting) but that's intentional
because of 1. time constraints and 2. I'm not sure what the solution
will look like; we could use the event infrastructure.  But a lot of
existing variable listeners don't use that.

Extract a context object we pass whenever we mutate the environment; While
at it, use it to pass EnvMode::USER, to reduce EnvMode responsibilities.

Fixes #12233
2025-12-30 17:20:42 +01:00
Johannes Altmanninger
0be3f9e57e Fix some non_upper_case_globals warnings 2025-12-30 17:20:42 +01:00
Johannes Altmanninger
2524ece2cc builtin function: error when trying to inherit read-only variable
Also, deduplicate error checks and do them as early as possible since
we always return on error.
2025-12-30 17:20:42 +01:00
Johannes Altmanninger
b975472828 builtin function: improve option parsing structure 2025-12-30 17:20:42 +01:00
Johannes Altmanninger
20427ff1f6 env: check cheaper condition first 2025-12-30 17:20:42 +01:00
Johannes Altmanninger
5b3b825ab2 env: dispatch variable changes again if global was modified explicitly
We set "global_modified" to true if the global exist, or if the
default scope is global but not if EnvMode::GLOBAL.

This is an accident from 77aeb6a2a8 (Port execution, 2023-10-08).
Restore it. Tested in a following commit.
2025-12-30 17:20:42 +01:00
Johannes Altmanninger
ccd3348eed env_init: early return 2025-12-30 17:20:42 +01:00
Johannes Altmanninger
845b9be1f5 builtin set: remove unused argument
The "user" bit is only for getting errors when trying to set read-only
variables.  It's not relevant for reading from variables.
2025-12-30 17:20:42 +01:00
Johannes Altmanninger
400f2b130a set --erase: simplify erasing from multiple scopes in one go
We have pretty weird behavior:

	   $ set --path somepath 1 2 3
	     set --erase --unpath somepath[2]
	[1]$ set --path somepath 1 2 3
	     set --erase --unpath somepath
	   $

The first command fails to erase from the variable, because the
--path/--unpath mismatch prevents us from accessing the variable.
The second succeeds at erasing because we ignore --path/--unpath.

We should probably fix this; for now only simplify the unrelated
change added by fed64999bc (Allow erasing in multiple scopes in one
go, 2022-10-15):
we implement "set --erase --global --path" as

	try_erase(scope="--global")
	try_erase(scope="--path")

Do this instead, which is closer to historical behavior.

	try_erase(scope="--global --path")

This also allows us to express more obviously the behavior if no scope
(out of -lfgU) was specified.
2025-12-30 17:20:42 +01:00
Johannes Altmanninger
362f7cedf6 builtin set: fix inconsistent name
The --path and --export flags are not scopes, so use "mode" name
as elsewhere.
2025-12-30 17:20:42 +01:00
Johannes Altmanninger
2c959469f0 env mode: extract constant for scope bits 2025-12-30 17:20:42 +01:00
Johannes Altmanninger
6c34bcf8f6 parser: reuse set_var() 2025-12-30 17:20:42 +01:00
Johannes Altmanninger
f510b62b7f Don't schedule redundant repaints as autosuggestions are toggled
Tweak 86b8cc2097 (Allow turning off autosuggestions, 2021-10-21)
to avoid redundantly executing the prompt and repainting.
2025-12-30 17:20:42 +01:00
Fabian Boehm
b31387416d Optimize fish_config theme choose
Just following basic shellscript optimization:

- Remove a useless use of cat (`status get-file` takes microseconds,
`status get-file | cat` is on the order of a millisecond - slower with
bigger $PATH)
- Pipe, don't run in a loop
- Filter early

This reduces the time taken from 12ms to 6ms on one of my systems, and
6.5ms to 4.5ms on another.

This is paid on every single shell startup, including
non-interactively, so it's worth it.

There's more to investigate, but this is a good first pass.
2025-12-29 17:26:55 +01:00
Johannes Altmanninger
941a6cb434 changelog for 4.3.2 2025-12-29 16:25:23 +01:00
Johannes Altmanninger
931072f5d1 macos packages: don't add redundant hardlinks to fat binary
Commit 7b4802091a installs fish_indent and fish_key_reader as
hardlinks to fish.  When we create our fat binary for macOS, we add
3 of these X86 binaries to the fattened one,
resulting in a corrupted Mach-O binary. Fix that.

Fixes #12224
2025-12-29 16:19:48 +01:00
Johannes Altmanninger
f4f9db73da Add a CMake option to work around broken cross-compilation builds
Commit 135fc73191 (Remove man/HTML docs from tarball, require Sphinx
instead, 2025-11-20) broke cross compilation of tarballs.

Add an option to allow users to pick any fish_indent (such as
"target/debug/fish_indent" as created by "cargo build"), to allow
cross compilation.

In future, we should remove this option in favor of doing all of this
transparently at build type (in build.rs).

Ref: https://matrix.to/#/!YLTeaulxSDauOOxBoR:matrix.org/$psPcu-ogWK5q9IkgvfdvBGTdJ2XGhNq5z_Ug0iTCx2Q
2025-12-29 16:19:48 +01:00
Johannes Altmanninger
9ef3f30c56 Revert "Re-enable focus reporting on non-tmux"
When I ssh to a macOS system, typing ctrl-p ctrl-j in quick succession
sometimes causes ^[[I (focus in) to be echoed.  Looks like we fail to
disable terminal-echo in time. Possible race condition?  Revert until
we find out more.

This reverts commit 7dd2004da7.

Closes #12232
2025-12-29 16:19:48 +01:00
Johannes Altmanninger
d19c927760 status get-file: simplify wrapper
The __fish_data_with_file wrapper was born out of a desire to simplify
handling of file paths that may or may not be embedded into the
fish binary.

Since 95aeb16ca2 (Remove embed-data feature flag, 2025-11-20) this is
no longer needed since almost everything is embedded unconditionally.
The exception is man pages (see a1baf97f54 (Do not embed man pages
in CMake builds, 2025-11-20)), but they use __fish_data_with_directory.
2025-12-29 16:19:48 +01:00
Misty De Meo
22e5b21f10 changelog: fix minor typo
Closes #12227
2025-12-29 16:19:48 +01:00
Johannes Altmanninger
f0d2444769 docs: removed dead code around FISH_BUILD_VERSION
Man pages used to be built by "build.rs" but now are built by a
dependent "crates/build-man-pages/build.rs". This means that changing
the environment of build.rs is ineffective.

In future, "fn get_version" should probably be a part of
"crates/build-helper/", so Cargo builds only need to compute the
version once.

Lack of this dependency means that "build-man-pages" does not
pass FISH_BUILD_VERSION, which means that Sphinx will fall back to
build_tools/git_version_gen.sh.  This acceptable for now given that
"build-man-pages" is not used in CMake builds.
2025-12-29 16:19:48 +01:00
Johannes Altmanninger
7975060e4a docs: consistently use FISH_BUILD_VERSION_FILE
Commit 2343a6b1f1 passed the FISH_BUILD_VERSION_FILE to
sphinx-manpages to remove the fish_indent dependency.

For sphinx-docs this has been solved in another way in e895f96f8a
(Do not rely on `fish_indent` for version in Sphinx, 2025-08-19).

This is a needless inconsistency.

Remove it. Use FISH_BUILD_VERSION_FILE whenever possible, since that
means that a full build process only needs to call git_version_gen.sh
once.

Keep the fallback to git_version_gen.sh, in case someone calls
sphinx-build directly.
2025-12-29 16:19:47 +01:00
Johannes Altmanninger
354dc3d272 docs: use correct version file for HTML docs from tarball
Prior to commit 135fc73191 (Remove man/HTML docs from tarball, require
Sphinx instead, 2025-11-20), HTML docs were built from a Git worktree.

Now they are built in the tarball.  We call
build_tools/git_version_gen.sh from doc_src so it fails to find the
version file. Fix that.

Fixes #12228
2025-12-29 16:19:47 +01:00
Johannes Altmanninger
7640e95bd7 Create user config file/directories only on first startup again
Not being able to delete these for good (if unused) seems to be
a nuisance.  Let's go back to storing universal __fish_initialized
also on fresh installations, which I guess is a small price to to
avoid recreating these files.

Closes #12230
2025-12-29 16:19:47 +01:00
Johannes Altmanninger
767115a93d build_tools/make_macos_pkg.sh: fix when no CMake option is passed 2025-12-29 16:19:47 +01:00
David Adam
f0c8788a52 exec: add custom message for EBADMACHO error on Apple platforms
Otherwise the error is 'unknown error number 88'.
2025-12-29 22:46:47 +08:00
Fabian Boehm
a3cbb01b27 source __fish_build_paths directly
This didn't work because the cheesy helper function added an extra
scope block.

Just skip it.

Fixes #12226
2025-12-28 19:57:08 +01:00
Johannes Altmanninger
d630b4ae8a start new cycle
Created by ./build_tools/release.sh 4.3.1
2025-12-28 17:16:50 +01:00
Johannes Altmanninger
a2c5b2a567 Release 4.3.1
Created by ./build_tools/release.sh 4.3.1
2025-12-28 16:54:44 +01:00
Johannes Altmanninger
18295f4402 Fix icase prefix/suffix checks
Commit 30942e16dc (Fix prefix/suffix icase comparisons, 2025-12-27)
incorrectly treated "gs " as prefix of "gs" which causes a crash
immediately after expanding that abbreviation iff "gs" is our
autosuggestion (i.e. there's no history autosuggestion taking
precedence).

Fixes #12223
2025-12-28 16:54:34 +01:00
Johannes Altmanninger
443fd604cc build_tools/release.sh: don't add dch entry for snapshot version 2025-12-28 10:55:09 +01:00
Johannes Altmanninger
9e022ff7cf build_tools/release.sh: fix undefined variable for next milestone 2025-12-28 10:52:20 +01:00
Johannes Altmanninger
aba927054f start new cycle
Created by ./build_tools/release.sh 4.3.0
2025-12-28 10:45:34 +01:00
Johannes Altmanninger
bdfc491d56 Release 4.3.0
Created by ./build_tools/release.sh 4.3.0
2025-12-28 10:20:47 +01:00
Johannes Altmanninger
b72127a0b7 build_tools/release-notes.sh: fix RST syntax 2025-12-28 10:03:08 +01:00
Johannes Altmanninger
0ab2a46424 build_tools/release.sh: docs have been removed from tarball
See 135fc73191 (Remove man/HTML docs from tarball, require Sphinx
instead, 2025-11-20).
2025-12-28 10:03:08 +01:00
Johannes Altmanninger
5844650881 Set color theme eagerly again
Now that the default theme no longer contains light/dark sections,
we don't need to wait for $fish_terminal_color_theme to be initialized
before setting it.

Let's set it before loading user config to allow users to do things
like "set -e fish_color_command" in their config.

Fixes #12209
2025-12-28 09:06:16 +01:00
Johannes Altmanninger
3c77a67668 uvar migration: remove extra message if there were no color uvars 2025-12-28 09:06:16 +01:00
Johannes Altmanninger
3105f88622 fish_config choose/save: improve docs 2025-12-28 09:06:16 +01:00
Johannes Altmanninger
5702b26b22 fish_config theme choose: don't add redundant hook
If we're overriding the theme with --color-theme=, we don't need to
register the hook because $fish_terminal_color_theme will be ignored.

Also if the theme is not color-theme aware, there is no need to
register the hook.
2025-12-27 17:28:20 +01:00
Johannes Altmanninger
60ef7ca210 test_complete: improve assertion to track down intermittent failure
The result vector sometimes has three instead of two elements.
See #12184
2025-12-27 13:10:36 +01:00
Johannes Altmanninger
c486c54120 fish_config theme choose: use captured data 2025-12-27 13:10:36 +01:00
Johannes Altmanninger
6273b9420b fish_config theme choose: capture theme by copy
We don't listen for changes to the theme file, so this seems more
appropriate and more robust.
2025-12-27 13:10:36 +01:00
Johannes Altmanninger
4bb0d956eb themes/default: revert to palette colors for now
The readability concern in ed881bcdd8 (Make default theme use named
colors only, 2023-07-25) was no longer relevant, but people might
prefer we use terminal colors by default, because other apps do too,
and because it's a well-known way to make colors look good across
both dark and light mode.

If we revert this, we should make sure fish_default_mode_prompt.fish
and prompt_login.fish also use RGB colors
2025-12-27 12:57:08 +01:00
Nikita COEUR
92db03ac9f feat: aquaproj/aqua completion support
Closes #12213
2025-12-27 12:19:24 +01:00
xtqqczze
0d6aaa36d3 Use mem::take
Closes #12217
2025-12-27 12:19:24 +01:00
Johannes Altmanninger
88a7888478 postfork: remove dead code checking setpgid() error 2025-12-27 12:19:24 +01:00
Nahor
74cb96d55b fd_monitor: add missing EAGAIN check
poll()/select() can return EAGAIN on some systems, which should be
treated like a EINTR according to the man page

Closes #12211
2025-12-27 12:19:24 +01:00
Johannes Altmanninger
30942e16dc Fix prefix/suffix icase comparisons
As reported on Gitter, running "echo İ" makes history autosuggestion
for "echo i" crash.  This is because history search correctly
returns the former, but string_prefixes_string_case_insensitive("i",
"İ") incorrectly returns false.  This is because the prefix check
is implemented by trimming the rhs to the length of the prefix and
checking if the result is equal to the prefix.  This is wrong because
the prefix computation should operate on the canonical lowercase
version, because that's what history search uses.
2025-12-27 12:19:24 +01:00
Johannes Altmanninger
a8ded9cb0d Separate wcscasecmp() concerns better 2025-12-27 09:28:01 +01:00
Johannes Altmanninger
4d67678217 changelog: update, clarify color variable fallback
Do a pass over the changelog and clarify the entry on the breaking
change that sparked #12209.
2025-12-26 18:10:47 +01:00
Johannes Altmanninger
9460559345 fish_config theme save: don't add --theme=$theme marker nor react to colortheme
The theme marker is set by "fish_config theme choose" to allow
us to react to terminal color theme changes, and to tell future
"fish_config theme choose" invocations which variables it should erase
-- it should not erase color variables not set in the theme file,
like Git prompt colors.

I'm not sure if either really makes sense for "fish_config theme save".
Reacting to terminal color theme changes is weird for universals.

I'm not sure if "fish_config theme save" should erase universal
variables that are not defined in the theme.  Historically, it did
so for a hardcoded list of colors, which is hacky. For now let's
err on the side of leaving around color variables.

The "save" subcommand is deprecated; it's possible that this changes
in future (and we add support for "--theme" to it) but I'm not sure
if we have enough need for that.
2025-12-26 18:10:47 +01:00
Johannes Altmanninger
74af4f10de uvar migration: tell users to restart running sessions
Users who run the default theme are silently migrated to global
variables. Universal color variables are deleted, leaving existing
sessions uncolored. Tell the user to restart them, and make some
other improvements; now it looks like:

	fish: upgraded to version 4.3:
	* Color variables are no longer set in universal scope.
	  To restore syntax highlighting in other fish sessions, please restart them.
	* The fish_key_bindings variable is no longer set in universal scope by default.
	  Migrated it to a global variable set in  ~/.config/fish/conf.d/fish_frozen_key_bindings.fish

Same for users who do not use the default theme (who already got a
message before this change). For them, the first bullet point looks
like this:

	[...]
	* Color variables are no longer set in universal scope by default.
	  Migrated them to global variables set in ~/.config/fish/conf.d/fish_frozen_theme.fish
	  To restore syntax highlighting in other fish sessions, please restart them.
	[...]

Closes #12161
2025-12-26 18:10:47 +01:00
Johannes Altmanninger
b2f4befc7e themes: don't define option/keyword colors redundantly
These fall back to param/command roles, so there's no need to
duplicate the value.

Make sure the "set fish_color_o<TAB>" still works if they're not
defined.

Leave it as a comment in theme files I guess, since users copy-pasting
a theme might reasonably want to set it.

Closes #12209
2025-12-26 18:09:15 +01:00
xtqqczze
771b33b3a3 Use Option<OwnedFd> instead of AutoCloseFd
Closes #12199
2025-12-26 18:09:15 +01:00
Johannes Altmanninger
1cf4e191b3 docs syntax-highlighting: show how to restore default theme 2025-12-26 18:09:15 +01:00
Johannes Altmanninger
b88a5eaad5 Allow tracing bindings, event handlers etc. with fish_trace=all
Might have helped with cases like #12209 where an event handler stomps
user preferences.
2025-12-26 18:09:15 +01:00
Johannes Altmanninger
1c1baddf4f docs: one sentence per line 2025-12-26 07:56:52 +01:00
Daniel Rainer
107cbaddf0 strings: take IntoCharIter instead of wstr
Change the input of some functions to take `impl IntoCharIter`, allowing
them to accept more input. Implementing this efficiently means that no
owned types should be passed into these functions, because their
`IntoCharIter` implementation would require unnecessary allocations.
Instead, convert the uses which previously passed `WString` by prefixing
an `&`, so the borrowed `&WString` is passed instead.

To allow for wider use of the modified functions, `IntoCharIter`
implementations are added for `&String`, `&Cow<str>`, and `&Cow<wstr>`.

Closes #12207
2025-12-25 21:52:42 +00:00
Denys Zhak
daa554123f fish_indent: Keep braces on same line after conjunctions, time, not
Closes #12144
2025-12-25 15:22:53 +01:00
Johannes Altmanninger
979320063e readme: remove stale MAC_CODESIGN_ID
Removed in 9edd0cf8ee (Remove some now unused CMake bits, 2024-07-07).
The replacements are not documented in prose but in the GitHub
release workflow.
2025-12-25 15:22:52 +01:00
Johannes Altmanninger
fed0269762 Tweak language in "status language" output
Ref: https://github.com/fish-shell/fish-shell/pull/12106#discussion_r2636320834
2025-12-25 15:22:52 +01:00
Johannes Altmanninger
6d68dfe12b doc terminal-compatibility: clarify 2025-12-25 15:22:52 +01:00
Johannes Altmanninger
97c59fa991 doc terminal-compatibility: fix formatting error 2025-12-25 15:22:52 +01:00
Johannes Altmanninger
128fafce1e uvar migration: improve output on accidental early exit
fish_job_summary shows this as "$sh -c ...".  Make it "/bin/sh -c ...".
2025-12-25 15:22:52 +01:00
Johannes Altmanninger
f1c8e6995d On process exit, read output into buffer to fix ordering
This command

	echo $(/bin/echo -n 1; echo -n 2)

sometimes outputs "21" because we implement this as

	let bufferfill = IoBufferfill::create_opts(...);
	...
	let eval_res = parser.eval_with(...);
	let buffer = IoBufferfill::finish(bufferfill);

i.e. /bin/echo and builtin echo both output to the same buffer; the
builtin does inside parser.eval_with(), and the external process may
or may not output before that, depending on when the FD monitor thread
gets scheduled (to run item_callback).

(Unrelated to that we make sure to consume all available input in
"IoBufferfill::finish(bufferfill)" but that doesn't help with
ordering.)

Fix this by reading all available data from stdout after the child
process has exited.

This means we need to pass the BufferFill down to
process_mark_finished_children().

We don't need to do this for builtins like "fg" or "wait",
because commands that buffer output do not get job control, see
2ca66cff53 (Disable job control inside command substitutions,
2021-07-26).
We also don't need to do it when reaping from reader because there
should be no buffering(?).

fish still deviates from other shells in that it doesn't wait for
it's child's stdout to be closed, meaning that this will behave
non-deterministically.

	fish -c '
	    echo -n $(
	        sh -c " ( for i in \$(seq 10000); do printf .; done ) & "
	    )
	' | wc -c

We should fix that later.

Closes #12018
2025-12-25 14:35:54 +01:00
Daniel Rainer
02061be279 io: allow unescape+write of str
The existing functionality of converting a `&wstr` to bytes (unescaping
PUA codepoints) and writing these to a file descriptor can be reused for
Rust's built-in strings by making the input type generic. This is
simple, because the only functionality we need is converting the input
into a `char` iterator, which is available for both types.

While at it, rename the functions to have more accurate and informative
names.

Closes #12206
2025-12-25 01:48:02 +00:00
Daniel Rainer
c9b30b748d cleanup: remove duplicate functions
Closes #12205
2025-12-25 01:10:36 +00:00
Daniel Rainer
167cfd0892 gettext-extraction: fix outdated docs
Closes #12204
2025-12-24 01:01:47 +00:00
Daniel Rainer
5c3941f0dd check.sh: rename template to gettext template
This is done in preparation for a second temporary directory used for
Fluent ID extraction.

Closes #12203
2025-12-24 00:47:01 +00:00
Daniel Rainer
d4745b633b check.sh: don't build docs of dependencies
We only run `cargo doc` here to check for issues with fish's
documentation, so there is no need to build docs of dependencies.

Closes #12201
2025-12-24 00:21:41 +00:00
Fabian Boehm
dd97842964 completions/rustc: Don't autoinstall nightly toolchain
Just calling `rustc +nightly` is enough to trigger automatic
installation.

Fixes #12202
2025-12-23 19:49:42 +01:00
Peter Ammon
5715d143a5 Fix some clipplies 2025-12-23 08:44:10 -08:00
Peter Ammon
41571dec0f Minor simplification of history 2025-12-23 08:34:36 -08:00
Nahor
3c7517bf28 tests: disable tmux tests on Cygwin/MSYS
Those tests are unreliable and sometimes even block forever on
Cygwin/MSYS.
2025-12-21 12:21:25 +01:00
Nahor
81fce66269 test_driver: flush output after each test
When the output is redirected, Python buffer its whole output, unlike
a TTY output where only lines are buffered.
In GitHub actions in particular, it means that we can't see any progress
after each test. And if a test blocks forever, there is no output at all.

So flush the output after printing each result to see the progress
being made
2025-12-21 12:21:25 +01:00
Nahor
7b8f97c1ff Run fish_indent on select test scripts
Run fish_indent on test scripts that will be modified in a later
commit

Not running it on all the files because a quarter of them need fixing,
some of which are badly formatted on purpose
2025-12-21 12:21:25 +01:00
Daniel Rainer
9b75b6ee88 l10n: move po/ to localization/po/
This is done in preparation for Fluent's FTL files, which will be placed
in `localization/fluent/`. Having a shared parent reduces top-level
clutter in the repo and makes it easier to find the localization files
for translators, including realizing that both PO and FTL files exist.

We keep PO and FTL files in separate directories because we need to
rebuild on any changes in the PO directory (technically only when there
are changes to `*.po` files, but due to technical limitations we can't
reliably trigger rebuilds only if those changes but not other files in
the same directory.) Changes to FTL files do not require rebuilds for
dev builds, since for these `rust-embed` does not actually embed them
into the binary but rather loads them from the file system at runtime.

Closes #12193
2025-12-21 12:11:49 +01:00
Branch Vincent
1ebf750bc0 completions: add docker
The upstream completions have not been updated for some time, but the
docker binary can generate completions. These include dynamic
completions for image names and so on.

Closes #12197.
2025-12-21 12:57:06 +08:00
Daniel Rainer
dcd07d754d l10n: move wutil/gettext to localization module
Localization deserves its own module. As a first step, this module is
created here. This will be followed up by significant refactoring in
preparation for supporting Fluent alongside gettext.

`localization/mod.rs` is used instead of `localization.rs` because it is
planned to split this module into submodules.

Part of #12190
2025-12-19 19:37:11 +01:00
Johannes Altmanninger
26873d4ad2 Use cfg_if expression syntax to fix let-and-return 2025-12-19 19:37:11 +01:00
Daniel Rainer
17c35217b9 lints: warn on needless_return
The needless_return lint was disabled almost two years ago, alongside
several other lints. It might have been helpful for porting from C++.
Now, I think we can enable that lint again, since omitting the returns
results in equivalent, more concise code, which should not be harder to
read.

The only manual changes in this commit are removing the lint exception
from `Cargo.toml` and removing the unnecessary returns inside `cfg_if!`
macro invocations (in `src/fd_monitor.rs` and `src/proc.rs`).
All other changes were generated by
`cargo clippy --workspace --all-targets --fix && cargo fmt`

Closes #12189
2025-12-19 19:37:11 +01:00
Daniel Rainer
b62a312cba rename: crate::wchar::prelude -> crate::prelude
Having the prelude in wchar is not great. The wchar module was empty
except for the prelude, and its prelude included things from wutil.

Having a top-level prelude module in the main crate resolves this. It
allows us to completely remove the wchar module, and a top-level prelude
module makes more sense conceptually. Putting non-wchar things into the
prelude also becomes more sensible, if we ever want to do that.

Closes #12182
2025-12-19 19:37:11 +01:00
xtqqczze
3b976a3364 clippy: fix map_unwrap_or lint
https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or

Closes #12188
2025-12-19 19:37:11 +01:00
Asuka Minato
3df88597ca add git shortlog completion
Closes #12186
2025-12-19 19:37:11 +01:00
Johannes Altmanninger
3ec603fc55 Send OSC 7 on fresh prompt (child may have changed it)
After I run a child process like "fish -C 'cd /tmp'", the terminal
will have a stale working directory.

Let's send the OSC 7 notification also once for every fresh prompt
(which is less frequent than the calls to fish_prompt).

This is not fully correct, since it will not work for cases like bind
ctrl-g 'fish -C "cd /tmp"' which allow running external commands
without creating a fresh prompt. We can fix those later, using the
code paths for bracketed paste and friends.
A minor argument for not fixing this just yet is that some people
override "__fish_update_cwd_osc" to work around bugs in their terminal.

Closes #12191
Closes #11778
Closes #11777
2025-12-19 19:36:51 +01:00
Johannes Altmanninger
5545c648d9 builtin read: don't enable TTY protocols while echo is off
When I run "read" and press enter on the foot terminal, I see a "^[[I"
echoed in the TTY.  This is because

1. builtin read creates a TtyHandoff and runs enable_tty_protocols()
2. it runs Reader::readline(), which creates another TtyHandoff.
3. before Reader::readline() returns, it unsets shell modes
   (turning ECHO back on). It also drops its TtyHandoff,
   which enables TTY protocols again.
4. Enabling focus reporting causes this terminal to send
   focus-in event immediately.

This is our fault; we should not have TTY protocols enabled while
ECHO is on.

Fix this by removing the first TtyHandoff (which seems redundant),
meaning that the second one will not try to re-enable protocols.
2025-12-19 19:13:21 +01:00
Johannes Altmanninger
765305d0e4 tty_handoff: remove stale comment
Fixes 1fe5497b5d (Remove redundant saving of TTY flags, 2025-12-14).
2025-12-19 19:13:21 +01:00
Johannes Altmanninger
a261ca2aff Reapply "Refactor common::is_console_session"
This reverts commit 556be5c4a8.

See #12192
2025-12-19 19:13:21 +01:00
Fabian Boehm
556be5c4a8 Revert "Refactor common::is_console_session"
Breaks the build on OpenBSD.

This is another case of a nix feature being unavailable on some platforms,
so start documenting them.

This reverts commit d6108e5bc0.

Fixes #12192
2025-12-19 17:36:47 +01:00
Daniel Rainer
92c5da1b25 contributing: add section about commit history
Adding this info here should hopefully reduce the number of instances
where we need to tell new contributors about it in pull requests.

Closes #12162
2025-12-18 22:22:26 +00:00
Fabian Boehm
fb161e9f4d Fix error squiggles when they run into a newline
Our error marking code:

```
function foobar
^~~~~~~^
```

runs fish_wcswidth to figure out how wide the squiggly line should be.

That function returns -1 when it runs into a codepoint that wcwidth
returns -1 for, so the marking would stop at a single `^`.

In some cases, this happens because the error range includes a
newline.

Since we already find the end of the line, and can only mark one line,
we clamp the squiggles at the end of that line.

This improves some markings.

See discussion in #12171
2025-12-18 17:56:04 +01:00
Daniel Rainer
67d78fb258 fallback: extract into crate
For faster incremental builds and to enable subsequent extraction.

Closes #12183
2025-12-18 15:13:50 +01:00
Daniel Rainer
caef2c309d build: extract some OS detection into build helper
A subsequent commit will need to test for cygwin in a new crate. On
current stable Rust (1.92) this works via `#[cfg(target_os = "cygwin)]`,
but our MSRV (1.85) does not support this. To avoid code duplication,
the OS detection logic is extracted into the build helper crate. For
now, only `detect_cygwin` is needed, but it would be inconsistent to
extract that but not the same functions for other operating systems.

Part of #12183
2025-12-18 15:13:50 +01:00
Daniel Rainer
2f37eda9d9 wchar: extract logic into separate crate
Another reduction in size of the main crate. Also allows other crates to
depend on the new wchar crate.

The original `src/wchar.rs` file is kept around for now to keep the
prelude imports working.

Part of #12182
2025-12-18 15:13:50 +01:00
Daniel Rainer
5a35acf2e7 crate splitting: create fish-common crate
Dependencies between crates must form a DAG. This means that breaking up
the large library crate requires breaking dependency cycles. The goal of
this commit is creating a crate which contains some of the main crate's
functionality, without depending on the main crate.

To start off, we only move things required for extracting `src/wchar.rs`
and `src/wchar_ext.rs`, which will happen in a subsequent commit.

Part of #12182
2025-12-18 15:13:50 +01:00
phanium
e1a6b7ea5a Support BEL terminator in OSC responses
Needed for NeoVim's :terminal.
Upstream tracking issue: https://github.com/neovim/neovim/issues/37018

Closes #12185
2025-12-18 15:13:50 +01:00
Daniel Rainer
c9fcd31480 widecharwidth: extract into separate crate
This should help with improving incremental build speed. Extracting this
code is easy, since it does not have dependencies. It also unblocks
further extraction of code which depends on widecharwidth.

Closes #12181
2025-12-18 15:13:50 +01:00
xtqqczze
d6108e5bc0 Refactor common::is_console_session
- Use nix::unistd::ttyname
- Simplify logic with pattern matching

Closes #12179
2025-12-18 15:13:50 +01:00
Johannes Altmanninger
2611646232 commandline --cursor: respect transient command line
We have logic to prevent "commandline --cursor 123" inside "complete
-C" from setting the transient commandline's cursor.

But reading this cursor ("commandline --cursor")
is fine and expected to work by completion scripts.

I don't expect there is a use case for setting the cursor while
computing completions, so let's make that an error for now.
Could revisit that.

Closes #11993
2025-12-18 15:13:50 +01:00
Johannes Altmanninger
08600d012f tests/complete: don't remove $PWD
This test removes $PWD which would cause an error if it were to start
a new process.  Normaly we would "cd -" before the removal but later
assertions expect an empty $PWD, so stay there, but don't remove it
to prevent possible surprise.
2025-12-18 15:04:06 +01:00
Johannes Altmanninger
16f14f0e89 test_wwrite_to_fd: test take random bytes, not chars
This function is not passed arbitrary chars.  The only exception is
builtin printf, which we should fix (see parent commit).  Pass random
bytes instead.
2025-12-18 15:04:06 +01:00
Johannes Altmanninger
50bcc3cf4f Isolate hack for skipping internal separator
stage_wildcards has no need for INTERNAL_SEPARATOR. Remove it already
at this stage, to avoid the need to have the workaround in the generic
decoding routine.

Unfortunately we still can't add assert!(!fish_reserved_codepoint(c));
to wcs2bytes_callback, since builtin printf still passes arbitrary
characters. We should fix that later.

Test that printf now behaves correctly for INTERNAL_SEPARATOR.
Also add a regression test for 0c9b73e317.
2025-12-18 15:04:06 +01:00
Johannes Altmanninger
ee2d99ecf3 Fix crash on kitty when clicking in scrolled command line
When the command line occupies the whole screen such that no prompt
line is shown, we crash due to overflow. Fix that.

Fixes #12178
2025-12-18 15:04:06 +01:00
Johannes Altmanninger
81e1f6937f Address some non_upper_case_globals lints 2025-12-18 15:04:06 +01:00
Johannes Altmanninger
03a3a3b52f Silence non_upper_case_globals for log categories
For some reason r-a shows this diagnostic even though we suppress it
in Cargo.toml (I still need to create a bug report).

Making these upper case would be noisy at the call sites, and it
probably doesn't help since we don't think of these identifiers
as global variables (we never access their fields directly).
Silence the r-a warning for now.

See #12156
2025-12-18 15:04:06 +01:00
Daniel Rainer
074ab45049 gettext: move gettext_impl into dedicated crate
This is part of the larger effort of splitting up fish's huge main crate
to improve incremental build speed.

We could extract more logic from `src/wutil/gettext.rs` into the new
crate, but this would require putting wide-string handling into that
crate, which I'm not sure we want. Doing so would have the advantage
that crates which don't depend on fish's main crate (i.e. all crates
other than fish's main crate itself and the binary crates built on top
of it) could then localize messages as well. This will be less relevant
if we replace gettext with Fluent for messages originating from the Rust
sources.

Closes #12108
2025-12-18 15:04:06 +01:00
Daniel Rainer
aa8f5fc77e l10n: implement status language builtin
Based on the discussion in
https://github.com/fish-shell/fish-shell/pull/11967

Introduce a `status language` builtin, which has subcommands for
controlling and inspecting fish's message localization status.

The motivation for this is that using only the established environment
variables `LANGUAGE`, `LC_ALL`, `LC_MESSAGES`, and `LANG` can cause
problems when fish interprets them differently from GNU gettext.
In addition, these are not well-suited for users who want to override
their normal localization settings only for fish, since fish would
propagate the values of these variables to its child processes.

Configuration via these variables still works as before, but now there
is the `status language set` command, which allows overriding the
localization configuration.
If `status language set` is used, the language precedence list will be
taken from its remaining arguments.
Warnings will be shown for invalid arguments.
Once this command was used, the localization related environment
variables are ignored.
To go back to taking the configuration from the environment variables
after `status language set` was executed, users can run `status language
unset`.

Running `status language` without arguments shows information about the
current message localization status, allowing users to better understand
how their settings are interpreted by fish.

The `status language list-available` command shows which languages are
available to choose from, which is used for completions.

This commit eliminates dependencies from the `gettext_impl` module to
code in fish's main crate, allowing for extraction of this module into
its own crate in a future commit.

Closes #12106
2025-12-18 15:04:06 +01:00
Johannes Altmanninger
c0b95a0ee1 readme: fix macOS version requirement 2025-12-17 17:59:24 +01:00
Daniel Rainer
dd93a7e003 test_driver: stop printing tmpdir on failure
We delete the tmpdir unconditionally once all tests are completed, so
there is no point in printing a path which will no longer exist when
analyzing test failures. The paths don't contain any useful information,
so let's delete them to avoid confusion and useless output.
2025-12-17 16:58:07 +01:00
Daniel Rainer
8064a13af9 test_driver: print littlecheck errors after result
Before this, our test driver printed littlecheck's output before the
test result (test name, duration, PASSED/FAILED/SKIPPED).
This makes it harder to read the output and is inconsistent with the way
pexpect test failures are shown.

Starting with this commit, the result is printed first for both test
types, followed by details about failures, if any.
2025-12-17 16:58:07 +01:00
Fabian Boehm
ab7b522b64 tests/tmux: Unset CDPATH
This would fail in tmux_complete
2025-12-17 16:55:58 +01:00
Fabian Boehm
0c9b73e317 Restore ~$foo expansion
This reverts commit 52ea511768 ("cleanup: remove useless `INTERNAL_SEPARATOR` handling").

I don't know how to test this given that we don't know what users exist (maybe "root"?).

Fixes #12175
2025-12-17 16:52:57 +01:00
David Adam
b29a85f45f fish.spec: clean up dependencies 2025-12-17 10:32:20 +08:00
David Adam
f0b4921ccf Fix export test on openSUSE
openSUSE defines a MANPATHSET variable, so make the match a bit tighter
2025-12-17 10:27:24 +08:00
Fabian Boehm
1d3aca2b44 fish_vi_key_bindings: Stop using alias
`alias` is terrible, but the main downside of this is that it shows up
in the output of `alias`, which it shouldn't because the user didn't
define these.
2025-12-16 20:27:47 +01:00
Fabian Boehm
574a8728af cmake: Stop setting RUSTFLAGS
This appends "-g" to RUSTFLAGS if the cmake build type is debug or
RelWithDebInfo.

However, these are already mapped to cargo profiles that enable these
things, see https://doc.rust-lang.org/cargo/reference/profiles.html:

> The debug setting controls the -C debuginfo flag which controls the
amount of debug information included in the compiled binary.

(`rustc -g` is equivalent to `debuginfo=2`)

By setting $RUSTFLAGS in cmake, we bake it into the generated
makefile (/ninja thing), which is surprising.

See #12165
2025-12-16 16:52:25 +01:00
Johannes Altmanninger
e7c1a6a67d Fix build on macOS 2025-12-16 15:36:25 +01:00
David Adam
54127e1028 CMake: stop trying to install groff macros
Dropped in 377abde112.
2025-12-16 22:31:48 +08:00
David Adam
a5330c7ba2 RPM/Debian packaging: add sphinx and man dependencies 2025-12-16 21:49:28 +08:00
David Adam
377abde112 drop obsolete groff macros 2025-12-16 21:43:16 +08:00
Johannes Altmanninger
62628f6fb1 CI: respect dependency cooldown in "uv lock check"
Fixes 1db4dc4c3e (build_tools/update-dependencies.sh: add dependency
cooldown for Python packages, 2025-12-16).
2025-12-16 14:22:24 +01:00
Johannes Altmanninger
64da7ca124 Run build_tools/update-dependencies.sh
Still need to upgrade Cargo deps.
2025-12-16 13:25:27 +01:00
Johannes Altmanninger
1db4dc4c3e build_tools/update-dependencies.sh: add dependency cooldown for Python packages
See
https://blog.yossarian.net/2025/11/21/We-should-all-be-using-dependency-cooldowns
2025-12-16 13:19:16 +01:00
Johannes Altmanninger
ebc140a3ea Hack path component movement to skip escaped spaces
Path component movement is not aware of fish syntax -- and we should
be careful as we teach it some fish syntax, because it is expected
to be used on command lines that have unclosed quotes etc.

Tab completion typically uses backslashes to escape paths with spaces.
Using ctrl-w on such path components doesn't work well because it
stops at the escaped space.

Add a quick hack to change it to skip over backslashed spaces.  Note that
this isn't fully correct because it will treat backslashes inside
quotes the same way.  Not sure what we should do here.  We could have
ctrl-w erase all of this

	"this is"'only\ one 'path component

But that might be surprising.
Regardless of what we end up with, skipping over backslashed whitespace
seems totally fine, so add that now

Closes #2016
2025-12-16 13:16:20 +01:00
Johannes Altmanninger
5e401fc6ea tokenizer test: slim down macro 2025-12-16 13:16:20 +01:00
ELginas
608269320e Added backward-path-component, forward-path-component and kill-path-component
Closes issue #12127

Closes #12147
2025-12-16 13:16:20 +01:00
Johannes Altmanninger
c3a9edceac Truncate autosuggestion lines only at screen edge
From each logical line in the autosuggestion, we show all or nothing.
This means that we may truncate too early -- specifically 1  + the
number of soft-wrappings in this line. Fix that.

See also #12153
2025-12-16 13:16:20 +01:00
Johannes Altmanninger
32cee9caec screen: remove obsolete type 2025-12-16 13:16:20 +01:00
Asuka Minato
f417cbc981 Show soft-wrapped portions in autosuggestions
Co-authored-by: Johannes Altmanninger <aclopte@gmail.com>

Closes #12153
2025-12-16 13:16:20 +01:00
Johannes Altmanninger
92c814841a Work around clippy assertions_on_constants false positive
Upstream issue: https://github.com/rust-lang/rust-clippy/issues/16242

Closes #12159
2025-12-16 13:04:43 +01:00
Toyosatomimi no Miko
bf38e1beca Rename FLOG, FLOGF, to lowercase flog, flogf
Closes #12156
2025-12-16 13:04:43 +01:00
Toyosatomimi no Miko
17ba602acf Use PascalCase for Enums
Part of #12156
2025-12-16 13:04:43 +01:00
チセ
5d37698ef8 fix: __fish_systemctl_services mode handling
Closes #12157
2025-12-16 13:04:16 +01:00
Johannes Altmanninger
7a7c0d6490 __fish_systemctl_services: remove code clone 2025-12-16 13:04:16 +01:00
Johannes Altmanninger
d88c0674a3 __fish_systemctl_services: early return 2025-12-16 13:04:16 +01:00
Johannes Altmanninger
0306ec673f __fish_systemctl_services: remove unused argument 2025-12-16 13:04:16 +01:00
Nahor
b14b6c9f42 fish_git_prompt: fix incorrect variable assignment
The intent was to create two local variables, not to assign one's name
to the other
2025-12-16 08:00:24 +01:00
Fabian Boehm
8061c41c9b docs/math: Clarify what it is for
See #12163
2025-12-15 19:23:01 +01:00
Johannes Altmanninger
190d367bc4 Use globals for color variables, react to light/dark mode
Implicitly-universal variables have some downsides:
- It's surprising that "set fish_color_normal ..."
  and "set fish_key_bindings fish_vi_key_bindings" propagate to other
  shells and persist, especially since all other variables (and other
  shells) would use the global scope.
- they don't play well with tracking configuration in Git.
- we don't know how to roll out updates to the default theme (which is
  problematic since can look bad depending on terminal background
  color scheme).

It's sort of possible to use only globals and unset universal variables
(because fish only sets them at first startup), but that requires
knowledge of fish internals; I don't think many people do that.

So:
- Set all color variables that are not already set as globals.
  - To enable this do the following, once, after upgrading:
    copy any existing universal color variables to globals, and:
    - if existing universal color variables exactly match
      the previous default theme, and pretend they didn't exist.
    - else migrate the universals to ~/.config/fish/conf.d/fish_frozen_theme.fish,
      which is a less surprising way of persisting this.
    - either way, delete all universals to do the right thing for most users.
- Make sure that webconfig's "Set Theme" continues to:
  - instantly update all running shells
    - This is achieved by a new universal variable (but only for
      notifying shells, so this doesn't actually need to be persisted).
      In future, we could use any other IPC mechanism such as "kill -SIGUSR1"
      or if we go for a new feature, "varsave" or "set --broadcast", see
      https://github.com/fish-shell/fish-shell/issues/7317#issuecomment-701165897
      https://github.com/fish-shell/fish-shell/pull/8455#discussion_r757837137.
  - persist the theme updates, completely overriding any previous theme.
    Use the same "fish_frozen_theme.fish" snippet as for migration (see above).
    It's not meant to be edited directly. If people want flexibility
    the should delete it.
    It could be a universal variable instead of a conf snippet file;
    but I figured that the separate file looks nicer
    (we can have better comments etc.)
- Ask the terminal whether it's using dark or light mode, and use an
  optimized default. Add dark/light variants to themes,
  and the "unknown" variant for the default theme.
  Other themes don't need the "unknown" variant;
  webconfig already has a background color in context,
  and CLI can require the user to specify variant explicitly if
  terminal doesn't advertise colors.
- Every variable that is set as part of fish's default behavior
  gets a "--label=default" tacked onto it.

  This is to allow our fish_terminal_color_theme event handler to
  know which variables it is allowed to update. It's also necessary
  until we revert 7e3fac561d (Query terminal only just before reading
  from it, 2025-09-25) because since commit, we need to wait until
  the first reader_push() to get query results.  By this time, the
  user's config.fish may already have set variables.

  If the user sets variables via either webconfig, "fish_config theme
  {choose,save}", or directly via "set fish_color_...", they'd almost
  always remove this label.
- For consistency, make default fish_key_bindings global
  (note that, for better or worse, fish_add_path still remains as
  one place that implicitly sets universal variables, but it's not
  something we inject by default)
- Have "fish_config theme choose" and webconfig equivalents reset
  all color variables. This makes much more sense than keeping a
  hardcoded subset of "known colors"; and now that we don't really
  expect to be deleting universals this way, it's actually possible
  to make this change without much fear.

Should have split this into two commits (the changelog entries are
intertwined though).

Closes #11580
Closes #11435
Closes #7317
Ref: https://github.com/fish-shell/fish-shell/issues/12096#issuecomment-3632065704
2025-12-14 17:03:03 +01:00
Johannes Altmanninger
f1f14cc8fa input: extract function for enqueuing query response 2025-12-14 16:29:14 +01:00
Johannes Altmanninger
707bfe3ce6 fish_config theme show: list default scheme first
Webconfig does the same ("Add the current scheme first, then the
default.").
2025-12-14 16:29:14 +01:00
Johannes Altmanninger
d8f1a2a24f Move sample_prompts/themes to share/
They are used by "fish_config" CLI too, so no need to make them
private to webconfig.  Putting them at top-level seems simpler overall.
2025-12-14 16:29:14 +01:00
Johannes Altmanninger
dbdecaba6d fish_config: remove hardcoded set of colors to erase
This is incomplete, and we'll solve the problem differently. For now,
leave colors that are not mentioned in the theme.  This causes problems
for sparse themes, but a following commit will fix that by making
"fish_config theme choose" erase all variables set by a previous
invocation (but not erase variables set by the user).  Only webconfig
won't do that since it (historically) uses copy semantics, but we
could change that too if needed.

This also breaks the guarantee mentioned by this comment in webconfig:

> Ensure that we have all the color names we know about, so that if the
> user deletes one he can still set it again via the web interface

which should be fine because:
1. a following commit will always set all globals at interactive init,
   so colors should only be missing in edge cases ("fish -c fish_config").
2. it's easy to recover from by setting a default theme.
2025-12-14 16:26:14 +01:00
Johannes Altmanninger
66f6493fbf fish_config theme dump: speed up
For better or worse, "set -L" prints all of $history, which makes
"fish_config theme show" very slow.  Fix this by only printing the
relevant variables.  While at, make the escaping function use the
shared subset of fish and POSIX shell quoting syntax, to allow a
following commit to use shlex.split().
2025-12-14 16:25:14 +01:00
Johannes Altmanninger
76e0f9a3e8 fish_config: extract function for iterating over themes 2025-12-14 16:24:13 +01:00
Johannes Altmanninger
697afdefeb fish_config: extract some functions for reading theme files
A following commit wants to add some more logic and call some of
fish_config's private APIs from webconfig.  We could keep it all in
one file but I'm not sure we should so try the splitting we usually do.
2025-12-14 16:23:53 +01:00
Johannes Altmanninger
aba89f9088 docs: one sentence per line 2025-12-14 16:23:37 +01:00
Johannes Altmanninger
344187e01a fish_config: extract function 2025-12-14 16:23:37 +01:00
Johannes Altmanninger
9f11de24d4 webconfig.py: remove dead code 2025-12-14 16:23:37 +01:00
Johannes Altmanninger
7996637db5 Make fish immediately show color changes again
Historically, fish tried to re-exec the prompt and repaint immediately
when a color variable changed.

Commit f5c6306bde (Do not repaint prompt on universal variable events,
but add event handler for fish_color_cwd, 2006-05-11) restricted this
to only variables used in the prompt like "fish_color_cwd".

Commit 0c9a1a56c2 (Lots of work on web config Change to make fish
immediately show color changes, 2012-03-25) added repainting back
for all colors (except for pager colors).

Commit ff62d172e5 (Stop repainting in C++, 2020-12-11) undid that.

Finally, 69c71052ef (Remove __fish_repaint, 2021-03-04) removed the
--on-variable repaint handlers added by the first commit.

So if color changes via either
1. webconfig
2. an event handler reacting to the terminal switching between light/dark mode
3. a binding etc.

then we fail to redraw. Affects both colors used in prompts and those
not used in prompts.

Fix that by adding back the hack from the second commit.  This is
particularly important for case 2, to be added by a following commit.

In future we can remove this hack by making "--on-variable" take
a wildcard.
2025-12-14 16:23:37 +01:00
Johannes Altmanninger
d67cdf5f6f Update changelog 2025-12-14 16:23:37 +01:00
Johannes Altmanninger
7dd2004da7 Re-enable focus reporting on non-tmux
I can no longer reproduce the issue described in bdd478bbd0 (Disable
focus reporting on non-tmux again for now, 2024-04-18).  Maybe the
TTY handoff changes fixed this.  Let's remove this workaround and
enable focus reporting everywhere.
2025-12-14 16:23:37 +01:00
Johannes Altmanninger
4000503c03 DRY DSR sequences a bit 2025-12-14 16:23:37 +01:00
Johannes Altmanninger
7c994cd784 fish_config: extract some functions for finding theme variables etc.
To be used in a following commit.
2025-12-14 16:23:37 +01:00
Johannes Altmanninger
e68ea35f02 fish_config: improve consistency
The theme variable filter applies to whole lines, so it's weird to
only apply it to the first token, and we don't do that elsewhere.
2025-12-14 16:21:16 +01:00
Johannes Altmanninger
5cc953b18d Default theme variants specialized for light/dark mode
Start by converting the "default" theme's colors to RGB, using
XTerm colors since they are simple, see
https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit.

Then make the following changes:

for both default-light and default-dark:
- Reinstate fish_color_command/fish_color_keyword as blue since one
  of the reasons in 81ff6db62d (default color scheme: Make commands
  "normal" color, 2024-10-02) doesn't hold anymore.
- bravely replace "fish_pager_color_selected_background -r" with
  something that hopefully matches better.
  Note we can't trivially use the fallback to
  "fish_color_search_match", since for unknown reasons,
  "fish_pager_color_selected_background" is only for background and
  the others are for foreground.

for default-light:
- brgreen (00ff00) looks bad on light background, so replace it with green (00cd00).
  This means that we no longer use two different shades of green in the default theme
  (which helps address the "fruit salad" mentioned 81ff6db62d).
- yellow (cdcd00) looks bad on light background, make it darker (a0a000)
- fish_pager_color_progress's --background=cyan (00cdcd) seems a bit too bright, make it darker
  - same for other uses of cyan (also for consistency)
  - this means fish_color_operator / fish_color_escape can use 00cdcd I guess.
for default-dark:
- use bright green (00ff00) for all greens
- use bright blue (5c5cff) instead of regular blue for command/keyword
- make autosuggestions a bit lighter (9f9f9f instead of 7f7f7f) 
- etc.. I think I made the two themes mostly symmetrical.

Part of #11580
2025-12-14 16:21:16 +01:00
Johannes Altmanninger
f264ee0b10 webconfig theme: rename "fish-default" theme
The "fish-" prefix is not needed here and it would add more noise to
a following commit which adds default-{dark,light} variants that use
24 bit RGB colors.
2025-12-14 15:44:58 +01:00
Johannes Altmanninger
3c6978c038 webconfig theme: minor changes to default theme
Don't set fish_pager_color_completion,
it already falls back to fish_color_normal.

Also remove a comment, it's obvious and maybe no longer
true, since 8 bit colors are widely available now, see
https://github.com/fish-shell/fish-shell/issues/11344#issuecomment-3568265178

Though we prefer 24 bit colors wherever we can.
2025-12-14 15:44:58 +01:00
Johannes Altmanninger
d5732b132e webconfig themes: consistent quoting 2025-12-14 15:44:58 +01:00
Johannes Altmanninger
ae22cc93db webconfig themes: shell-friendly filenames
For historical reasons (namely the webconfig origin), our theme
names contain spaces and uppercase letters which can be inconvenient
when using the "fish_config theme choose" shell command.  Use more
conventional file names.

Web config still uses the pretty names, using the ubiquitous "# name:"
property.
2025-12-14 15:44:58 +01:00
Johannes Altmanninger
51e551fe5f doc/fish_config: fix copy-paste errors 2025-12-14 15:44:58 +01:00
Johannes Altmanninger
33cb8679ba fish_config theme show: remove unused argument 2025-12-14 15:44:58 +01:00
Johannes Altmanninger
655b1aa7a1 fish_prompt: remove stray initialization of fish_color_status
I can't reproduce the problem mentioned in 0420901cb2 (default prompt:
Set fish_color_status if unset, 2021-04-11).
2025-12-14 15:44:58 +01:00
Johannes Altmanninger
daba5fdbcd fish_delta: acknowledge workaround for no-stdin-in-cmdsub 2025-12-14 15:44:58 +01:00
Johannes Altmanninger
01bd380d00 webconfig.py: reuse "fish_config theme dump" 2025-12-14 15:44:58 +01:00
Johannes Altmanninger
801c4f8158 webconfig.py: simplify "functions" output parsing
This outputs one-item-per-line if stdout is a terminal.
2025-12-14 15:44:58 +01:00
Johannes Altmanninger
e331e30e38 __fish_config_interactive: minor style change 2025-12-14 15:44:58 +01:00
Johannes Altmanninger
7bd30ac3c4 __fish_config_interactive: inline function 2025-12-14 15:44:58 +01:00
Johannes Altmanninger
5631a7ec86 Fix color variable filter regex 2025-12-14 15:44:58 +01:00
Johannes Altmanninger
a9c43e7396 webconfig js: remove redundant code clone
The server side (webconfig.py) will already reset missing colors
to empty.
2025-12-14 15:44:58 +01:00
Johannes Altmanninger
2a6c0e4437 webconfig js: remove obsolete workaround
I cannot reproduce.
2025-12-14 15:44:58 +01:00
Johannes Altmanninger
7f224d0dfd DRY color variable names 2025-12-14 15:42:56 +01:00
Johannes Altmanninger
f818002f38 webconfig.py: use full color names
Optimize for ease of maintenance.
2025-12-14 15:42:56 +01:00
Johannes Altmanninger
6b8e82946a webconfig.py: remove dead code
Both descriptions for bindings and color variables are unused.
2025-12-14 15:42:56 +01:00
Johannes Altmanninger
faaff2754b webconfig.py: fix stale return type
This function returns a heterogeneous list (containing dicts/lists)
by accident. Fix that and reduce code duplication.  Fixes c018bfdb4d
(Initial work to add support for angularjs, 2013-08-17).
2025-12-14 15:42:12 +01:00
Johannes Altmanninger
5c0e72bb89 webconfig.py: simplify 2025-12-14 15:40:03 +01:00
Johannes Altmanninger
b3b7d2cb00 webconfig js: remove debug log statement 2025-12-14 15:40:03 +01:00
Johannes Altmanninger
f503dcb92d webconfig js: resolve a comment 2025-12-14 15:37:46 +01:00
Johannes Altmanninger
7f1b53a9f1 webconfig.py: remove a workaround for Python 2 2025-12-14 15:37:46 +01:00
Johannes Altmanninger
938e780007 fish_config theme save: remove dead code 2025-12-14 15:37:46 +01:00
Johannes Altmanninger
3e7c5ae399 __fish_config_interactive: make config file initialization independent of uvars
Helps the following commits.
2025-12-14 15:37:46 +01:00
Johannes Altmanninger
548f37eabb Simplify make_tarball.sh
Note that we don't need to set the mtime anymore -- it was added in
5b5b53872c (tarball generation: include config.h.in, set mode and
ownership, 2013-09-09) for autotools' multi-stage builds.
2025-12-14 15:37:46 +01:00
Johannes Altmanninger
135fc73191 Remove man/HTML docs from tarball, require Sphinx instead
Advantages of prebuilt docs:
- convenient for users who compile the tarball
- convenient for packagers who don't have sphinx-build packaged
  (but packaging/installing that should be easy nowadays?
  see https://github.com/fish-shell/fish-shell/issues/12052#issuecomment-3520336984)

Disadvantages:
- Extra build stage / code path
- Users who switch from building from tarball to building from source
  might be surprised to lose docs.
- People put the [tarballs into Git repositories](https://salsa.debian.org/debian/fish), which seems weird.

Remove the tarball.

Let's hope this is not too annoying to users who build on outdated
distros that don't have sphinx -- but those users can probably use
our static builds, skipping all compilation.

To avoid breaking packagers who use `-DBUILD_DOCS=OFF` (which still
installs prebuilt docs), rename the option.

Closes #12088
2025-12-14 15:37:46 +01:00
Johannes Altmanninger
1fe5497b5d Remove redundant saving of TTY flags
The logic added by 2dbaf10c36 (Also refresh TTY timestamps
after external commands from bindings, 2024-10-21) is obsoleted
by TtyHandoff.  That module is also responsible for calling
reader_save_screen_state after it writes to the TTY, so we don't
actually need to check if it wrote anything.
2025-12-14 15:37:46 +01:00
Johannes Altmanninger
6b85450dea Silence error log for legacy escape specifically
This happens after pressing escape while kitty keyboard protocol is
disabled (or not supported).
2025-12-14 15:37:46 +01:00
Johannes Altmanninger
2309c27a2d Replace ifdef with cfg! 2025-12-14 10:24:49 +01:00
exploide
8b5d66def8 completions: added arp-scan
Closes #12158
2025-12-14 08:44:08 +01:00
xtqqczze
27852a6734 fix: clippy::ptr_as_ptr
https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr

Closes #12136
2025-12-11 17:46:58 +01:00
xtqqczze
cc29216ea9 fix: clippy::ptr_cast_constness
https://rust-lang.github.io/rust-clippy/master/index.html#ptr_cast_constness

Part of #12136
2025-12-11 17:46:58 +01:00
xtqqczze
831411ddd5 fix: clippy::ref_as_ptr
https://rust-lang.github.io/rust-clippy/master/index.html#ref_as_ptr

Part of #12136
2025-12-11 17:46:58 +01:00
xtqqczze
ec77c34ebe fix: clippy::borrow_as_ptr
https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr

Part of #12136
2025-12-11 17:46:58 +01:00
Johannes Altmanninger
d1c7a4b6e7 Don't silence errors in fish_key_bindings/fish_user_key_bindings
We silence erros from the key binding function since 2c5151bb78 (Fix
bug in key binding code causing unneeded error messages, 2007-10-31).

Reasons are
1. fish_key_bindings is not a valid function
2. fish_key_bindings runs "bind -k" for things that are not supported (#1155)

Both reasons are obsolete:
1. we already check that earlier
2. "-k" is gone. (Also, we could have silenced "bind -k" invocations instead).
2025-12-11 17:46:58 +01:00
Nahor
7fc27e9e54 cygwin: improve handling of .exe file extension
- Prefer the command name without `.exe` since the extension is optional
when launching application on Windows...
- ... but if the user started to type the extension, then use it.
- If there is no description and/or completion for `foo.exe` then
use those for `foo`

Closes #12100
2025-12-10 16:15:41 +01:00
Johannes Altmanninger
a4b949b0ca Remove uname= prefix from query-os-name
Discussion with terminal authors indicates a slight preference for
removing the prefix.  We don't need it at this point, since we only
use it to detect macOS clients.
2025-12-10 16:15:41 +01:00
Johannes Altmanninger
64196a2a9e complete: fix erasing wrapped functions
Two issues:
1. "complete -e foo" doesn't erase wrap definitions
2. "complete -e foo --wraps" erases things other than the wrap definitions

Fix them.

There's another issue, the second erase command leaves around an
extra complete entry. Let's leave that for later.

Fixes #12150
2025-12-10 16:15:41 +01:00
Johannes Altmanninger
353ecfadaf complete: fix naming convention 2025-12-10 16:15:41 +01:00
Johannes Altmanninger
0e9ceb154e fish_config theme dump: check for extra arguments 2025-12-10 16:15:41 +01:00
Johannes Altmanninger
baaf06b2c6 cmake/Tests: enable Cargo features
For unknown reasons, we don't enable Cargo features on the test target.
Let's try to, simplifying things overall.

Ref: https://github.com/fish-shell/fish-shell/issues/12120#issuecomment-3627842316
2025-12-10 16:15:41 +01:00
Johannes Altmanninger
106f7b86dc cmake/Tests: split long line 2025-12-10 16:15:41 +01:00
Daniel Rainer
64e3b419b6 ci: disable pexpect and tmux tests in sanitizer jobs
These tests are unreliable in CI when running with address sanitiation
enabled, resulting in intermittent CI failures.
Disable them to get rid of the many false positives to reduce annoyance
and to avoid desensitization regarding failures of the asan CI job.

Suggested in
https://github.com/fish-shell/fish-shell/pull/12132#issuecomment-3605639954

Closes #12142
Closes #12132
Closes #12126
2025-12-10 16:15:41 +01:00
Kristof Mattei
3237efc582 fix: invoke the rm command, bypassing custom rm functions which might block
Closes #12141
2025-12-10 16:15:41 +01:00
xtqqczze
717d301b7f refactor: replace addr_of! macros with raw pointer syntax
`addr_of!` and `addr_of_mut!` have been soft-deprecated as of Rust 1.82.0.

Closes #12139
2025-12-10 16:15:41 +01:00
Daniel Rainer
6a36d92173 lint: fix function_casts_as_integer lint in nightly
This lint is intended to make it harder to unintentionally cast a
function to an int. We do want to store function pointers in a usize
here, so add the intermediate cast suggested by the lint.

Closes #12131
2025-12-10 16:15:41 +01:00
Daniel Rainer
75c005a4d4 lint: convert is_some+unwrap into if let
This fixes an `unnecessary_unwrap` lint shown by nightly Clippy.

Closes #12130
2025-12-10 16:15:41 +01:00
Johannes Altmanninger
07389055f1 fish_indent: handle invalid arguments 2025-12-10 16:15:41 +01:00
Johannes Altmanninger
692e6d57cf fish_indent --ansi to emit SGR0 before final newline
Buggy programs parsing "fish_indent --ansi" output break because
we wemit SGR0 after the final newline.  I suppose this is weird,
and there's no need to wait until after the \n to emit SGR0 so let's
move it before that.

Closes #12096
2025-12-10 16:15:41 +01:00
Johannes Altmanninger
7952545460 Prefer cfg! over #[cfg] for embedding
We should isolate conditionally-compiled code as much as possible,
to allow the compiler to do more work, and to simplify code.
Do that for the embedding definition for __fish_build_paths, like we
already do for "Docs".  Duplicate the workaround we use to set the
embedded struct to a "virtually empty" directory.  Don't set it to
"$FISH_RESOLVED_BUILD_DIR", because that directory can contain >50k
files, and in debug mode listing files (even if all are excluded)
would take a second.

Ref: https://github.com/fish-shell/fish-shell/pull/12103#issuecomment-3592280477

This should also fix #12120.
2025-12-10 16:15:41 +01:00
Johannes Altmanninger
4aa967cd5d builtins: extract struct for input value from arg or stdin line 2025-12-10 16:15:07 +01:00
Daniel Rainer
3e2336043a gettext-extract: fix race condition
Multiple gettext-extraction proc macro instances can run at the same
time due to Rust's compilation model. In the previous implementation,
where every instance appended to the same file, this has resulted in
corruption of the file. This was reported and discussed in
https://github.com/fish-shell/fish-shell/pull/11928#discussion_r2488047964
for the equivalent macro for Fluent message ID extraction. The
underlying problem is the same.

The best way we have found to avoid such race condition is to write each
entry to a new file, and concatenate them together before using them.
It's not a beautiful approach, but it should be fairly robust and
portable.

Closes #12125
2025-12-10 16:15:07 +01:00
Johannes Altmanninger
c1db2744cf Revert "translations: Add Spanish translation"
This reverts commit 0fb5ab4098 because
review comments have not yet been addressed.
2025-12-10 16:15:07 +01:00
Daniel Rainer
c6252967ab rust-embed: update to 8.9.0
This eliminates the proc-macro panic occasionally observed in CI,
instead ignoring paths which cannot be canonicalized.
See #12120.

While this does not address the underlying issue of why the proc-macro
fail happens, it should result in builds no longer failing, and since
they failed in the case where no embedding is desired, there should be
no issue with ignoring failed path canonicalization, since we do not
want to embed anything in these cases.
2025-12-09 11:34:08 +08:00
Julio Napurí
0fb5ab4098 translations: Add Spanish translation
Closes #12089
2025-12-08 18:34:15 +01:00
Daniel Rainer
de3d390391 cleanup: remove unused import
Closes #12151
2025-12-08 17:08:51 +00:00
Daniel Rainer
76b0961e91 tempfile: use new logic for uvar tests
The old approach does not properly protect from concurrent tests
accessing the same tempdir. Fix this by moving to the new tempfile
functionality. This also eliminates the need to explicitly create and
delete the directory. There is also no longer a reason to specify names
for the temporary directories, since name collisions are avoided using
more robust means, and there is little benefit to having the names in
the directory name.

Closes #12030
2025-12-08 16:51:20 +00:00
Daniel Rainer
c0f91a50fa fix: avoid race conditions of mkstemp
The `mkstemp` function opens files without setting `O_CLOEXEC`. We could
manually set this using `fnctl` once the file is opened, but that has
the issue of introducing race conditions. If fish `exec`s in another
thread before the `fnctl` call completes, the file would be left open.

One way of mitigating this is `mkostemp`, but that function is not
available on all systems fish supports, so we can't rely on it.

Instead, build our own tempfile creation logic which uses the `rand`
crate for getting entropy and relies on Rust's stdlib for the rest.
The stdlib functions we use set `O_CLOEXEC` by default.

For directory creation we keep using `mkdtemp`, since there we don't
open anything. We could replace this by extending our custom logic a
bit, which would allow us to drop the `nix` dependency for our
`tempfile` crate, but since the code is simpler as it is now and we need
nix in fish's main crate, there is no need to modify the directory
creation code.

Part of #12030
2025-12-08 16:51:20 +00:00
Daniel Rainer
77fbd0a005 rand: use ThreadRng wherever reseeding is ok
`SmallRng` was chosen in part due to limitation of old macOS versions.
This is no longer relevant, since the affected versions are not
supported by Rust anymore.

Switch everything which does not need fixed sequences based on a seed to
`ThreadRng`, which has better cryptographic properties and is
occasionally reseeded. Performance differences should not matter much,
since initialization of `ThreadRng` is not that expensive and it's
generation speed is easily fast enough for our purposes.

In some cases, like tests and the `random` builtin, we want a PRNG which
consistently produces the same sequence of values for a given seed.
`ThreadRng` does not do this, since it is occasionally reseeded
automatically. In these cases, we keep using `SmallRng`.

Part of #12030
2025-12-08 16:51:20 +00:00
Daniel Rainer
c9ab2c26aa Update rand crate to 0.9.2
Function names containing `gen` have been deprecated to avoid conflicts
with the Rust 2024 keyword, so this commit also switches to the new
names.

Part of #12030
2025-12-08 16:51:20 +00:00
Daniel Rainer
b1769658f5 cleanup: remove obsolete comment
Since we changed our MSRV to 1.85, macOS < 10.12 is no longer supported,
so the comment removed here is no longer relevant.

The workaround for old macOS was introduced in
9337c20c2 (Stop using the getrandom feature of the rand crate, 2024-10-07)

We might want to reconsider which RNGs and other features of the `rand`
crate we use, but there is no immediate need to do so, since the current
approach seems to have worked well enough.

Part of #12030
2025-12-08 16:51:20 +00:00
Gleb Smirnov
3518d4f6ad fix: treat run0 and please as a prefix in __fish_man_page
Closes #12145
2025-12-08 21:30:29 +08:00
Jesse Harwin
aa4ebd96f9 Fixed typo
Missed a spot in the first attempt #12135 as pointed out by [xtqqczze](https://github.com/fish-shell/fish-shell/pull/12135#issuecomment-3609441130)

Small typo found in doc_src/cmds/fish_opt.rst and tests/checks/argparse.fish. `valu` to `value`
2025-12-08 21:19:43 +08:00
Asuka Minato
656b39a0b3 Also show case-insensitive prefix matches in completion pager
[ja: made some changes and added commit message]

Fixes #7944
Closes #11910
2025-12-05 16:12:34 +01:00
Johannes Altmanninger
fceb600be5 math: remove logb
As discussed in #12112, this is a false friend (the libc logb()
does something else), and without keyword arguments or at least
function overloading, this is hard to read.
Better use "/ log(base)" trick.
2025-12-05 16:06:22 +01:00
PowerUser64
47c773300a feat(math): add logb function
Closes #12112
2025-11-30 09:20:33 +01:00
Daniel Rainer
2d29749eae printf: improve Unicode escape + PUA handling
The old handling of Unicode escape sequences seems partially obsolete
and unnecessarily complicated. For our purposes, Rust's u32 to char
parsing should be exactly what we want.

Due to fish treating certain code points specially, we need to check
if the provided character is among the special chars, and if so we need
to encode it using our PUA scheme. This was not done in the old code,
but is now.
Add tests for this.
Fixes #12081

Rework the error message for invalid code points.
The old message about invalid code points not yet being supported seems
odd. I don't think we should support this, so stop implying that we
might do so in the future.
In the new code, indicating that a Unicode character is
out of range is also not ideal, since the range is not contiguous.
E.g. `\uD800` is invalid, but \U0010FFFF is valid.
Refrain from referring to a "range" and instead just state that the
character is invalid.

Move formatting of the escape sequence into Rust's `format!` to simplify
porting to Fluent.

Closes #12118
2025-11-30 09:20:33 +01:00
Daniel Rainer
22a252d064 gettext: rebuild extract macro on env var change
While it's not necessary to rebuild the proc macro for extraction when
the env var controlling the output location changes, this way everything
using the macro will automatically be rebuilt when this env var changes,
making it impossible to forget adding this to other `build.rs` files.

For now, keeping the rebuild instructions in fish's main crate's
`build.rs` would be fine as well, but if we start breaking this crate
into smaller parts, it would become annoying to add the rebuild command
in every crate depending on gettext extraction.

Closes #12107
2025-11-30 09:20:33 +01:00
Daniel Rainer
4642b28ea7 gettext: enable default features for extraction
There was a mismatch between the extraction done from
`build_tools/check.sh` and the one done in
`build_tools/fish_xgettext.fish`, resulting in messages guarded by
default features being extracted by the former but not the latter.
This brings them in sync.

Ideally, we would enable all features for extraction, but compiling with
`--all-features` is broken and manually keeping the features lists
updated is tedious and error prone, so we'll settle on only using
default features for now.

Closes #12103
2025-11-30 09:20:33 +01:00
Daniel Rainer
b8662f9c7f cleanup: move use statements to start of module
Closes #12102
2025-11-30 09:20:33 +01:00
Daniel Rainer
9af85f4c3b gettext: separate catalog lookup from interning
Improve separation of concerns by handling catalog lookup in the
`gettext_impl` module, while taking care of string interning and string
type conversion in the outer `gettext` function as before.

This improves isolation, meaning we no longer have to expose the
catalogs from `gettext_impl` to its parent module.

It also helps with readability.

Part of #12102
2025-11-30 09:20:33 +01:00
Daniel Rainer
aa742f0b57 refactor: eliminate duplicate enum variant spec
Modify the `str_enum!` macro to be able to handle multiple strings per
enum variant. This means that no separate step is required for defining
the enum. Instead definition of the enum and implementation of the
string conversion functions is now combined into a single macro
invocation, eliminating code duplication, as well as making it easier to
add and identify aliases for an enum variant.

Closes #12101
2025-11-30 09:20:33 +01:00
Daniel Rainer
e648da98da cleanup: sort StatusCmd variants alphabetically
Part of #12101
2025-11-30 09:20:33 +01:00
Daniel Rainer
94367b08ba cleanup: do not set enum variant to value
This seems to be a left-over detail from the C++ version. Setting the
first element to 0 does not have any effect, since that's the default,
and setting it to 1 seems to serve no purpose in the current code.

Part of #12101
2025-11-30 09:20:33 +01:00
Daniel Rainer
97b45dc705 cleanup: remove duplicate enum variants
There is no difference in how these two variants are treated. Keeping
the old command name around for compatibility is nice, but it does not
need to have its own enum variant.

Part of #12101
2025-11-30 09:20:33 +01:00
Johannes Altmanninger
198768a1c7 CONTRIBUTING: remove -n from sphinx-build, add cmake target
The -n flag adds warnings about preexisting problems because we don't
check it in CI, so let's not recommend that.

Not everyone uses cmake but it's still the source of truth for docs,
so mention the cmake incantation.  Though it seems like the direct
sphinx-build invocation works just fine, so keep it first.
2025-11-30 09:20:33 +01:00
Johannes Altmanninger
7c27c1e7d0 editorconfig / doc_src: trim trailing whitespace
Commit 0893134543 (Added .editorconfig file (#3332) (#3313),
2016-08-25) trimmed trailing whitespace  for Markdown file (which do
have significant trailing whitespace) but ReStructuredText does not,
and none of our Markdown files cares about this, so let's clean up
whitespace always.
2025-11-30 09:20:33 +01:00
Daniel Rainer
9f8d8ebc2c cleanup: remove unused message
This message is no longer used since we unconditionally embed files now.
The `#[allow(dead_code)]` annotation was needed to avoid warnings while
ensuring that message extraction was not broken.

Closes #12099
2025-11-30 09:20:33 +01:00
Johannes Altmanninger
87a312ccba test/tmux-prompt: fix quoting that looks like a bug 2025-11-30 09:20:33 +01:00
Johannes Altmanninger
838f48a861 Show completions from separator only if there are no whole-token ones
Completions for "foo bar=baz" are sourced from
1. file paths (and custom completions) where "bar=baz" is a subsequence.
2. file paths where "baz" is a subsequence.

I'm not sure if there is ever a case where 1 is non-empty and we
still want to show 2.

Bravely include this property in our completion ranking, and only
show the best ones.

This enables us to get rid of the hack added by b6c249be0c (Back out
"Escape : and = in file completions", 2025-01-10).

Closes #5363
2025-11-30 09:20:33 +01:00
Johannes Altmanninger
52286f087d builtins set: only one space between list elements 2025-11-30 09:20:33 +01:00
Johannes Altmanninger
902d4be664 __fish_git_remotes: use POSIX regex syntax
Fixes https://github.com/fish-shell/fish-shell/pull/11941#issuecomment-3574089216
2025-11-30 09:19:03 +01:00
Johannes Altmanninger
16a91f8e3c Fix regression interpreting prompt y on multiline commandlines
Fixes a772470b76 (Multi-line autosuggestions, 2025-05-18)
Fixes #12121
2025-11-30 08:09:28 +01:00
Johannes Altmanninger
3cac5ff75e Fix "cargo-deny" check 2025-11-30 08:09:28 +01:00
Daniel Rainer
5210c16bfa printf: simplify conversion specifiers
This is another case where we can don't need to use complicated
formatting specifiers. There is also no need to use `wgettext_fmt` for
messages which don't contain any localizable parts.

The instances using `sprintf` could be kept as is, but I think it's
better to keep the width computation at the place where it can sensibly
be done, even though it is not done correctly at the moment because it
does not use grapheme widths.

Removing complicated conversion specifiers from localized messages helps
with the transition to Fluent.

Closes #12119
2025-11-30 00:40:52 +00:00
Daniel Rainer
8a75bccf00 printf: simplify conversion specifier
We already compute the length of the substring we want to print in Rust.
Passing that length as the precision to let printf formatting limit the
length is brittle, as it requires that the same semantics for "length"
are used.

Simplifying conversion specifiers also makes the transition to Fluent
easier.

Part of #12119
2025-11-30 00:40:52 +00:00
David Adam
5130fc6f17 build_tools: add script to get supported Ubuntu versions 2025-11-29 00:05:24 +08:00
David Adam
b0a04f8648 release.sh: update Debian changelog as part of release 2025-11-28 21:21:27 +08:00
David Adam
60f41a738d Debian packaging: add historical changelog 2025-11-27 23:39:47 +08:00
David Adam
9814bae727 Debian packaging: upgrade to debhelper compat 13 2025-11-26 22:55:33 +08:00
David Adam
63cc800cd1 Debian packaging: fix variable exports in debian/rules
This ended up setting the variable to "true ", which cargo did not
understand.
2025-11-26 22:05:35 +08:00
David Adam
47c139b916 Debian packaging: drop obsolete dependencies 2025-11-26 21:03:52 +08:00
David Adam
107352c000 Debian packaging: update to Debian policy 4.6.2 2025-11-26 20:58:17 +08:00
David Adam
26cb522140 Debian packaging: move to contrib/debian directory
This is the location requested by Debian's Upstream Guide, and means it
can be included in tarballs, simplifying the build process.
2025-11-25 21:43:36 +08:00
oliwia
7db3009968 docs: typo 2025-11-25 10:30:47 +08:00
Daniel Rainer
13e296db76 license: allow fish's own license identifiers
The newly added `cargo deny check licenses` command complains about the
licenses added in this commit not being explicitly allowed, so add them
to the config here. There is no intended change in semantics.

Closes #12097
2025-11-23 16:25:12 +01:00
Johannes Altmanninger
6fa992c8d3 sphinx: exclude *.inc.rst files
Sections like "Synopsis" wrongly show up in the toctree in
build/user_doc/html/commands.html.  Skip them.
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
7ca78e7178 share/functions: fix path to /bin/sh on android
As mentioned in https://github.com/fish-shell/fish-shell/issues/12055#issuecomment-3554869126

> instances of `/bin/sh` as freestanding commands within `.fish`
> files are most likely not automatically handled by `termux-exec`

and

> This topic is complicated by the fact that some Android ROMs _do_
> contain real `/bin/sh` files

Core uses /system/bin/sh on Android.  Let's do the same from script
(even if /bin/sh exists), for consistency.
2025-11-23 12:30:22 +01:00
Daniel Rainer
2ee4f239d1 build_helper: add rebuild function for embedded path change
For paths embedded via `rust-embed`, we only need to rebuild on path
changes if the files are actually embedded.

To avoid having to remember and duplicate this logic for all embedded
paths, extract it into the build helper.

Closes #12083
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
4ffa06fb7e Fix section names in suggested "help" commands
Fixes 2cd60077e6 (help: get section titles from Sphinx, 2025-11-05)
Fixes #12082
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
a1baf97f54 Do not embed man pages in CMake builds
Commit 0709e4be8b (Use standalone code paths by default, 2025-10-26)
mainly wanted to embed internal functions to unbreak upgrade scenarios.

Embedding man pages in CMake has small-ish disadvantages:
1. extra space usage
2. less discoverability
3. a "cyclic" dependency:
   1. "sphinx-docs" depends on "fish_indent"
   2. "fish_indent" via "crates/build-man-pages" depends on "doc_src/".
   So every "touch doc_src/foo.rst && ninja -Cbuild sphinx-docs"
   re-builds fish, just to re-run sphinx-build.

The significant one is number 3.  It can be worked around by running
sphinx-build with stale "fish_indent" but I don't think we want to
do that.

Let's backtrack a little by stopping embedding man pages in CMake
builds; use the on-disk man pages (which we still install).

The remaining "regression" from 0709e4be8b is that "ninja -Cbuild
fish" needs to rebuild whenever anything in "share/" changes.  I don't
know if that's also annoying?

Since man pages for "build/fish" are not in share/, expose the exact
path as $__fish_man_dir.
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
2a7c57035e Readme: remove INSTALL_DOCS
As mentioned in b5aea3fd8b (Revert "[cmake] Fix installing docs",
2018-03-13), this should not be set by the user.
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
19e1ad7cfe config_paths: don't fall back to compiled-in DOC_DIR from relocatable tree
When $RELOCATED_PREFIX/bin/fish detects that
$RELOCATED_PREFIX/share/fish and $RELOCATED_PREFIX/etc/fish exist,
it uses paths from $RELOCATED_PREFIX (which is determined at runtime,
based on $argv), and not the prefix determined at compile time.

Except we do use DOCDIR (= ${CMAKE_INSTALL_FULL_DOCDIR}). This
seems like a weird side effect of commit b758c0c335 (Relax the
requirement that we find a working 'doc' directory in order for fish
to be relocatable.  2014-01-17) which enabled relocatable logic in
scenarios where $PREFIX/share/doc/fish was not installed.

This is weird; fish's "help" command should not take docs from a
different prefix.

Always use the matching docdir. This shouldn't make any practical
difference because since commit b5aea3fd8b (Revert "[cmake] Fix
installing docs", 2018-03-13), $PREFIX/share/doc/fish/CHANGELOG.rst
is always installed.
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
2ea3da4819 config_paths: disable the relocatable tree logic on Cargo builds
This only makes sense for builds that install directories like
$PREFIX/share/fish, $PREFIX/etc/fish.
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
3b100fce2d Fix __fish_help_dir when running from build directory
When running fish from a CMake build directory ("build/fish"),
"__fish_help_dir" is wrong because we assume the tarball layout. Fix
that.
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
0c67d0565a Commit help_sections data file
The help_sections.rs file was added to the tarball only as a quick hack.
There is a cyclic dependency between docs and fish:

"fish_indent" via "crates/build-man-pages" depends on "doc_src/".
So every "touch doc_src/foo.rst && ninja -Cbuild sphinx-docs"
re-builds fish.

In future "fish_indent" should not depend on "crates/build-man-pages".
Until then, a following commit wants to break this cyclic dependency
in a different way: we won't embed man pages (matching historical
behavior), which means that CMake builds won't need to run
sphinx-build.

But sphinx-build is also used for extracting help sections.

Also, the fix for #12082 will use help sections elsewhere in the code.

Prepare to remove the dependency on doc_src by committing the help
sections (we already do elsewhere).
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
95aeb16ca2 Remove embed-data feature flag
This mode still has some problems (see the next commit) but having
it behind a feature flag doesn't serve us. Let's commit to the parts
we want to keep and drop anything that we don't want.

To reduce diff noise, use "if false" in the code paths used by man
pages; a following commit will use them.
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
4770ef2df8 Remove comment
There are lots of places that assume this.
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
0ee0f5df97 Remove stale comment
See e895f96f8a (Do not rely on `fish_indent` for version in Sphinx, 2025-08-19).
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
4f5cad977c release.yml: assert that Sphinx is installed on macOS package builder 2025-11-23 12:30:22 +01:00
Johannes Altmanninger
bed2da9b83 Check license of dependencies in CI 2025-11-23 12:30:22 +01:00
Johannes Altmanninger
462fa9077a Fix wcwidth() on systems without C.UTF-8 locale
macOS has en_US.UTF-8 but not C.UTF-8, which leads to a glitch when
typing "🐟". Fix that by trying the user's locale first, restoring
historical behavior.

Fixes 71a962653d (Be explicit about setlocale() scope, 2025-10-24).

Reported-by: Ilya Grigoriev <ilyagr@users.noreply.github.com>
2025-11-23 12:30:22 +01:00
Fabian Boehm
0f9749c140 docs/set: Try to make it a little clearer that you can also query -x
Fixes #12094
2025-11-23 09:59:52 +01:00
Johannes Altmanninger
acae88e618 test tmux-first-prompt: disable in ASAN
Tracking issue: #11815
Closes #12084
2025-11-21 08:21:11 +01:00
Fabian Boehm
75bf46de70 Disable posix_spawn on android
Apparently it's broken:

9cbb0715a9/packages/fish/0001-build.rs.patch

Part of #12055
2025-11-19 17:42:41 +01:00
Fabian Boehm
f598186574 Move posix_spawn detection to build.rs
This is *simpler*, and would have made us detect the issue with broken
includes before.

Part of #12055
2025-11-19 17:42:35 +01:00
Fabian Boehm
d99aa696db Fix includes for systems without posix_spawn
Fixes:

9cbb0715a9/packages/fish/0002-fix-import-when-posix-spawn-disabled.patch

Part of #12055
2025-11-19 17:42:17 +01:00
Johannes Altmanninger
790beedbb0 Prefer terminal (client) OS for selecting native key bindings
When running fish inside SSH and local and remote OS differ, fish
uses key bindings for the remote OS, which is weird.  Fix that by
asking the terminal for the OS name.

This should be available on foot and kitty soon, see
https://codeberg.org/dnkl/foot/pulls/2217#issuecomment-8249741

Ref: #11107
2025-11-19 17:13:58 +01:00
Johannes Altmanninger
b1e681030b Add OSC 133 prompt end marker
iTerm2 displays commands in other UI widgets such as in Command History
(View → Toolbelt → Command History).  This needs prompt end marker
so the terminal can distinguish prompt from the command line.

Closes #11837
2025-11-19 17:13:58 +01:00
Johannes Altmanninger
5225b477ff io: minor cleanup 2025-11-19 17:13:58 +01:00
Johannes Altmanninger
c53b787339 Don't try to use partially-parsed colors on error
Historically, "fish_color_*" variables used a hand-written option
parser that would try to recover from invalid options.

Commit 037c1896d4 (Reuse wgetopt parsing for set_color for internal
colors, 2025-04-13) tried to preserve this recovering, but that
breaks WGetopter invariants.

I don't think this type of fallback is important, because
there's no reason we can't parse color variables (except maybe
forward-compatibility in future). Let's turn errors into the default
style; this should tell the user that their color is unsupported.

Fixes #12078
2025-11-19 17:13:58 +01:00
Johannes Altmanninger
64cb4398c6 Longer timeout on invalid escape sequences
For historical reasons, fish does not implement the VT state
machine but uses a relatively low timeout when reading
escape sequences.  This might be causing issues like
https://github.com/fish-shell/fish-shell/issues/11841#issuecomment-3544505235
so let's cautiously increase the timeout.

Make it opt-in and print a noisy error when we time out, so we can
find affected terminals.
2025-11-19 17:13:58 +01:00
Johannes Altmanninger
9165251a0b Feature flag for eventually disabling terminal workarounds
We've removed several terminal-specific workarounds but also added
some recently.  Most of the non-Apple issues have been reported
upstream.  Many of our workarounds were only meant to be temporary.
Some workarounds are unreliable and some can cause introduce other
problems.

Add a feature flag we can use later to let users turn off workarounds.

This doesn't disable anything yet, mostly because because despite being
off-by-default, this might surprise people who use "fish_features=all".
The fix would be "fish_features=all,no-omit-term-workarounds".
So we'd want a high degree of confidence.

For now we'll use this only with the next commit.

Closes #11819
2025-11-19 17:06:25 +01:00
Johannes Altmanninger
7e86e07fb0 Rename method for reading trailing escape sequence bytes 2025-11-19 16:42:45 +01:00
Johannes Altmanninger
02a6afd2b0 Link terminal workarounds with comments
It's hard to get rid of these workarounds. Let's at least brand them.

Ref: #11819
2025-11-19 16:42:45 +01:00
Johannes Altmanninger
0b414b9662 __fish_print_help: proper error message when 'man' is missing
Commit 3f2e4b71bc (functions/__fish_print_help: bravely remove
fallback to mandoc/groff, 2025-11-09) savagely made commands like
"abbr -h" require man, or else it prints a noisy stack trace.
Some packages have not yet replaced "nroff/mandoc" with "man"
as dependency, and, independent of that, man is still an optional
dependency.

Make missing "man" a proper error, which is easier to read tan the
stack trace.
2025-11-19 16:40:57 +01:00
Johannes Altmanninger
012007ce7b Test balloon for ST OSC terminator
Use \e\\ as sequence terminator for the first OSC 133 prompt start
marker.  The intention is to find out if a terminal fails to parse
this. If needed, the user can turn it off by adding "no-mark-prompt"
to their feature flags; but the shell is still usable without this.

Part of #12032
2025-11-19 16:40:57 +01:00
Daniel Rainer
031d501381 history: move consts into function
These consts are only used in a single function, so they don't need to
be defined outside of the function.

c323a2d5fe (r170141065)

Closes #12075
2025-11-19 16:40:57 +01:00
Johannes Altmanninger
b0773890ea Fix inconsistent usage of /bin/sh vs. sh
For now, we mostly use "/bin/sh" for background tasks; but sometimes
we call it as "sh". The former does not work on android, but I'm not
yet sure how we should fix that.

Let's make things consistent first, so they are easier to change
(we might remove some background tasks, or reimplement them in fish
script).
2025-11-19 16:40:57 +01:00
Johannes Altmanninger
55b3b6af79 __fish_anypython: remove hardcoded list of minor versions
We always use python3 but don't use, say python3.999 until we update
our hardcoded list.  This is inconsistent (we're careful about the
latter but not the former).  Fix this by always picking the highest
minor version (>= 3.5+) there is. This also reduces maintenance work.

Note that NetBSD is the only OS we know about that doesn't provide
the python3 symlink OOTB.  In future, the NetBSD package should
add a patch to replace "python3" in __fish_anypython.fish
with the correct path.  Downstream discussion is at
https://mail-index.netbsd.org/pkgsrc-users/2025/11/17/msg042205.html
2025-11-19 16:40:57 +01:00
Johannes Altmanninger
1bcfc64e13 status list-files: support multiple arguments
This fixes an issue in fish_config, see
ee94272eaf (commitcomment-170660405)
2025-11-19 16:36:14 +01:00
Johannes Altmanninger
7b4802091a cmake: install fish_{indent,key_reader} as hardlinks to fish
As mentioned in
https://github.com/fish-shell/fish-shell/issues/11921#issuecomment-3540587001,
binaries duplicate a lot of information unnecessarily.

	$ cargo b --release
	$ du -h target/release/fish{,_indent,_key_reader}
	15M	target/release/fish
	15M	target/release/fish_indent
	4.1M	target/release/fish_key_reader
	34M	total

Remove the duplication in CMake-installed builds by creating hard
links. I'm not sure how to do that for Cargo binaries yet (can we
write a portable wrapper script)?

This is still a bit weird because hardlinks are rarely used like
this; but symlinks may cause issues on MSYS2.  Maybe we should write
a /bin/sh wrapper script instead.
2025-11-19 16:36:14 +01:00
Zhizhen He
7774c74826 Fix typos
Closes #12071
2025-11-19 16:36:14 +01:00
Johannes Altmanninger
97e58f381b Fix warning when libc::c_char is unsigned (ARM) 2025-11-19 16:36:14 +01:00
Johannes Altmanninger
015f247998 build.rs: use the correct target OS where easily possible
This might help cross-compilation; even if it doesn't matter in
practice, this is more correct.  While at it, get rid of $TARGET in
favor of the more accurate $CARGO_CFG_TARGET_OS.
Not yet sure how to do it for "has_small_stack".
2025-11-19 16:36:14 +01:00
Johannes Altmanninger
ef3923c992 Clean up cfg() definitions
Sort. Use idiomatic names. Use cfg_if.
2025-11-19 16:36:14 +01:00
Johannes Altmanninger
7fdb1b4d1f dir_iter: unexport a test-only getter 2025-11-19 16:36:14 +01:00
Juho Kuisma
fb83a86c17 Fix broken translations
Fixes 302c33e377 (Deduplicate and fix broken translations, 2025-11-16)
2025-11-16 19:16:07 +01:00
Johannes Altmanninger
289057f981 reset_abandoning_line: actually clear line on first prompt
Frequently when I launch a new shell and type away, my input is
echoed in the terminal before fish gets a chance to set shell modes
(turn off ECHO and put the terminal into non-canonical mode).

This means that fish assumption about the cursor x=0 is wrong, which
causes the prompt (here '$ ') to be draw at the wrong place, making
it look like this:

	ececho hello

This seems to have been introduced in 4.1.0 in a0e687965e (Fix
unsaved screen modification, 2025-01-14). Not sure how it wasn't a
problem before.

Fix this by clearing to the beginning of the line after turning off
ECHO but before we draw anything to the screen.

This turns this comment in the patch context into a true statement:

> This means that `printf %s foo; fish` will overwrite the `foo`

Note that this currently applies per-reader, so builtin "read" will
also clear the line before doing anything.
We could potentially change this in future, if we both
1. query the cursor x before we output anything
2. refactor the screen module to properly deal with x>0 states.

But even if we did that, it wouldn't change the fact that we want
to force x=0 if the cursor has only been moved due to ECHO, so I'm
not sure.
2025-11-16 11:47:47 +01:00
Johannes Altmanninger
b48106a741 reset_abandoning_line: extract function
Helps the next commit.
2025-11-16 11:33:11 +01:00
Johannes Altmanninger
fbad0ab50a reset_abandoning_line: remove redundant allocations 2025-11-16 11:33:11 +01:00
Johannes Altmanninger
a8bf4db32d release.sh: minor fixes
After pushing to the fish-site repo, make sure to update the local
repo's view of origin/master to what we have successfully pushed.
2025-11-16 11:33:11 +01:00
huaji2369
b33bad1258 add waydroid completions
Closes #12056
2025-11-16 11:27:02 +01:00
seg6
29024806e5 tests: minimum python version validation for user facing scripts
Adds [`Vermin`](https://github.com/netromdk/vermin) to make sure our
user facing Python scripts conform to the proper minimum Python version
requirements.

The tool parses Python code into an AST and checks it against a database
of rules covering Python 2.0-3.13.

Testing Python version compatibility is tricky because most issues only
show up at runtime. Type annotations like `str | None` (Python 3.10+)
compile just fine (under pyc) and they don't throw an exception until
you actually call the relevant function.

This makes it hard to catch compatibility bugs without running the code
(through all possible execution paths) on every Python version.

Signed-off-by: seg6 <hi@seg6.space>

Closes #12044
2025-11-16 11:21:18 +01:00
seg6
4ef1f993a1 share: functions: __fish_anypython: update Python 3 version list
Signed-off-by: seg6 <hi@seg6.space>

Part of #12044
2025-11-16 11:21:18 +01:00
seg6
7297178091 share: tools: create_manpage_completions: drop Python 2 related code
Signed-off-by: seg6 <hi@seg6.space>

Part of #12044
2025-11-16 11:21:18 +01:00
seg6
3786c20dcf share: tools: web_config: drop Python 2 related code
Signed-off-by: seg6 <hi@seg6.space>

Part of #12044
2025-11-16 11:21:18 +01:00
Nahor
125fc142ba Add mutex around flock on Cygwin
Issue #11933 stems from Cygwin's flock implementation not being thread
safe. Previous code fixed the worst behavior (deadlock), at least in
some circumstance but still failed the history tests.

This new workaround correctly make flock usage thread safe by surrounding
it with a mutex lock.
The failing test can now pass and is much faster (5s instead of 15 on
my machine)

Upstream bug report: https://cygwin.com/pipermail/cygwin/2025-November/258963.html

Closes #12068
Closes #11933
2025-11-16 11:21:18 +01:00
exploide
d716b32bed completions/john: updated and improved completions
Closes #12051
2025-11-16 11:21:18 +01:00
Daniel Rainer
a56b1099aa tempfile: use std::env:temp_dir
This is a more robust implementation of our custom `get_tmpdir`.

Closes #12046
2025-11-16 11:21:18 +01:00
Johannes Altmanninger
f3f231cf70 __fish_apropos: assume only /usr/bin/apropos is broken
As discovered in #12062, man-db as installed from brew is not affected
by the "read-only-filesystem" problem what makewhatis suffers from
(though users do need to run "gmandb" once).

Restrict the workaround to the path to macOS's apropos; hopefully
that's stable.

This breaks people who define an alias for apropos; I guess for those
we could check "command -v" instead, but that's weird because the thing
found by type is the actual thing we're gonna execute.  So I'd first
rather want to find out why anyone would override this obscure command.
2025-11-16 11:21:18 +01:00
Johannes Altmanninger
c49ff931b1 __fish_apropos: remove stale version check
Most macOS versions are affeced nowadays.  Let's make this more
predictable by applying the workaround to them all.
2025-11-16 11:12:30 +01:00
Johannes Altmanninger
d3c3dfda7d Extract workaround for calling man/apropos without manpager
See ec939fb22f (Work around BSD man calling pager when stdout is
not a TTY, 2024-10-30).
2025-11-16 11:12:30 +01:00
Juho Kuisma
302c33e377 Deduplicate and fix broken translations
Co-authored-by: Johannes Altmanninger <aclopte@gmail.com>
2025-11-16 11:12:30 +01:00
Johannes Altmanninger
e77b1c9744 __fish_cached: tolerate trailing newline
In POSIX sh, ";" is not a valid statement, leading to errors when the
__fish_cached callback argument has a trailing newline (5c2073135e
(Fix syntax error in __fish_print_port_packages.fish, 2025-11-14)).

Use a newline instead of ";", to avoid this error.

While at it, replace rogue tab characters.
2025-11-16 11:04:23 +01:00
689 changed files with 210115 additions and 26737 deletions

View File

@@ -1,8 +1,2 @@
# This file is _not_ included in the tarballs for now
# Binary builds on Linux packaging infrastructure need to overwrite it to make `cargo vendor` work
# Releases and development builds made using OBS/Launchpad will _not_ reflect the contents of this
# file
[resolver]
# Make cargo 1.84+ respect MSRV (rust-version in Cargo.toml)
incompatible-rust-versions = "fallback"
[alias]
xtask = "run --package xtask --"

View File

@@ -27,8 +27,9 @@ freebsd_task:
matrix:
- name: FreeBSD Stable
freebsd_instance:
image: freebsd-14-3-release-amd64-ufs # updatecli.d/cirrus-freebsd.yml
image: freebsd-15-0-release-amd64-ufs # updatecli.d/cirrus-freebsd.yml
tests_script:
- pkg update
- pkg install -y cmake-core devel/pcre2 devel/ninja gettext git-lite lang/rust misc/py-pexpect
# libclang.so is a required build dependency for rust-c++ ffi bridge
- pkg install -y llvm

View File

@@ -9,12 +9,11 @@ trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 100
[{Makefile,*.in}]
[{Makefile,{BSD,GNU}makefile}]
indent_style = tab
[*.{md,rst}]
max_line_length = unset
trim_trailing_whitespace = false
[*.sh]
indent_size = 4
@@ -22,7 +21,7 @@ indent_size = 4
[build_tools/release.sh]
max_line_length = 72
[Dockerfile]
[Vagrantfile]
indent_size = 2
[share/{completions,functions}/**.fish]
@@ -30,3 +29,6 @@ max_line_length = unset
[{COMMIT_EDITMSG,git-revise-todo,*.jjdescription}]
max_line_length = 72
[*.yml]
indent_size = 2

5
.gitattributes vendored
View File

@@ -14,15 +14,10 @@
.gitattributes export-ignore
.gitignore export-ignore
/build_tools/make_tarball.sh export-ignore
/debian export-ignore
/debian/* export-ignore
/.github export-ignore
/.github/* export-ignore
/.builds export-ignore
/.builds/* export-ignore
# to make cargo vendor work correctly
/.cargo/ export-ignore
/.cargo/config.toml export-ignore
# for linguist, which drives GitHub's language statistics
alpine.js linguist-vendored

View File

@@ -1,11 +1,8 @@
## Description
Talk about your changes here.
Fixes issue #
## TODOs:
<!-- Just check off what what we know been done so far. We can help you with this stuff. -->
<!-- Check off what what has been done so far. -->
- [ ] If addressing an issue, a commit message mentions `Fixes issue #<issue-number>`
- [ ] Changes to fish usage are reflected in user documentation/manpages.
- [ ] Tests have been added for regressions fixed
- [ ] User-visible changes noted in CHANGELOG.rst <!-- Don't document changes for completions inside CHANGELOG.rst, there are lot of such edits -->
- [ ] User-visible changes noted in CHANGELOG.rst <!-- Usually skipped for changes to completions -->

View File

@@ -10,11 +10,13 @@ runs:
run: |
set -x
sudo pip install uv --break-system-packages
command -v uv
command -v uvx
# Check that pyproject.toml and the lock file are in sync.
# TODO Use "uv" to install Python as well.
: 'Note that --no-managed-python below would be implied but be explicit'
uv='env UV_PYTHON=python uv --no-managed-python'
$uv lock --check
$uv lock --check --exclude-newer="$(awk -F'"' <uv.lock '/^exclude-newer[[:space:]]*=/ {print $2}')"
# Install globally.
sudo $uv pip install --group=dev --system --break-system-packages
# Smoke test.

View File

@@ -25,7 +25,7 @@ runs:
set -x
toolchain=$(
case "$toolchain_channel" in
(stable) echo 1.91 ;; # updatecli.d/rust.yml
(stable) echo 1.93 ;; # updatecli.d/rust.yml
(msrv) echo 1.85 ;; # updatecli.d/rust.yml
(*)
printf >&2 "error: unsupported toolchain channel %s" "$toolchain_channel"

View File

@@ -10,7 +10,7 @@ jobs:
steps:
- name: Set label and milestone
id: set-label-milestone
uses: actions/github-script@v7
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8, build_tools/update-dependencies.sh
with:
script: |
const completionsLabel = 'completions';

View File

@@ -37,10 +37,10 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
-
name: Login to Container registry
uses: docker/login-action@v3
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0, build_tools/update-dependencies.sh
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
@@ -48,14 +48,14 @@ jobs:
-
name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0, build_tools/update-dependencies.sh
with:
images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.NAMESPACE }}/${{ matrix.target }}
flavor: |
latest=true
-
name: Build and push
uses: docker/build-push-action@v6
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0, build_tools/update-dependencies.sh
with:
context: docker/context
push: true

View File

@@ -0,0 +1,32 @@
name: Linux development builds
on:
push:
branches:
- buildscript
jobs:
deploy:
runs-on: ubuntu-latest
environment: linux-development
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
- uses: astral-sh/setup-uv@v7
- name: Update package database
run: sudo apt-get update
- name: Install deps
run: sudo apt install debhelper devscripts dpkg-dev
- name: Create tarball and source packages
run: |
version=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
mkdir /tmp/gpg
echo "$SIGNING_GPG_KEY" > /tmp/gpg/signing-gpg-key
mkdir /tmp/fish-built
FISH_ARTEFACT_PATH=/tmp/fish-built ./build_tools/make_tarball.sh
FISH_ARTEFACT_PATH=/tmp/fish-built DEB_SIGN_KEYFILE=/tmp/gpg/signing-gpg-key ./build_tools/make_linux_packages.sh $version
- uses: actions/upload-artifact@v6
with:
name: linux-source-packages
path: |
/tmp/fish-built
! /tmp/fish-built/fish-*/* # don't include the unpacked source directory

24
.github/workflows/lint-dependencies.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Lint Dependencies
on:
push:
paths:
- '.github/workflows/lint-dependencies.yml'
- 'Cargo.lock'
- '**/Cargo.toml'
- 'deny.toml'
pull_request:
paths:
- '.github/workflows/lint-dependencies.yml'
- 'Cargo.lock'
- '**/Cargo.toml'
- 'deny.toml'
jobs:
cargo-deny:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
- uses: EmbarkStudios/cargo-deny-action@44db170f6a7d12a6e90340e9e0fca1f650d34b14 # v2.0.15, build_tools/update-dependencies.sh
with:
command: check licenses
arguments: --all-features --locked --exclude-dev
rust-version: 1.93 # updatecli.d/rust.yml

View File

@@ -9,16 +9,16 @@ jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
- uses: ./.github/actions/rust-toolchain@stable
with:
components: rustfmt
- name: install dependencies
run: pip install ruff
- name: build fish
run: cargo build
run: cargo build --bin fish_indent
- name: check format
run: PATH="target/debug:$PATH" build_tools/style.fish --all --check
run: PATH="target/debug:$PATH" cargo xtask format --all --check
- name: check rustfmt
run: find build.rs crates src -type f -name '*.rs' | xargs rustfmt --check
@@ -35,22 +35,31 @@ jobs:
- rust_version: "msrv"
features: ""
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
- uses: ./.github/actions/rust-toolchain
with:
toolchain_channel: ${{ matrix.rust_version }}
components: clippy
- name: Update package database
run: sudo apt-get update
- name: Install deps
run: |
sudo apt install gettext
- name: Patch Cargo.toml to deny unknown lints
run: |
if [ "${{ matrix.rust_version }}" = stable ]; then
sed -i /^rust.unknown_lints/d Cargo.toml
fi
- name: cargo clippy
run: cargo clippy --workspace --all-targets ${{ matrix.features }} -- --deny=warnings
rustdoc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
- uses: ./.github/actions/rust-toolchain@stable
- name: Update package database
run: sudo apt-get update
- name: Install deps
run: |
sudo apt install gettext

View File

@@ -18,7 +18,7 @@ jobs:
pull-requests: write # for dessant/lock-threads to lock PRs
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v4
- uses: dessant/lock-threads@f5f995c727ac99a91dec92781a8e34e7c839a65e # v6.0.0, build_tools/update-dependencies.sh
with:
github-token: ${{ github.token }}
issue-inactive-days: '365'

View File

@@ -16,7 +16,7 @@ jobs:
name: Pre-release checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
with:
# Workaround for https://github.com/actions/checkout/issues/882
ref: ${{ inputs.version }}
@@ -36,10 +36,12 @@ jobs:
version: ${{ steps.version.outputs.version }}
tarball-name: ${{ steps.version.outputs.tarball-name }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
with:
# Workaround for https://github.com/actions/checkout/issues/882
ref: ${{ inputs.version }}
- name: Update package database
run: sudo apt-get update
- name: Install dependencies
run: sudo apt install cmake gettext ninja-build python3-pip
- uses: ./.github/actions/install-sphinx
@@ -61,7 +63,7 @@ jobs:
sed -n 2p "$relnotes" | grep -q '^$'
sed -i 1,2d "$relnotes"
- name: Upload tarball artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0, build_tools/update-dependencies.sh
with:
name: source-tarball
path: |
@@ -74,7 +76,7 @@ jobs:
name: Build single-file fish for Linux
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
with:
# Workaround for https://github.com/actions/checkout/issues/882
ref: ${{ inputs.version }}
@@ -82,6 +84,8 @@ jobs:
uses: ./.github/actions/rust-toolchain@stable
with:
targets: x86_64-unknown-linux-musl,aarch64-unknown-linux-musl
- name: Update package database
run: sudo apt-get update
- name: Install dependencies
run: sudo apt install crossbuild-essential-arm64 gettext musl-tools
- uses: ./.github/actions/install-sphinx
@@ -100,7 +104,7 @@ jobs:
tar -cazf fish-$(git describe)-linux-$arch.tar.xz \
-C target/$arch-unknown-linux-musl/release fish
done
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0, build_tools/update-dependencies.sh
with:
name: Static builds for Linux
path: fish-${{ inputs.version }}-linux-*.tar.xz
@@ -114,19 +118,19 @@ jobs:
name: Create release draft
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
with:
# Workaround for https://github.com/actions/checkout/issues/882
ref: ${{ inputs.version }}
- name: Download all artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0, build_tools/update-dependencies.sh
with:
merge-multiple: true
path: /tmp/artifacts
- name: List artifacts
run: find /tmp/artifacts -type f
- name: Create draft release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0, build_tools/update-dependencies.sh
with:
tag_name: ${{ inputs.version }}
name: fish ${{ inputs.version }}
@@ -142,7 +146,7 @@ jobs:
runs-on: macos-latest
environment: macos-codesign
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
with:
# Workaround for https://github.com/actions/checkout/issues/882
ref: ${{ inputs.version }}
@@ -172,7 +176,7 @@ jobs:
echo "$MACOS_NOTARIZE_JSON" >/tmp/notarize.json
./build_tools/make_macos_pkg.sh -s -f /tmp/app.p12 \
-i /tmp/installer.p12 -p "$MAC_CODESIGN_PASSWORD" \
-n -j /tmp/notarize.json
-n -j /tmp/notarize.json -- -c "-DWITH_DOCS=ON"
version=$(git describe)
[ -f "${FISH_ARTEFACT_PATH}/fish-$version.app.zip" ]
[ -f "${FISH_ARTEFACT_PATH}/fish-$version.pkg" ]

View File

@@ -13,7 +13,7 @@ jobs:
ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
- uses: ./.github/actions/rust-toolchain@oldest-supported
- name: Install deps
uses: ./.github/actions/install-dependencies
@@ -44,18 +44,19 @@ jobs:
ubuntu-32bit-static-pcre2:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
- uses: ./.github/actions/rust-toolchain@oldest-supported
with:
targets: "i586-unknown-linux-gnu"
- name: Update package database
run: sudo apt-get update
- name: Install deps
uses: ./.github/actions/install-dependencies
with:
include_pcre: false
include_sphinx: false
- name: Install g++-multilib
run: |
sudo apt install g++-multilib
run: sudo apt install g++-multilib
- name: cmake
env:
CFLAGS: "-m32"
@@ -80,13 +81,15 @@ jobs:
RUSTFLAGS: "-Zsanitizer=address"
# RUSTFLAGS: "-Zsanitizer=memory -Zsanitizer-memory-track-origins"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
# All -Z options require running nightly
- uses: dtolnay/rust-toolchain@nightly
with:
# ASAN uses `cargo build -Zbuild-std` which requires the rust-src component
# this is comma-separated
components: rust-src
- name: Update package database
run: sudo apt-get update
- name: Install deps
uses: ./.github/actions/install-dependencies
with:
@@ -128,7 +131,7 @@ jobs:
# of crates.io, so give this a try. It's also sometimes significantly faster on all platforms.
CARGO_NET_GIT_FETCH_WITH_CLI: true
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
- uses: ./.github/actions/rust-toolchain@oldest-supported
- name: Install deps
run: |
@@ -155,15 +158,22 @@ jobs:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
- uses: msys2/setup-msys2@4f806de0a5a7294ffabaff804b38a9b435a73bda # v2.30.0, build_tools/update-dependencies.sh
with:
update: true
msystem: MSYS
id: msys2
- name: Install deps
# Not using setup-msys2 `install` option to make it easier to copy/paste
run: |
pacman --noconfirm -S --needed git rust
- name: rebase
env:
MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }}
shell: cmd
run: |
"%MSYS2_LOCATION%\usr\bin\dash" /usr/bin/rebaseall -p -v
- name: cargo build
run: |
cargo build

View File

@@ -1,6 +1,232 @@
fish ?.?.? (released ???)
=========================
Notable improvements and fixes
------------------------------
Deprecations and removed features
---------------------------------
Interactive improvements
------------------------
Improved terminal support
-------------------------
Other improvements
------------------
- History is no longer corrupted with NUL bytes when fish receives SIGTERM or SIGHUP (:issue:`10300`).
- ``fish_update_completions`` now handles groff ``\X'...'`` device control escapes, fixing completion generation for man pages produced by help2man 1.50 and later (such as coreutils 9.10).
For distributors and developers
-------------------------------
- When the default global config directory (``$PREFIX/etc/fish``) exists but has been overridden with ``-DCMAKE_INSTALL_SYSCONFDIR``, fish will now respect that override (:issue:`10748`).
Regression fixes:
-----------------
fish 4.6.0 (released March 28, 2026)
====================================
Notable improvements and fixes
------------------------------
- New Spanish translations (:issue:`12489`).
- New Japanese translations (:issue:`12499`).
Deprecations and removed features
---------------------------------
- The default width for emoji is switched from 1 to 2, improving the experience for users connecting to old systems from modern desktops. Users of old desktops who notice that lines containing emoji are misaligned can set ``$fish_emoji_width`` back to 1 (:issue:`12562`).
Interactive improvements
------------------------
- The tab completion pager now left-justifies the description of each column (:issue:`12546`).
- fish now supports the ``SHELL_PROMPT_PREFIX``, ``SHELL_PROMPT_SUFFIX``, and ``SHELL_WELCOME`` environment variables. The prefix and suffix are automatically prepended and appended to the left prompt, and the welcome message is displayed on startup after the greeting.
These variables are set by systemd's ``run0`` for example (:issue:`10924`).
Improved terminal support
-------------------------
- ``set_color`` is able to turn off italics, reverse mode, strikethrough and underline individually (e.g. ``--italics=off``).
- ``set_color`` learned the foreground (``--foreground`` or ``-f``) and reset (``--reset``) options.
- An error caused by slow terminal responses at macOS startup has been addressed (:issue:`12571`).
Other improvements
------------------
- Signals like ``SIGWINCH`` (as sent on terminal resize) no longer interrupt builtin output (:issue:`12496`).
- For compatibility with Bash, fish now accepts ``|&`` as alternate spelling of ``&|``, for piping both standard output and standard error (:issue:`11516`).
- ``fish_indent`` now preserves comments and newlines immediately preceding a brace block (``{ }``) (:issue:`12505`).
- A crash when suspending certain pipelines with :kbd:`ctrl-z` has been fixed (:issue:`12301`).
For distributors and developers
-------------------------------
- ``cargo xtask`` subcommands no longer panic on test failures.
Regression fixes:
-----------------
- (from 4.5.0) Intermediate ```` artifact when redrawing prompt (:issue:`12476`).
- (from 4.4.0) ``history`` honors explicitly specified ``--color=`` again (:issue:`12512`).
- (from 4.4.0) Vi mode ``dl`` and ``dh`` (:issue:`12461`).
- (from 4.3.0) Error completing of commands starting with ``-`` (:issue:`12522`).
fish 4.5.0 (released February 17, 2026)
=======================================
This is mostly a patch release for Vi mode regressions in 4.4.0 but other minor behavior changes are included as well.
Interactive improvements
------------------------
- :kbd:`ctrl-l` no longer cancels history search (:issue:`12436`).
- History search cursor positioning now works correctly with characters of arbitrary width.
Deprecations and removed features
---------------------------------
- fish no longer reads the terminfo database to alter behaviour based on the :envvar:`TERM` environment variable, and does not depend on ncurses or terminfo. The ``ignore-terminfo`` feature flag, introduced and enabled by default in fish 4.1, is now permanently enabled. fish may no longer work correctly on Data General Dasher D220 and Wyse WY-350 terminals, but should continue to work on all known terminal emulators released in the 21st century.
Regression fixes:
-----------------
- (from 4.4.0) Vi mode ``d,f`` key binding did not work (:issue:`12417`).
- (from 4.4.0) Vi mode ``c,w`` key binding wrongly deleted trailing spaces (:issue:`12443`).
- (from 4.4.0) Vi mode crash on ``c,i,w`` after accepting autosuggestion (:issue:`12430`).
- (from 4.4.0) ``fish_vi_key_bindings`` called with a mode argument produced an error (:issue:`12413`).
- (from 4.0.0) Build on Illumos (:issue:`12410`).
fish 4.4.0 (released February 03, 2026)
=======================================
Deprecations and removed features
---------------------------------
- The default fossil prompt has been disabled (:issue:`12342`).
Interactive improvements
------------------------
- The ``bind`` builtin lists mappings from all modes if ``--mode`` is not provided (:issue:`12214`).
- Line-wise autosuggestions that don't start a command are no longer shown (739b82c34db, 58e7a50de8a).
- Builtin ``history`` now assumes that :envvar:`PAGER` supports ANSI color sequences.
- fish now clears the terminal's ``FLUSHO`` flag when acquiring control of the terminal, to fix an issue caused by pressing :kbd:`ctrl-o` on macOS (:issue:`12304`).
New or improved bindings
------------------------
- Vi mode word movements (``w``, ``W``, ``e``, and ``E``) are now largely in line with Vim. The only exception is that underscores are treated as word separators (:issue:`12269`).
- New special input functions to support these movements: ``forward-word-vi``, ``kill-word-vi``, ``forward-bigword-vi``, ``kill-bigword-vi``, ``forward-word-end``, ``backward-word-end``, ``forward-bigword-end``, ``backward-bigword-end``, ``kill-a-word``, ``kill-inner-word``, ``kill-a-bigword``, and ``kill-inner-bigword``.
- Vi mode key bindings now support counts for movement and deletion commands (e.g. `d3w` or `3l`), via a new operator mode (:issue:`2192`).
- New ``catppuccin-*`` color themes.
Improved terminal support
-------------------------
- ``set_color`` learned the strikethrough (``--strikethrough`` or ``-s``) modifier.
For distributors and developers
-------------------------------
- The CMake option ``WITH_GETTEXT`` has been renamed to ``WITH_MESSAGE_LOCALIZATION``, to reflect that it toggles localization independently of the backend used in the implementation.
- New ``cargo xtask`` commands can replace some CMake workflows.
Regression fixes:
-----------------
- (from 4.1.0) Crash when autosuggesting Unicode characters with nontrivial lowercase mapping (:issue:`12326`, 78f4541116e).
- (from 4.3.0) Glitch on ``read --prompt-str ""`` (:issue:`12296`).
fish 4.3.3 (released January 07, 2026)
======================================
This release fixes the following problems identified in fish 4.3.0:
- Selecting a completion could insert only part of the token (:issue:`12249`).
- Glitch with soft-wrapped autosuggestions and :doc:`fish_right_prompt <cmds/fish_right_prompt>` (:issue:`12255`).
- Spurious echo in tmux when typing a command really fast (:issue:`12261`).
- ``tomorrow`` theme always using the light variant (:issue:`12266`).
- ``fish_config theme choose`` sometimes not shadowing themes set by e.g. webconfig (:issue:`12278`).
- The sample prompts and themes are correctly installed (:issue:`12241`).
- Last line of command output could be hidden when missing newline (:issue:`12246`).
Other improvements include:
- The ``abbr``, ``bind``, ``complete``, ``functions``, ``history`` and ``type`` commands now support a ``--color`` option to control syntax highlighting in their output. Valid values are ``auto`` (default), ``always``, or ``never``.
- Existing file paths in redirection targets such as ``> file.txt`` are now highlighted using :envvar:`fish_color_valid_path`, indicating that ``file.txt`` will be clobbered (:issue:`12260`).
fish 4.3.2 (released December 30, 2025)
=======================================
This release fixes the following problems identified in 4.3.0:
- Pre-built macOS packages failed to start due to a ``Malformed Mach-O file`` error (:issue:`12224`).
- ``extra_functionsdir`` (usually ``vendor_functions.d``) and friends were not used (:issue:`12226`).
- Sample config file ``~/.config/fish/config.fish/`` and config directories ``~/.config/fish/conf.d/``, ``~/.config/fish/completions/`` and ``~/.config/fish/functions/`` were recreated on every startup instead of only the first time fish runs on a system (:issue:`12230`).
- Spurious echo of ``^[[I`` in some scenarios (:issue:`12232`).
- Infinite prompt redraw loop on some prompts (:issue:`12233`).
- The removal of pre-built HTML docs from tarballs revealed that cross compilation is broken because we use ``${CMAKE_BINARY_DIR}/fish_indent`` for building HTML docs.
As a workaround, the new CMake build option ``FISH_INDENT_FOR_BUILDING_DOCS`` can be set to the path of a runnable ``fish_indent`` binary.
fish 4.3.1 (released December 28, 2025)
=======================================
This release fixes the following problem identified in 4.3.0:
- Possible crash after expanding an abbreviation (:issue:`12223`).
fish 4.3.0 (released December 28, 2025)
=======================================
Deprecations and removed features
---------------------------------
- fish no longer sets user-facing :ref:`universal variables <variables-universal>` by default, making the configuration easier to understand.
Specifically, the ``fish_color_*``, ``fish_pager_color_*`` and ``fish_key_bindings`` variables are now set in the global scope by default.
After upgrading to 4.3.0, fish will (once and never again) migrate these universals to globals set at startup in the
``~/.config/fish/conf.d/fish_frozen_theme.fish`` and
``~/.config/fish/conf.d/fish_frozen_key_bindings.fish`` files.
We suggest that you delete those files and :ref:`set your theme <syntax-highlighting>` in ``~/.config/fish/config.fish``.
- You can still configure fish to propagate theme changes instantly; see :ref:`here <syntax-highlighting-instant-update>` for an example.
- You can still opt into storing color variables in the universal scope
via ``fish_config theme save`` though unlike ``fish_config theme choose``,
it does not support dynamic theme switching based on the terminal's color theme (see below).
- In addition to setting the variables which are explicitly defined in the given theme,
``fish_config theme choose`` now clears only color variables that were set by earlier invocations of a ``fish_config theme choose`` command
(which is how fish's default theme is set).
Scripting improvements
----------------------
- New :ref:`status language <status-language>` command allows showing and modifying language settings for fish messages without having to modify environment variables.
- When using a noninteractive fish instance to compute completions, ``commandline --cursor`` works as expected instead of throwing an error (:issue:`11993`).
- :envvar:`fish_trace` can now be set to ``all`` to also trace execution of key bindings, event handlers as well as prompt and title functions.
Interactive improvements
------------------------
- When typing immediately after starting fish, the first prompt is now rendered correctly.
- Completion accuracy was improved for file paths containing ``=`` or ``:`` (:issue:`5363`).
- Prefix-matching completions are now shown even if they don't match the case typed by the user (:issue:`7944`).
- On Cygwin/MSYS, command name completion will favor the non-exe name (``foo``) unless the user started typing the extension.
- When using the exe name (``foo.exe``), fish will use the description and completions for ``foo`` if there are none for ``foo.exe``.
- Autosuggestions now also show soft-wrapped portions (:issue:`12045`).
New or improved bindings
------------------------
- :kbd:`ctrl-w` (``backward-kill-path-component``) also deletes escaped spaces (:issue:`2016`).
- New special input functions ``backward-path-component``, ``forward-path-component`` and ``kill-path-component`` (:issue:`12127`).
Improved terminal support
-------------------------
- Themes can now be made color-theme-aware by including both ``[light]`` and ``[dark]`` sections in the :ref:`theme file <fish-config-theme-files>`.
Some default themes have been made color-theme-aware, meaning they dynamically adjust as your terminal's background color switches between light and dark colors (:issue:`11580`).
- The working directory is now reported on every fresh prompt (via OSC 7), fixing scenarios where a child process (like ``ssh``) left behind a stale working directory (:issue:`12191`).
- OSC 133 prompt markers now also mark the prompt end, which improves shell integration with terminals like iTerm2 (:issue:`11837`).
- Operating-system-specific key bindings are now decided based on the :ref:`terminal's host OS <status-terminal-os>`.
- New :ref:`feature flag <featureflags>` ``omit-term-workarounds`` can be turned on to prevent fish from trying to work around some incompatible terminals.
For distributors and developers
-------------------------------
- Tarballs no longer contain prebuilt documentation,
so building and installing documentation requires Sphinx.
To avoid users accidentally losing docs, the ``BUILD_DOCS`` and ``INSTALL_DOCS`` configuration options have been replaced with a new ``WITH_DOCS`` option.
- ``fish_key_reader`` and ``fish_indent`` are now installed as hardlinks to ``fish``, to save some space.
Regression fixes:
-----------------
- (from 4.1.0) Crash on incorrectly-set color variables (:issue:`12078`).
- (from 4.1.0) Crash when autosuggesting Unicode characters with nontrivial lowercase mapping.
- (from 4.2.0) Incorrect emoji width computation on macOS.
- (from 4.2.0) Mouse clicks and :kbd:`ctrl-l` edge cases in multiline command lines (:issue:`12121`).
- (from 4.2.0) Completions for Git remote names on some non-glibc systems.
- (from 4.2.0) Expansion of ``~$USER``.
fish 4.2.1 (released November 13, 2025)
=======================================
@@ -105,15 +331,13 @@ This release fixes the following regressions identified in 4.1.0:
This will not affect fish's child processes unless ``LC_MESSAGES`` was already exported.
- Some :doc:`fish_config <cmds/fish_config>` subcommands for showing prompts and themes had been broken in standalone Linux builds (those using the ``embed-data`` cargo feature), which has been fixed (:issue:`11832`).
- On Windows Terminal, we observed an issue where fish would fail to read the terminal's response to our new startup queries, causing noticeable lags and a misleading error message. A workaround has been added (:issue:`11841`).
- On Windows Terminal, we observed an issue where fish would fail to read the terminal's response to our new startup queries, causing noticeable lags and a misleading error message. A workaround has been added (:issue:`11841`).
- A WezTerm `issue breaking shifted key input <https://github.com/wezterm/wezterm/issues/6087>`__ has resurfaced on some versions of WezTerm; our workaround has been extended to cover all versions for now (:issue:`11204`).
- Fixed a crash in :doc:`the web-based configuration tool <cmds/fish_config>` when using the new underline styles (:issue:`11840`).
fish 4.1.0 (released September 27, 2025)
========================================
.. ignore for 4.1: 10929 10940 10948 10955 10965 10975 10989 10990 10998 11028 11052 11055 11069 11071 11079 11092 11098 11104 11106 11110 11140 11146 11148 11150 11214 11218 11259 11288 11299 11328 11350 11373 11395 11417 11419
Notable improvements and fixes
------------------------------
- Compound commands (``begin; echo 1; echo 2; end``) can now be written using braces (``{ echo1; echo 2 }``), like in other shells.

View File

@@ -24,12 +24,12 @@ include(cmake/Rust.cmake)
# Work around issue where archive-built libs go in the wrong place.
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
# Set up the machinery around FISH-BUILD-VERSION-FILE
# This defines the FBVF variable.
include(Version)
find_program(SPHINX_EXECUTABLE NAMES sphinx-build
HINTS
$ENV{SPHINX_DIR}
PATH_SUFFIXES bin
DOC "Sphinx documentation generator")
# Set up the docs.
include(cmake/Docs.cmake)
# Tell Cargo where our build directory is so it can find Cargo.toml.
set(VARS_FOR_CARGO
@@ -42,9 +42,7 @@ set(VARS_FOR_CARGO
"CARGO_TARGET_DIR=${FISH_RUST_BUILD_DIR}"
"CARGO_BUILD_RUSTC=${Rust_COMPILER}"
"${FISH_PCRE2_BUILDFLAG}"
"RUSTFLAGS=$ENV{RUSTFLAGS} ${rust_debugflags}"
"FISH_SPHINX=${SPHINX_EXECUTABLE}"
"FISH_USE_PREBUILT_DOCS=${USE_PREBUILT_DOCS}"
)
# Let fish pick up when we're running out of the build directory without installing
@@ -55,40 +53,46 @@ add_definitions(-DCMAKE_SOURCE_DIR="${REAL_CMAKE_SOURCE_DIR}")
set(build_types Release RelWithDebInfo Debug "")
if(NOT "${CMAKE_BUILD_TYPE}" IN_LIST build_types)
message(WARNING "Unsupported build type ${CMAKE_BUILD_TYPE}. If this doesn't build, try one of Release, RelWithDebInfo or Debug")
message(WARNING "Unsupported build type ${CMAKE_BUILD_TYPE}. If this doesn't build, try one of Release, RelWithDebInfo or Debug")
endif()
# Define a function to build and link dependencies.
function(CREATE_TARGET target)
add_custom_target(
${target} ALL
add_custom_target(
fish ALL
COMMAND
"${CMAKE_COMMAND}" -E
env ${VARS_FOR_CARGO}
${Rust_CARGO}
build --bin ${target}
$<$<CONFIG:Release>:--release>
$<$<CONFIG:RelWithDebInfo>:--profile=release-with-debug>
--target ${Rust_CARGO_TARGET}
--no-default-features
--features=${FISH_CARGO_FEATURES}
${CARGO_FLAGS}
&&
"${CMAKE_COMMAND}" -E
copy "${rust_target_dir}/${rust_profile}/${target}" "${CMAKE_CURRENT_BINARY_DIR}"
"${CMAKE_COMMAND}" -E
env ${VARS_FOR_CARGO}
${Rust_CARGO}
build --bin fish
$<$<CONFIG:Release>:--release>
$<$<CONFIG:RelWithDebInfo>:--profile=release-with-debug>
--target ${Rust_CARGO_TARGET}
--no-default-features
--features=${FISH_CARGO_FEATURES}
${CARGO_FLAGS}
&&
"${CMAKE_COMMAND}" -E
copy "${rust_target_dir}/${rust_profile}/fish" "${CMAKE_CURRENT_BINARY_DIR}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
USES_TERMINAL
)
endfunction(CREATE_TARGET)
)
# Define fish.
create_target(fish)
function(CREATE_LINK target)
add_custom_target(
${target} ALL
DEPENDS fish
COMMAND ln -f fish ${target}
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
)
endfunction(CREATE_LINK)
# Define fish_indent.
create_target(fish_indent)
create_link(fish_indent)
# Define fish_key_reader.
create_target(fish_key_reader)
create_link(fish_key_reader)
# Set up the docs.
include(cmake/Docs.cmake)
# Set up tests.
include(cmake/Tests.cmake)

View File

@@ -21,10 +21,10 @@ Archives are available at https://lists.sr.ht/~krobelus/fish-shell/.
GitHub
======
Fish is available on Github, at https://github.com/fish-shell/fish-shell.
Fish is available on GitHub, at https://github.com/fish-shell/fish-shell.
First, you'll need an account there, and you'll need a git clone of fish.
Fork it on Github and then run::
Fork it on GitHub and then run::
git clone https://github.com/<USERNAME>/fish-shell.git
@@ -50,7 +50,24 @@ Guidelines
In short:
- Be conservative in what you need (keep to the agreed minimum supported Rust version, limit new dependencies)
- Use automated tools to help you (``build_tools/check.sh``)
- Use automated tools to help you (``cargo xtask check``)
Commit History
==============
We use a linear, `recipe-style <https://www.bitsnbites.eu/git-history-work-log-vs-recipe/>`__ history.
Every commit should pass our checks.
We do not want "fixup" commits in our history.
If you notice an issue with a commit in a pull request, or get feedback suggesting changes,
you should rewrite the commit history and fix the relevant commits directly,
instead of adding new "fixup" commits.
When a pull request is ready, we rebase it on top of the current master branch,
so don't be shy about rewriting the history of commits which are not on master yet.
Rebasing (not merging) your pull request on the latest version of master is also welcome, especially if it resolves conflicts.
If you're using Git, consider using `jj <https://www.jj-vcs.dev/>`__ to make this easier.
If a commit should close an issue, add a ``Fixes #<issue-number>`` line at the end of the commit description.
Contributing completions
========================
@@ -88,14 +105,24 @@ Contributing documentation
==========================
The documentation is stored in ``doc_src/``, and written in ReStructured Text and built with Sphinx.
The builtins and various functions shipped with fish are documented in ``doc_src/cmds/``.
To build it locally, run from the main fish-shell directory::
To build an HTML version of the docs locally, run::
sphinx-build -j 8 -b html -n doc_src/ /tmp/fish-doc/
cargo xtask html-docs
which will build the docs as html in /tmp/fish-doc. You can open it in a browser and see that it looks okay.
will output to ``target/fish-docs/html`` or, if you use CMake::
cmake --build build -t sphinx-docs
will output to ``build/cargo/fish-docs/html/``. You can also run ``sphinx-build`` directly, which allows choosing the output directory::
sphinx-build -j auto -b html doc_src/ /tmp/fish-doc/
will output HTML docs to ``/tmp/fish-doc``.
After building them, you can open the HTML docs in a browser and see that it looks okay.
The builtins and various functions shipped with fish are documented in doc_src/cmds/.
Code Style
==========
@@ -106,12 +133,12 @@ For formatting, we use:
- ``fish_indent`` (shipped with fish) for fish script
- ``ruff format`` for Python
To reformat files, there is a script
To reformat files, there is an xtask
::
build_tools/style.fish --all
build_tools/style.fish somefile.rs some.fish
cargo xtask format --all
cargo xtask format somefile.rs some.fish
Fish Script Style Guide
-----------------------
@@ -175,7 +202,7 @@ The source code for fish includes a large collection of tests. If you
are making any changes to fish, running these tests is a good way to make
sure the behaviour remains consistent and regressions are not
introduced. Even if you dont run the tests on your machine, they will
still be run via Github Actions.
still be run via GitHub Actions.
You are strongly encouraged to add tests when changing the functionality
of fish, especially if you are fixing a bug to help ensure there are no
@@ -221,7 +248,7 @@ In this example we're in the root of the workspace and have run ``cargo build``
To run all tests and linters, use::
build_tools/check.sh
cargo xtask check
Contributing Translations
=========================
@@ -233,9 +260,8 @@ It also means that some features are not supported, such as message context and
We also expect all files to be UTF-8-encoded.
In practice, this should not matter much for contributing translations.
Translation sources are
stored in the ``po`` directory, named ``ll_CC.po``, where ``ll`` is the
two (or possibly three) letter ISO 639-1 language code of the target language
Translation sources are stored in the ``localization/po`` directory and named ``ll_CC.po``,
where ``ll`` is the two (or possibly three) letter ISO 639-1 language code of the target language
(e.g. ``pt`` for Portuguese). ``CC`` is an ISO 3166 country/territory code,
(e.g. ``BR`` for Brazil).
An example for a valid name is ``pt_BR.po``, indicating Brazilian Portuguese.
@@ -249,7 +275,7 @@ More specifically, you will need ``msguniq`` and ``msgmerge`` for creating trans
language.
To create a new translation, run::
build_tools/update_translations.fish po/ll_CC.po
build_tools/update_translations.fish localization/po/ll_CC.po
This will create a new PO file containing all messages available for translation.
If the file already exists, it will be updated.
@@ -295,7 +321,7 @@ Editing PO files
Many tools are available for editing translation files, including
command-line and graphical user interface programs. For simple use, you can use your text editor.
Open up the PO file, for example ``po/sv.po``, and you'll see something like::
Open up the PO file, for example ``localization/po/sv.po``, and you'll see something like::
msgid "%s: No suitable job\n"
msgstr ""
@@ -323,7 +349,7 @@ Modifications to strings in source files
If a string changes in the sources, the old translations will no longer work.
They will be preserved in the PO files, but commented-out (starting with ``#~``).
If you add/remove/change a translatable strings in a source file,
run ``build_tools/update_translations.fish`` to propagate this to all translation files (``po/*.po``).
run ``build_tools/update_translations.fish`` to propagate this to all translation files (``localization/po/*.po``).
This is only relevant for developers modifying the source files of fish or fish scripts.
Setting Code Up For Translations

552
Cargo.lock generated
View File

@@ -4,9 +4,9 @@ version = 4
[[package]]
name = "aho-corasick"
version = "1.1.3"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
@@ -17,6 +17,62 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "anstream"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys",
]
[[package]]
name = "assert_matches"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
[[package]]
name = "autocfg"
version = "1.5.0"
@@ -40,9 +96,9 @@ dependencies = [
[[package]]
name = "bstr"
version = "1.12.0"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
dependencies = [
"memchr",
"serde",
@@ -50,9 +106,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.41"
version = "1.2.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7"
checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -72,6 +128,52 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "clap"
version = "4.5.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75ca66430e33a14957acc24c5077b503e7d374151b2b4b3a10c83b4ceb4be0e"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793207c7fa6300a0608d1080b858e5fdbe713cdc1c8db9fb17777d8a13e63df0"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "cpufeatures"
version = "0.2.17"
@@ -83,9 +185,9 @@ dependencies = [
[[package]]
name = "crypto-common"
version = "0.1.6"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
@@ -122,6 +224,12 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -146,25 +254,36 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "find-msvc-tools"
version = "0.1.4"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "fish"
version = "4.2.1-snapshot"
version = "4.6.0"
dependencies = [
"assert_matches",
"bitflags",
"cc",
"cfg-if",
"errno",
"fish-build-helper",
"fish-build-man-pages",
"fish-color",
"fish-common",
"fish-fallback",
"fish-feature-flags",
"fish-gettext",
"fish-gettext-extraction",
"fish-gettext-maps",
"fish-gettext-mo-file-parser",
"fish-printf",
"fish-tempfile",
"fish-util",
"fish-wcstringutil",
"fish-wgetopt",
"fish-widecharwidth",
"fish-widestring",
"itertools",
"libc",
"lru",
"macro_rules_attribute",
@@ -172,16 +291,14 @@ dependencies = [
"num-traits",
"once_cell",
"pcre2",
"phf 0.12.1",
"phf_codegen 0.12.1",
"phf_codegen",
"portable-atomic",
"rand",
"rsconf",
"rust-embed",
"serial_test",
"terminfo",
"unix_path",
"widestring",
"xterm-color",
]
[[package]]
@@ -199,11 +316,62 @@ dependencies = [
"rsconf",
]
[[package]]
name = "fish-color"
version = "0.0.0"
dependencies = [
"fish-common",
"fish-widestring",
]
[[package]]
name = "fish-common"
version = "0.0.0"
dependencies = [
"bitflags",
"fish-build-helper",
"fish-feature-flags",
"fish-widestring",
"libc",
"nix",
"rsconf",
]
[[package]]
name = "fish-fallback"
version = "0.0.0"
dependencies = [
"fish-build-helper",
"fish-common",
"fish-widecharwidth",
"fish-widestring",
"libc",
"rsconf",
"widestring",
]
[[package]]
name = "fish-feature-flags"
version = "0.0.0"
dependencies = [
"fish-widestring",
]
[[package]]
name = "fish-gettext"
version = "0.0.0"
dependencies = [
"fish-gettext-maps",
"phf",
]
[[package]]
name = "fish-gettext-extraction"
version = "0.0.0"
dependencies = [
"fish-tempfile",
"proc-macro2",
"rsconf",
]
[[package]]
@@ -212,8 +380,8 @@ version = "0.0.0"
dependencies = [
"fish-build-helper",
"fish-gettext-mo-file-parser",
"phf 0.12.1",
"phf_codegen 0.12.1",
"phf",
"phf_codegen",
"rsconf",
]
@@ -225,6 +393,7 @@ version = "0.0.0"
name = "fish-printf"
version = "0.2.1"
dependencies = [
"assert_matches",
"libc",
"unicode-segmentation",
"unicode-width",
@@ -236,25 +405,63 @@ name = "fish-tempfile"
version = "0.0.0"
dependencies = [
"nix",
"rand",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
name = "fish-util"
version = "0.0.0"
dependencies = [
"errno",
"fish-widestring",
"libc",
"nix",
"rand",
]
[[package]]
name = "fish-wcstringutil"
version = "0.0.0"
dependencies = [
"fish-build-helper",
"fish-fallback",
"fish-widestring",
"rsconf",
]
[[package]]
name = "fish-wgetopt"
version = "0.0.0"
dependencies = [
"assert_matches",
"fish-wcstringutil",
"fish-widestring",
]
[[package]]
name = "fish-widecharwidth"
version = "0.0.0"
[[package]]
name = "fish-widestring"
version = "0.0.0"
dependencies = [
"libc",
"unicode-width",
"widestring",
]
[[package]]
name = "foldhash"
version = "0.1.5"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "generic-array"
version = "0.14.9"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
@@ -262,9 +469,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.16"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
@@ -285,9 +492,9 @@ dependencies = [
[[package]]
name = "globset"
version = "0.4.16"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
dependencies = [
"aho-corasick",
"bstr",
@@ -298,15 +505,36 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.15.5"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "jobserver"
version = "0.1.34"
@@ -319,15 +547,15 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.177"
version = "0.2.180"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
[[package]]
name = "libredox"
version = "0.1.10"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
dependencies = [
"bitflags",
"libc",
@@ -344,15 +572,15 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.28"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lru"
version = "0.13.0"
version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465"
checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
dependencies = [
"hashbrown",
]
@@ -379,17 +607,11 @@ version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nix"
version = "0.30.1"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
checksum = "225e7cfe711e0ba79a68baeddb2982723e4235247aefce1482f2f16c27865b66"
dependencies = [
"bitflags",
"cfg-if",
@@ -397,16 +619,6 @@ dependencies = [
"libc",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -422,6 +634,12 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "option-ext"
version = "0.2.0"
@@ -479,76 +697,38 @@ dependencies = [
[[package]]
name = "phf"
version = "0.11.3"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
dependencies = [
"phf_shared 0.11.3",
]
[[package]]
name = "phf"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7"
dependencies = [
"phf_shared 0.12.1",
"phf_shared",
]
[[package]]
name = "phf_codegen"
version = "0.11.3"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1"
dependencies = [
"phf_generator 0.11.3",
"phf_shared 0.11.3",
]
[[package]]
name = "phf_codegen"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efbdcb6f01d193b17f0b9c3360fa7e0e620991b193ff08702f78b3ce365d7e61"
dependencies = [
"phf_generator 0.12.1",
"phf_shared 0.12.1",
"phf_generator",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.3"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared 0.11.3",
"rand",
]
[[package]]
name = "phf_generator"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cbb1126afed61dd6368748dae63b1ee7dc480191c6262a3b4ff1e29d86a6c5b"
checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
dependencies = [
"fastrand",
"phf_shared 0.12.1",
"phf_shared",
]
[[package]]
name = "phf_shared"
version = "0.11.3"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
dependencies = [
"siphasher",
]
[[package]]
name = "phf_shared"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981"
checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
dependencies = [
"siphasher",
]
@@ -561,24 +741,33 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "portable-atomic"
version = "1.11.1"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.101"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.41"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
dependencies = [
"proc-macro2",
]
@@ -591,18 +780,32 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.8.5"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom 0.3.4",
]
[[package]]
name = "redox_syscall"
@@ -619,7 +822,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
"getrandom 0.2.16",
"getrandom 0.2.17",
"libredox",
"thiserror",
]
@@ -643,18 +846,18 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "rsconf"
version = "0.2.2"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd2af859f1af0401e7fc7577739c87b0d239d8a5da400d717183bca92336bcdc"
checksum = "06cbd984e96cc891aa018958ac3d09986c0ea7635eedfff670b99a90970f159f"
dependencies = [
"cc",
]
[[package]]
name = "rust-embed"
version = "8.8.0"
version = "8.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb44e1917075637ee8c7bcb865cf8830e3a92b5b1189e44e3a0ab5a0d5be314b"
checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
@@ -663,9 +866,9 @@ dependencies = [
[[package]]
name = "rust-embed-impl"
version = "8.8.0"
version = "8.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "382499b49db77a7c19abd2a574f85ada7e9dbe125d5d1160fa5cad7c4cf71fc9"
checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa"
dependencies = [
"proc-macro2",
"quote",
@@ -677,9 +880,9 @@ dependencies = [
[[package]]
name = "rust-embed-utils"
version = "8.8.0"
version = "8.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21fcbee55c2458836bcdbfffb6ec9ba74bbc23ca7aa6816015a3dd2c4d8fc185"
checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1"
dependencies = [
"globset",
"sha2",
@@ -747,9 +950,9 @@ dependencies = [
[[package]]
name = "serial_test"
version = "3.2.0"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9"
checksum = "0d0b343e184fc3b7bb44dff0705fffcf4b3756ba6aff420dddd8b24ca145e555"
dependencies = [
"once_cell",
"parking_lot",
@@ -759,9 +962,9 @@ dependencies = [
[[package]]
name = "serial_test_derive"
version = "3.2.0"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef"
checksum = "6f50427f258fb77356e4cd4aa0e87e2bd2c66dbcee41dc405282cae2bfc26c83"
dependencies = [
"proc-macro2",
"quote",
@@ -781,9 +984,9 @@ dependencies = [
[[package]]
name = "shellexpand"
version = "3.1.1"
version = "3.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb"
checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8"
dependencies = [
"dirs",
]
@@ -796,9 +999,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "siphasher"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
[[package]]
name = "smallvec"
@@ -807,42 +1010,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "syn"
version = "2.0.107"
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "terminfo"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662"
dependencies = [
"fnv",
"nom",
"phf 0.11.3",
"phf_codegen 0.11.3",
]
[[package]]
name = "thiserror"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
@@ -857,9 +1054,9 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "unicode-ident"
version = "1.0.20"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unicode-segmentation"
@@ -888,6 +1085,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ace0b4755d0a2959962769239d56267f8a024fef2d9b32666b3dcd0946b0906"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "version_check"
version = "0.9.5"
@@ -954,3 +1157,40 @@ name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "xtask"
version = "0.0.0"
dependencies = [
"anstyle",
"clap",
"fish-build-helper",
"fish-tempfile",
"walkdir",
]
[[package]]
name = "xterm-color"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7008a9d8ba97a7e47d9b2df63fcdb8dade303010c5a7cd5bf2469d4da6eba673"
[[package]]
name = "zerocopy"
version = "0.8.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7456cf00f0685ad319c5b1693f291a650eaf345e941d082fc4e03df8a03996ac"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1328722bbf2115db7e19d69ebcc15e795719e2d66b60827c6a69a117365e37a0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View File

@@ -6,59 +6,80 @@ members = ["crates/*"]
rust-version = "1.85"
edition = "2024"
repository = "https://github.com/fish-shell/fish-shell"
# see doc_src/license.rst for details
# don't forget to update COPYING and debian/copyright too
license = "GPL-2.0-only AND LGPL-2.0-or-later AND MIT AND PSF-2.0"
[workspace.dependencies]
anstyle = "1.0.13"
assert_matches = "1.5.0"
bitflags = "2.5.0"
cc = "1.0.94"
cfg-if = "1.0.3"
clap = { version = "4.5.54", features = ["derive"] }
errno = "0.3.0"
fish-build-helper = { path = "crates/build-helper" }
fish-build-man-pages = { path = "crates/build-man-pages" }
fish-color = { path = "crates/color" }
fish-common = { path = "crates/common" }
fish-fallback = { path = "crates/fallback" }
fish-feature-flags = { path = "crates/feature-flags" }
fish-gettext = { path = "crates/gettext" }
fish-gettext-extraction = { path = "crates/gettext-extraction" }
fish-gettext-maps = { path = "crates/gettext-maps" }
fish-gettext-mo-file-parser = { path = "crates/gettext-mo-file-parser" }
fish-printf = { path = "crates/printf", features = ["widestring"] }
fish-tempfile = { path = "crates/tempfile" }
fish-util = { path = "crates/util" }
fish-wcstringutil = { path = "crates/wcstringutil" }
fish-widecharwidth = { path = "crates/widecharwidth" }
fish-widestring = { path = "crates/widestring" }
fish-wgetopt = { path = "crates/wgetopt" }
itertools = "0.14.0"
libc = "0.2.177"
# lru pulls in hashbrown by default, which uses a faster (though less DoS resistant) hashing algo.
# disabling default features uses the stdlib instead, but it doubles the time to rewrite the history
# files as of 22 April 2024.
lru = "0.13.0"
nix = { version = "0.30.1", default-features = false, features = [
lru = "0.16.2"
nix = { version = "0.31.1", default-features = false, features = [
"event",
"inotify",
"resource",
"fs",
"inotify",
"hostname",
"resource",
"process",
"signal",
"term",
"user",
] }
num-traits = "0.2.19"
once_cell = "1.19.0"
pcre2 = { git = "https://github.com/fish-shell/rust-pcre2", tag = "0.2.9-utf32", default-features = false, features = [
"utf32",
] }
phf = { version = "0.12", default-features = false }
phf_codegen = { version = "0.12" }
phf = { version = "0.13", default-features = false }
phf_codegen = "0.13"
portable-atomic = { version = "1", default-features = false, features = [
"fallback",
] }
proc-macro2 = "1.0"
# Don't use the "getrandom" feature as it requires "getentropy" which was not
# available on macOS < 10.12. We can enable "getrandom" when we raise the
# minimum supported version to 10.12.
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
rsconf = "0.2.2"
rust-embed = { version = "8.7.2", features = [
rand = { version = "0.9.2", default-features = false, features = [
"small_rng",
"thread_rng",
] }
rsconf = "0.3.0"
rust-embed = { version = "8.11.0", features = [
"deterministic-timestamps",
"include-exclude",
"interpolate-folder-path",
] }
serial_test = { version = "3", default-features = false }
# We need 0.9.0 specifically for some crash fixes.
terminfo = "0.9.0"
widestring = "1.2.0"
unicode-segmentation = "1.12.0"
unicode-width = "0.2.0"
unix_path = "1.0.1"
walkdir = "2.5.0"
xterm-color = "1.0.1"
[profile.release]
overflow-checks = true
@@ -70,26 +91,35 @@ debug = true
[package]
name = "fish"
version = "4.2.1-snapshot"
version = "4.6.0"
edition.workspace = true
rust-version.workspace = true
default-run = "fish"
# see doc_src/license.rst for details
# don't forget to update COPYING and debian/copyright too
license = "GPL-2.0-only AND LGPL-2.0-or-later AND MIT AND PSF-2.0"
license.workspace = true
homepage = "https://fishshell.com"
readme = "README.rst"
[dependencies]
assert_matches.workspace = true
bitflags.workspace = true
cfg-if.workspace = true
errno.workspace = true
fish-build-helper.workspace = true
fish-build-man-pages = { workspace = true, optional = true }
fish-color.workspace = true
fish-common.workspace = true
fish-fallback.workspace = true
fish-feature-flags.workspace = true
fish-gettext = { workspace = true, optional = true }
fish-gettext-extraction = { workspace = true, optional = true }
fish-gettext-maps = { workspace = true, optional = true }
fish-printf.workspace = true
fish-tempfile.workspace = true
fish-util.workspace = true
fish-wcstringutil.workspace = true
fish-wgetopt.workspace = true
fish-widecharwidth.workspace = true
fish-widestring.workspace = true
itertools.workspace = true
libc.workspace = true
lru.workspace = true
macro_rules_attribute = "0.2.2"
@@ -97,23 +127,21 @@ nix.workspace = true
num-traits.workspace = true
once_cell.workspace = true
pcre2.workspace = true
phf = { workspace = true, optional = true }
rand.workspace = true
terminfo.workspace = true
widestring.workspace = true
xterm-color.workspace = true
[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
portable-atomic.workspace = true
[target.'cfg(windows)'.dependencies]
rust-embed = { workspace = true, optional = true, features = [
rust-embed = { workspace = true, features = [
"deterministic-timestamps",
"debug-embed",
"include-exclude",
"interpolate-folder-path",
] }
[target.'cfg(not(windows))'.dependencies]
rust-embed = { workspace = true, optional = true, features = [
rust-embed = { workspace = true, features = [
"deterministic-timestamps",
"include-exclude",
"interpolate-folder-path",
@@ -149,41 +177,56 @@ name = "fish_key_reader"
path = "src/bin/fish_key_reader.rs"
[features]
default = ["embed-data", "localize-messages"]
default = ["embed-manpages", "localize-messages"]
benchmark = []
embed-data = ["dep:rust-embed", "dep:fish-build-man-pages"]
embed-manpages = ["dep:fish-build-man-pages"]
# Enable gettext localization at runtime. Requires the `msgfmt` tool to generate catalog data at
# build time.
localize-messages = ["dep:phf", "dep:fish-gettext-maps"]
localize-messages = ["dep:fish-gettext"]
# This feature is used to enable extracting messages from the source code for localization.
# It only needs to be enabled if updating these messages (and the corresponding PO files) is
# desired. This happens when running tests via `build_tools/check.sh` and when calling
# desired. This happens when running tests via `cargo xtask check` and when calling
# `build_tools/update_translations.fish`, so there should not be a need to enable it manually.
gettext-extract = ["dep:fish-gettext-extraction"]
# The following features are auto-detected by the build-script and should not be enabled manually.
asan = []
tsan = []
[workspace.lints]
rust.non_camel_case_types = "allow"
rust.non_upper_case_globals = "allow"
rust.unknown_lints = "allow"
rust.unknown_lints = { level = "allow", priority = -1 }
rust.unstable_name_collisions = "allow"
rustdoc.private_intra_doc_links = "allow"
clippy.len_without_is_empty = "allow" # we're not a library crate
clippy.let_and_return = "allow"
clippy.manual_range_contains = "allow"
clippy.needless_lifetimes = "allow"
clippy.needless_return = "allow"
clippy.new_without_default = "allow"
clippy.option_map_unit_fn = "allow"
[workspace.lints.clippy]
assigning_clones = "warn"
cloned_instead_of_copied = "warn"
explicit_into_iter_loop = "warn"
format_push_string = "warn"
implicit_clone = "warn"
len_without_is_empty = "allow" # we're not a library crate
let_and_return = "allow"
manual_assert = "warn"
manual_range_contains = "allow"
map_unwrap_or = "warn"
mut_mut = "warn"
needless_lifetimes = "allow"
new_without_default = "allow"
option_map_unit_fn = "allow"
ptr_offset_by_literal = "warn"
ref_option = "warn"
semicolon_if_nothing_returned = "warn"
stable_sort_primitive = "warn"
str_to_string = "warn"
unnecessary_semicolon = "warn"
unused_trait_names = "warn"
# We do not want to use the e?print(ln)?! macros.
# These lints flag their use.
# In the future, they might change to flag other methods of printing.
clippy.print_stdout = "deny"
clippy.print_stderr = "deny"
print_stdout = "deny"
print_stderr = "deny"
[lints]
workspace = true

View File

@@ -1,18 +0,0 @@
FROM centos:latest
# Build dependency
RUN yum update -y &&\
yum install -y epel-release &&\
yum install -y clang cmake3 gcc-c++ make &&\
yum clean all
# Test dependency
RUN yum install -y expect vim-common
ADD . /src
WORKDIR /src
# Build fish
RUN cmake3 . &&\
make &&\
make install

View File

@@ -7,7 +7,7 @@
CMAKE ?= cmake
GENERATOR ?= $(shell (which ninja > /dev/null 2> /dev/null && echo Ninja) || \
echo 'Unix Makefiles')
echo 'Unix Makefiles')
prefix ?= /usr/local
PREFIX ?= $(prefix)
@@ -34,7 +34,7 @@ all: .begin build/fish
.PHONY: .begin
.begin:
@which $(CMAKE) > /dev/null 2> /dev/null || \
(echo 'Please install CMake and then re-run the `make` command!' 1>&2 && false)
(echo 'Please install CMake and then re-run the `make` command!' 1>&2 && false)
.PHONY: build/fish
build/fish: build/$(BUILDFILE)

View File

@@ -37,7 +37,7 @@ fish can be installed:
- using the `installer from fishshell.com <https://fishshell.com/>`__
- as a `standalone app from fishshell.com <https://fishshell.com/>`__
Note: The minimum supported macOS version is 10.10 "Yosemite".
Note: The minimum supported macOS version is 10.12.
Packages for Linux
~~~~~~~~~~~~~~~~~~
@@ -117,7 +117,7 @@ Dependencies
Compiling fish requires:
- Rust (version 1.85 or later)
- Rust (version 1.85 or later), including cargo
- CMake (version 3.15 or later)
- a C compiler (for system feature detection and the test helper binary)
- PCRE2 (headers and libraries) - optional, this will be downloaded if missing
@@ -157,11 +157,14 @@ In addition to the normal CMake build options (like ``CMAKE_INSTALL_PREFIX``), f
- Rust_COMPILER=path - the path to rustc. If not set, cmake will check $PATH and ~/.cargo/bin
- Rust_CARGO=path - the path to cargo. If not set, cmake will check $PATH and ~/.cargo/bin
- Rust_CARGO_TARGET=target - the target to pass to cargo. Set this for cross-compilation.
- BUILD_DOCS=ON|OFF - whether to build the documentation. This is automatically set to OFF when Sphinx isn't installed.
- INSTALL_DOCS=ON|OFF - whether to install the docs. This is automatically set to on when BUILD_DOCS is or prebuilt documentation is available (like when building in-tree from a tarball).
- WITH_DOCS=ON|OFF - whether to build the documentation. By default, this is ON when Sphinx is installed.
- FISH_INDENT_FOR_BUILDING_DOCS - useful for cross-compilation.
Set this to the path to the ``fish_indent`` executable to use for building HTML docs.
By default, ``${CMAKE_BINARY_DIR}/fish_indent`` will be used.
If that's not runnable on the compile host,
you can build a native one with ``cargo build --bin fish_indent`` and set this to ``$PWD/target/debug/fish_indent``.
- FISH_USE_SYSTEM_PCRE2=ON|OFF - whether to use an installed pcre2. This is normally autodetected.
- MAC_CODESIGN_ID=String|OFF - the codesign ID to use on Mac, or "OFF" to disable codesigning.
- WITH_GETTEXT=ON|OFF - whether to include translations.
- WITH_MESSAGE_LOCALIZATION=ON|OFF - whether to include translations.
- extra_functionsdir, extra_completionsdir and extra_confdir - to compile in an additional directory to be searched for functions, completions and configuration snippets
Building fish with Cargo
@@ -185,13 +188,14 @@ You can also install Sphinx another way and drop the ``uv run --no-managed-pytho
This will place standalone binaries in ``~/.cargo/bin/``, but you can move them wherever you want.
To disable translations, disable the ``localize-messages`` feature by passing ``--no-default-features --features=embed-data`` to cargo.
To disable translations, disable the ``localize-messages`` feature by passing ``--no-default-features --features=embed-manpages`` to cargo.
You can also link this build statically (but not against glibc) and move it to other computers.
Here are the remaining advantages of a full installation, as currently done by CMake:
- Man pages like ``fish(1)`` installed in standard locations, easily accessible from outside fish.
- Separate files for builtins (e.g. ``$PREFIX/share/fish/man/man1/abbr.1``).
- A local copy of the HTML documentation, typically accessed via the ``help`` fish function.
In Cargo builds, ``help`` will redirect to `<https://fishshell.com/docs/current/>`__
- Ability to use our CMake options extra_functionsdir, extra_completionsdir and extra_confdir,

228
build.rs
View File

@@ -1,12 +1,10 @@
use fish_build_helper::{env_var, fish_build_dir, workspace_root};
use fish_build_helper::{
env_var, fish_build_dir, target_os, target_os_is_apple, target_os_is_bsd, target_os_is_cygwin,
workspace_root,
};
use rsconf::Target;
use std::env;
use std::path::{Path, PathBuf};
fn canonicalize<P: AsRef<Path>>(path: P) -> PathBuf {
std::fs::canonicalize(path).unwrap()
}
fn main() {
setup_paths();
@@ -17,14 +15,14 @@ fn main() {
"FISH_RESOLVED_BUILD_DIR",
// If set by CMake, this might include symlinks. Since we want to compare this to the
// dir fish is executed in we need to canonicalize it.
canonicalize(fish_build_dir()).to_str().unwrap(),
fish_build_dir().canonicalize().unwrap().to_str().unwrap(),
);
// We need to canonicalize (i.e. realpath) the manifest dir because we want to be able to
// compare it directly as a string at runtime.
rsconf::set_env_value(
"CARGO_MANIFEST_DIR",
canonicalize(workspace_root()).to_str().unwrap(),
workspace_root().canonicalize().unwrap().to_str().unwrap(),
);
// Some build info
@@ -32,22 +30,11 @@ fn main() {
rsconf::set_env_value("BUILD_HOST_TRIPLE", &env_var("HOST").unwrap());
rsconf::set_env_value("BUILD_PROFILE", &env_var("PROFILE").unwrap());
let version = &get_version(&env::current_dir().unwrap());
// Per https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script,
// the source directory is the current working directory of the build script
rsconf::set_env_value("FISH_BUILD_VERSION", version);
rsconf::set_env_value("FISH_BUILD_VERSION", &get_version());
// safety: single-threaded code.
unsafe { std::env::set_var("FISH_BUILD_VERSION", version) };
// These are necessary if built with embedded functions,
// but only in release builds (because rust-embed in debug builds reads from the filesystem).
#[cfg(feature = "embed-data")]
#[cfg(any(windows, not(debug_assertions)))]
rsconf::rebuild_if_path_changed("share");
#[cfg(feature = "gettext-extract")]
rsconf::rebuild_if_env_changed("FISH_GETTEXT_EXTRACTION_FILE");
fish_build_helper::rebuild_if_embedded_path_changed("share");
let build = cc::Build::new();
let mut target = Target::new_from(build).unwrap();
@@ -73,77 +60,48 @@ fn main() {
/// `Cargo.toml`) behind a feature we just enabled.
///
/// [0]: https://github.com/rust-lang/cargo/issues/5499
#[rustfmt::skip]
fn detect_cfgs(target: &mut Target) {
for (name, handler) in [
// Ignore the first entry, it just sets up the type inference. Model new entries after the
// second line.
// Ignore the first entry, it just sets up the type inference.
("", &(|_: &Target| false) as &dyn Fn(&Target) -> bool),
("apple", &detect_apple),
("bsd", &detect_bsd),
("using_cmake", &|_| option_env!("FISH_CMAKE_BINARY_DIR").is_some()),
("use_prebuilt_docs", &|_| env_var("FISH_USE_PREBUILT_DOCS").is_some_and(|v| v == "TRUE") ),
("cygwin", &detect_cygwin),
("small_main_stack", &has_small_stack),
// See if libc supports the thread-safe localeconv_l(3) alternative to localeconv(3).
("localeconv_l", &|target| {
target.has_symbol("localeconv_l")
}),
("FISH_USE_POSIX_SPAWN", &|target| {
target.has_header("spawn.h")
}),
("HAVE_PIPE2", &|target| {
target.has_symbol("pipe2")
}),
("HAVE_EVENTFD", &|target| {
("apple", &(|_| target_os_is_apple())),
("bsd", &(|_| target_os_is_bsd())),
("cygwin", &(|_| target_os_is_cygwin())),
("have_eventfd", &|target| {
// FIXME: NetBSD 10 has eventfd, but the libc crate does not expose it.
if cfg!(target_os = "netbsd") {
false
} else {
target.has_header("sys/eventfd.h")
if target_os() == "netbsd" {
false
} else {
target.has_header("sys/eventfd.h")
}
}),
("HAVE_WAITSTATUS_SIGNAL_RET", &|target| {
("have_localeconv_l", &|target| {
target.has_symbol("localeconv_l")
}),
("have_pipe2", &|target| target.has_symbol("pipe2")),
("have_posix_spawn", &|target| {
if matches!(target_os().as_str(), "openbsd" | "android") {
// OpenBSD's posix_spawn returns status 127 instead of erroring with ENOEXEC when faced with a
// shebang-less script. Disable posix_spawn on OpenBSD.
//
// Android is broken for unclear reasons
false
} else {
target.has_header("spawn.h")
}
}),
("small_main_stack", &has_small_stack),
("using_cmake", &|_| {
option_env!("FISH_CMAKE_BINARY_DIR").is_some()
}),
("waitstatus_signal_ret", &|target| {
target.r#if("WEXITSTATUS(0x007f) == 0x7f", &["sys/wait.h"])
}),
] {
rsconf::declare_cfg(name, handler(target))
rsconf::declare_cfg(name, handler(target));
}
}
fn detect_apple(_: &Target) -> bool {
cfg!(any(target_os = "ios", target_os = "macos"))
}
fn detect_cygwin(_: &Target) -> bool {
// Cygwin target is usually cross-compiled.
env_var("CARGO_CFG_TARGET_OS").unwrap() == "cygwin"
}
/// Detect if we're being compiled for a BSD-derived OS, allowing targeting code conditionally with
/// `#[cfg(bsd)]`.
///
/// Rust offers fine-grained conditional compilation per-os for the popular operating systems, but
/// doesn't necessarily include less-popular forks nor does it group them into families more
/// specific than "windows" vs "unix" so we can conditionally compile code for BSD systems.
fn detect_bsd(_: &Target) -> bool {
// Instead of using `uname`, we can inspect the TARGET env variable set by Cargo. This lets us
// support cross-compilation scenarios.
let mut target = env_var("TARGET").unwrap();
if !target.chars().all(|c| c.is_ascii_lowercase()) {
target = target.to_ascii_lowercase();
}
let is_bsd = target.ends_with("bsd") || target.ends_with("dragonfly");
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
))]
assert!(is_bsd, "Target incorrectly detected as not BSD!");
is_bsd
}
/// Rust sets the stack size of newly created threads to a sane value, but is at at the mercy of the
/// OS when it comes to the size of the main stack. Some platforms we support default to a tiny
/// 0.5 MiB main stack, which is insufficient for fish's MAX_EVAL_DEPTH/MAX_STACK_DEPTH values.
@@ -201,41 +159,25 @@ fn join_if_relative(parent_if_relative: &Path, path: String) -> PathBuf {
}
let prefix = overridable_path("PREFIX", |env_prefix| {
Some(PathBuf::from(
env_prefix.unwrap_or("/usr/local".to_string()),
))
Some(PathBuf::from(env_prefix.unwrap_or("/usr/local".to_owned())))
})
.unwrap();
overridable_path("SYSCONFDIR", |env_sysconfdir| {
Some(join_if_relative(
&prefix,
env_sysconfdir.unwrap_or(
// Embedded builds use "/etc," not "$PREFIX/etc".
if cfg!(feature = "embed-data") {
"/etc/"
} else {
"etc/"
}
.to_string(),
),
env_sysconfdir.unwrap_or("/etc/".to_owned()),
))
});
let default_ok = !cfg!(feature = "embed-data");
let datadir = overridable_path("DATADIR", |env_datadir| {
let default = default_ok.then_some("share/".to_string());
env_datadir
.or(default)
.map(|p| join_if_relative(&prefix, p))
env_datadir.map(|p| join_if_relative(&prefix, p))
});
overridable_path("BINDIR", |env_bindir| {
let default = default_ok.then_some("bin/".to_string());
env_bindir.or(default).map(|p| join_if_relative(&prefix, p))
env_bindir.map(|p| join_if_relative(&prefix, p))
});
overridable_path("DOCDIR", |env_docdir| {
let default = default_ok.then_some("doc/fish".to_string());
env_docdir.or(default).map(|p| {
env_docdir.map(|p| {
join_if_relative(
&datadir
.expect("Setting DOCDIR without setting DATADIR is not currently supported"),
@@ -245,79 +187,15 @@ fn join_if_relative(parent_if_relative: &Path, path: String) -> PathBuf {
});
}
fn get_version(src_dir: &Path) -> String {
use std::fs::read_to_string;
fn get_version() -> String {
use std::process::Command;
if let Some(var) = env_var("FISH_BUILD_VERSION") {
return var;
}
let path = src_dir.join("version");
if let Ok(strver) = read_to_string(path) {
return strver;
}
let args = &["describe", "--always", "--dirty=-dirty"];
if let Ok(output) = Command::new("git").args(args).output() {
let rev = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !rev.is_empty() {
// If it contains a ".", we have a proper version like "3.7",
// or "23.2.1-1234-gfab1234"
if rev.contains('.') {
return rev;
}
// If it doesn't, we probably got *just* the commit SHA,
// like "f1242abcdef".
// So we prepend the crate version so it at least looks like
// "3.8-gf1242abcdef"
// This lacks the commit *distance*, but that can't be helped without
// tags.
let version = env!("CARGO_PKG_VERSION").to_owned();
return version + "-g" + &rev;
}
}
// git did not tell us a SHA either because it isn't installed,
// or because it refused (safe.directory applies to `git describe`!)
// So we read the SHA ourselves.
fn get_git_hash() -> Result<String, Box<dyn std::error::Error>> {
let workspace_root = workspace_root();
let gitdir = workspace_root.join(".git");
let jjdir = workspace_root.join(".jj");
let commit_id = if gitdir.exists() {
// .git/HEAD contains ref: refs/heads/branch
let headpath = gitdir.join("HEAD");
let headstr = read_to_string(headpath)?;
let headref = headstr.split(' ').nth(1).unwrap().trim();
// .git/refs/heads/branch contains the SHA
let refpath = gitdir.join(headref);
// Shorten to 9 characters (what git describe does currently)
read_to_string(refpath)?
} else if jjdir.exists() {
let output = Command::new("jj")
.args([
"log",
"--revisions",
"@",
"--no-graph",
"--ignore-working-copy",
"--template",
"commit_id",
])
.output()
.unwrap();
String::from_utf8_lossy(&output.stdout).to_string()
} else {
return Err("did not find either of .git or .jj".into());
};
let refstr = &commit_id[0..9];
let refstr = refstr.trim();
let version = env!("CARGO_PKG_VERSION").to_owned();
Ok(version + "-g" + refstr)
}
get_git_hash().expect("Could not get a version. Either set $FISH_BUILD_VERSION or install git.")
String::from_utf8(
Command::new("build_tools/git_version_gen.sh")
.output()
.unwrap()
.stdout,
)
.unwrap()
.trim_ascii_end()
.to_owned()
}

View File

@@ -8,11 +8,29 @@ if [ "$FISH_CHECK_LINT" = false ]; then
lint=false
fi
case "$(uname)" in
MSYS*)
is_cygwin=true
cygwin_var=MSYS
;;
CYGWIN*)
is_cygwin=true
cygwin_var=CYGWIN
;;
*)
is_cygwin=false
;;
esac
check_dependency_versions=false
if [ "${FISH_CHECK_DEPENDENCY_VERSIONS:-false}" != false ]; then
check_dependency_versions=true
fi
green='\e[0;32m'
yellow='\e[1;33m'
reset='\e[m'
if $check_dependency_versions; then
command -v curl
command -v jq
@@ -33,13 +51,18 @@ fi
cargo() {
subcmd=$1
shift
# shellcheck disable=2086
command cargo "$subcmd" $cargo_args "$@"
if [ -n "$FISH_CHECK_RUST_TOOLCHAIN" ]; then
# shellcheck disable=2086
command cargo "+$FISH_CHECK_RUST_TOOLCHAIN" "$subcmd" $cargo_args "$@"
else
# shellcheck disable=2086
command cargo "$subcmd" $cargo_args "$@"
fi
}
cleanup () {
if [ -n "$template_file" ] && [ -e "$template_file" ]; then
rm "$template_file"
if [ -n "$gettext_template_dir" ] && [ -e "$gettext_template_dir" ]; then
rm -r "$gettext_template_dir"
fi
}
@@ -64,23 +87,59 @@ if [ -n "$FISH_TEST_MAX_CONCURRENCY" ]; then
export CARGO_BUILD_JOBS="$FISH_TEST_MAX_CONCURRENCY"
fi
template_file=$(mktemp)
gettext_template_dir=$(mktemp -d)
(
export FISH_GETTEXT_EXTRACTION_FILE="$template_file"
export FISH_GETTEXT_EXTRACTION_DIR="$gettext_template_dir"
cargo build --workspace --all-targets --features=gettext-extract
)
if $lint; then
PATH="$build_dir:$PATH" "$workspace_root/build_tools/style.fish" --all --check
if command -v cargo-deny >/dev/null; then
cargo deny --all-features --locked --exclude-dev check licenses
fi
PATH="$build_dir:$PATH" cargo xtask format --all --check
for features in "" --no-default-features; do
cargo clippy --workspace --all-targets $features
done
fi
cargo test --no-default-features --workspace --all-targets
# When running `cargo test`, some binaries (e.g. `fish_gettext_extraction`)
# are dynamically linked against Rust's `std-xxx.dll` instead of being
# statically link as they usually are.
# On Cygwin, `PATH`is not properly updated to point to the `std-xxx.dll`
# location, so we have to do it manually.
# See:
# - https://github.com/rust-lang/rust/issues/149050
# - https://github.com/msys2/MSYS2-packages/issues/5784
(
if $is_cygwin; then
export PATH="$PATH:$(rustc --print target-libdir)"
fi
cargo test --no-default-features --workspace --all-targets
)
cargo test --doc --workspace
if $lint; then
cargo doc --workspace
cargo doc --workspace --no-deps
fi
# Using "()" not "{}" because we do want a subshell (for the export)
system_tests() (
[ -n "$@" ] && export "$@"
export FISH_GETTEXT_EXTRACTION_DIR="$gettext_template_dir"
"$workspace_root/tests/test_driver.py" "$build_dir"
)
test_cmd='FISH_GETTEXT_EXTRACTION_DIR="$gettext_template_dir" "$workspace_root/tests/test_driver.py" "$build_dir"'
if $is_cygwin; then
echo -e "=== Running ${green}integration tests ${yellow}with${green} symlinks${reset}"
system_tests $cygwin_var=winsymlinks
echo -e "=== Running ${green}integration tests ${yellow}without${green} symlinks${reset}"
system_tests $cygwin_var=winsymlinks
else
echo -e "=== Running ${green}integration tests${reset}"
system_tests
fi
FISH_GETTEXT_EXTRACTION_FILE=$template_file "$workspace_root/tests/test_driver.py" "$build_dir"
exit
}

View File

@@ -12,22 +12,18 @@ begin
# Note that this results in the file being overwritten.
# This is desired behavior, to get rid of the results of prior invocations
# of this script.
begin
echo 'msgid ""'
echo 'msgstr ""'
echo '"Content-Type: text/plain; charset=UTF-8\n"'
echo ""
end
set -l header 'msgid ""\nmsgstr "Content-Type: text/plain; charset=UTF-8\\\\n"\n\n'
printf $header
set -g workspace_root (path resolve (status dirname)/..)
set -l rust_extraction_file
set -l rust_extraction_dir
if set -l --query _flag_use_existing_template
set rust_extraction_file $_flag_use_existing_template
set rust_extraction_dir $_flag_use_existing_template
else
set rust_extraction_file (mktemp)
set rust_extraction_dir (mktemp -d)
# We need to build to ensure that the proc macro for extracting strings runs.
FISH_GETTEXT_EXTRACTION_FILE=$rust_extraction_file cargo check --no-default-features --features=gettext-extract
FISH_GETTEXT_EXTRACTION_DIR=$rust_extraction_dir cargo check --features=gettext-extract
or exit 1
end
@@ -41,11 +37,18 @@ begin
mark_section tier1-from-rust
# Get rid of duplicates and sort.
msguniq --no-wrap --sort-output $rust_extraction_file
or exit 1
begin
# Without providing this header, msguniq complains when a msgid is non-ASCII.
printf $header
find $rust_extraction_dir -type f -exec cat {} +
end |
msguniq --no-wrap --sort-output |
# Remove the header again. Otherwise it would appear twice, breaking the msguniq at the end
# of this file.
sed '/^msgid ""$/ {N; /\nmsgstr "Content-Type: text\/plain; charset=UTF-8\\\\n"$/ {N; d}}'
if not set -l --query _flag_use_existing_template
rm $rust_extraction_file
rm -r $rust_extraction_dir
end
function extract_fish_script_messages_impl

View File

@@ -1,70 +1,22 @@
#!/bin/sh
# Originally from the git sources (GIT-VERSION-GEN)
# Presumably (C) Junio C Hamano <junkio@cox.net>
# Reused under GPL v2.0
# Modified for fish by David Adam <zanchey@ucc.gu.uwa.edu.au>
set -e
# Find the fish directory as two levels up from script directory.
FISH_BASE_DIR="$( cd "$( dirname "$( dirname "$0" )" )" && pwd )"
DEF_VER=unknown
git_permission_failed=0
# First see if there is a version file (included in release tarballs),
# then try git-describe, then default.
if test -f version
then
VN=$(cat version) || VN="$DEF_VER"
else
if VN=$(git -C "$FISH_BASE_DIR" describe --always --dirty 2>/dev/null); then
:
version=$(
awk <"$FISH_BASE_DIR/Cargo.toml" -F'"' '$1 == "version = " { print $2 }'
)
if git_version=$(
GIT_CEILING_DIRECTORIES=$FISH_BASE_DIR/.. \
git -C "$FISH_BASE_DIR" describe --always --dirty 2>/dev/null); then
if [ "$git_version" = "${git_version#"$version"}" ]; then
version=$version-g$git_version
else
if test $? = 128; then
# Current git versions return status 128
# when run in a repo owned by another user.
# Even for describe and everything.
# This occurs for `sudo make install`.
git_permission_failed=1
fi
VN="$DEF_VER"
version=$git_version
fi
fi
# If the first param is --stdout, then output to stdout and exit.
if test "$1" = '--stdout'
then
echo $VN
exit 0
fi
# Set the output directory as either the first param or cwd.
test -n "$1" && OUTPUT_DIR=$1/ || OUTPUT_DIR=
FBVF="${OUTPUT_DIR}FISH-BUILD-VERSION-FILE"
if test "$VN" = unknown && test -r "$FBVF" && test "$git_permission_failed" = 1
then
# HACK: Git failed, so we keep the current version file.
# This helps in case you built fish as a normal user
# and then try to `sudo make install` it.
date +%s > "${OUTPUT_DIR}"fish-build-version-witness.txt
exit 0
fi
if test -r "$FBVF"
then
VC=$(cat "$FBVF")
else
VC="unset"
fi
# Maybe output the FBVF
# It looks like "2.7.1-621-ga2f065e6"
test "$VN" = "$VC" || {
echo >&2 "$VN"
echo "$VN" >"$FBVF"
}
# Output the fish-build-version-witness.txt
# See https://cmake.org/cmake/help/v3.4/policy/CMP0058.html
date +%s > "${OUTPUT_DIR}"fish-build-version-witness.txt
echo "$version"

View File

@@ -1,3 +0,0 @@
# LSAN can detect leaks tracing back to __asan::AsanThread::ThreadStart (probably caused by our
# threads not exiting before their TLS dtors are called). Just ignore it.
leak:AsanThread

View File

@@ -0,0 +1,83 @@
#!/bin/sh
# This script takes a source tarball (from build_tools/make_tarball.sh) and a vendor tarball (from
# build_tools/make_vendor_tarball.sh, generated if not present), and produces:
# * Appropriately-named symlinks to look like a Debian package
# * Debian .changes and .dsc files with plain names ($version-1) and supported Ubuntu prefixes
# ($version-1~somedistro)
# * An RPM spec file
# By default, input and output files go in ~/fish_built, but this can be controlled with the
# FISH_ARTEFACT_PATH environment variable.
{
set -e
version=$1
[ -n "$version" ] || { echo "Version number required as argument" >&2; exit 1; }
[ -n "$DEB_SIGN_KEYID$DEB_SIGN_KEYFILE" ] ||
echo "Warning: neither DEB_SIGN_KEYID or DEB_SIGN_KEYFILE environment variables are set; you
will need a signing key for the author of the most recent debian/changelog entry." >&2
workpath=${FISH_ARTEFACT_PATH:-~/fish_built}
source_tarball="$workpath"/fish-"$version".tar.xz
vendor_tarball="$workpath"/fish-"$version"-vendor.tar.xz
[ -e "$source_tarball" ] || { echo "Missing source tarball, expected at $source_tarball" >&2; exit 1; }
cd "$workpath"
# Unpack the sources
tar xf "$source_tarball"
sourcepath="$workpath"/fish-"$version"
# Generate the vendor tarball if it is not already present
[ -e "$vendor_tarball" ] || (cd "$sourcepath"; build_tools/make_vendor_tarball.sh;)
# This step requires network access, so do it early in case it fails
# sh has no real array support
ubuntu_versions=$(uv run --script "$sourcepath"/build_tools/supported_ubuntu_versions.py)
# Write the specfile
[ -e "$workpath"/fish.spec ] && { echo "Cowardly refusing to overwite an existing fish.spec" >&2;
exit 1; }
rpmversion=$(echo "$version" |sed -e 's/-/+/' -e 's/-/./g')
sed -e "s/@version@/$version/g" -e "s/@rpmversion@/$rpmversion/g" \
< "$sourcepath"/fish.spec.in > "$workpath"/fish.spec
# Make the symlinks for Debian
ln -s "$source_tarball" "$workpath"/fish_"$version".orig.tar.xz
ln -s "$vendor_tarball" "$workpath"/fish_"$version".orig-cargo-vendor.tar.xz
# Set up the Debian source tree
cd "$sourcepath"
mkdir cargo-vendor
tar -C cargo-vendor -x -f "$vendor_tarball"
cp -r contrib/debian debian
# The vendor tarball contains a new .cargo/config.toml, which has the
# vendoring overrides appended to it. dpkg-source will add this as a
# patch using the flags in debian/
cp cargo-vendor/.cargo/config.toml .cargo/config.toml
# Update the Debian changelog
# The release scripts do this for release builds - skip if it has already been done
if head -n1 debian/changelog | grep --invert-match --quiet --fixed-strings "$version"; then
debchange --newversion "$version-1" --distribution unstable "Snapshot build"
fi
# Builds the "plain" Debian package
# debuild runs lintian, which takes ten minutes to run over the vendor directories
# just use dpkg-buildpackage directly
dpkg-buildpackage --build=source -d
# Build the Ubuntu packages
# deb-reversion does not work on source packages, so do the whole thing ourselves
for series in $ubuntu_versions; do
sed -i -e "1 s/$version-1)/$version-1~$series)/" -e "1 s/unstable/$series/" debian/changelog
dpkg-buildpackage --build=source -d
sed -i -e "1 s/$version-1~$series)/$version-1)/" -e "1 s/$series/unstable/" debian/changelog
done
}

View File

@@ -25,9 +25,11 @@ NOTARIZE=
ARM64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=11.0'
X86_64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=10.12'
cmake_args=()
while getopts "sf:i:p:e:nj:" opt; do
while getopts "c:sf:i:p:e:nj:" opt; do
case $opt in
c) cmake_args+=("$OPTARG");;
s) SIGN=1;;
f) P12_APP_FILE=$(realpath "$OPTARG");;
i) P12_INSTALL_FILE=$(realpath "$OPTARG");;
@@ -47,7 +49,7 @@ if [ -n "$NOTARIZE" ] && [ -z "$API_KEY_FILE" ]; then
usage
fi
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
VERSION=$(build_tools/git_version_gen.sh)
echo "Version is $VERSION"
@@ -65,6 +67,7 @@ do_cmake() {
-DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \
-DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \
-DFISH_USE_SYSTEM_PCRE2=OFF \
"${cmake_args[@]}" \
"$@" \
"$SRC_DIR"
}
@@ -79,17 +82,16 @@ do_cmake() {
&& env DESTDIR="$PKGDIR/root/" $ARM64_DEPLOY_TARGET make install;
}
# Build for x86-64 but do not install; instead we will make some fat binaries inside the root.
# Build for x86-64 but do not install; instead we will make a fat binary inside the root.
{ cd "$PKGDIR/build_x86_64" \
&& do_cmake -DRust_CARGO_TARGET=x86_64-apple-darwin \
&& env $X86_64_DEPLOY_TARGET make VERBOSE=1 -j 12; }
# Fatten them up.
for FILE in "$PKGDIR"/root/usr/local/bin/*; do
X86_FILE="$PKGDIR/build_x86_64/$(basename "$FILE")"
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
chmod 755 "$FILE"
done
# Fatten it up.
FILE=$PKGDIR/root/usr/local/bin/fish
X86_FILE=$PKGDIR/build_x86_64/$(basename "$FILE")
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
chmod 755 "$FILE"
if test -n "$SIGN"; then
echo "Signing executables"
@@ -102,9 +104,7 @@ if test -n "$SIGN"; then
if [ -n "$ENTITLEMENTS_FILE" ]; then
ARGS+=(--entitlements-xml-file "$ENTITLEMENTS_FILE")
fi
for FILE in "$PKGDIR"/root/usr/local/bin/*; do
(set +x; rcodesign sign "${ARGS[@]}" "$FILE")
done
(set +x; rcodesign sign "${ARGS[@]}" "$PKGDIR"/root/usr/local/bin/fish)
fi
pkgbuild --scripts "$SRC_DIR/build_tools/osx_package_scripts" --root "$PKGDIR/root/" --identifier 'com.ridiculousfish.fish-shell-pkg' --version "$VERSION" "$PKGDIR/intermediates/fish.pkg"
@@ -125,15 +125,13 @@ fi
(cd "$PKGDIR/build_arm64" && env $ARM64_DEPLOY_TARGET make -j 12 fish_macapp)
(cd "$PKGDIR/build_x86_64" && env $X86_64_DEPLOY_TARGET make -j 12 fish_macapp)
# Make the app's /usr/local/bin binaries universal. Note fish.app/Contents/MacOS/fish already is, courtesy of CMake.
# Make the app's /usr/local/bin/fish binary universal. Note fish.app/Contents/MacOS/fish already is, courtesy of CMake.
cd "$PKGDIR/build_arm64"
for FILE in fish.app/Contents/Resources/base/usr/local/bin/*; do
X86_FILE="$PKGDIR/build_x86_64/fish.app/Contents/Resources/base/usr/local/bin/$(basename "$FILE")"
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
# macho-universal-create screws up the permissions.
chmod 755 "$FILE"
done
FILE=fish.app/Contents/Resources/base/usr/local/bin/fish
X86_FILE=$PKGDIR/build_x86_64/fish.app/Contents/Resources/base/usr/local/bin/$(basename "$FILE")
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
# macho-universal-create screws up the permissions.
chmod 755 "$FILE"
if test -n "$SIGN"; then
echo "Signing app"

View File

@@ -1,88 +1,42 @@
#!/bin/sh
# Script to generate a tarball
# We use git to output a tree. But we also want to build the user documentation
# and put that in the tarball, so that nobody needs to have sphinx installed
# to build it.
# Outputs to $FISH_ARTEFACT_PATH or ~/fish_built by default
# Exit on error
set -e
# We will generate a tarball with a prefix "fish-VERSION"
# git can do that automatically for us via git-archive
# but to get the documentation in, we need to make a symlink called "fish-VERSION"
# and tar from that, so that the documentation gets the right prefix
# Use Ninja if available, as it automatically parallelises
BUILD_TOOL="make"
BUILD_GENERATOR="Unix Makefiles"
if command -v ninja >/dev/null; then
BUILD_TOOL="ninja"
BUILD_GENERATOR="Ninja"
fi
# We need GNU tar as that supports the --mtime and --transform options
TAR=notfound
for try in tar gtar gnutar; do
if $try -Pcf /dev/null --mtime now /dev/null >/dev/null 2>&1; then
TAR=$try
break
fi
done
if [ "$TAR" = "notfound" ]; then
echo 'No suitable tar (supporting --mtime) found as tar/gtar/gnutar in PATH'
exit 1
fi
# Get the current directory, which we'll use for symlinks
wd="$PWD"
# Get the version
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
tag_creation_date=$(
# If not dirty (i.e. we're building an immutable tag), pin the build date.
if [ "$VERSION" = "$(git describe)" ]; then
git log --format=%ad '--date=format:%b %d, %Y' -1
fi
)
VERSION=$(build_tools/git_version_gen.sh)
# The name of the prefix, which is the directory that you get when you untar
prefix="fish-$VERSION"
prefix=fish-$VERSION
path=${FISH_ARTEFACT_PATH:-~/fish_built}/$prefix.tar.xz
# The path where we will output the tar file
# Defaults to ~/fish_built
path=${FISH_ARTEFACT_PATH:-~/fish_built}/$prefix.tar
tmpdir=$(mktemp -d)
manifest=$tmpdir/Cargo.toml
lockfile=$tmpdir/Cargo.lock
# Clean up stuff we've written before
rm -f "$path" "$path".xz
sed "s/^version = \".*\"\$/version = \"$VERSION\"/g" Cargo.toml >"$manifest"
awk -v version=$VERSION '
/^name = "fish"$/ { ok=1 }
ok == 1 && /^version = ".*"$/ {
ok = 2;
$0 = "version = \"" version "\"";
}
{print}
' \
Cargo.lock >"$lockfile"
# git starts the archive
git archive --format=tar --prefix="$prefix"/ HEAD > "$path"
git archive \
--prefix="$prefix/" \
--add-virtual-file="$prefix/Cargo.toml:$(cat "$manifest")" \
--add-virtual-file="$prefix/Cargo.lock:$(cat "$lockfile")" \
HEAD |
xz >"$path"
# tarball out the documentation, generate a version file
PREFIX_TMPDIR=$(mktemp -d)
cd "$PREFIX_TMPDIR"
echo "$VERSION" > version
cmake -G "$BUILD_GENERATOR" -DCMAKE_BUILD_TYPE=Debug "$wd"
mkdir $PWD/user_doc/src
FISH_SPHINX_BUILD_DATE=$tag_creation_date \
FISH_SPHINX_HELP_SECTIONS_OUTPUT=$PWD/user_doc/src/help_sections.rs \
$BUILD_TOOL doc
TAR_APPEND="$TAR --append --file=$path --mtime=now --owner=0 --group=0 \
--mode=g+w,a+rX --transform s/^/$prefix\//"
$TAR_APPEND --no-recursion user_doc
$TAR_APPEND user_doc/html user_doc/man user_doc/src/help_sections.rs
$TAR_APPEND version
cd -
rm -r "$PREFIX_TMPDIR"
# xz it
xz "$path"
rm "$manifest"
rm "$lockfile"
rmdir "$tmpdir"
# Output what we did, and the sha256 hash
echo "Tarball written to $path".xz
openssl dgst -sha256 "$path".xz
echo "Tarball written to $path"
openssl dgst -sha256 "$path"

View File

@@ -8,25 +8,11 @@
# Exit on error
set -e
# We need GNU tar as that supports the --mtime and --transform options
TAR=notfound
for try in tar gtar gnutar; do
if $try -Pcf /dev/null --mtime now /dev/null >/dev/null 2>&1; then
TAR=$try
break
fi
done
if [ "$TAR" = "notfound" ]; then
echo 'No suitable tar (supporting --mtime) found as tar/gtar/gnutar in PATH'
exit 1
fi
# Get the current directory, which we'll use for telling Cargo where to find the sources
wd="$PWD"
# Get the version from git-describe
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
VERSION=$(build_tools/git_version_gen.sh)
# The name of the prefix, which is the directory that you get when you untar
prefix="fish-$VERSION"
@@ -42,8 +28,14 @@ rm -f "$path" "$path".xz
PREFIX_TMPDIR=$(mktemp -d)
cd "$PREFIX_TMPDIR"
# Add .cargo/config.toml. This means that the caller may need to remove that file from the tarball.
# See e4674cd7b5f (.cargo/config.toml: exclude from tarball, 2025-01-12)
mkdir .cargo
cargo vendor --manifest-path "$wd/Cargo.toml" > .cargo/config.toml
{
cat "$wd"/.cargo/config.toml
cargo vendor --manifest-path "$wd/Cargo.toml"
} > .cargo/config.toml
tar cfvJ "$path".xz vendor .cargo

View File

@@ -1,28 +1,28 @@
<html>
<head>
<style>
body {
font-family: system-ui, -apple-system, "Helvetica Neue", sans-serif;
font-size: 10pt;
}
code, tt {
font-family: ui-monospace, Menlo, monospace;
}
</style>
</head>
<body>
<p>
<strong>fish</strong> is a smart and user-friendly command line shell. For more information, visit <a href="https://fishshell.com">fishshell.com</a>.
</p>
<p>
<strong>fish</strong> will be installed into <tt>/usr/local/</tt>, and its path will be added to <wbr><tt>/etc/shells</tt> if necessary.
</p>
<p>
Your default shell will <em>not</em> be changed. To make <strong>fish</strong> your login shell after the installation, run:
</p>
<p>
<code>chsh -s /usr/local/bin/fish</code>
</p>
<p>Enjoy! Bugs can be reported on <a href="https://github.org/fish-shell/fish-shell/">GitHub</a>.</p>
</body>
<head>
<style>
body {
font-family: system-ui, -apple-system, "Helvetica Neue", sans-serif;
font-size: 10pt;
}
code, tt {
font-family: ui-monospace, Menlo, monospace;
}
</style>
</head>
<body>
<p>
<strong>fish</strong> is a smart and user-friendly command line shell. For more information, visit <a href="https://fishshell.com">fishshell.com</a>.
</p>
<p>
<strong>fish</strong> will be installed into <tt>/usr/local/</tt>, and its path will be added to <wbr><tt>/etc/shells</tt> if necessary.
</p>
<p>
Your default shell will <em>not</em> be changed. To make <strong>fish</strong> your login shell after the installation, run:
</p>
<p>
<code>chsh -s /usr/local/bin/fish</code>
</p>
<p>Enjoy! Bugs can be reported on <a href="https://github.com/fish-shell/fish-shell/">GitHub</a>.</p>
</body>
</html>

View File

@@ -4,14 +4,14 @@
if test $# -eq 0
then
echo "usage: $0 shellname [shellname ...]"
exit 1
echo "usage: $0 shellname [shellname ...]"
exit 1
fi
scriptname=$(basename "$0")
if [ "$(id -u)" -ne 0 ]; then
echo "${scriptname} must be run as root"
exit 1
echo "${scriptname} must be run as root"
exit 1
fi
file=/etc/shells

View File

@@ -2,6 +2,6 @@
echo "Removing any previous installation"
pkgutil --pkg-info "${INSTALL_PKG_SESSION_ID}" && pkgutil --only-files --files "${INSTALL_PKG_SESSION_ID}" | while read -r installed
do rm -v "${DSTVOLUME}${installed}"
do rm -v "${DSTVOLUME}${installed}"
done
echo "... removed"

View File

@@ -47,7 +47,7 @@ if test -z "$CI" || [ "$(git -C "$workspace_root" tag | wc -l)" -gt 1 ]; then {
num_authors=$(wc -l <"$relnotes_tmp/committers-now")
num_new_authors=$(wc -l <"$relnotes_tmp/committers-new")
printf %s \
"This release comprises $num_commits commits since $previous_version," \
"This release brings $num_commits new commits since $previous_version," \
" contributed by $num_authors authors, $num_new_authors of which are new committers."
echo
echo
@@ -87,7 +87,7 @@ if test -z "$CI" || [ "$(git -C "$workspace_root" tag | wc -l)" -gt 1 ]; then {
echo 'Download links:'
echo 'To download the source code for fish, we suggest the file named ``fish-'"$version"'.tar.xz``.'
echo 'The file downloaded from ``Source code (tar.gz)`` will not build correctly.'
echo 'A GPG signature using the key published at '"${FISH_GPG_PUBLIC_KEY_URL:-???}"' is available as ``fish-'"$version"'.tar.xz.asc``.'
echo 'A GPG signature using `this key <'"${FISH_GPG_PUBLIC_KEY_URL:-???}"'>`__ is available as ``fish-'"$version"'.tar.xz.asc``.'
echo
echo 'The files called ``fish-'"$version"'-linux-*.tar.xz`` contain'
echo '`standalone fish binaries <https://github.com/fish-shell/fish-shell/?tab=readme-ov-file#building-fish-with-cargo>`__'

View File

@@ -18,15 +18,18 @@ fi
[ -n "$version" ]
for tool in \
cmake \
bundle \
diff \
gh \
gpg \
jq \
ninja \
ruby \
tar \
timeout \
uv \
xz \
; do
if ! command -v "$tool" >/dev/null; then
echo >&2 "$0: missing command: $1"
@@ -80,17 +83,35 @@ sed -i \
-e "2c$(printf %s "$changelog_title" | sed s/./=/g)" \
CHANGELOG.rst
CommitVersion() {
sed -i "s/^version = \".*\"/version = \"$1\"/g" Cargo.toml
cargo fetch --offline
git add CHANGELOG.rst Cargo.toml Cargo.lock
git commit -m "$2
CreateCommit() {
git commit -m "$1
Created by ./build_tools/release.sh $version"
}
CommitVersion "$version" "Release $version"
sed -i "s/^version = \".*\"/version = \"$1\"/g" Cargo.toml
cargo fetch --offline # bumps the version in Cargo.lock
if [ "$1" = "$version" ]; then
# debchange is a Debian script to manage the Debian changelog, but
# it's too annoying to install everywhere. Just do it by hand.
cat - contrib/debian/changelog > contrib/debian/changelog.new <<EOF
fish (${version}-1) stable; urgency=medium
* Release of new version $version.
See https://github.com/fish-shell/fish-shell/releases/tag/$version for details.
-- $committer $(date -R)
EOF
mv contrib/debian/changelog.new contrib/debian/changelog
git add contrib/debian/changelog
fi
git add CHANGELOG.rst Cargo.toml Cargo.lock
CreateCommit "Release $version"
# Tags must be full objects, not lightweight tags, for
# git_version-gen.sh to work.
git -c "user.signingKey=$committer" \
tag --sign --message="Release $version" $version
@@ -121,7 +142,7 @@ fish_tar_xz=fish-$version.tar.xz
(
local_tarball=$tmpdir/local-tarball
mkdir "$local_tarball"
FISH_ARTEFACT_PATH=$local_tarball uv run ./build_tools/make_tarball.sh
FISH_ARTEFACT_PATH=$local_tarball ./build_tools/make_tarball.sh
cd "$local_tarball"
tar xf "$fish_tar_xz"
)
@@ -149,9 +170,16 @@ actual_tag_oid=$(git ls-remote "$remote" |
gh release upload "$version" "$fish_tar_xz.asc"
)
(
cd "$tmpdir/local-tarball/fish-$version"
uv --no-managed-python venv
. .venv/bin/activate
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug .
ninja doc
)
CopyDocs() {
rm -rf "$fish_site/site/docs/$1"
cp -r "$tmpdir/fish-$version/user_doc/html" "$fish_site/site/docs/$1"
cp -r "$tmpdir/local-tarball/fish-$version/cargo/fish-docs/html" "$fish_site/site/docs/$1"
git -C $fish_site add "site/docs/$1"
}
minor_version=${version%.*}
@@ -239,6 +267,8 @@ done
# This takes care to support remote names that are different from
# fish-shell remote name. Also, support detached HEAD state.
git push "$fish_site_repo" HEAD:master
git fetch "$fish_site_repo" \
"$(git rev-parse HEAD):refs/remotes/origin/master"
)
if [ -n "$integration_branch" ]; then {
@@ -251,7 +281,8 @@ fish ?.?.? (released ???)
EOF
)
printf %s\\n "$changelog" >CHANGELOG.rst
CommitVersion ${version}-snapshot "start new cycle"
git add CHANGELOG.rst
CreateCommit "start new cycle"
git push $remote HEAD:master
} fi
@@ -262,14 +293,14 @@ milestone_version="$(
echo "$version"
fi
)"
milestone_number=$(
milestone_number() {
gh_api_repo milestones?state=open |
jq --arg name "fish $1" '
.[] | select(.title == $name) | .number
'
)
gh_api_repo milestones/"$milestone_number" --method PATCH \
--raw-field state=closed
}
gh_api_repo milestones/"$(milestone_number "$milestone_version")" \
--method PATCH --raw-field state=closed
next_minor_version=$(echo "$minor_version" |
awk -F. '{ printf "%s.%s", $1, $2+1 }')

View File

@@ -1,123 +0,0 @@
#!/usr/bin/env fish
#
# This runs Python files, fish scripts (*.fish), and Rust files
# through their respective code formatting programs.
#
# `--all`: Format all eligible files instead of the ones specified as arguments.
# `--check`: Instead of reformatting, fail if a file is not formatted correctly.
# `--force`: Proceed without asking if uncommitted changes are detected.
# Only relevant if `--all` is specified but `--check` is not specified.
set -l fish_files
set -l python_files
set -l rust_files
set -l all no
argparse all check force -- $argv
or exit $status
if set -l -q _flag_all
set all yes
if set -q argv[1]
echo "Unexpected arguments: '$argv'"
exit 1
end
end
set -l workspace_root (status dirname)/..
if test $all = yes
if not set -l -q _flag_force; and not set -l -q _flag_check
# Potential for false positives: Not all fish files are formatted, see the `fish_files`
# definition below.
set -l relevant_uncommitted_changes (git status --porcelain --short --untracked-files=all | sed -e 's/^ *[^ ]* *//' | grep -E '.*\.(fish|py|rs)$')
if set -q relevant_uncommitted_changes[1]
for changed_file in $relevant_uncommitted_changes
echo $changed_file
end
echo
echo 'You have uncommitted changes (listed above). Are you sure you want to restyle?'
read -P 'y/N? ' -n1 -l ans
if not string match -qi y -- $ans
exit 1
end
end
end
set fish_files $workspace_root/{benchmarks,build_tools,etc,share}/**.fish
set python_files $workspace_root
else
# Format the files specified as arguments.
set -l files $argv
set fish_files (string match -r '^.*\.fish$' -- $files)
set python_files (string match -r '^.*\.py$' -- $files)
set rust_files (string match -r '^.*\.rs$' -- $files)
end
set -l red (set_color red)
set -l green (set_color green)
set -l yellow (set_color yellow)
set -l normal (set_color normal)
function die -V red -V normal
echo $red$argv[1]$normal
exit 1
end
if set -q fish_files[1]
if not type -q fish_indent
echo
echo $yellow'Could not find `fish_indent` in `$PATH`.'$normal
exit 127
end
echo === Running "$green"fish_indent"$normal"
if set -l -q _flag_check
fish_indent --check -- $fish_files
or die "Fish files are not formatted correctly."
else
fish_indent -w -- $fish_files
end
end
if set -q python_files[1]
if not type -q ruff
echo
echo $yellow'Please install `ruff` to style python'$normal
exit 127
end
echo === Running "$green"ruff format"$normal"
if set -l -q _flag_check
ruff format --check $python_files
or die "Python files are not formatted correctly."
else
ruff format $python_files
end
end
if test $all = yes; or set -q rust_files[1]
if not cargo fmt --version >/dev/null
echo
echo $yellow'Please install "rustfmt" to style Rust, e.g. via:'
echo "rustup component add rustfmt"$normal
exit 127
end
set -l edition_spec string match -r '^edition\s*=.*'
test "$($edition_spec <Cargo.toml)" = "$($edition_spec <.rustfmt.toml)"
or die "Cargo.toml and .rustfmt.toml use different editions"
echo === Running "$green"rustfmt"$normal"
if set -l -q _flag_check
if test $all = yes
cargo fmt --all --check
else
rustfmt --check --files-with-diff $rust_files
end
or die "Rust files are not formatted correctly."
else
if test $all = yes
cargo fmt --all
else
rustfmt $rust_files
end
end
end

View File

@@ -0,0 +1,22 @@
# /// script
# requires-python = ">=3.5"
# dependencies = [
# "launchpadlib",
# ]
# ///
from launchpadlib.launchpad import Launchpad
if __name__ == "__main__":
launchpad = Launchpad.login_anonymously(
"fish shell build script", "production", "~/.cache", version="devel"
)
ubu = launchpad.projects("ubuntu")
print(
"\n".join(
x["name"]
for x in ubu.series.entries
if x["supported"] == True
and x["name"] not in ("trusty", "xenial", "bionic", "focal")
)
)

View File

@@ -10,22 +10,49 @@ command -v updatecli
command -v uv
sort --version-sort </dev/null
uv lock --check
# TODO This is copied from .github/actions/install-sphinx/action.yml
uv lock --check --exclude-newer="$(awk -F'"' <uv.lock '/^exclude-newer[[:space:]]*=/ {print $2}')"
update_gh_action() {
repo=$1
version=$(curl -fsS "https://api.github.com/repos/$repo/releases/latest" | jq -r .tag_name)
[ -n "$version" ]
tag_oid=$(git ls-remote "https://github.com/$repo.git" "refs/tags/$version" | cut -f1)
[ -n "$tag_oid" ]
find .github/workflows -name '*.yml' -type f -exec \
sed -i "s|uses: $repo@\S\+\( \+#.*\)\?|\
uses: $repo@$tag_oid # $version, build_tools/update-dependencies.sh|g" {} +
}
update_gh_action actions/checkout
update_gh_action actions/github-script
update_gh_action actions/upload-artifact
update_gh_action actions/download-artifact
update_gh_action docker/login-action
update_gh_action docker/build-push-action
update_gh_action docker/metadata-action
update_gh_action EmbarkStudios/cargo-deny-action
update_gh_action dessant/lock-threads
update_gh_action softprops/action-gh-release
update_gh_action msys2/setup-msys2
updatecli "${@:-apply}"
uv lock # Python version constraints may have changed.
uv lock --upgrade
# Python version constraints may have changed.
uv lock --upgrade --exclude-newer="$(date --date='7 days ago' --iso-8601)"
from_gh() {
repo=$1
path=$2
out_dir=$3
destination=$3
contents=$(curl -fsS https://raw.githubusercontent.com/"${repo}"/refs/heads/master/"${path}")
printf '%s\n' >"$out_dir/$(basename "$path")" "$contents"
printf '%s\n' "$contents" >"$destination"
}
from_gh ridiculousfish/widecharwidth widechar_width.rs src/widecharwidth/
from_gh ridiculousfish/littlecheck littlecheck/littlecheck.py tests/
from_gh ridiculousfish/widecharwidth widechar_width.rs crates/widecharwidth/src/widechar_width.rs
from_gh ridiculousfish/littlecheck littlecheck/littlecheck.py tests/littlecheck.py
from_gh catppuccin/fish themes/catppuccin-frappe.theme share/themes/catppuccin-frappe.theme
from_gh catppuccin/fish themes/catppuccin-macchiato.theme share/themes/catppuccin-macchiato.theme
from_gh catppuccin/fish themes/catppuccin-mocha.theme share/themes/catppuccin-mocha.theme
# Update Cargo.lock
cargo update

View File

@@ -9,29 +9,30 @@
# For developers:
# - Run with no args to update all PO files after making changes to Rust/fish sources.
# For translators:
# - Specify the language you want to work on as an argument, which must be a file in the po/
# directory. You can specify a language which does not have translations yet by specifying the
# name of a file which does not yet exist. Make sure to follow the naming convention.
# - Specify the language you want to work on as an argument, which must be a file in the
# localization/po/ directory. You can specify a language which does not have translations
# yet by specifying the name of a file which does not yet exist.
# Make sure to follow the naming convention.
# For testing:
# - Specify `--dry-run` to see if any updates to the PO files would by applied by this script.
# If this flag is specified, the script will exit with an error if there are outstanding
# changes, and will display the diff. Do not specify other flags if `--dry-run` is specified.
#
# Specify `--use-existing-template=FILE` to prevent running cargo for extracting an up-to-date
# Specify `--use-existing-template=DIR` to prevent running cargo for extracting an up-to-date
# version of the localized strings. This flag is intended for testing setups which make it
# inconvenient to run cargo here, but run it in an earlier step to ensure up-to-date values.
# This argument is passed on to the `fish_xgettext.fish` script and has no other uses.
# `FILE` must be the path to a gettext template file generated from our compilation process.
# `DIR` must be the path to a gettext template file generated from our compilation process.
# It can be obtained by running:
# set -l FILE (mktemp)
# FISH_GETTEXT_EXTRACTION_FILE=$FILE cargo check --features=gettext-extract
# set -l DIR (mktemp -d)
# FISH_GETTEXT_EXTRACTION_DIR=$DIR cargo check --features=gettext-extract
# The sort utility is locale-sensitive.
# Ensure that sorting output is consistent by setting LC_ALL here.
set -gx LC_ALL C.UTF-8
set -l build_tools (status dirname)
set -l po_dir $build_tools/../po
set -l po_dir $build_tools/../localization/po
set -l extract
@@ -89,8 +90,9 @@ if set -l --query extract
end
if set -l --query _flag_dry_run
# On a dry run, we do not modify po/ but write to a temporary directory instead and check if
# there is a difference between po/ and the tmpdir after re-generating the PO files.
# On a dry run, we do not modify localization/po/ but write to a temporary directory instead
# and check if there is a difference between localization/po/ and the tmpdir after re-generating
# the PO files.
set -g tmpdir (mktemp -d)
# Ensure tmpdir has the same initial state as the po dir.
@@ -146,7 +148,7 @@ end
if set -g --query tmpdir[1]
diff -ur $po_dir $tmpdir
or begin
echo ERROR: translations in ./po/ are stale. Try running build_tools/update_translations.fish
echo ERROR: translations in localization/po/ are stale. Try running build_tools/update_translations.fish
cleanup_exit
end
end

View File

@@ -1,88 +1,60 @@
find_program(SPHINX_EXECUTABLE NAMES sphinx-build
HINTS
$ENV{SPHINX_DIR}
PATH_SUFFIXES bin
DOC "Sphinx documentation generator")
include(FeatureSummary)
set(SPHINX_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/doc_src")
set(SPHINX_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/user_doc")
set(SPHINX_BUILD_DIR "${SPHINX_ROOT_DIR}/build")
set(SPHINX_HTML_DIR "${SPHINX_ROOT_DIR}/html")
set(SPHINX_MANPAGE_DIR "${SPHINX_ROOT_DIR}/man")
set(SPHINX_OUTPUT_DIR "${FISH_RUST_BUILD_DIR}/fish-docs")
set(FISH_INDENT_FOR_BUILDING_DOCS "" CACHE FILEPATH "Path to fish_indent executable for building HTML docs")
if(FISH_INDENT_FOR_BUILDING_DOCS)
set(SPHINX_HTML_FISH_INDENT_DEP)
else()
set(FISH_INDENT_FOR_BUILDING_DOCS "${CMAKE_CURRENT_BINARY_DIR}/fish_indent")
set(SPHINX_HTML_FISH_INDENT_DEP fish_indent)
endif()
set(VARS_FOR_CARGO_SPHINX_WRAPPER
"CARGO_TARGET_DIR=${FISH_RUST_BUILD_DIR}"
"FISH_SPHINX=${SPHINX_EXECUTABLE}"
)
# sphinx-docs uses fish_indent for highlighting.
# Prepend the output dir of fish_indent to PATH.
add_custom_target(sphinx-docs
mkdir -p ${SPHINX_HTML_DIR}/_static/
COMMAND env PATH="${CMAKE_BINARY_DIR}:$$PATH"
${SPHINX_EXECUTABLE}
-j auto
-q -b html
-c "${SPHINX_SRC_DIR}"
-d "${SPHINX_ROOT_DIR}/.doctrees-html"
"${SPHINX_SRC_DIR}"
"${SPHINX_HTML_DIR}"
DEPENDS ${SPHINX_SRC_DIR}/fish_indent_lexer.py fish_indent
COMMAND env ${VARS_FOR_CARGO_SPHINX_WRAPPER}
${Rust_CARGO} xtask html-docs --fish-indent=${FISH_INDENT_FOR_BUILDING_DOCS}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
DEPENDS ${SPHINX_HTML_FISH_INDENT_DEP}
COMMENT "Building HTML documentation with Sphinx")
# sphinx-manpages needs the fish_indent binary for the version number
add_custom_target(sphinx-manpages
env FISH_BUILD_VERSION_FILE=${CMAKE_CURRENT_BINARY_DIR}/${FBVF}
${SPHINX_EXECUTABLE}
-j auto
-q -b man
-c "${SPHINX_SRC_DIR}"
-d "${SPHINX_ROOT_DIR}/.doctrees-man"
"${SPHINX_SRC_DIR}"
# TODO: This only works if we only have section 1 manpages.
"${SPHINX_MANPAGE_DIR}/man1"
DEPENDS CHECK-FISH-BUILD-VERSION-FILE
COMMAND env ${VARS_FOR_CARGO_SPHINX_WRAPPER}
${Rust_CARGO} xtask man-pages
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Building man pages with Sphinx")
if(SPHINX_EXECUTABLE)
option(BUILD_DOCS "build documentation (requires Sphinx)" ON)
else(SPHINX_EXECUTABLE)
option(BUILD_DOCS "build documentation (requires Sphinx)" OFF)
endif(SPHINX_EXECUTABLE)
if(NOT DEFINED WITH_DOCS) # Don't check for legacy options if the new one is defined, to help bisecting.
if(DEFINED BUILD_DOCS)
message(FATAL_ERROR "the BUILD_DOCS option is no longer supported, use -DWITH_DOCS=ON|OFF")
endif()
if(DEFINED INSTALL_DOCS)
message(FATAL_ERROR "the INSTALL_DOCS option is no longer supported, use -DWITH_DOCS=ON|OFF")
endif()
endif()
if(BUILD_DOCS AND NOT SPHINX_EXECUTABLE)
if(SPHINX_EXECUTABLE)
option(WITH_DOCS "build documentation (requires Sphinx)" ON)
else()
option(WITH_DOCS "build documentation (requires Sphinx)" OFF)
endif()
if(WITH_DOCS AND NOT SPHINX_EXECUTABLE)
message(FATAL_ERROR "build documentation selected, but sphinx-build could not be found")
endif()
if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/user_doc/html
AND IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/user_doc/man)
set(HAVE_PREBUILT_DOCS TRUE)
else()
set(HAVE_PREBUILT_DOCS FALSE)
endif()
if(BUILD_DOCS OR HAVE_PREBUILT_DOCS)
set(INSTALL_DOCS ON)
else()
set(INSTALL_DOCS OFF)
endif()
add_feature_info(Documentation INSTALL_DOCS "user manual and documentation")
set(USE_PREBUILT_DOCS FALSE)
if(BUILD_DOCS)
configure_file("${SPHINX_SRC_DIR}/conf.py" "${SPHINX_BUILD_DIR}/conf.py" @ONLY)
add_custom_target(doc ALL
DEPENDS sphinx-docs sphinx-manpages)
add_feature_info(Documentation WITH_DOCS "user manual and documentation")
if(WITH_DOCS)
add_custom_target(doc ALL DEPENDS sphinx-docs sphinx-manpages)
# Group docs targets into a DocsTargets folder
set_property(TARGET doc sphinx-docs sphinx-manpages
PROPERTY FOLDER cmake/DocTargets)
elseif(HAVE_PREBUILT_DOCS)
set(USE_PREBUILT_DOCS TRUE)
if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
# Out of tree build - link the prebuilt documentation to the build tree
add_custom_target(link_doc ALL)
add_custom_command(TARGET link_doc
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/user_doc ${CMAKE_CURRENT_BINARY_DIR}/user_doc
POST_BUILD)
endif()
endif(BUILD_DOCS)
set_property(
TARGET doc sphinx-docs sphinx-manpages
PROPERTY FOLDER cmake/DocTargets
)
endif()

View File

@@ -22,17 +22,17 @@ foreach(_VAR ${_Rust_USER_VARS})
endforeach()
if (NOT DEFINED Rust_CARGO_CACHED)
find_program(Rust_CARGO_CACHED cargo PATHS "$ENV{HOME}/.cargo/bin")
find_program(Rust_CARGO_CACHED cargo PATHS "$ENV{HOME}/.cargo/bin")
endif()
if (NOT EXISTS "${Rust_CARGO_CACHED}")
message(FATAL_ERROR "The cargo executable ${Rust_CARGO_CACHED} was not found. "
"Consider setting `Rust_CARGO_CACHED` to the absolute path of `cargo`."
)
message(FATAL_ERROR "The cargo executable ${Rust_CARGO_CACHED} was not found. "
"Consider setting `Rust_CARGO_CACHED` to the absolute path of `cargo`."
)
endif()
if (NOT DEFINED Rust_COMPILER_CACHED)
find_program(Rust_COMPILER_CACHED rustc PATHS "$ENV{HOME}/.cargo/bin")
find_program(Rust_COMPILER_CACHED rustc PATHS "$ENV{HOME}/.cargo/bin")
endif()
@@ -45,31 +45,31 @@ endif()
# Figure out the target by just using the host target.
# If you want to cross-compile, you'll have to set Rust_CARGO_TARGET
if(NOT Rust_CARGO_TARGET_CACHED)
execute_process(
COMMAND "${Rust_COMPILER_CACHED}" --version --verbose
OUTPUT_VARIABLE _RUSTC_VERSION_RAW
RESULT_VARIABLE _RUSTC_VERSION_RESULT
)
if(NOT ( "${_RUSTC_VERSION_RESULT}" EQUAL "0" ))
message(FATAL_ERROR "Failed to get rustc version.\n"
"${Rust_COMPILER} --version failed with error: `${_RUSTC_VERSION_RESULT}`")
endif()
if (_RUSTC_VERSION_RAW MATCHES "host: ([a-zA-Z0-9_\\-]*)\n")
set(Rust_DEFAULT_HOST_TARGET "${CMAKE_MATCH_1}")
else()
message(FATAL_ERROR
"Failed to parse rustc host target. `rustc --version --verbose` evaluated to:\n${_RUSTC_VERSION_RAW}"
execute_process(
COMMAND "${Rust_COMPILER_CACHED}" --version --verbose
OUTPUT_VARIABLE _RUSTC_VERSION_RAW
RESULT_VARIABLE _RUSTC_VERSION_RESULT
)
endif()
if(CMAKE_CROSSCOMPILING)
message(FATAL_ERROR "CMake is in cross-compiling mode."
"Manually set `Rust_CARGO_TARGET`."
)
endif()
set(Rust_CARGO_TARGET_CACHED "${Rust_DEFAULT_HOST_TARGET}" CACHE STRING "Target triple")
if(NOT ( "${_RUSTC_VERSION_RESULT}" EQUAL "0" ))
message(FATAL_ERROR "Failed to get rustc version.\n"
"${Rust_COMPILER} --version failed with error: `${_RUSTC_VERSION_RESULT}`")
endif()
if (_RUSTC_VERSION_RAW MATCHES "host: ([a-zA-Z0-9_\\-]*)\n")
set(Rust_DEFAULT_HOST_TARGET "${CMAKE_MATCH_1}")
else()
message(FATAL_ERROR
"Failed to parse rustc host target. `rustc --version --verbose` evaluated to:\n${_RUSTC_VERSION_RAW}"
)
endif()
if(CMAKE_CROSSCOMPILING)
message(FATAL_ERROR "CMake is in cross-compiling mode."
"Manually set `Rust_CARGO_TARGET`."
)
endif()
set(Rust_CARGO_TARGET_CACHED "${Rust_DEFAULT_HOST_TARGET}" CACHE STRING "Target triple")
endif()
# Set the input variables as non-cache variables so that the variables are available after

View File

@@ -1,7 +1,5 @@
set(CMAKE_INSTALL_MESSAGE NEVER)
set(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fish ${CMAKE_CURRENT_BINARY_DIR}/fish_indent ${CMAKE_CURRENT_BINARY_DIR}/fish_key_reader)
set(prefix ${CMAKE_INSTALL_PREFIX})
set(bindir ${CMAKE_INSTALL_BINDIR})
set(sysconfdir ${CMAKE_INSTALL_SYSCONFDIR})
@@ -16,32 +14,36 @@ set(rel_completionsdir "fish/vendor_completions.d")
set(rel_functionsdir "fish/vendor_functions.d")
set(rel_confdir "fish/vendor_conf.d")
set(extra_completionsdir
"${datadir}/${rel_completionsdir}"
CACHE STRING "Path for extra completions")
set(
extra_completionsdir "${datadir}/${rel_completionsdir}"
CACHE STRING "Path for extra completions"
)
set(extra_functionsdir
"${datadir}/${rel_functionsdir}"
CACHE STRING "Path for extra functions")
set(
extra_functionsdir "${datadir}/${rel_functionsdir}"
CACHE STRING "Path for extra functions"
)
set(extra_confdir
"${datadir}/${rel_confdir}"
CACHE STRING "Path for extra configuration")
set(
extra_confdir "${datadir}/${rel_confdir}"
CACHE STRING "Path for extra configuration"
)
# These are the man pages that go in system manpath; all manpages go in the fish-specific manpath.
set(MANUALS ${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish.1
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish_indent.1
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish_key_reader.1
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-doc.1
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-tutorial.1
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-language.1
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-interactive.1
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-terminal-compatibility.1
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-completions.1
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-prompt-tutorial.1
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-for-bash-users.1
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-faq.1
set(MANUALS
${SPHINX_OUTPUT_DIR}/man/man1/fish.1
${SPHINX_OUTPUT_DIR}/man/man1/fish_indent.1
${SPHINX_OUTPUT_DIR}/man/man1/fish_key_reader.1
${SPHINX_OUTPUT_DIR}/man/man1/fish-doc.1
${SPHINX_OUTPUT_DIR}/man/man1/fish-tutorial.1
${SPHINX_OUTPUT_DIR}/man/man1/fish-language.1
${SPHINX_OUTPUT_DIR}/man/man1/fish-interactive.1
${SPHINX_OUTPUT_DIR}/man/man1/fish-terminal-compatibility.1
${SPHINX_OUTPUT_DIR}/man/man1/fish-completions.1
${SPHINX_OUTPUT_DIR}/man/man1/fish-prompt-tutorial.1
${SPHINX_OUTPUT_DIR}/man/man1/fish-for-bash-users.1
${SPHINX_OUTPUT_DIR}/man/man1/fish-faq.1
)
# Determine which man page we don't want to install.
@@ -50,118 +52,167 @@ set(MANUALS ${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish.1
# On other operating systems, don't install a realpath man page, as they almost all have a realpath
# command, while macOS does not.
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(CONDEMNED_PAGE "open.1")
set(CONDEMNED_PAGE "open.1")
else()
set(CONDEMNED_PAGE "realpath.1")
set(CONDEMNED_PAGE "realpath.1")
endif()
# Define a function to help us create directories.
function(FISH_CREATE_DIRS)
foreach(dir ${ARGV})
install(DIRECTORY DESTINATION ${dir})
endforeach(dir)
foreach(dir ${ARGV})
install(DIRECTORY DESTINATION ${dir})
endforeach(dir)
endfunction(FISH_CREATE_DIRS)
function(FISH_TRY_CREATE_DIRS)
foreach(dir ${ARGV})
if(NOT IS_ABSOLUTE ${dir})
set(abs_dir "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${dir}")
else()
set(abs_dir "\$ENV{DESTDIR}${dir}")
endif()
install(SCRIPT CODE "EXECUTE_PROCESS(COMMAND mkdir -p ${abs_dir} OUTPUT_QUIET ERROR_QUIET)
execute_process(COMMAND chmod 755 ${abs_dir} OUTPUT_QUIET ERROR_QUIET)
")
endforeach()
foreach(dir ${ARGV})
if(NOT IS_ABSOLUTE ${dir})
set(abs_dir "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${dir}")
else()
set(abs_dir "\$ENV{DESTDIR}${dir}")
endif()
install(SCRIPT CODE "
EXECUTE_PROCESS(COMMAND mkdir -p ${abs_dir} OUTPUT_QUIET ERROR_QUIET)
execute_process(COMMAND chmod 755 ${abs_dir} OUTPUT_QUIET ERROR_QUIET)
")
endforeach()
endfunction(FISH_TRY_CREATE_DIRS)
install(PROGRAMS ${PROGRAMS}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ
GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
DESTINATION ${bindir})
install(
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fish
PERMISSIONS
OWNER_READ
OWNER_WRITE
OWNER_EXECUTE
GROUP_READ
GROUP_EXECUTE
WORLD_READ
WORLD_EXECUTE
DESTINATION ${bindir}
)
if(NOT IS_ABSOLUTE ${bindir})
set(abs_bindir "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${bindir}")
else()
set(abs_bindir "\$ENV{DESTDIR}${bindir}")
endif()
install(CODE "file(CREATE_LINK ${abs_bindir}/fish ${abs_bindir}/fish_indent)")
install(CODE "file(CREATE_LINK ${abs_bindir}/fish ${abs_bindir}/fish_key_reader)")
fish_create_dirs(${sysconfdir}/fish/conf.d ${sysconfdir}/fish/completions
${sysconfdir}/fish/functions)
install(FILES etc/config.fish DESTINATION ${sysconfdir}/fish/)
fish_create_dirs(${rel_datadir}/fish ${rel_datadir}/fish/completions
${rel_datadir}/fish/functions ${rel_datadir}/fish/groff
${rel_datadir}/fish/man/man1 ${rel_datadir}/fish/tools
${rel_datadir}/fish/tools/web_config
${rel_datadir}/fish/tools/web_config/js
${rel_datadir}/fish/tools/web_config/sample_prompts
${rel_datadir}/fish/tools/web_config/themes
)
fish_create_dirs(
${rel_datadir}/fish ${rel_datadir}/fish/completions
${rel_datadir}/fish/functions
${rel_datadir}/fish/man/man1 ${rel_datadir}/fish/tools
${rel_datadir}/fish/tools/web_config
${rel_datadir}/fish/tools/web_config/js
${rel_datadir}/fish/prompts
${rel_datadir}/fish/themes
)
configure_file(share/__fish_build_paths.fish.in share/__fish_build_paths.fish)
install(FILES share/config.fish
${CMAKE_CURRENT_BINARY_DIR}/share/__fish_build_paths.fish
DESTINATION ${rel_datadir}/fish)
${CMAKE_CURRENT_BINARY_DIR}/share/__fish_build_paths.fish
DESTINATION ${rel_datadir}/fish
)
# Create only the vendor directories inside the prefix (#5029 / #6508)
fish_create_dirs(${rel_datadir}/fish/vendor_completions.d ${rel_datadir}/fish/vendor_functions.d
${rel_datadir}/fish/vendor_conf.d)
fish_create_dirs(
${rel_datadir}/fish/vendor_completions.d
${rel_datadir}/fish/vendor_functions.d
${rel_datadir}/fish/vendor_conf.d
)
fish_try_create_dirs(${rel_datadir}/pkgconfig)
configure_file(fish.pc.in fish.pc.noversion @ONLY)
add_custom_command(OUTPUT fish.pc
add_custom_command(
OUTPUT fish.pc
COMMAND sed '/Version/d' fish.pc.noversion > fish.pc
COMMAND printf "Version: " >> fish.pc
COMMAND cat ${FBVF} >> fish.pc
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/git_version_gen.sh >> fish.pc
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS CHECK-FISH-BUILD-VERSION-FILE ${CMAKE_CURRENT_BINARY_DIR}/fish.pc.noversion)
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/fish.pc.noversion
)
add_custom_target(build_fish_pc ALL DEPENDS fish.pc)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fish.pc
DESTINATION ${rel_datadir}/pkgconfig)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/fish.pc
DESTINATION ${rel_datadir}/pkgconfig
)
install(DIRECTORY share/completions/
DESTINATION ${rel_datadir}/fish/completions
FILES_MATCHING PATTERN "*.fish")
install(
DIRECTORY share/completions/
DESTINATION ${rel_datadir}/fish/completions
FILES_MATCHING PATTERN "*.fish"
)
install(DIRECTORY share/functions/
DESTINATION ${rel_datadir}/fish/functions
FILES_MATCHING PATTERN "*.fish")
install(
DIRECTORY share/functions/
DESTINATION ${rel_datadir}/fish/functions
FILES_MATCHING PATTERN "*.fish"
)
install(DIRECTORY share/groff
DESTINATION ${rel_datadir}/fish)
install(
DIRECTORY share/prompts/
DESTINATION ${rel_datadir}/fish/prompts
FILES_MATCHING PATTERN "*.fish"
)
install(
DIRECTORY share/themes/
DESTINATION ${rel_datadir}/fish/themes
FILES_MATCHING PATTERN "*.theme"
)
# CONDEMNED_PAGE is managed by the conditional above
# Building the man pages is optional: if sphinx isn't installed, they're not built
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/
DESTINATION ${rel_datadir}/fish/man/man1
FILES_MATCHING
PATTERN "*.1"
PATTERN ${CONDEMNED_PAGE} EXCLUDE)
install(
DIRECTORY ${SPHINX_OUTPUT_DIR}/man/man1/
DESTINATION ${rel_datadir}/fish/man/man1
FILES_MATCHING
PATTERN "*.1"
PATTERN ${CONDEMNED_PAGE} EXCLUDE
)
install(PROGRAMS share/tools/create_manpage_completions.py
DESTINATION ${rel_datadir}/fish/tools/)
install(
PROGRAMS share/tools/create_manpage_completions.py
DESTINATION ${rel_datadir}/fish/tools/
)
install(DIRECTORY share/tools/web_config
DESTINATION ${rel_datadir}/fish/tools/
FILES_MATCHING
PATTERN "*.png"
PATTERN "*.css"
PATTERN "*.html"
PATTERN "*.py"
PATTERN "*.js"
PATTERN "*.theme"
PATTERN "*.fish")
install(
DIRECTORY share/tools/web_config
DESTINATION ${rel_datadir}/fish/tools/
FILES_MATCHING
PATTERN "*.png"
PATTERN "*.css"
PATTERN "*.html"
PATTERN "*.py"
PATTERN "*.js"
)
# Building the man pages is optional: if Sphinx isn't installed, they're not built
install(FILES ${MANUALS} DESTINATION ${mandir}/man1/ OPTIONAL)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/user_doc/html/ # Trailing slash is important!
DESTINATION ${docdir} OPTIONAL)
install(
DIRECTORY ${SPHINX_OUTPUT_DIR}/html/ # Trailing slash is important!
DESTINATION ${docdir} OPTIONAL
)
install(FILES CHANGELOG.rst DESTINATION ${docdir})
# Group install targets into a InstallTargets folder
set_property(TARGET build_fish_pc CHECK-FISH-BUILD-VERSION-FILE
PROPERTY FOLDER cmake/InstallTargets)
set_property(
TARGET build_fish_pc
PROPERTY FOLDER cmake/InstallTargets
)
# Make a target build_root that installs into the buildroot directory, for testing.
set(BUILDROOT_DIR ${CMAKE_CURRENT_BINARY_DIR}/buildroot)
add_custom_target(build_root
COMMAND DESTDIR=${BUILDROOT_DIR} ${CMAKE_COMMAND}
--build ${CMAKE_CURRENT_BINARY_DIR} --target install)
add_custom_target(
build_root
COMMAND DESTDIR=${BUILDROOT_DIR} ${CMAKE_COMMAND}
--build ${CMAKE_CURRENT_BINARY_DIR} --target install
)

View File

@@ -26,7 +26,7 @@ add_executable(fish_macapp EXCLUDE_FROM_ALL
# so cmake must be re-run after version changes for the app to be updated. But
# generally this will be run by make_macos_pkg.sh which always re-runs cmake.
execute_process(
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/git_version_gen.sh --stdout
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/git_version_gen.sh
COMMAND cut -d- -f1
OUTPUT_VARIABLE FISH_SHORT_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)

View File

@@ -1,9 +1,10 @@
set(FISH_USE_SYSTEM_PCRE2 ON CACHE BOOL
"Try to use PCRE2 from the system, instead of the pcre2-sys version")
"Try to use PCRE2 from the system, instead of the pcre2-sys version"
)
if(FISH_USE_SYSTEM_PCRE2)
message(STATUS "Trying to use PCRE2 from the system")
message(STATUS "Trying to use PCRE2 from the system")
else()
message(STATUS "Forcing static build of PCRE2")
set(FISH_PCRE2_BUILDFLAG "PCRE2_SYS_STATIC=1")
message(STATUS "Forcing static build of PCRE2")
set(FISH_PCRE2_BUILDFLAG "PCRE2_SYS_STATIC=1")
endif(FISH_USE_SYSTEM_PCRE2)

View File

@@ -1,13 +1,11 @@
include(FeatureSummary)
include(FindRust)
find_package(Rust REQUIRED)
set(FISH_RUST_BUILD_DIR "${CMAKE_BINARY_DIR}/cargo/build")
list(APPEND FISH_CARGO_FEATURES_LIST "embed-data")
set(FISH_RUST_BUILD_DIR "${CMAKE_BINARY_DIR}/cargo")
if(DEFINED ASAN)
list(APPEND CARGO_FLAGS "-Z" "build-std")
list(APPEND FISH_CARGO_FEATURES_LIST "asan")
endif()
if(DEFINED TSAN)
list(APPEND CARGO_FLAGS "-Z" "build-std")
@@ -21,12 +19,18 @@ else()
endif()
set(rust_profile $<IF:$<CONFIG:Debug>,debug,$<IF:$<CONFIG:RelWithDebInfo>,release-with-debug,release>>)
set(rust_debugflags "$<$<CONFIG:Debug>:-g>$<$<CONFIG:RelWithDebInfo>:-g>")
option(WITH_GETTEXT "Build with gettext localization support. Requires `msgfmt` to work." ON)
if (NOT DEFINED WITH_MESSAGE_LOCALIZATION) # Don't check for legacy options if the new one is defined, to help bisecting.
if(DEFINED WITH_GETTEXT)
message(FATAL_ERROR "the WITH_GETTEXT option is no longer supported, use -DWITH_MESSAGE_LOCALIZATION=ON|OFF")
endif()
endif()
option(WITH_MESSAGE_LOCALIZATION "Build with localization support. Requires `msgfmt` to work." ON)
# Enable gettext feature unless explicitly disabled.
if(NOT DEFINED WITH_GETTEXT OR "${WITH_GETTEXT}")
if(NOT DEFINED WITH_MESSAGE_LOCALIZATION OR "${WITH_MESSAGE_LOCALIZATION}")
list(APPEND FISH_CARGO_FEATURES_LIST "localize-messages")
endif()
add_feature_info(Translation WITH_MESSAGE_LOCALIZATION "message localization (requires gettext)")
list(JOIN FISH_CARGO_FEATURES_LIST , FISH_CARGO_FEATURES)

View File

@@ -1,29 +1,27 @@
add_executable(fish_test_helper tests/fish_test_helper.c)
FILE(GLOB FISH_CHECKS CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/tests/checks/*.fish)
foreach(CHECK ${FISH_CHECKS})
get_filename_component(CHECK_NAME ${CHECK} NAME)
add_custom_target(
test_${CHECK_NAME}
COMMAND ${CMAKE_SOURCE_DIR}/tests/test_driver.py ${CMAKE_CURRENT_BINARY_DIR}
checks/${CHECK_NAME}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
DEPENDS fish fish_indent fish_key_reader fish_test_helper
USES_TERMINAL
)
get_filename_component(CHECK_NAME ${CHECK} NAME)
add_custom_target(
test_${CHECK_NAME}
COMMAND ${CMAKE_SOURCE_DIR}/tests/test_driver.py ${CMAKE_CURRENT_BINARY_DIR}
checks/${CHECK_NAME}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
DEPENDS fish fish_indent fish_key_reader
USES_TERMINAL
)
endforeach(CHECK)
FILE(GLOB PEXPECTS CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/tests/pexpects/*.py)
foreach(PEXPECT ${PEXPECTS})
get_filename_component(PEXPECT ${PEXPECT} NAME)
add_custom_target(
test_${PEXPECT}
COMMAND ${CMAKE_SOURCE_DIR}/tests/test_driver.py ${CMAKE_CURRENT_BINARY_DIR}
pexpects/${PEXPECT}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
DEPENDS fish fish_indent fish_key_reader fish_test_helper
USES_TERMINAL
)
get_filename_component(PEXPECT ${PEXPECT} NAME)
add_custom_target(
test_${PEXPECT}
COMMAND ${CMAKE_SOURCE_DIR}/tests/test_driver.py ${CMAKE_CURRENT_BINARY_DIR}
pexpects/${PEXPECT}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
DEPENDS fish fish_indent fish_key_reader
USES_TERMINAL
)
endforeach(PEXPECT)
# Rust stuff.
@@ -32,14 +30,20 @@ if(DEFINED ASAN)
# Rust w/ -Zsanitizer=address requires explicitly specifying the --target triple or else linker
# errors pertaining to asan symbols will ensue.
if(NOT DEFINED Rust_CARGO_TARGET)
message(FATAL_ERROR "ASAN requires defining the CMake variable Rust_CARGO_TARGET to the
intended target triple")
message(
FATAL_ERROR
"ASAN requires defining the CMake variable Rust_CARGO_TARGET to the
intended target triple"
)
endif()
endif()
if(DEFINED TSAN)
if(NOT DEFINED Rust_CARGO_TARGET)
message(FATAL_ERROR "TSAN requires defining the CMake variable Rust_CARGO_TARGET to the
intended target triple")
message(
FATAL_ERROR
"TSAN requires defining the CMake variable Rust_CARGO_TARGET to the
intended target triple"
)
endif()
endif()
@@ -55,10 +59,18 @@ endif()
# The top-level test target is "fish_run_tests".
add_custom_target(fish_run_tests
# TODO: This should be replaced with a unified solution, possibly build_tools/check.sh.
COMMAND ${CMAKE_SOURCE_DIR}/tests/test_driver.py ${max_concurrency_flag} ${CMAKE_CURRENT_BINARY_DIR}
COMMAND env ${VARS_FOR_CARGO} ${Rust_CARGO} test --no-default-features ${CARGO_FLAGS} --workspace --target-dir ${rust_target_dir} ${cargo_test_flags}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
DEPENDS fish fish_indent fish_key_reader fish_test_helper
USES_TERMINAL
# TODO: This should be replaced with a unified solution, possibly build_tools/check.sh.
COMMAND ${CMAKE_SOURCE_DIR}/tests/test_driver.py ${max_concurrency_flag} ${CMAKE_CURRENT_BINARY_DIR}
COMMAND
env ${VARS_FOR_CARGO} ${Rust_CARGO}
test
--no-default-features
--features=${FISH_CARGO_FEATURES}
${CARGO_FLAGS}
--workspace
--target-dir ${rust_target_dir}
${cargo_test_flags}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
DEPENDS fish fish_indent fish_key_reader
USES_TERMINAL
)

View File

@@ -1,55 +0,0 @@
# This file adds commands to manage the FISH-BUILD-VERSION-FILE (hereafter
# FBVF). This file exists in the build directory and is used to populate the
# documentation and also the version string in fish_version.o (printed with
# `echo $version` and also fish --version). The essential idea is that we are
# going to invoke git_version_gen.sh, which will update the
# FISH-BUILD-VERSION-FILE only if it needs to change; this is what makes
# incremental rebuilds fast.
#
# This code is delicate, with the chief subtlety revolving around Ninja. A
# natural and naive approach would tell the generated build system that FBVF is
# a dependency of fish_version.o, and that git_version_gen.sh updates it. Make
# will then invoke the script, check the timestamp on fish_version.o and FBVF,
# see that FBVF is earlier, and then not rebuild fish_version.o. Ninja,
# however, decides what to build up-front and will unconditionally rebuild
# fish_version.o.
#
# To avoid this with Ninja, we want to hook into its 'restat' option which we
# can do through the BYPRODUCTS feature of CMake. See
# https://cmake.org/cmake/help/latest/policy/CMP0058.html
#
# Unfortunately BYPRODUCTS behaves strangely with the Makefile generator: it
# marks FBVF as generated and then CMake itself will `touch` it on every build,
# meaning that using BYPRODUCTS will cause fish_version.o to be rebuilt
# unconditionally with the Makefile generator. Thus we want to use the
# natural-and-naive approach for Makefiles.
# **IMPORTANT** If you touch these build rules, please test both Ninja and
# Makefile generators with both a clean and dirty git tree. Verify that both
# generated build systems rebuild fish when the git tree goes from dirty to
# clean (and vice versa), and verify they do NOT rebuild it when the git tree
# stays the same (incremental builds must be fast).
# Just a handy abbreviation.
set(FBVF FISH-BUILD-VERSION-FILE)
# TODO: find a cleaner way to do this.
IF (${CMAKE_GENERATOR} STREQUAL Ninja)
set(FBVF-OUTPUT fish-build-version-witness.txt)
set(CFBVF-BYPRODUCTS ${FBVF})
else(${CMAKE_GENERATOR} STREQUAL Ninja)
set(FBVF-OUTPUT ${FBVF})
set(CFBVF-BYPRODUCTS)
endif(${CMAKE_GENERATOR} STREQUAL Ninja)
# Set up the version targets
add_custom_target(CHECK-FISH-BUILD-VERSION-FILE
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/git_version_gen.sh ${CMAKE_CURRENT_BINARY_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
BYPRODUCTS ${CFBVF-BYPRODUCTS})
add_custom_command(OUTPUT ${FBVF-OUTPUT}
DEPENDS CHECK-FISH-BUILD-VERSION-FILE)
# Abbreviation for the target.
set(CFBVF CHECK-FISH-BUILD-VERSION-FILE)

512
contrib/debian/changelog Normal file
View File

@@ -0,0 +1,512 @@
fish (4.6.0-1) stable; urgency=medium
* Release of new version 4.6.0.
See https://github.com/fish-shell/fish-shell/releases/tag/4.6.0 for details.
-- Johannes Altmanninger <aclopte@gmail.com> Sat, 28 Mar 2026 12:56:37 +0800
fish (4.5.0-1) stable; urgency=medium
* Release of new version 4.5.0.
See https://github.com/fish-shell/fish-shell/releases/tag/4.5.0 for details.
-- Johannes Altmanninger <aclopte@gmail.com> Tue, 17 Feb 2026 11:32:33 +1100
fish (4.4.0-1) stable; urgency=medium
* Release of new version 4.4.0.
See https://github.com/fish-shell/fish-shell/releases/tag/4.4.0 for details.
-- Johannes Altmanninger <aclopte@gmail.com> Tue, 03 Feb 2026 12:11:51 +1100
fish (4.3.3-1) stable; urgency=medium
* Release of new version 4.3.3.
See https://github.com/fish-shell/fish-shell/releases/tag/4.3.3 for details.
-- Johannes Altmanninger <aclopte@gmail.com> Wed, 07 Jan 2026 08:34:20 +0100
fish (4.3.2-1) stable; urgency=medium
* Release of new version 4.3.2.
See https://github.com/fish-shell/fish-shell/releases/tag/4.3.2 for details.
-- Johannes Altmanninger <aclopte@gmail.com> Tue, 30 Dec 2025 17:21:04 +0100
fish (4.3.1-1) stable; urgency=medium
* Release of new version 4.3.1.
See https://github.com/fish-shell/fish-shell/releases/tag/4.3.1 for details.
-- Johannes Altmanninger <aclopte@gmail.com> Sun, 28 Dec 2025 16:54:44 +0100
fish (4.3.0-1) stable; urgency=medium
* Release of new version 4.3.0.
See https://github.com/fish-shell/fish-shell/releases/tag/4.3.0 for details.
-- Johannes Altmanninger <aclopte@gmail.com> Sun, 28 Dec 2025 10:20:47 +0100
fish (4.2.1-1) testing; urgency=medium
* Release of new version 4.2.1.
See https://github.com/fish-shell/fish-shell/releases/tag/4.2.1 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Thu, 13 Nov 2025 20:42:43 +0800
fish (4.2.0-1) testing; urgency=medium
* Release of new version 4.2.0.
See https://github.com/fish-shell/fish-shell/releases/tag/4.2.0 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Mon, 10 Nov 2025 19:29:03 +0800
fish (4.1.2-1) testing; urgency=medium
* Release of new version 4.1.2.
See https://github.com/fish-shell/fish-shell/releases/tag/4.1.2 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Wed, 08 Oct 2025 13:46:45 +0800
fish (4.1.1-1) testing; urgency=medium
* Release of new version 4.1.1.
See https://github.com/fish-shell/fish-shell/releases/tag/4.1.1 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Fri, 03 Oct 2025 16:43:43 +0800
fish (4.0.8-1) testing; urgency=medium
* Release of new version 4.0.8.
See https://github.com/fish-shell/fish-shell/releases/tag/4.0.8 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Thu, 18 Sep 2025 22:17:43 +0800
fish (4.0.6-1) testing; urgency=medium
* Release of new version 4.0.6.
See https://github.com/fish-shell/fish-shell/releases/tag/4.0.6 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Wed, 17 Sep 2025 12:27:09 +0800
fish (4.0.2-2) testing; urgency=medium
* Fix tests on Debian.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sun, 20 Apr 2025 23:08:14 +0800
fish (4.0.2-1) testing; urgency=medium
* Release of new version 4.0.2.
See https://github.com/fish-shell/fish-shell/releases/tag/4.0.2 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sun, 20 Apr 2025 21:24:18 +0800
fish (4.0.1-1) testing; urgency=medium
* Release of new version 4.0.1.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Thu, 13 Mar 2025 11:30:21 +0800
fish (4.0.0-2) testing; urgency=medium
* Fix tests on Debian.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Thu, 27 Feb 2025 21:50:33 +0800
fish (4.0.0-1) testing; urgency=medium
* Release of new version 4.0.0.
See https://github.com/fish-shell/fish-shell/releases/tag/4.0.0 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Thu, 27 Feb 2025 19:22:30 +0800
fish (4.0.1-1) testing; urgency=medium
* Release of new beta version 4.0b1.
See https://github.com/fish-shell/fish-shell/releases/tag/4.0b1 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Tue, 17 Dec 2024 23:42:25 +0800
fish (3.7.1-1) testing; urgency=medium
* Release of new version 3.7.1.
See https://github.com/fish-shell/fish-shell/releases/tag/3.7.1 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Tue, 19 Mar 2024 13:26:22 +0800
fish (3.7.0-1) testing; urgency=medium
* Release of new version 3.7.0.
See https://github.com/fish-shell/fish-shell/releases/tag/3.7.0 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Mon, 01 Jan 2024 23:32:55 +0800
fish (3.6.4-1) testing; urgency=medium
* Release of new version 3.6.4.
See https://github.com/fish-shell/fish-shell/releases/tag/3.6.4 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Tue, 05 Dec 2023 22:34:09 +0800
fish (3.6.3-1) testing; urgency=medium
* Release of new version 3.6.3.
See https://github.com/fish-shell/fish-shell/releases/tag/3.6.3 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Tue, 05 Dec 2023 00:11:12 +0800
fish (3.6.2-1) testing; urgency=medium
* Release of new version 3.6.2.
* Includes a fix for CVE-2023-49284.
See https://github.com/fish-shell/fish-shell/releases/tag/3.6.2 for details.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Mon, 04 Dec 2023 23:16:42 +0800
fish (3.6.1-1) testing; urgency=medium
* Release of new version 3.6.1.
See https://github.com/fish-shell/fish-shell/releases/tag/3.6.1 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sat, 25 Mar 2023 17:22:12 +0800
fish (3.6.0-1) testing; urgency=medium
* Release of new version 3.6.0.
See https://github.com/fish-shell/fish-shell/releases/tag/3.6.0 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sat, 07 Jan 2023 22:41:32 +0800
fish (3.5.1-1) testing; urgency=medium
* Release of new version 3.5.1.
See https://github.com/fish-shell/fish-shell/releases/tag/3.5.1 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Wed, 20 Jul 2022 21:54:09 +0800
fish (3.5.0-1) testing; urgency=medium
* Release of new version 3.5.0.
See https://github.com/fish-shell/fish-shell/releases/tag/3.5.0 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Thu, 16 Jun 2022 19:45:33 +0800
fish (3.4.0-1) testing; urgency=medium
* Release of new version 3.4.0.
See https://github.com/fish-shell/fish-shell/releases/tag/3.4.0 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sat, 12 Mar 2022 23:24:22 +0800
fish (3.3.1-1) testing; urgency=medium
* Release of new version 3.3.1.
See https://github.com/fish-shell/fish-shell/releases/tag/3.3.1 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Tue, 06 Jul 2021 23:22:36 +0800
fish (3.3.0-1) testing; urgency=medium
* Release of new version 3.3.0.
See https://github.com/fish-shell/fish-shell/releases/tag/3.3.0 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Mon, 28 Jun 2021 23:06:36 +0800
fish (3.2.2-1) testing; urgency=medium
* Release of new version 3.2.2.
See https://github.com/fish-shell/fish-shell/releases/tag/3.2.2 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Wed, 07 Apr 2021 21:10:54 +0800
fish (3.2.1-1) testing; urgency=medium
* Release of new version 3.2.1.
See https://github.com/fish-shell/fish-shell/releases/tag/3.2.1 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Thu, 18 Mar 2021 12:08:13 +0800
fish (3.2.0-1) testing; urgency=medium
* Release of new version 3.2.0.
See https://github.com/fish-shell/fish-shell/releases/tag/3.2.0 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Mon, 01 Mar 2021 21:22:39 +0800
fish (3.1.2-1) testing; urgency=medium
* Release of new version 3.1.2.
See https://github.com/fish-shell/fish-shell/releases/tag/3.1.2 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Wed, 29 Apr 2020 11:22:35 +0800
fish (3.1.1-1) testing; urgency=medium
* Release of new version 3.1.1.
See https://github.com/fish-shell/fish-shell/releases/tag/3.1.1 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Mon, 27 Apr 2020 22:45:35 +0800
fish (3.1.0-1) testing; urgency=medium
* Release of new version 3.1.0.
See https://github.com/fish-shell/fish-shell/releases/tag/3.1.0 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Wed, 12 Feb 2020 22:34:53 +0800
fish (3.1.1-1) testing; urgency=medium
* Release of new beta version 3.1b1.
See https://github.com/fish-shell/fish-shell/releases/tag/3.1b1 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sun, 26 Jan 2020 21:42:46 +0800
fish (3.0.2-1) testing; urgency=medium
* Release of new version 3.0.2.
See https://github.com/fish-shell/fish-shell/releases/tag/3.0.2 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Tue, 19 Feb 2019 21:45:05 +0800
fish (3.0.1-1) testing; urgency=medium
* Release of new version 3.0.1.
See https://github.com/fish-shell/fish-shell/releases/tag/3.0.1 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Mon, 11 Feb 2019 20:23:55 +0800
fish (3.0.0-1) testing; urgency=medium
* Release of new version 3.0.0.
See https://github.com/fish-shell/fish-shell/releases/tag/3.0.0 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Fri, 28 Dec 2018 21:10:28 +0800
fish (3.0.1-1) testing; urgency=medium
* Release of new beta version 3.0b1.
See https://github.com/fish-shell/fish-shell/releases/tag/3.0b1 for
significant changes, which includes backward incompatibility.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Tue, 11 Dec 2018 22:59:15 +0800
fish (2.7.1-1) testing; urgency=medium
* Release of new bug fix version 2.7.1. On all Linux platforms, this is
release will behave identically to 2.7.0.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sat, 23 Dec 2017 00:43:12 +0800
fish (2.7.0-1) testing; urgency=medium
* Release of new version 2.7.0.
See https://github.com/fish-shell/fish-shell/releases/tag/2.7.0 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Thu, 23 Nov 2017 18:38:21 +0800
fish (2.7.1-1) testing; urgency=medium
* Release of new beta version 2.7b1.
See https://github.com/fish-shell/fish-shell/releases/tag/2.7b1 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Tue, 31 Oct 2017 20:32:29 +0800
fish (2.6.0-1) testing; urgency=medium
* Relase of new version 2.6.0.
See https://github.com/fish-shell/fish-shell/releases/tag/2.6.0 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sat, 03 Jun 2017 20:51:50 +0800
fish (2.6b1-1) testing; urgency=medium
* Release of new beta version 2.6b1.
See https://github.com/fish-shell/fish-shell/releases/tag/2.6b1 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sun, 14 May 2017 10:47:39 +0800
fish (2.5.0-1) testing; urgency=medium
* Release of new version 2.5.0.
See https://github.com/fish-shell/fish-shell/releases/tag/2.5.0 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Fri, 03 Feb 2017 09:52:57 +0800
fish (2.5b1-1) testing; urgency=medium
* Release of new beta version 2.5b1.
See https://github.com/fish-shell/fish-shell/releases/tag/2.5b1 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sat, 14 Jan 2017 08:49:34 +0800
fish (2.4.0-2) testing; urgency=medium
* Change recommendation of xdg-utils to suggestion (closes
https://github.com/fish-shell/fish-shell/issues/3534).
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Wed, 09 Nov 2016 22:56:16 +0800
fish (2.4.0-1) testing; urgency=medium
* Release of new version 2.4.0.
See https://github.com/fish-shell/fish-shell/releases/tag/2.4.0 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Tue, 08 Nov 2016 11:29:57 +0800
fish (2.4b1-1) testing; urgency=medium
* Release of new beta version 2.4b1.
See https://github.com/fish-shell/fish-shell/releases/tag/2.4b1 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Tue, 18 Oct 2016 22:25:26 +0800
fish (2.3.1-1) testing; urgency=medium
* Release of new version 2.3.1.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sun, 03 Jul 2016 21:21:51 +0800
fish (2.3.0-1) testing; urgency=medium
* Release of new version 2.3.0.
See http://fishshell.com/release_notes.html for significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sat, 21 May 2016 06:59:54 +0800
fish (2.3b2-1) testing; urgency=medium
* Release of new beta version 2.3b2.
See https://github.com/fish-shell/fish-shell/releases/tag/2.3b2 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Thu, 05 May 2016 06:22:37 +0800
fish (2.3.1-1) testing; urgency=medium
* Release of new beta version 2.3b1.
See http://github.com/fish-shell/fish-shell/releases/tag/2.3b1 for
significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Tue, 19 Apr 2016 21:07:17 +0800
fish (2.2.0-2) testing; urgency=medium
* Binary rebuild only to resynchronise repository state.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Mon, 15 Feb 2016 22:45:05 +0800
fish (2.2.0-1) testing; urgency=medium
* Release of new version 2.2.0.
See http://fishshell.com/release_notes.html for significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sun, 12 Jul 2015 18:52:29 +0800
fish (2.2b1-1) testing; urgency=low
* Release of new beta version 2.2b1.
See http://fishshell.com/staging/release_notes.html for significant
changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Thu, 07 May 2015 14:48:21 +0800
fish (2.1.1-1) unstable; urgency=high
* Release of new version 2.1.1.
See http://fishshell.com/release_notes.html for significant changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sun, 07 Sep 2014 17:06:31 +0800
fish (2.1.0-1) unstable; urgency=low
* Release of new version 2.1.0.
See http://fishshell.com/staging/release_notes.html for significant
changes.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Sat, 19 Oct 2013 14:35:23 +0800
fish (2.0.0-0) unstable; urgency=low
* Initial release of fish 2.0.0.
-- David Adam <zanchey@ucc.gu.uwa.edu.au> Thu, 19 Jul 2012 23:17:58 +0800

View File

@@ -3,33 +3,31 @@ Section: shells
Priority: optional
Maintainer: ridiculous_fish <corydoras@ridiculousfish.com>
Uploaders: David Adam <zanchey@ucc.gu.uwa.edu.au>
Build-Depends: debhelper (>= 12),
Build-Depends: debhelper-compat (= 13),
# -web is for Debian's updated version, -X.Y is for Ubuntu's backported versions
cargo (>= 1.85) | cargo-web (>= 1.85) | cargo-1.85,
cmake (>= 3.15.0),
gettext,
libpcre2-dev,
rustc (>= 1.85) | rustc-web (>= 1.85) | rustc-1.85,
python3-sphinx,
# Test dependencies
locales-all,
ncurses-base,
man-db | man,
python3
Standards-Version: 4.1.5
# 4.6.2 is Debian 12/Ubuntu Noble 24.04; Ubuntu Jammy is 4.6.0.1
Standards-Version: 4.6.2
Homepage: https://fishshell.com/
Vcs-Git: https://github.com/fish-shell/fish-shell.git
Vcs-Browser: https://github.com/fish-shell/fish-shell
Package: fish
Architecture: any
# for col and lock - bsdmainutils is required in Ubuntu focal
Depends: bsdextrautils | bsdmainutils,
# for col and lock
Depends: bsdextrautils,
file,
# for the msgfmt command
gettext-base,
# for man
man-db,
# for terminal definitions
ncurses-base,
# for showing built-in help pages
man-db | man,
# for kill
procps,
python3 (>=3.5),

View File

@@ -17,11 +17,11 @@ Files: share/tools/web_config/js/alpine.js
Copyright: 2019-2021 Caleb Porzio and contributors
License: MIT
Files: share/tools/web_config/themes/Dracula.theme
Files: share/themes/Dracula.theme
Copyright: 2018 Dracula Team
License: MIT
Files: share/tools/web_config/themes/Nord.theme
Files: share/themes/Nord.theme
Copyright: 2016-2024 Sven Greb
License: MIT
@@ -34,7 +34,7 @@ Copyright: 1990-2007 Free Software Foundation, Inc.
2022 fish-shell contributors
License: GPL-2+
Files: src/wgetopt.rs
Files: crates/wgetopt/src/wgetopt.rs
Copyright: 1989-1994 Free Software Foundation, Inc.
License: LGPL-2+

View File

@@ -1,8 +1,11 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Uncomment this to turn on verbose mode.
export DH_VERBOSE=1
ifeq (,$(filter terse,$(DEB_BUILD_OPTIONS)))
export DH_VERBOSE=1
# VERBOSE to satisfy Debian policy 4.9, introduced in version 4.2.0
export CARGO_TERM_VERBOSE=true
endif
# The LTO profile sets CFLAGS/CXXFLAGS which confuse the compilation process; disable it
# LTO is still performed by rustc based on Cargo.toml
export DEB_BUILD_MAINT_OPTIONS=optimize=-lto
@@ -13,14 +16,12 @@ export DEB_BUILD_MAINT_OPTIONS=optimize=-lto
# Setting the build system is still required, because otherwise the GNUmakefile gets picked up
override_dh_auto_configure:
ln -s cargo-vendor/vendor vendor
ln -s cargo-vendor/.cargo .cargo
dh_auto_configure -- -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DRust_CARGO=$$(command -v cargo-1.85 || command -v cargo) \
-DRust_COMPILER=$$(command -v rustc-1.85 || command -v rustc)
override_dh_clean:
dh_clean --exclude=Cargo.toml.orig
-unlink .cargo
-unlink vendor
override_dh_auto_test:

View File

@@ -0,0 +1,3 @@
# The vendor tarball drops a new version of .cargo/config into place. Representing this as a patch
# in automated workflows is tricky, so for our purposes auto-commit is fine.
auto-commit

33
contrib/shell.nix Normal file
View File

@@ -0,0 +1,33 @@
# Environment containing all dependencies needed for
# - building fish,
# - building documentation,
# - running all tests,
# - formatting and checking lints.
#
# enter interactive bash shell:
# nix-shell contrib/shell.nix
#
# using system nixpkgs (otherwise fetches pinned version):
# nix-shell contrib/shell.nix --arg pkgs 'import <nixpkgs> {}'
#
# run single command:
# nix-shell contrib/shell --run "cargo xtask check"
{ pkgs ? (import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/nixos-25.11.tar.gz";
sha256 = "1ia5kjykm9xmrpwbzhbaf4cpwi3yaxr7shl6amj8dajvgbyh2yh4";
}) { }), ... }:
pkgs.mkShell {
buildInputs = [
(pkgs.python3.withPackages (pyPkgs: [ pyPkgs.pexpect ]))
pkgs.cargo
pkgs.clippy
pkgs.cmake
pkgs.gettext
pkgs.pcre2
pkgs.procps # tests use pgrep/pkill
pkgs.ruff
pkgs.rustc
pkgs.rustfmt
pkgs.sphinx
];
}

View File

@@ -4,6 +4,7 @@ edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[dependencies]
rsconf.workspace = true

View File

@@ -1,4 +1,4 @@
use std::{borrow::Cow, env, os::unix::ffi::OsStrExt, path::Path};
use std::{borrow::Cow, env, os::unix::ffi::OsStrExt as _, path::Path};
pub fn env_var(name: &str) -> Option<String> {
let err = match env::var(name) {
@@ -34,6 +34,18 @@ pub fn fish_build_dir() -> Cow<'static, Path> {
.unwrap_or(cargo_target_dir())
}
pub fn fish_doc_dir() -> Cow<'static, Path> {
cargo_target_dir().join("fish-docs").into()
}
fn l10n_dir() -> Cow<'static, Path> {
workspace_root().join("localization").into()
}
pub fn po_dir() -> Cow<'static, Path> {
l10n_dir().join("po").into()
}
// TODO Move this to rsconf
pub fn rebuild_if_path_changed<P: AsRef<Path>>(path: P) {
rsconf::rebuild_if_path_changed(path.as_ref().to_str().unwrap());
@@ -45,3 +57,56 @@ pub fn rebuild_if_paths_changed<P: AsRef<Path>, I: IntoIterator<Item = P>>(paths
rsconf::rebuild_if_path_changed(path.as_ref().to_str().unwrap());
}
}
pub fn rebuild_if_embedded_path_changed<P: AsRef<Path>>(path: P) {
// Not necessary in debug builds, where rust-embed reads from the filesystem.
if cfg!(any(not(debug_assertions), windows)) {
rebuild_if_path_changed(path);
}
}
// Target OS for compiling our crates, as opposed to the build script.
pub fn target_os() -> String {
env_var("CARGO_CFG_TARGET_OS").unwrap()
}
pub fn target_os_is_apple() -> bool {
matches!(target_os().as_str(), "ios" | "macos")
}
/// Detect if we're being compiled for a BSD-derived OS, allowing targeting code conditionally with
/// `#[cfg(bsd)]`.
///
/// Rust offers fine-grained conditional compilation per-os for the popular operating systems, but
/// doesn't necessarily include less-popular forks nor does it group them into families more
/// specific than "windows" vs "unix" so we can conditionally compile code for BSD systems.
pub fn target_os_is_bsd() -> bool {
let target_os = target_os();
let is_bsd = target_os.ends_with("bsd") || target_os == "dragonfly";
if matches!(
target_os.as_str(),
"dragonfly" | "freebsd" | "netbsd" | "openbsd"
) {
assert!(is_bsd, "Target incorrectly detected as not BSD!");
}
is_bsd
}
pub fn target_os_is_cygwin() -> bool {
target_os() == "cygwin"
}
#[macro_export]
macro_rules! as_os_strs {
[ $( $x:expr, )* ] => {
{
use std::ffi::OsStr;
fn as_os_str<S: AsRef<OsStr> + ?Sized>(s: &S) -> &OsStr {
s.as_ref()
}
&[
$( as_os_str($x), )*
]
}
}
}

View File

@@ -4,6 +4,7 @@ edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[build-dependencies]
fish-build-helper.workspace = true

View File

@@ -1,43 +1,24 @@
use fish_build_helper::{env_var, workspace_root};
use std::path::Path;
use fish_build_helper::{as_os_strs, fish_doc_dir};
fn main() {
let man_dir = fish_build_helper::fish_build_dir().join("fish-man");
let sec1_dir = man_dir.join("man1");
// Running `cargo clippy` on a clean build directory panics, because when rust-embed tries to
// embed a directory which does not exist it will panic.
let sec1_dir = fish_doc_dir().join("man").join("man1");
// Running `cargo clippy` on a clean build directory panics, because when rust-embed
// tries to embed a directory which does not exist it will panic.
let _ = std::fs::create_dir_all(&sec1_dir);
let help_sections_path = Path::new(&env_var("OUT_DIR").unwrap()).join("help_sections.rs");
if env_var("FISH_USE_PREBUILT_DOCS").is_some_and(|v| v == "TRUE") {
std::fs::copy(
workspace_root().join("user_doc/src/help_sections.rs"),
help_sections_path,
)
.unwrap();
return;
if !cfg!(clippy) {
build_man(&sec1_dir);
}
std::fs::write(
help_sections_path.clone(),
r#"pub static HELP_SECTIONS: &str = "";"#,
)
.unwrap();
#[cfg(not(clippy))]
build_man(&man_dir, &sec1_dir, &help_sections_path);
}
#[cfg(not(clippy))]
fn build_man(man_dir: &Path, sec1_dir: &Path, help_sections_path: &Path) {
use std::{
ffi::OsStr,
process::{Command, Stdio},
};
fn build_man(sec1_dir: &Path) {
use fish_build_helper::{env_var, workspace_root};
use std::process::{Command, Stdio};
let workspace_root = workspace_root();
let doc_src_dir = workspace_root.join("doc_src");
let doctrees_dir = fish_doc_dir().join(".doctrees-man");
fish_build_helper::rebuild_if_paths_changed([
&workspace_root.join("CHANGELOG.rst"),
@@ -45,39 +26,24 @@ fn build_man(man_dir: &Path, sec1_dir: &Path, help_sections_path: &Path) {
&doc_src_dir,
]);
let help_sections_arg = format!("fish_help_sections_output={}", help_sections_path.display());
let args: &[&OsStr] = {
fn as_os_str<S: AsRef<OsStr> + ?Sized>(s: &S) -> &OsStr {
s.as_ref()
}
macro_rules! as_os_strs {
( [ $( $x:expr, )* ] ) => {
&[
$( as_os_str($x), )*
]
}
}
as_os_strs!([
"-j",
"auto",
"-q",
"-b",
"man",
"-c",
&doc_src_dir,
// doctree path - put this *above* the man1 dir to exclude it.
// this is ~6M
"-d",
&man_dir,
&doc_src_dir,
&sec1_dir,
"-D",
&help_sections_arg,
])
};
let args = as_os_strs![
"-j",
"auto",
"-q",
"-b",
"man",
"-c",
&doc_src_dir,
// doctree path - put this *above* the man1 dir to exclude it.
// this is ~6M
"-d",
&doctrees_dir,
&doc_src_dir,
&sec1_dir,
];
rsconf::rebuild_if_env_changed("FISH_BUILD_DOCS");
if env_var("FISH_BUILD_DOCS") == Some("0".to_string()) {
if env_var("FISH_BUILD_DOCS") == Some("0".to_owned()) {
rsconf::warn!("Skipping man pages because $FISH_BUILD_DOCS is set to 0");
return;
}
@@ -94,12 +60,12 @@ macro_rules! as_os_strs {
.spawn()
{
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
if env_var("FISH_BUILD_DOCS") == Some("1".to_string()) {
panic!(
"Could not find sphinx-build required to build man pages.\n\
Install Sphinx or disable building the docs by setting $FISH_BUILD_DOCS=0."
);
}
assert_ne!(
env_var("FISH_BUILD_DOCS"),
Some("1".to_owned()),
"Could not find sphinx-build required to build man pages.\n\
Install Sphinx or disable building the docs by setting $FISH_BUILD_DOCS=0."
);
rsconf::warn!(
"Could not find sphinx-build required to build man pages. \
If you install Sphinx now, you need to trigger a rebuild to include man pages. \
@@ -126,9 +92,10 @@ macro_rules! as_os_strs {
rsconf::warn!("sphinx-build: {}", String::from_utf8_lossy(&out.stderr));
}
assert_eq!(&String::from_utf8_lossy(&out.stdout), "");
if !out.status.success() {
panic!("sphinx-build failed to build the man pages.");
}
assert!(
out.status.success(),
"sphinx-build failed to build the man pages."
);
}
}
}

View File

@@ -1 +1 @@
include!(concat!(env!("OUT_DIR"), "/help_sections.rs"));

14
crates/color/Cargo.toml Normal file
View File

@@ -0,0 +1,14 @@
[package]
name = "fish-color"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[dependencies]
fish-common.workspace = true
fish-widestring.workspace = true
[lints]
workspace = true

View File

@@ -1,6 +1,7 @@
use std::cmp::Ordering;
use crate::wchar::prelude::*;
use fish_common::assert_sorted_by_name;
use fish_widestring::{L, wstr};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Color24 {
@@ -321,8 +322,8 @@ fn term256_color_for_rgb(color: Color24) -> u8 {
#[cfg(test)]
mod tests {
use crate::color::{Color, Color24};
use crate::wchar::prelude::*;
use super::{Color, Color24};
use fish_widestring::L;
#[test]
fn parse() {
@@ -342,9 +343,18 @@ fn parse() {
#[test]
fn parse_rgb() {
assert!(Color::from_wstr(L!("##FF00A0")).is_none());
assert!(Color::from_wstr(L!("#FF00A0")) == Some(Color::from_rgb(0xff, 0x00, 0xa0)));
assert!(Color::from_wstr(L!("FF00A0")) == Some(Color::from_rgb(0xff, 0x00, 0xa0)));
assert!(Color::from_wstr(L!("FAF")) == Some(Color::from_rgb(0xff, 0xaa, 0xff)));
assert_eq!(
Color::from_wstr(L!("#FF00A0")),
Some(Color::from_rgb(0xff, 0x00, 0xa0))
);
assert_eq!(
Color::from_wstr(L!("FF00A0")),
Some(Color::from_rgb(0xff, 0x00, 0xa0))
);
assert_eq!(
Color::from_wstr(L!("FAF")),
Some(Color::from_rgb(0xff, 0xaa, 0xff))
);
}
// Regression test for multiplicative overflow in convert_color.

21
crates/common/Cargo.toml Normal file
View File

@@ -0,0 +1,21 @@
[package]
name = "fish-common"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[dependencies]
bitflags.workspace = true
fish-feature-flags.workspace = true
fish-widestring.workspace = true
libc.workspace = true
nix.workspace = true
[build-dependencies]
fish-build-helper.workspace = true
rsconf.workspace = true
[lints]
workspace = true

5
crates/common/build.rs Normal file
View File

@@ -0,0 +1,5 @@
use fish_build_helper::target_os_is_apple;
fn main() {
rsconf::declare_cfg("apple", target_os_is_apple());
}

1598
crates/common/src/lib.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
[package]
name = "fish-fallback"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[dependencies]
fish-common.workspace = true
fish-widecharwidth.workspace = true
fish-widestring.workspace = true
libc.workspace = true
widestring.workspace = true
[build-dependencies]
fish-build-helper.workspace = true
rsconf.workspace = true
[lints]
workspace = true

5
crates/fallback/build.rs Normal file
View File

@@ -0,0 +1,5 @@
use fish_build_helper::target_os_is_cygwin;
fn main() {
rsconf::declare_cfg("cygwin", target_os_is_cygwin());
}

174
crates/fallback/src/lib.rs Normal file
View File

@@ -0,0 +1,174 @@
//! This file only contains fallback implementations of functions which have been found to be missing
//! or broken by the configuration scripts.
//!
//! Many of these functions are more or less broken and incomplete.
use fish_widecharwidth::{WcLookupTable, WcWidth};
use fish_widestring::prelude::*;
use std::cmp;
use std::sync::{
LazyLock,
atomic::{AtomicUsize, Ordering},
};
/// Width of ambiguous East Asian characters and, as of TR11, all private-use characters.
/// 1 is the typical default, but we accept any non-negative override via `$fish_ambiguous_width`.
pub static FISH_AMBIGUOUS_WIDTH: AtomicUsize = AtomicUsize::new(1);
/// Width of emoji characters.
///
/// This must be configurable because the value changed between Unicode 8 and Unicode 9, `wcwidth()`
/// is emoji-unaware, and terminal emulators do different things.
///
/// See issues like [#4539](https://github.com/fish-shell/fish-shell/issues/4539) and <https://github.com/neovim/neovim/issues/4976> for how painful this is.
///
/// Valid values are 1, and 2. 1 is the typical emoji width used in Unicode 8 while some newer
/// terminals use a width of 2 since Unicode 9.
// For some reason, this is declared here and exposed here, but is set in `env_dispatch`.
pub static FISH_EMOJI_WIDTH: AtomicUsize = AtomicUsize::new(2);
static WC_LOOKUP_TABLE: LazyLock<WcLookupTable> = LazyLock::new(WcLookupTable::new);
pub fn fish_wcwidth(c: char) -> Option<usize> {
// Check for VS16 which selects emoji presentation. This "promotes" a character like U+2764
// (width 1) to an emoji (probably width 2). So treat it as width 1 so the sums work. See #2652.
// VS15 selects text presentation.
let variation_selector_16 = '\u{FE0F}';
let variation_selector_15 = '\u{FE0E}';
if c == variation_selector_16 {
return Some(1);
} else if c == variation_selector_15 {
return Some(0);
}
// Check for Emoji_Modifier property. Only the Fitzpatrick modifiers have this, in range
// 1F3FB..1F3FF. This is a hack because such an emoji appearing on its own would be drawn as
// width 2, but that's unlikely to be useful. See #8275.
if ('\u{1F3FB}'..='\u{1F3FF}').contains(&c) {
return Some(0);
}
let width = WC_LOOKUP_TABLE.classify(c);
Some(match width {
WcWidth::NonPrint => return None,
WcWidth::NonCharacter | WcWidth::Combining | WcWidth::Unassigned => 0,
WcWidth::Ambiguous | WcWidth::PrivateUse => {
// TR11: "All private-use characters are by default classified as Ambiguous".
FISH_AMBIGUOUS_WIDTH.load(Ordering::Relaxed)
}
WcWidth::One => 1,
WcWidth::Two => 2,
WcWidth::WidenedIn9 => FISH_EMOJI_WIDTH.load(Ordering::Relaxed),
})
}
pub fn fish_wcswidth(s: &wstr) -> Option<usize> {
fish_wcswidth_canonicalizing(s, std::convert::identity)
}
pub fn fish_wcswidth_canonicalizing(s: &wstr, canonicalize: fn(char) -> char) -> Option<usize> {
let chars = s.chars().map(canonicalize);
// ascii fast path
if chars.clone().all(|c| c.is_ascii() && !c.is_ascii_control()) {
return Some(s.len());
}
let mut result = 0;
for c in chars {
result += fish_wcwidth(c)?;
}
Some(result)
}
pub fn wcscasecmp(lhs: &wstr, rhs: &wstr) -> cmp::Ordering {
wcscasecmp_fuzzy(lhs, rhs, std::convert::identity)
}
/// Compare two wide strings in a case-insensitive fashion
pub fn wcscasecmp_fuzzy(
lhs: &wstr,
rhs: &wstr,
extra_canonicalization: fn(char) -> char,
) -> cmp::Ordering {
lowercase(lhs.chars())
.map(extra_canonicalization)
.cmp(lowercase(rhs.chars()).map(extra_canonicalization))
}
pub fn lowercase(chars: impl Iterator<Item = char>) -> impl Iterator<Item = char> {
lowercase_impl(chars, |c| c.to_lowercase())
}
pub fn lowercase_rev(chars: impl DoubleEndedIterator<Item = char>) -> impl Iterator<Item = char> {
lowercase_impl(chars.rev(), |c| c.to_lowercase().rev())
}
fn lowercase_impl<ToLowercase: Iterator<Item = char>>(
chars: impl Iterator<Item = char>,
to_lowercase: fn(char) -> ToLowercase,
) -> impl Iterator<Item = char> {
/// This struct streams the underlying lowercase chars of a string without allocating.
struct ToLowerBuffer<Chars: Iterator<Item = char>, ToLowercase: Iterator<Item = char>> {
to_lowercase: fn(char) -> ToLowercase,
current: ToLowercase,
chars: Chars,
}
impl<Chars: Iterator<Item = char>, ToLowercase: Iterator<Item = char>> Iterator
for ToLowerBuffer<Chars, ToLowercase>
{
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
if let Some(c) = self.current.next() {
return Some(c);
}
self.current = (self.to_lowercase)(self.chars.next()?);
self.current.next()
}
}
impl<Chars: Iterator<Item = char>, ToLowercase: Iterator<Item = char>>
ToLowerBuffer<Chars, ToLowercase>
{
pub fn new(mut chars: Chars, to_lowercase: fn(char) -> ToLowercase) -> Self {
Self {
to_lowercase,
current: chars.next().map_or_else(
|| {
let mut empty = to_lowercase('a');
let _ = empty.next();
debug_assert!(empty.next().is_none());
empty
},
to_lowercase,
),
chars,
}
}
}
ToLowerBuffer::new(chars, to_lowercase)
}
#[cfg(test)]
mod tests {
use super::wcscasecmp;
use fish_widestring::prelude::*;
use std::cmp::Ordering;
#[test]
fn test_wcscasecmp() {
// Comparison with empty
assert_eq!(wcscasecmp(L!("a"), L!("")), Ordering::Greater);
assert_eq!(wcscasecmp(L!(""), L!("a")), Ordering::Less);
assert_eq!(wcscasecmp(L!(""), L!("")), Ordering::Equal);
// Basic comparison
assert_eq!(wcscasecmp(L!("A"), L!("a")), Ordering::Equal);
assert_eq!(wcscasecmp(L!("B"), L!("a")), Ordering::Greater);
assert_eq!(wcscasecmp(L!("A"), L!("B")), Ordering::Less);
// Multi-byte comparison
assert_eq!(wcscasecmp(L!("İ"), L!("i\u{307}")), Ordering::Equal);
assert_eq!(wcscasecmp(L!("ia"), L!("İa")), Ordering::Less);
}
}

View File

@@ -0,0 +1,13 @@
[package]
name = "fish-feature-flags"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[dependencies]
fish-widestring.workspace = true
[lints]
workspace = true

View File

@@ -1,41 +1,43 @@
//! Flags to enable upcoming features
use crate::wchar::prelude::*;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
#[cfg(test)]
use std::cell::RefCell;
use fish_widestring::{L, WExt as _, wstr};
use std::{
cell::RefCell,
sync::atomic::{AtomicBool, Ordering},
};
/// The list of flags.
#[repr(u8)]
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum FeatureFlag {
/// Whether ^ is supported for stderr redirection.
stderr_nocaret,
StderrNoCaret,
/// Whether ? is supported as a glob.
qmark_noglob,
QuestionMarkNoGlob,
/// Whether string replace -r double-unescapes the replacement.
string_replace_backslash,
StringReplaceBackslash,
/// Whether "&" is not-special if followed by a word character.
ampersand_nobg_in_token,
AmpersandNoBgInToken,
/// Whether "%self" is expanded to fish's pid
remove_percent_self,
RemovePercentSelf,
/// Remove `test`'s one and zero arg mode (make `test -n` return false etc)
test_require_arg,
TestRequireArg,
/// Whether to write OSC 133 prompt markers
mark_prompt,
MarkPrompt,
/// Do not look up $TERM in terminfo database.
ignore_terminfo,
IgnoreTerminfo,
/// Whether we are allowed to query the TTY for extra information.
query_term,
QueryTerm,
/// Do not try to work around incompatible terminal.
OmitTermWorkarounds,
}
struct Features {
@@ -60,16 +62,16 @@ pub struct FeatureMetadata {
pub description: &'static wstr,
/// Default flag value.
pub default_value: bool,
default_value: bool,
/// Whether the value can still be changed or not.
pub read_only: bool,
read_only: bool,
}
/// The metadata, indexed by flag.
pub const METADATA: &[FeatureMetadata] = &[
FeatureMetadata {
flag: FeatureFlag::stderr_nocaret,
flag: FeatureFlag::StderrNoCaret,
name: L!("stderr-nocaret"),
groups: L!("3.0"),
description: L!("^ no longer redirects stderr (historical, can no longer be changed)"),
@@ -77,7 +79,7 @@ pub struct FeatureMetadata {
read_only: true,
},
FeatureMetadata {
flag: FeatureFlag::qmark_noglob,
flag: FeatureFlag::QuestionMarkNoGlob,
name: L!("qmark-noglob"),
groups: L!("3.0"),
description: L!("? no longer globs"),
@@ -85,7 +87,7 @@ pub struct FeatureMetadata {
read_only: false,
},
FeatureMetadata {
flag: FeatureFlag::string_replace_backslash,
flag: FeatureFlag::StringReplaceBackslash,
name: L!("regex-easyesc"),
groups: L!("3.1"),
description: L!("string replace -r needs fewer \\'s"),
@@ -93,7 +95,7 @@ pub struct FeatureMetadata {
read_only: false,
},
FeatureMetadata {
flag: FeatureFlag::ampersand_nobg_in_token,
flag: FeatureFlag::AmpersandNoBgInToken,
name: L!("ampersand-nobg-in-token"),
groups: L!("3.4"),
description: L!("& only backgrounds if followed by a separator"),
@@ -101,7 +103,7 @@ pub struct FeatureMetadata {
read_only: false,
},
FeatureMetadata {
flag: FeatureFlag::remove_percent_self,
flag: FeatureFlag::RemovePercentSelf,
name: L!("remove-percent-self"),
groups: L!("4.0"),
description: L!("%self is no longer expanded (use $fish_pid)"),
@@ -109,7 +111,7 @@ pub struct FeatureMetadata {
read_only: false,
},
FeatureMetadata {
flag: FeatureFlag::test_require_arg,
flag: FeatureFlag::TestRequireArg,
name: L!("test-require-arg"),
groups: L!("4.0"),
description: L!("builtin test requires an argument"),
@@ -117,7 +119,7 @@ pub struct FeatureMetadata {
read_only: false,
},
FeatureMetadata {
flag: FeatureFlag::mark_prompt,
flag: FeatureFlag::MarkPrompt,
name: L!("mark-prompt"),
groups: L!("4.0"),
description: L!("write OSC 133 prompt markers to the terminal"),
@@ -125,49 +127,54 @@ pub struct FeatureMetadata {
read_only: false,
},
FeatureMetadata {
flag: FeatureFlag::ignore_terminfo,
flag: FeatureFlag::IgnoreTerminfo,
name: L!("ignore-terminfo"),
groups: L!("4.1"),
description: L!("do not look up $TERM in terminfo database"),
description: L!(
"do not look up $TERM in terminfo database (historical, can no longer be changed)"
),
default_value: true,
read_only: false,
read_only: true,
},
FeatureMetadata {
flag: FeatureFlag::query_term,
flag: FeatureFlag::QueryTerm,
name: L!("query-term"),
groups: L!("4.1"),
description: L!("query the TTY to enable extra functionality"),
default_value: true,
read_only: false,
},
FeatureMetadata {
flag: FeatureFlag::OmitTermWorkarounds,
name: L!("omit-term-workarounds"),
groups: L!("4.3"),
description: L!("skip workarounds for incompatible terminals"),
default_value: false,
read_only: false,
},
];
thread_local!(
#[cfg(test)]
static LOCAL_FEATURES: RefCell<Option<Features>> = const { RefCell::new(None) };
static LOCAL_OVERRIDE_STACK: RefCell<Vec<(FeatureFlag, bool)>> =
const { RefCell::new(Vec::new()) };
);
/// The singleton shared feature set.
static FEATURES: Features = Features::new();
/// Perform a feature test on the global set of features.
pub fn test(flag: FeatureFlag) -> bool {
#[cfg(test)]
{
LOCAL_FEATURES.with(|fc| fc.borrow().as_ref().unwrap_or(&FEATURES).test(flag))
pub fn feature_test(flag: FeatureFlag) -> bool {
if let Some(value) = LOCAL_OVERRIDE_STACK.with(|stack| {
for &(overridden_feature, value) in stack.borrow().iter().rev() {
if flag == overridden_feature {
return Some(value);
}
}
None
}) {
return value;
}
#[cfg(not(test))]
{
FEATURES.test(flag)
}
}
pub use test as feature_test;
/// Set a flag.
#[cfg(test)]
pub fn set(flag: FeatureFlag, value: bool) {
LOCAL_FEATURES.with(|fc| fc.borrow().as_ref().unwrap_or(&FEATURES).set(flag, value));
FEATURES.test(flag)
}
/// Parses a comma-separated feature-flag string, updating ourselves with the values.
@@ -175,37 +182,31 @@ pub fn set(flag: FeatureFlag, value: bool) {
/// The special group name "all" may be used for those who like to live on the edge.
/// Unknown features are silently ignored.
pub fn set_from_string<'a>(str: impl Into<&'a wstr>) {
let wstr: &wstr = str.into();
#[cfg(test)]
{
LOCAL_FEATURES.with(|fc| {
fc.borrow()
.as_ref()
.unwrap_or(&FEATURES)
.set_from_string(wstr)
});
}
#[cfg(not(test))]
{
FEATURES.set_from_string(wstr)
}
FEATURES.set_from_string(str.into());
}
impl Features {
const fn new() -> Self {
Features {
values: [
AtomicBool::new(METADATA[0].default_value),
AtomicBool::new(METADATA[1].default_value),
AtomicBool::new(METADATA[2].default_value),
AtomicBool::new(METADATA[3].default_value),
AtomicBool::new(METADATA[4].default_value),
AtomicBool::new(METADATA[5].default_value),
AtomicBool::new(METADATA[6].default_value),
AtomicBool::new(METADATA[7].default_value),
AtomicBool::new(METADATA[8].default_value),
],
}
// TODO: feature(const_array): use std::array::from_fn()
use std::mem::{MaybeUninit, transmute};
let values = {
let mut data: [MaybeUninit<AtomicBool>; METADATA.len()] =
[const { MaybeUninit::uninit() }; METADATA.len()];
let mut i = 0;
while i < METADATA.len() {
data[i].write(AtomicBool::new(METADATA[i].default_value));
i += 1;
}
// SAFETY: `data` is guaranteed initialized by the loop
unsafe {
transmute::<[MaybeUninit<AtomicBool>; METADATA.len()], [AtomicBool; METADATA.len()]>(
data,
)
}
};
Features { values }
}
fn test(&self, flag: FeatureFlag) -> bool {
@@ -213,23 +214,18 @@ fn test(&self, flag: FeatureFlag) -> bool {
}
fn set(&self, flag: FeatureFlag, value: bool) {
self.values[flag as usize].store(value, Ordering::SeqCst)
self.values[flag as usize].store(value, Ordering::SeqCst);
}
fn set_from_string(&self, str: &wstr) {
let whitespace = L!("\t\n\0x0B\0x0C\r ").as_char_slice();
for entry in str.as_char_slice().split(|c| *c == ',') {
for entry in str.split(',') {
let entry = entry.trim();
if entry.is_empty() {
continue;
}
// Trim leading and trailing whitespace
let entry = &entry[entry.iter().take_while(|c| whitespace.contains(c)).count()..];
let entry =
&entry[..entry.len() - entry.iter().take_while(|c| whitespace.contains(c)).count()];
// A "no-" prefix inverts the sense.
let (name, value) = match entry.strip_prefix(L!("no-").as_char_slice()) {
let (name, value) = match entry.strip_prefix("no-") {
Some(suffix) => (suffix, false),
None => (entry, true),
};
@@ -255,36 +251,28 @@ fn set_from_string(&self, str: &wstr) {
}
}
#[cfg(test)]
pub fn scoped_test(flag: FeatureFlag, value: bool, test_fn: impl FnOnce()) {
LOCAL_FEATURES.with(|fc| {
assert!(
fc.borrow().is_none(),
"scoped_test() does not support nesting"
);
let f = Features::new();
f.set(flag, value);
*fc.borrow_mut() = Some(f);
/// Run code with a feature overridden.
/// This should only be used in tests.
pub fn with_overridden_feature(flag: FeatureFlag, value: bool, test_fn: impl FnOnce()) {
LOCAL_OVERRIDE_STACK.with(|stack| {
stack.borrow_mut().push((flag, value));
test_fn();
*fc.borrow_mut() = None;
stack.borrow_mut().pop();
});
}
#[cfg(test)]
mod tests {
use super::{FeatureFlag, Features, METADATA, scoped_test, set, test};
use crate::wchar::prelude::*;
use super::{FeatureFlag, Features, METADATA, feature_test, with_overridden_feature};
use fish_widestring::L;
#[test]
fn test_feature_flags() {
let f = Features::new();
f.set_from_string(L!("stderr-nocaret,nonsense"));
assert!(f.test(FeatureFlag::stderr_nocaret));
assert!(f.test(FeatureFlag::StderrNoCaret));
f.set_from_string(L!("stderr-nocaret,no-stderr-nocaret,nonsense"));
assert!(f.test(FeatureFlag::stderr_nocaret));
assert!(f.test(FeatureFlag::StderrNoCaret));
// Ensure every metadata is represented once.
let mut counts: [usize; METADATA.len()] = [0; METADATA.len()];
@@ -296,31 +284,25 @@ fn test_feature_flags() {
}
assert_eq!(
METADATA[FeatureFlag::stderr_nocaret as usize].name,
METADATA[FeatureFlag::StderrNoCaret as usize].name,
L!("stderr-nocaret")
);
}
#[test]
fn test_scoped() {
scoped_test(FeatureFlag::qmark_noglob, true, || {
assert!(test(FeatureFlag::qmark_noglob));
fn test_overridden_feature() {
with_overridden_feature(FeatureFlag::QuestionMarkNoGlob, true, || {
assert!(feature_test(FeatureFlag::QuestionMarkNoGlob));
});
set(FeatureFlag::qmark_noglob, true);
scoped_test(FeatureFlag::qmark_noglob, false, || {
assert!(!test(FeatureFlag::qmark_noglob));
with_overridden_feature(FeatureFlag::QuestionMarkNoGlob, false, || {
assert!(!feature_test(FeatureFlag::QuestionMarkNoGlob));
});
set(FeatureFlag::qmark_noglob, false);
}
#[test]
#[should_panic]
fn test_nested_scopes_not_supported() {
scoped_test(FeatureFlag::qmark_noglob, true, || {
scoped_test(FeatureFlag::qmark_noglob, false, || {});
with_overridden_feature(FeatureFlag::QuestionMarkNoGlob, false, || {
with_overridden_feature(FeatureFlag::QuestionMarkNoGlob, true, || {
assert!(feature_test(FeatureFlag::QuestionMarkNoGlob));
});
});
}
}

View File

@@ -4,13 +4,18 @@ edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
description = "proc-macro for extracting strings for gettext translation"
[lib]
proc-macro = true
[dependencies]
fish-tempfile.workspace = true
proc-macro2.workspace = true
[build-dependencies]
rsconf.workspace = true
[lints]
workspace = true

View File

@@ -0,0 +1,3 @@
fn main() {
rsconf::rebuild_if_env_changed("FISH_GETTEXT_EXTRACTION_DIR");
}

View File

@@ -1,6 +1,7 @@
extern crate proc_macro;
use fish_tempfile::random_filename;
use proc_macro::TokenStream;
use std::{ffi::OsString, fs::OpenOptions, io::Write};
use std::{ffi::OsString, io::Write as _, path::PathBuf};
fn unescape_multiline_rust_string(s: String) -> String {
if !s.contains('\n') {
@@ -25,7 +26,7 @@ enum State {
Escaped => match c {
'\\' => {
unescaped.push('\\');
state = Ground
state = Ground;
}
'\n' => state = ContinuationLineLeadingWhitespace,
_ => panic!("Unsupported escape sequence '\\{c}' in message string '{s}'"),
@@ -34,7 +35,7 @@ enum State {
' ' | '\t' => (),
_ => {
unescaped.push(c);
state = Ground
state = Ground;
}
},
}
@@ -42,18 +43,22 @@ enum State {
unescaped
}
fn append_po_entry_to_file(message: &TokenStream, file_name: &OsString) {
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open(file_name)
.unwrap_or_else(|e| panic!("Could not open file {file_name:?}: {e}"));
// Each entry is written to a fresh file to avoid race conditions arising when there are multiple
// unsynchronized writers to the same file.
fn write_po_entry_to_file(message: &TokenStream, dir: &OsString) {
let message_string = unescape_multiline_rust_string(message.to_string());
if message_string.contains('\n') {
panic!(
"Gettext strings may not contain unescaped newlines. Unescaped newline found in '{message_string}'"
)
}
assert!(
!message_string.contains('\n'),
"Gettext strings may not contain unescaped newlines. Unescaped newline found in '{message_string}'"
);
let msgid_without_quotes = &message_string[1..(message_string.len() - 1)];
// We don't want leading or trailing whitespace in our messages.
let trimmed_msgid = msgid_without_quotes.trim();
assert_eq!(msgid_without_quotes, trimmed_msgid);
assert!(!trimmed_msgid.starts_with("\\n"));
assert!(!trimmed_msgid.ends_with("\\n"));
assert!(!trimmed_msgid.starts_with("\\t"));
assert!(!trimmed_msgid.ends_with("\\t"));
// Crude check for format strings. This might result in false positives.
let format_string_annotation = if message_string.contains('%') {
"#, c-format\n"
@@ -61,35 +66,41 @@ fn append_po_entry_to_file(message: &TokenStream, file_name: &OsString) {
""
};
let po_entry = format!("{format_string_annotation}msgid {message_string}\nmsgstr \"\"\n\n");
let dir = PathBuf::from(dir);
let (path, result) =
fish_tempfile::create_file_with_retry(|| dir.join(random_filename(OsString::new())));
let mut file = result.unwrap_or_else(|e| {
panic!("Failed to create temporary file {path:?}:\n{e}");
});
file.write_all(po_entry.as_bytes()).unwrap();
}
/// The `message` is passed through unmodified.
/// If `FISH_GETTEXT_EXTRACTION_FILE` is defined in the environment,
/// this file is used to write the message,
/// If `FISH_GETTEXT_EXTRACTION_DIR` is defined in the environment,
/// the message ID is written into a new file in this directory,
/// so that it can then be used for generating gettext PO files.
/// The `message` must be a string literal.
///
/// # Panics
///
/// This macro panics if the `FISH_GETTEXT_EXTRACTION_FILE` variable is set and `message` has an
/// This macro panics if the `FISH_GETTEXT_EXTRACTION_DIR` variable is set and `message` has an
/// unexpected format.
/// Note that for example `concat!(...)` cannot be passed to this macro, because expansion works
/// outside in, meaning this macro would still see the `concat!` macro invocation, instead of a
/// string literal.
#[proc_macro]
pub fn gettext_extract(message: TokenStream) -> TokenStream {
if let Some(file_path) = std::env::var_os("FISH_GETTEXT_EXTRACTION_FILE") {
if let Some(dir_path) = std::env::var_os("FISH_GETTEXT_EXTRACTION_DIR") {
let pm2_message = proc_macro2::TokenStream::from(message.clone());
let mut token_trees = pm2_message.into_iter();
let first_token = token_trees
.next()
.expect("gettext_extract got empty token stream. Expected one token.");
if token_trees.next().is_some() {
panic!(
"Invalid number of tokens passed to gettext_extract. Expected one token, but got more."
)
}
assert!(
token_trees.next().is_none(),
"Invalid number of tokens passed to gettext_extract. Expected one token, but got more."
);
let proc_macro2::TokenTree::Group(group) = first_token else {
panic!("Expected group in gettext_extract, but got: {first_token:?}");
};
@@ -97,13 +108,12 @@ pub fn gettext_extract(message: TokenStream) -> TokenStream {
let first_group_token = group_tokens
.next()
.expect("gettext_extract expected one group token but got none.");
if group_tokens.next().is_some() {
panic!(
"Invalid number of tokens in group passed to gettext_extract. Expected one token, but got more."
)
}
assert!(
group_tokens.next().is_none(),
"Invalid number of tokens in group passed to gettext_extract. Expected one token, but got more."
);
if let proc_macro2::TokenTree::Literal(_) = first_group_token {
append_po_entry_to_file(&message, &file_path);
write_po_entry_to_file(&message, &dir_path);
} else {
panic!("Expected literal in gettext_extract, but got: {first_group_token:?}");
}

View File

@@ -4,6 +4,7 @@ edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[dependencies]
phf.workspace = true

View File

@@ -11,18 +11,16 @@ fn main() {
PathBuf::from(fish_build_helper::fish_build_dir()).join("fish-localization-map-cache");
embed_localizations(&cache_dir);
fish_build_helper::rebuild_if_path_changed(fish_build_helper::workspace_root().join("po"));
fish_build_helper::rebuild_if_path_changed(fish_build_helper::po_dir());
}
fn embed_localizations(cache_dir: &Path) {
use fish_gettext_mo_file_parser::parse_mo_file;
use std::{
fs::File,
io::{BufWriter, Write},
io::{BufWriter, Write as _},
};
let po_dir = fish_build_helper::workspace_root().join("po");
// Ensure that the directory is created, because clippy cannot compile the code if the
// directory does not exist.
std::fs::create_dir_all(cache_dir).unwrap();
@@ -41,7 +39,7 @@ fn embed_localizations(cache_dir: &Path) {
"Could not find msgfmt required to build message catalogs. \
Localization will not work. \
If you install gettext now, you need to trigger a rebuild to include localization support. \
For example by running `touch po` followed by the build command."
For example by running `touch localization/po` followed by the build command."
);
}
Err(e) => {
@@ -50,7 +48,7 @@ fn embed_localizations(cache_dir: &Path) {
Ok(output) => {
let has_check_format =
String::from_utf8_lossy(&output.stdout).contains("--check-format");
for dir_entry_result in po_dir.read_dir().unwrap() {
for dir_entry_result in fish_build_helper::po_dir().read_dir().unwrap() {
let dir_entry = dir_entry_result.unwrap();
let po_file_path = dir_entry.path();
if po_file_path.extension() != Some(OsStr::new("po")) {
@@ -85,7 +83,7 @@ fn embed_localizations(cache_dir: &Path) {
if cached_map_mtime > po_mtime {
// Cached map file is considered up-to-date.
continue;
};
}
}
// Generate the map file.
@@ -98,7 +96,7 @@ fn embed_localizations(cache_dir: &Path) {
cmd = cmd.arg("--check-format");
} else {
tmp_mo_file = Some(cache_dir.join("messages.mo"));
};
}
cmd.arg(format!(
"--output-file={}",
tmp_mo_file
@@ -109,12 +107,11 @@ fn embed_localizations(cache_dir: &Path) {
.output()
.unwrap()
};
if !output.status.success() {
panic!(
"msgfmt failed:\n{}",
String::from_utf8(output.stderr).unwrap()
);
}
assert!(
output.status.success(),
"msgfmt failed:\n{}",
String::from_utf8(output.stderr).unwrap()
);
let mo_data =
tmp_mo_file.map_or(output.stdout, |path| std::fs::read(path).unwrap());

View File

@@ -4,6 +4,7 @@ edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[lints]
workspace = true

View File

@@ -1,6 +1,6 @@
use std::collections::HashMap;
const U32_SIZE: usize = std::mem::size_of::<u32>();
const U32_SIZE: usize = size_of::<u32>();
fn read_le_u32(bytes: &[u8]) -> u32 {
u32::from_le_bytes(bytes[..U32_SIZE].try_into().unwrap())
@@ -47,9 +47,12 @@ fn check_if_revision_is_supported(revision: u32) -> std::io::Result<()> {
}
fn as_usize(value: u32) -> usize {
use std::mem::size_of;
const _: () = assert!(size_of::<u32>() <= size_of::<usize>());
usize::try_from(value).unwrap()
const {
assert!(size_of::<u32>() <= size_of::<usize>());
}
// SAFETY: `usize` is guaranteed to be at least as wide as `u32` by the const assert above.
unsafe { usize::try_from(value).unwrap_unchecked() }
}
fn parse_strings(

14
crates/gettext/Cargo.toml Normal file
View File

@@ -0,0 +1,14 @@
[package]
name = "fish-gettext"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[dependencies]
fish-gettext-maps.workspace = true
phf.workspace = true
[lints]
workspace = true

59
crates/gettext/src/lib.rs Normal file
View File

@@ -0,0 +1,59 @@
use fish_gettext_maps::CATALOGS;
use std::{
collections::HashMap,
sync::{LazyLock, Mutex},
};
type Catalog = &'static phf::Map<&'static str, &'static str>;
static LANGUAGE_PRECEDENCE: Mutex<Vec<(&'static str, Catalog)>> = Mutex::new(Vec::new());
pub fn gettext(message_str: &'static str) -> Option<&'static str> {
let language_precedence = LANGUAGE_PRECEDENCE.lock().unwrap();
// Use the localization from the highest-precedence language that has one available.
for (_, catalog) in language_precedence.iter() {
if let Some(localized_str) = catalog.get(message_str) {
return Some(localized_str);
}
}
None
}
#[derive(Clone, Copy)]
pub struct GettextLocalizationLanguage {
language: &'static str,
}
static AVAILABLE_LANGUAGES: LazyLock<HashMap<&'static str, GettextLocalizationLanguage>> =
LazyLock::new(|| {
HashMap::from_iter(
CATALOGS
.entries()
.map(|(&language, _)| (language, GettextLocalizationLanguage { language })),
)
});
pub fn get_available_languages() -> &'static HashMap<&'static str, GettextLocalizationLanguage> {
&AVAILABLE_LANGUAGES
}
pub fn set_language_precedence(new_precedence: &[GettextLocalizationLanguage]) {
let catalogs = new_precedence
.iter()
.map(|lang| {
(
lang.language,
*CATALOGS
.get(lang.language)
.expect("Only languages for which catalogs exist may be passed to gettext."),
)
})
.collect();
*LANGUAGE_PRECEDENCE.lock().unwrap() = catalogs;
}
pub fn get_language_precedence() -> Vec<&'static str> {
let language_precedence = LANGUAGE_PRECEDENCE.lock().unwrap();
language_precedence.iter().map(|&(lang, _)| lang).collect()
}

View File

@@ -8,10 +8,11 @@ description = "printf implementation, based on musl"
license = "MIT"
[dependencies]
assert_matches.workspace = true
libc.workspace = true
widestring = { workspace = true, optional = true }
unicode-segmentation.workspace = true
unicode-width.workspace = true
widestring = { workspace = true, optional = true }
[lints]
workspace = true

View File

@@ -199,27 +199,28 @@ fn to_arg(self) -> Arg<'a> {
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
#[cfg(feature = "widestring")]
use widestring::utf32str;
#[test]
fn test_to_arg() {
assert!(matches!("test".to_arg(), Arg::Str("test")));
assert!(matches!(String::from("test").to_arg(), Arg::Str(_)));
assert_matches!("test".to_arg(), Arg::Str("test"));
assert_matches!(String::from("test").to_arg(), Arg::Str(_));
#[cfg(feature = "widestring")]
assert!(matches!(utf32str!("test").to_arg(), Arg::WStr(_)));
assert_matches!(utf32str!("test").to_arg(), Arg::WStr(_));
#[cfg(feature = "widestring")]
assert!(matches!(WString::from("test").to_arg(), Arg::WStr(_)));
assert!(matches!(42f32.to_arg(), Arg::Float(_)));
assert!(matches!(42f64.to_arg(), Arg::Float(_)));
assert!(matches!('x'.to_arg(), Arg::UInt(120)));
assert_matches!(WString::from("test").to_arg(), Arg::WStr(_));
assert_matches!(42f32.to_arg(), Arg::Float(_));
assert_matches!(42f64.to_arg(), Arg::Float(_));
assert_matches!('x'.to_arg(), Arg::UInt(120));
let mut usize_val: usize = 0;
assert!(matches!((&mut usize_val).to_arg(), Arg::USizeRef(_)));
assert!(matches!(42i8.to_arg(), Arg::SInt(42)));
assert!(matches!(42i16.to_arg(), Arg::SInt(42)));
assert!(matches!(42i32.to_arg(), Arg::SInt(42)));
assert!(matches!(42i64.to_arg(), Arg::SInt(42)));
assert!(matches!(42isize.to_arg(), Arg::SInt(42)));
assert_matches!((&mut usize_val).to_arg(), Arg::USizeRef(_));
assert_matches!(42i8.to_arg(), Arg::SInt(42));
assert_matches!(42i16.to_arg(), Arg::SInt(42));
assert_matches!(42i32.to_arg(), Arg::SInt(42));
assert_matches!(42i64.to_arg(), Arg::SInt(42));
assert_matches!(42isize.to_arg(), Arg::SInt(42));
assert_eq!((-42i8).to_arg(), Arg::SInt(-42));
assert_eq!((-42i16).to_arg(), Arg::SInt(-42));
@@ -227,14 +228,14 @@ fn test_to_arg() {
assert_eq!((-42i64).to_arg(), Arg::SInt(-42));
assert_eq!((-42isize).to_arg(), Arg::SInt(-42));
assert!(matches!(42u8.to_arg(), Arg::UInt(42)));
assert!(matches!(42u16.to_arg(), Arg::UInt(42)));
assert!(matches!(42u32.to_arg(), Arg::UInt(42)));
assert!(matches!(42u64.to_arg(), Arg::UInt(42)));
assert!(matches!(42usize.to_arg(), Arg::UInt(42)));
assert_matches!(42u8.to_arg(), Arg::UInt(42));
assert_matches!(42u16.to_arg(), Arg::UInt(42));
assert_matches!(42u32.to_arg(), Arg::UInt(42));
assert_matches!(42u64.to_arg(), Arg::UInt(42));
assert_matches!(42usize.to_arg(), Arg::UInt(42));
let ptr = &42f32 as *const f32;
assert!(matches!(ptr.to_arg(), Arg::UInt(_)));
let ptr = std::ptr::from_ref(&42f32);
assert_matches!(ptr.to_arg(), Arg::UInt(_));
}
#[test]

View File

@@ -266,7 +266,7 @@ fn should_round_up(&self, digit_idx: i32, remainder: u32, mod_base: u32) -> bool
if rounding_digit & 1 != 0 {
round += 2.0;
// round now has an odd lsb (though round itself is even).
debug_assert!(round.to_bits() & 1 != 0);
debug_assert_ne!(round.to_bits() & 1, 0);
}
// Set 'small' to a value which is less than halfway, exactly halfway, or more than halfway

View File

@@ -4,6 +4,7 @@
use super::locale::Locale;
use super::printf_impl::{ConversionSpec, Error, ModifierFlags, pad};
use assert_matches::debug_assert_matches;
use decimal::{DIGIT_WIDTH, Decimal, DigitLimit};
use std::cmp::min;
use std::fmt::Write;
@@ -129,10 +130,10 @@ pub(crate) fn format_float(
) -> Result<usize, Error> {
// Only float conversions are expected.
type CS = ConversionSpec;
debug_assert!(matches!(
debug_assert_matches!(
conv_spec,
CS::e | CS::E | CS::f | CS::F | CS::g | CS::G | CS::a | CS::A
));
);
let prefix = match (y.is_sign_negative(), flags.mark_pos, flags.pad_pos) {
(true, _, _) => "-",
(false, true, _) => "+",

View File

@@ -51,7 +51,7 @@ macro_rules! sprintf {
{
// May be no args!
#[allow(unused_imports)]
use $crate::ToArg;
use $crate::ToArg as _;
$crate::printf_c_locale(
$target,
$fmt,
@@ -84,7 +84,7 @@ macro_rules! sprintf {
///
/// let result = printf_c_locale(&mut output, fmt, &mut args);
///
/// assert!(result == Ok(10));
/// assert_eq!(result, Ok(10));
/// assert_eq!(output, "1.2346e+05");
/// ```
pub fn printf_c_locale(

View File

@@ -2,11 +2,12 @@
use super::arg::Arg;
use super::fmt_fp::format_float;
use super::locale::Locale;
use assert_matches::assert_matches;
use std::fmt::{self, Write};
use std::mem;
use std::result::Result;
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
use unicode_segmentation::UnicodeSegmentation as _;
use unicode_width::UnicodeWidthStr as _;
#[cfg(feature = "widestring")]
use widestring::Utf32Str as wstr;
@@ -57,7 +58,7 @@ fn try_set(&mut self, c: char) -> bool {
'+' => self.mark_pos = true,
'\'' => self.grouped = true,
_ => return false,
};
}
true
}
}
@@ -301,7 +302,7 @@ pub(super) fn pad(
min_width: usize,
current_width: usize,
) -> fmt::Result {
assert!(c == '0' || c == ' ');
assert_matches!(c, '0' | ' ');
if current_width >= min_width {
return Ok(());
}
@@ -341,7 +342,7 @@ pub(super) fn pad(
///
/// let result = sprintf_locale(&mut output, fmt, &locale::EN_US_LOCALE, &mut args);
///
/// assert!(result == Ok(12));
/// assert_eq!(result, Ok(12));
/// assert_eq!(output, "1,234,567.89");
/// ```
pub fn sprintf_locale(
@@ -371,7 +372,7 @@ pub fn sprintf_locale(
}
// Consume the % at the start of the format specifier.
debug_assert!(s.at(0) == Some('%'));
debug_assert_eq!(s.at(0), Some('%'));
s.advance_by(1);
// Read modifier flags. '-' and '0' flags are mutually exclusive.
@@ -561,7 +562,7 @@ pub fn sprintf_locale(
};
// Numeric output should be empty iff the value is 0.
if spec_is_numeric && body.is_empty() {
debug_assert!(arg.as_uint().unwrap() == 0);
debug_assert_eq!(arg.as_uint().unwrap(), 0);
}
// Decide if we want to apply thousands grouping to the body, and compute its size.

View File

@@ -1,7 +1,6 @@
use crate::arg::ToArg;
use crate::locale::{C_LOCALE, EN_US_LOCALE, Locale};
use crate::{Error, FormatString, sprintf_locale};
use libc::c_char;
use crate::{Error, FormatString as _, sprintf_locale};
use std::f64::consts::{E, PI, TAU};
use std::ffi::CStr;
use std::fmt;
@@ -14,7 +13,7 @@ macro_rules! sprintf_check {
$(,)? // optional trailing comma
) => {
{
use unicode_width::UnicodeWidthStr;
use unicode_width::UnicodeWidthStr as _;
let mut target = String::new();
let mut args = [$($arg.to_arg()),*];
let len = $crate::printf_c_locale(
@@ -401,8 +400,10 @@ fn test_char() {
#[test]
fn test_ptr() {
assert_fmt!("%p", core::ptr::null::<u8>() => "0");
assert_fmt!("%p", 0xDEADBEEF_usize as *const u8 => "0xdeadbeef");
assert_fmt!("%p", core::ptr::null::<()>() => "0");
let tmp = core::ptr::without_provenance::<()>(0xDEADBEEF);
assert_fmt!("%p", tmp => "0xdeadbeef");
}
#[test]
@@ -862,8 +863,8 @@ fn test_float_hex_prec() {
let mut libc_sprintf = libc_sprintf_one_float_with_precision(&mut c_storage, c"%.*a");
let mut failed = false;
for sign in [1.0, -1.0].into_iter() {
for mut v in [0.0, 0.5, 1.0, 1.5, PI, TAU, E].into_iter() {
for sign in [1.0, -1.0] {
for mut v in [0.0, 0.5, 1.0, 1.5, PI, TAU, E] {
v *= sign;
for preci in 1..=200_usize {
rust_str.clear();
@@ -890,10 +891,16 @@ fn libc_sprintf_one_float_with_precision<'a>(
fmt: &'a CStr,
) -> impl FnMut(usize, f64) -> &'a str {
|preci, float_val| unsafe {
let storage_ptr = storage.as_mut_ptr() as *mut c_char;
let len = libc::snprintf(storage_ptr, storage.len(), fmt.as_ptr(), preci, float_val);
let storage_ptr = storage.as_mut_ptr();
let len = libc::snprintf(
storage_ptr.cast(),
storage.len(),
fmt.as_ptr(),
preci,
float_val,
);
assert!(len >= 0);
let sl = std::slice::from_raw_parts(storage_ptr as *const u8, len as usize);
let sl = std::slice::from_raw_parts(storage_ptr, len as usize);
std::str::from_utf8(sl).unwrap()
}
}

View File

@@ -4,9 +4,11 @@ edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[dependencies]
nix = { workspace = true, features = ["fs", "feature"] }
rand.workspace = true
[lints]
workspace = true

View File

@@ -1,4 +1,11 @@
use std::{fs::File, path::PathBuf};
use std::{
ffi::OsString,
fs::{File, OpenOptions},
io::ErrorKind,
path::PathBuf,
};
use rand::distr::{Alphanumeric, Distribution as _};
pub struct TempFile {
file: File,
@@ -41,20 +48,56 @@ fn drop(&mut self) {
}
}
fn get_tmpdir() -> PathBuf {
PathBuf::from(std::env::var_os("TMPDIR").unwrap_or("/tmp".into()))
/// Creates a random filename with the given prefix.
/// Appends a random sequence of alphanumeric ASCII characters.
pub fn random_filename(mut prefix: OsString) -> OsString {
let mut rng = rand::rng();
let suffix_length = 10;
let random_part: String = Alphanumeric
.sample_iter(&mut rng)
.take(suffix_length)
.map(char::from)
.collect();
assert_eq!(random_part.len(), suffix_length);
prefix.push(random_part);
prefix
}
fn get_template() -> PathBuf {
get_tmpdir().join("fish_tmp_XXXXXX")
/// Tries to create a new file at the path returned by `generate_path`.
/// If a file already exists at this path, `generate_path` will be called again and file creation
/// will be retried. This is repeated until a new file is created, or an error occurs.
pub fn create_file_with_retry(
generate_path: impl Fn() -> PathBuf,
) -> (PathBuf, std::io::Result<File>) {
loop {
let path = generate_path();
match OpenOptions::new()
.read(true)
.write(true)
.create_new(true)
.open(&path)
{
Ok(file) => {
return (path, Ok(file));
}
Err(e) => {
if e.kind() != ErrorKind::AlreadyExists {
return (path, Err(e));
}
}
}
}
}
/// Tries to create a new temporary file using `mkstemp`.
/// Tries to create a new temporary file in the operating system's default location for temporary
/// files.
/// On success, a [`TempFile`] is returned.
/// When this struct is dropped, the backing file will be deleted.
pub fn new_file() -> std::io::Result<TempFile> {
let (fd, path) = nix::unistd::mkstemp(&get_template())?;
let file = File::from(fd);
let (path, result) = create_file_with_retry(|| {
std::env::temp_dir().join(random_filename(OsString::from("fish_tmp_")))
});
let file = result?;
Ok(TempFile { file, path })
}
@@ -62,7 +105,8 @@ pub fn new_file() -> std::io::Result<TempFile> {
/// On success, a [`TempDir`] is returned.
/// When this struct is dropped, the backing directory, including all its contents, will be deleted.
pub fn new_dir() -> std::io::Result<TempDir> {
let path = nix::unistd::mkdtemp(&get_template())?;
let template = std::env::temp_dir().join("fish_tmp_XXXXXX");
let path = nix::unistd::mkdtemp(&template)?;
Ok(TempDir { path })
}
@@ -70,7 +114,7 @@ pub fn new_dir() -> std::io::Result<TempDir> {
mod tests {
use std::{
fs::File,
io::{Read, Seek, Write},
io::{Read as _, Seek as _, Write as _},
};
#[test]

17
crates/util/Cargo.toml Normal file
View File

@@ -0,0 +1,17 @@
[package]
name = "fish-util"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[dependencies]
errno.workspace = true
fish-widestring.workspace = true
libc.workspace = true
nix.workspace = true
rand.workspace = true
[lints]
workspace = true

View File

@@ -1,10 +1,15 @@
//! Generic utilities library.
use crate::wchar::prelude::*;
use rand::{SeedableRng, rngs::SmallRng};
use std::cmp::Ordering;
use std::time;
use std::time::{SystemTime, UNIX_EPOCH};
use errno::errno;
use fish_widestring::prelude::*;
use rand::{SeedableRng as _, rngs::SmallRng};
use std::{
cmp::Ordering,
ffi::CStr,
io::Write as _,
os::fd::{BorrowedFd, RawFd},
time,
};
/// Compares two wide character strings with an (arguably) intuitive ordering. This function tries
/// to order strings in a way which is intuitive to humans with regards to sorting strings
@@ -94,7 +99,7 @@ pub fn wcsfilecmp(a: &wstr, b: &wstr) -> Ordering {
Ordering::Less // string a is a prefix of b and b is longer
}
} else {
assert!(bi == b.len());
assert_eq!(bi, b.len());
Ordering::Greater // string b is a prefix of a and a is longer
}
}
@@ -159,7 +164,7 @@ pub fn wcsfilecmp_glob(a: &wstr, b: &wstr) -> Ordering {
Ordering::Less // string a is a prefix of b and b is longer
}
} else {
assert!(bi == b.len());
assert_eq!(bi, b.len());
Ordering::Greater // string b is a prefix of a and a is longer
}
}
@@ -172,27 +177,14 @@ pub fn get_time() -> i64 {
}
}
// Helper to get a small RNG seed, based on nanoseconds.
pub fn get_rng_seed() -> u128 {
// Note we use an explicit seed to avoid the "getrandom" crate, which uses `getentropy()` on macOS which
// is not available before macOS 10.12.
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos()
}
// Helper to get a small RNG with a seed.
pub fn get_seeded_rng(seed: u128) -> SmallRng {
let seed = ((seed >> 64) as u64) ^ (seed as u64);
/// Helper to get a small RNG with a seed.
/// This should only be used for testing, where a PRNG which always produces the same output for a
/// given seed is useful, e.g. to reproduce a failing test.
/// In cases where reproducible results are not important, prefer `rand::rng()`.
pub fn get_seeded_rng(seed: u64) -> SmallRng {
SmallRng::seed_from_u64(seed)
}
// Helper to get a small RNG using the current time.
pub fn get_rng() -> SmallRng {
get_seeded_rng(get_rng_seed())
}
// Compare the strings to see if they begin with an integer that can be compared and return the
// result of that comparison.
fn wcsfilecmp_leading_digits(a: &wstr, b: &wstr) -> (Ordering, usize, usize) {
@@ -248,33 +240,30 @@ fn wcsfilecmp_leading_digits(a: &wstr, b: &wstr) -> (Ordering, usize, usize) {
(ret, ai, bi)
}
/// Finds `needle` in a `haystack` and returns the index of the first matching element, if any.
///
/// # Examples
///
/// ```
/// use fish::util::find_subslice;
/// let haystack = b"ABC ABCDAB ABCDABCDABDE";
///
/// assert_eq!(find_subslice(b"ABCDABD", haystack), Some(15));
/// assert_eq!(find_subslice(b"ABCDE", haystack), None);
/// ```
pub fn find_subslice<T: PartialEq>(
needle: impl AsRef<[T]>,
haystack: impl AsRef<[T]>,
) -> Option<usize> {
let needle = needle.as_ref();
if needle.is_empty() {
return Some(0);
pub fn write_to_fd(input: &[u8], fd: RawFd) -> nix::Result<usize> {
nix::unistd::write(unsafe { BorrowedFd::borrow_raw(fd) }, input)
}
/// Prints the provided string, followed by a colon, space, and the string representation of the
/// current errno via [`libc::strerror`].
pub fn perror(s: &str) {
let e = errno().0;
let mut stderr = std::io::stderr().lock();
if !s.is_empty() {
let _ = write!(stderr, "{s}: ");
}
let haystack = haystack.as_ref();
haystack.windows(needle.len()).position(|w| w == needle)
let slice = unsafe {
let msg = libc::strerror(e);
CStr::from_ptr(msg).to_bytes()
};
let _ = stderr.write_all(slice);
let _ = stderr.write_all(b"\n");
}
#[cfg(test)]
mod tests {
use super::wcsfilecmp;
use crate::wchar::prelude::*;
use fish_widestring::prelude::*;
use std::cmp::Ordering;
/// Verify the behavior of the `wcsfilecmp()` function.

View File

@@ -0,0 +1,20 @@
[package]
name = "fish-wcstringutil"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[dependencies]
fish-fallback.workspace = true
fish-widestring.workspace = true
# Only needed for cygwin detection.
# TODO(MSRV>=1.86): remove
[build-dependencies]
fish-build-helper.workspace = true
rsconf.workspace = true
[lints]
workspace = true

View File

@@ -0,0 +1,3 @@
fn main() {
rsconf::declare_cfg("cygwin", fish_build_helper::target_os_is_cygwin());
}

View File

@@ -1,8 +1,7 @@
//! Helper functions for working with wcstring.
use crate::common::{get_ellipsis_char, get_ellipsis_str};
use crate::fallback::{fish_wcwidth, wcscasecmp, wcscasecmp_fuzzy};
use crate::wchar::{decode_byte_from_char, prelude::*};
use fish_fallback::{fish_wcwidth, lowercase, lowercase_rev, wcscasecmp, wcscasecmp_fuzzy};
use fish_widestring::{ELLIPSIS_CHAR, prelude::*};
/// Return the number of newlines in a string.
pub fn count_newlines(s: &wstr) -> usize {
@@ -18,10 +17,35 @@ pub fn count_newlines(s: &wstr) -> usize {
count
}
#[derive(Eq, PartialEq)]
pub enum IsPrefix {
Prefix,
Equal,
}
pub fn is_prefix(
mut lhs: impl Iterator<Item = char>,
mut rhs: impl Iterator<Item = char>,
) -> Option<IsPrefix> {
use IsPrefix::*;
loop {
match (lhs.next(), rhs.next()) {
(None, None) => return Some(Equal),
(None, Some(_)) => return Some(Prefix),
(Some(_), None) => return None,
(Some(lhs), Some(rhs)) => {
if lhs != rhs {
return None;
}
}
}
}
}
/// Test if a string prefixes another without regard to case. Returns true if a is a prefix of b.
pub fn string_prefixes_string_case_insensitive(proposed_prefix: &wstr, value: &wstr) -> bool {
let prefix_size = proposed_prefix.len();
prefix_size <= value.len() && wcscasecmp(&value[..prefix_size], proposed_prefix).is_eq()
let proposed_prefix = lowercase(proposed_prefix.chars());
let value = lowercase(value.chars());
is_prefix(proposed_prefix, value).is_some()
}
pub fn string_prefixes_string_maybe_case_insensitive(
@@ -36,11 +60,19 @@ pub fn string_prefixes_string_maybe_case_insensitive(
})(proposed_prefix, value)
}
/// Remove the optional executable extension if there is one
/// Always returns None on non-Cygwin platforms
pub fn strip_executable_suffix(path: &wstr) -> Option<&wstr> {
const DOT_EXE: &wstr = L!(".exe");
(cfg!(cygwin) && { string_suffixes_string_case_insensitive(DOT_EXE, path) })
.then(|| &path[..path.len() - DOT_EXE.len()])
}
/// Test if a string is a suffix of another.
pub fn string_suffixes_string_case_insensitive(proposed_suffix: &wstr, value: &wstr) -> bool {
let suffix_size = proposed_suffix.len();
suffix_size <= value.len()
&& wcscasecmp(&value[value.len() - suffix_size..], proposed_suffix).is_eq()
let proposed_suffix = lowercase_rev(proposed_suffix.chars());
let value = lowercase_rev(value.chars());
is_prefix(proposed_suffix, value).is_some()
}
/// Test if a string prefixes another. Returns true if a is a prefix of b.
@@ -114,7 +146,7 @@ fn fuzzy_canonicalize(c: char) -> char {
// Note that the order of entries below affects the sort order of completions.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ContainType {
/// Exact match: `foobar` matches `foo`
/// Exact match
Exact,
/// Prefix match: `foo` matches `foobar`
Prefix,
@@ -140,13 +172,18 @@ pub enum CaseSensitivity {
/// A lightweight value-type describing how closely a string fuzzy-matches another string.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct StringFuzzyMatch {
pub from_separator: bool,
pub typ: ContainType,
pub case_fold: CaseSensitivity,
}
impl StringFuzzyMatch {
pub fn new(typ: ContainType, case_fold: CaseSensitivity) -> Self {
Self { typ, case_fold }
Self {
from_separator: false,
typ,
case_fold,
}
}
// Helper to return an exact match.
#[inline(always)]
@@ -184,7 +221,7 @@ pub fn try_create(
// Helper to lazily compute if case insensitive matches should use icase or smartcase.
// Use icase if the input contains any uppercase characters, smartcase otherwise.
#[inline(always)]
fn get_case_fold(s: &widestring::Utf32Str) -> CaseSensitivity {
fn get_case_fold(s: &wstr) -> CaseSensitivity {
if s.chars().any(|c| c.is_uppercase()) {
CaseSensitivity::Insensitive
} else {
@@ -282,8 +319,10 @@ pub fn rank(&self) -> u32 {
self.case_fold
};
// Type dominates fold.
effective_type as u32 * 8 + effective_case as u32
// Separator dominates type dominates fold.
((self.from_separator as u32) << 4)
+ ((effective_type as u32) << 2)
+ (effective_case as u32)
}
}
@@ -296,29 +335,6 @@ pub fn string_fuzzy_match_string(
StringFuzzyMatch::try_create(string, match_against, anchor_start)
}
/// Implementation of wcs2bytes that accepts a callback.
/// This invokes `func` with byte slices containing the UTF-8 encoding of the characters in the
/// input, doing one invocation per character.
/// If `func` returns false, it stops; otherwise it continues.
/// Return false if the callback returned false, otherwise true.
pub fn wcs2bytes_callback(input: &wstr, mut func: impl FnMut(&[u8]) -> bool) -> bool {
// A `char` represents an Unicode scalar value, which takes up at most 4 bytes when encoded in UTF-8.
let mut converted = [0_u8; 4];
for c in input.chars() {
let bytes = if let Some(byte) = decode_byte_from_char(c) {
converted[0] = byte;
&converted[..=0]
} else {
c.encode_utf8(&mut converted).as_bytes()
};
if !func(bytes) {
return false;
}
}
true
}
/// Split a string by runs of any of the separator characters provided in `seps`.
/// Note the delimiters are the characters in `seps`, not `seps` itself.
/// `seps` may contain the NUL character.
@@ -350,15 +366,14 @@ pub fn split_string_tok<'val>(
let next_sep = val[pos..]
.iter()
.position(|c| seps.contains(*c))
.map(|p| pos + p)
.unwrap_or(end);
.map_or(end, |p| pos + p);
out.push(wstr::from_char_slice(&val[pos..next_sep]));
// Note we skip exactly one sep here. This is because on the last iteration we retain all
// but the first leading separators. This is historical.
pos = next_sep + 1;
}
if pos < end && max_results > 0 {
assert!(out.len() + 1 == max_results, "Should have split the max");
assert_eq!(out.len() + 1, max_results, "Should have split the max");
out.push(wstr::from_char_slice(&val[pos..]));
}
assert!(out.len() <= max_results, "Got too many results");
@@ -433,32 +448,13 @@ pub fn split_about<'haystack>(
output
}
#[derive(Eq, PartialEq)]
pub enum EllipsisType {
None,
// Prefer niceness over minimalness
Prettiest,
// Make every character count ($ instead of ...)
Shortest,
}
pub fn truncate(input: &wstr, max_len: usize, etype: Option<EllipsisType>) -> WString {
let etype = etype.unwrap_or(EllipsisType::Prettiest);
// TODO: This should work on render width rather than the number of codepoints.
pub fn truncate(input: &wstr, max_len: usize) -> WString {
if input.len() <= max_len {
return input.to_owned();
}
if etype == EllipsisType::None {
return input[..max_len].to_owned();
}
if etype == EllipsisType::Prettiest {
let ellipsis_str = get_ellipsis_str();
let mut output = input[..max_len - ellipsis_str.len()].to_owned();
output += ellipsis_str;
return output;
}
let mut output = input[..max_len - 1].to_owned();
output.push(get_ellipsis_char());
output.push(ELLIPSIS_CHAR);
output
}
@@ -513,8 +509,7 @@ fn next(&mut self) -> Option<Self::Item> {
let newline_or_end = self.coll[self.current..]
.iter()
.position(|b| *b == b'\n')
.map(|pos| self.current + pos)
.unwrap_or(self.coll.len());
.map_or(self.coll.len(), |pos| self.current + pos);
let result = &self.coll[self.current..newline_or_end];
self.current = newline_or_end;
@@ -526,21 +521,61 @@ fn next(&mut self) -> Option<Self::Item> {
}
}
/// Like fish_wcwidth, but returns 0 for characters with no real width instead of -1.
/// Like fish_wcwidth, but returns 0 for characters with no real width instead of none.
pub fn fish_wcwidth_visible(c: char) -> isize {
if c == '\x08' {
return -1;
}
fish_wcwidth(c).max(0)
fish_wcwidth(c).unwrap_or_default().try_into().unwrap()
}
#[cfg(test)]
mod tests {
use super::{
CaseSensitivity, ContainType, LineIterator, count_newlines, ifind, join_strings,
split_string_tok, string_fuzzy_match_string,
split_string_tok, string_fuzzy_match_string, string_prefixes_string_case_insensitive,
string_suffixes_string_case_insensitive,
};
use crate::wchar::prelude::*;
use fish_widestring::prelude::*;
#[test]
fn test_string_prefixes_string_case_insensitive() {
macro_rules! validate {
($prefix:literal, $s:literal, $expected:expr) => {
assert_eq!(
string_prefixes_string_case_insensitive(L!($prefix), L!($s)),
$expected
);
};
}
validate!("i", "i_", true);
validate!("İ", "i\u{307}_", true);
validate!("i\u{307}", "İ", true); // prefix is longer
validate!("i", "İ", true);
validate!("gs", "gs_", true);
validate!("gs_", "gs", false);
assert_eq!("İn".to_lowercase().as_str(), "i\u{307}n");
validate!("echo in", "echo İnstall", false);
}
#[test]
fn test_string_suffixes_string_case_insensitive() {
macro_rules! validate {
($suffix:literal, $s:literal, $expected:expr) => {
assert_eq!(
string_suffixes_string_case_insensitive(L!($suffix), L!($s)),
$expected
);
};
}
validate!("i", "_i", true);
validate!("i\u{307}", "İ", true);
validate!("İ", "i\u{307}", true); // suffix is longer
validate!("İ", "", true);
validate!("i", "", false);
validate!("gs", "_gs", true);
validate!("_gs ", "gs", false);
}
#[test]
fn test_ifind() {

15
crates/wgetopt/Cargo.toml Normal file
View File

@@ -0,0 +1,15 @@
[package]
name = "fish-wgetopt"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[dependencies]
assert_matches.workspace = true
fish-wcstringutil.workspace = true
fish-widestring.workspace = true
[lints]
workspace = true

Some files were not shown because too many files have changed in this diff Show More