Compare commits

...

765 Commits

Author SHA1 Message Date
Johannes Altmanninger
0c7062def9 WIP cirrus: trigger after github action finishes
(TODO this is totally untested and mostly vibe coded.)

Whenever we push changes do docker/**, our docker images for Cirrus
CI will be rebuilt.

However, the Cirrus CI jobs will kick off at the same time as the
Docker builds, so they will likely use old images.  This can cause
surprising (albeit transient) failures.

Fix this by having Cirrus wait for GitHub Actions.

This addresses the second part of
https://github.com/fish-shell/fish-shell/pull/11884#discussion_r2423344925
2025-10-12 08:41:14 +02:00
Johannes Altmanninger
8a4a8bb6a0 cirrus: disable jammy-armv7-32bit for now
This fails with "exec format error" because our container is built
on a 64 bit system on GitHub Actions.  Not yet sure how to fix that.
2025-10-12 07:17:48 +02:00
David Adam
e1b064f6cc Bravely revert "CI: Disable some Cirrus CI jobs during RIIR transition"
This reverts what remains of commit
91be7489bc.

Closes #11871
Closes #11884
2025-10-12 07:17:48 +02:00
Johannes Altmanninger
68a8cd4501 cirrus.yml: switch back to org docker image paths
These should work now that we have (automatically-updated) docker
builds via .github/workflows/docker_builds.yml.

Ref: https://github.com/fish-shell/fish-shell/pull/11884#discussion_r2405536855
2025-10-12 07:17:48 +02:00
Johannes Altmanninger
5f0d83d2f2 cirrus: remove "file" dependency from focal-arm64 builds
Probably that was also part of --install-recommends.
fish requires "file", but system tests don't.
2025-10-12 07:17:48 +02:00
Johannes Altmanninger
03f54171c6 cirrus: delete commented configurations 2025-10-12 07:17:48 +02:00
Johannes Altmanninger
f391b4a179 docker: add back CMake for the images used in Cirrus
build_tools/check.sh would give more coverage (the translation
checks is the main difference) but it tests embed-data builds;
for now testing, traditionally installed builds is more important
(especially since I always test check.sh locally already). In future
we will probably make embedding mandatory and get rid of this schism.
2025-10-12 07:17:48 +02:00
Johannes Altmanninger
8db674b6b5 docker: fix SSL error
Cirrus builds fail with

	error: failed to get `pcre2` as a dependency of package `fish v4.1.0-snapshot (/tmp/cirrus-ci-build)`
	...
	Caused by:
	  the SSL certificate is invalid; class=Ssl (16); code=Certificate (-17)

Likely introduced by b644fdbb04 (docker: do not install recommended
packages on Ubuntu, 2025-10-06).  Some Ubuntu Dockerfiles already
install ca-certificates explicitly but others do not. Fix the latter.
2025-10-12 07:17:48 +02:00
Johannes Altmanninger
f03113d048 __fish_cache_put: fix for BusyBox stat
On alpine, tests/checks/check-completions fails with

	stat: can't read file system information for '%u:%g': No such file or directory
2025-10-12 07:17:48 +02:00
Johannes Altmanninger
6c48e214ca tests/checks/check-completions: fix for embed-data builds 2025-10-12 07:17:48 +02:00
Johannes Altmanninger
189a2e90dd __fish_print_commands: remove code clone
Also, use it for help completions also on embed-data builds.
2025-10-12 07:11:35 +02:00
Johannes Altmanninger
9b44138917 __fish_print_commands: fix environment variable injection 2025-10-12 07:08:24 +02:00
Johannes Altmanninger
ec7d20b347 Reapply "test_driver: support Python 3.8 for now"
Re-apply commit ec27b418e after it was accidentally reverted in
5102c8b137 (Update littlecheck, 2025-10-11),
fixing a hang in e.g.

	sudo docker/docker_run_tests.sh docker/jammy.Dockerfile
2025-10-12 07:01:36 +02:00
Peter Ammon
84b52a3ed1 Resurrect some Dockerfiles
Add missing black and rustfmt
2025-10-11 12:24:20 -07:00
Peter Ammon
30b1c9570f Suppress a deprecation warning on time_t
Continue to pull cirrus builds back into the land of the living.
2025-10-11 12:24:20 -07:00
Johannes Altmanninger
b88622bc35 tests/tmux-job: fix on macOS CI
The rapid input can make the screen look like this:

    fish: Job 1, 'sleep 0.5 &' has ended
    prompt 0> echo hello world
    prompt 0> sleep 3 | cat &
    bg %1 <= no check matches this, previous check on line 24
2025-10-11 18:02:57 +02:00
Johannes Altmanninger
a4edb4020d test_driver.py: output as tests/checks/... not checks/...
Something like

	tests/test_driver.py target/debug checks/foo.fish

is invalid (needs "tests/checks/foo.fish").
Let's make failure output list the right fiel name.

At least when run from the root directory.
Don't change the behavior when run from "tests/";
in that case the path was already relative.
2025-10-11 17:56:31 +02:00
Johannes Altmanninger
e1e5dfdd62 test_driver: don't chdir in async driver
The test driver is async now; so we can't change the process-wide
working directory without causing unpredictable behavior.
For example, given these two tests:

	$ cat tests/checks/a.fish
	#RUN: %fish %s
	#REQUIRES: sleep 1
	pwd >/tmp/pwd-of-a

	$ cat tests/checks/b.fish
	#RUN: %fish %s
	pwd >/tmp/pwd-of-b

running them may give both fish processes the same working directory.

	$ tests/test_driver.py target/debug tests/checks/a.fish tests/checks/b.fish
	tests/checks/b.fish  PASSED      13 ms
	tests/checks/a.fish  PASSED    1019 ms
	2 / 2 passed (0 skipped)
	$ grep . /tmp/pwd-of-a /tmp/pwd-of-b
	/tmp/pwd-of-a:/tmp/fishtest-root-1q_tnyqa/fishtest-s9cyqkgz
	/tmp/pwd-of-b:/tmp/fishtest-root-1q_tnyqa/fishtest-s9cyqkgz
2025-10-11 17:56:31 +02:00
Johannes Altmanninger
5102c8b137 Update littlecheck
Commit c12b8ed (Clean up some lints, 2025-08-28)
2025-10-11 17:54:09 +02:00
Johannes Altmanninger
9ae9db7f70 test_driver: fix code clone 2025-10-11 17:54:09 +02:00
Johannes Altmanninger
d0aaa8d809 create_manpage_completions: fix deprecation warning
Fixes #11930
2025-10-11 17:54:09 +02:00
Johannes Altmanninger
6024539c12 CI lint: consolidate clippy definitions 2025-10-11 17:54:09 +02:00
Johannes Altmanninger
fb06ad4a44 Update sourcehut build images 2025-10-11 17:54:09 +02:00
Johannes Altmanninger
598e98794c Fixes for tmux-fish_config.fish
This fails sometimes in high-concurrency scenarios
(build_tools/check.sh), so allow sleeping a bit longer.
2025-10-11 17:54:09 +02:00
Johannes Altmanninger
b3a295959d webconfig: remove obsolete macOS workaround
As mentioned in #11926 our "fish_config" workaround for macOS
10.12.5 or later has been fixed in macOS 10.12.6 according to
https://andrewjaffe.net/blog/2017/05/python_bug_hunt/, I think we
can assume all users have upgraded to that patch version Remove
the workaround.
2025-10-11 17:54:09 +02:00
Johannes Altmanninger
50dfd962ec Document system test dependencies
Notably, the parent commit adds wget.

While at it, extract a reusable action.
2025-10-11 17:54:09 +02:00
Nahor
65332eaacc Add test for fish_config in browser modified
In particular
- test that it will return an error if the URL is invalid
- that the main page matches the index.html in git
- that "Enter" key will exit

Part of #11907
2025-10-11 17:54:09 +02:00
Nahor
a00e6f8696 Add Github action to compile for MSYS2
Closes #11907
2025-10-11 17:54:09 +02:00
Nahor
6415dfbd35 Ensure different network ports in fish_config
- Windows allows port reuse under certain conditions. In fish_config
case, this allows the signal socket to use the same port as http (e.g.
when using MINGW python). This can cause some browsers to access the
signal socket rather than the http one (e.g. when connecting using
IPv4/"127.0.0.1" instead of IPv6/"::1").
- This is also more efficient since we already know that all ports up to
and including the http one are not available

Fixes #11805

Part of #11907
2025-10-11 17:54:09 +02:00
Nahor
8c387c58de Fix build for MSYS2 (missing ulimits)
Part of #11907
2025-10-11 17:54:09 +02:00
Johannes Altmanninger
da411f6fa7 bg/fg/wait/disown/function: check for negative PID argument
While at it, extract a function.

Seems to have regressed in 4.0.0 (fc47d9fa1d (Use strongly typed
`Pid` for job control, 2024-11-11)).

Fixes #11929
2025-10-11 17:54:09 +02:00
Johannes Altmanninger
8fe402e9f7 fg: remove rogue abs() on PID argument
Present since the initial commit but I don't think anyone relies
on this.
2025-10-11 17:54:09 +02:00
Johannes Altmanninger
c41fc52077 bg: deduplicate job argument
"bg %1" of a pipline prints the same line twice because it tries
to background the same job twice.  This doesn't make sense and
other builtins like "disown" already deduplicate, so do the same.
Unfortunately we can't use the same approach as "disown" because we
can't hold on to references to job, since we're modifying the job list.
2025-10-11 17:53:30 +02:00
Johannes Altmanninger
f7d730390c Rename process id -> process ID 2025-10-11 11:47:34 +02:00
Johannes Altmanninger
8fc30d5243 po/fr.po: delete bad translation 2025-10-11 11:47:16 +02:00
Isaac Oscar Gariano
93c4d63295 Allow overwriting argv with function -a and -V
Previously, if you called a function parameter 'argv', within the body
of the function, argv would be set to *all* the arguments to the
function, and not the one indicated by the parameter name.
The same behaviour happened if you inherited a variable named 'argv'.
Both behaviours were quite surprising, so this commit makes things more
obvious, although they could alternatively simply be made errors.

Part of #11780
2025-10-11 10:51:36 +02:00
Isaac Oscar Gariano
7a07c08860 Output function argument-names in one group.
This makes it so that printing a function definition will only use one
--argument-names group, instead of one for argument name.
For example, "function foo -a x y; ..." will print with "function foo
--argument-names x y" instead of "function foo --argument-names x
--argument-names y", which is very bizarre.

Moreover, the documentation no longer says that argument-names "Has to
be the last option.". This sentence appears to have been introduced in
error by pull #10524, since the ability to have options afterwards was
deliberately added by pull #6188.

Part of #11780
2025-10-11 10:50:07 +02:00
Isaac Oscar Gariano
1cf110d083 Use idiomatic Rust error handling for function builtin.
This simply does the same thing as #10948, but for the function builtin.

Part of #11780
2025-10-11 10:46:21 +02:00
Johannes Altmanninger
fef358fc74 Move remaining builtin implementations to dedicated files
This makes them a bit easier to find I guess.
2025-10-11 09:40:37 +02:00
Daniel Rainer
b5feb79a7c style: format entire repo by default
We want all Rust and Python files formatted, and making formatting
behavior dependent on the directory `style.fish` is called from can be
counter-intuitive, especially since `check.sh`, which also calls
`style.fish` is otherwise written in a way that allows calling it from
arbitrary working directories and getting the same results.

Closes #11925
2025-10-10 10:36:52 +02:00
Daniel Rainer
4d52245617 ci: run style.fish
This allows checking formatting of fish script and Python files, in
addition to Rust files.

Closes #11923
2025-10-10 10:36:52 +02:00
Daniel Rainer
ff308b36af ci: rename workflows
The new names are consistently formulated as commands.

`main` is not very descriptive, so change it to `test`, which is more
informative and accurate.

`rust_checks` are replaced be the more general `lint` in preparation for
non-Rust-related checks.

These changes were suggested in
https://github.com/fish-shell/fish-shell/pull/11918#discussion_r2415957733

Closes #11922
2025-10-10 10:36:08 +02:00
Johannes Altmanninger
fa8cf8a1a5 abbr: fix extra Chinese translation
Closes #11919
2025-10-09 18:12:04 +02:00
Daniel Rainer
5ade4a037e style: replace black with ruff for Python formatting
Ruff's default format is very similar to black's, so there are only a
few changes made to our Python code. They are all contained in this
commit. The primary benefit of this change is that ruff's performance is
about an order of magnitude better, reducing runtime on this repo down
to under 20ms on my machine, compared to over 150ms with black, and even
more if any changes are performed by black.

Closes #11894

Closes #11918
2025-10-09 18:12:03 +02:00
Johannes Altmanninger
861002917a tests/checks/po-files-well-formed: fix inconsistent msgfmt check 2025-10-09 18:12:03 +02:00
Fabian Boehm
eddb26d490 completions/ssh: Don't read ":" from historical hosts
Fixes #11917
2025-10-09 17:14:09 +02:00
Johannes Altmanninger
b7fe3190bb Revert "builtin function: remove dead code"
This reverts commit 993b977c9b.

Fixes #11912
2025-10-08 10:51:47 +02:00
Johannes Altmanninger
b6ddb56cc7 release.sh: fix milestone API calls 2025-10-08 10:51:47 +02:00
Johannes Altmanninger
8dd59081d7 github workflows lockthreads: only run on main repo
We should disable the whole action instead of the job but I don't
know how to do that.
2025-10-08 10:51:47 +02:00
Johannes Altmanninger
3ae17ea100 release.sh: fix deployment approval logic
(cherry picked from commit e074b27abf)
2025-10-08 10:51:47 +02:00
Fabian Boehm
10c34c5353 Revert "Move the C compiler requrement in readme"
build.rs still uses cc:: for feature detection, as 50819666b1 points out.

This reverts commit 594f1df39c.
2025-10-07 22:47:14 +02:00
Johannes Altmanninger
e3ebda3647 tests/checks/fish_config: fix for non-embedded builds 2025-10-07 22:23:04 +02:00
Johannes Altmanninger
092e7fa274 release.sh: check fish-site worktree staleness 2025-10-07 22:23:04 +02:00
Johannes Altmanninger
dd47c2baa2 Sanitize cursor position report on kitty click_events
This is easy to trigger by having a background process do "echo" to
move the terminal cursor to the next line, and then clicking anywhere.

Fixes #11905
2025-10-07 21:54:26 +02:00
Johannes Altmanninger
15065255e9 fish_config: fix regression "theme show" not showing custom themes
This regressed in 6f0532460a5~2..6f0532460a5 (fish_config: fix for
non-embedded builds, 2025-09-28).

Fixes #11903
2025-10-07 21:54:26 +02:00
Johannes Altmanninger
594f1df39c Move the C compiler requrement in readme
Fixes #11908
2025-10-07 21:54:26 +02:00
Johannes Altmanninger
724416125e tests/checks/config-paths-standalone.fish: fix bad assertion
Fixes #11906
2025-10-07 17:26:48 +02:00
Johannes Altmanninger
3afafe6398 Fix build on OpenBSD/NetBSD
Co-authored-by: Michael Nickerson <darkshadow02@me.com>
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>

Tested on OpenBSD; we'll see about NetBSD.

Closes #11893
Closes #11892
Closes #11904
2025-10-07 15:24:02 +02:00
Johannes Altmanninger
af7446a055 Start using cfg_if 2025-10-07 15:24:02 +02:00
Johannes Altmanninger
7be101e8c9 Add OpenBSD sourcehut config 2025-10-07 15:24:01 +02:00
Johannes Altmanninger
5f18b173dd ulimit: add back RLIMIT_NICE on linux 2025-10-07 15:24:01 +02:00
Johannes Altmanninger
3fec9c8145 Embedded builds to use $workspace_root/etc again if run from build dir
Commit f05ad46980 (config_paths: remove vestiges of installable
builds, 2025-09-06) removed a bunch of code paths for embed-data
builds, since those builds can do without most config paths.

However they still want the sysconfig path.  That commit made
embedded builds use "/etc/fish" unconditionally.  Previously they
used "$workspace_root/etc".  This is important when running tests,
which should not read /etc/fish.

tests/checks/invocation.fish tests this implicitly: if /etc/fish does
not exist, then

	fish --profile-startup /dev/stdout

will not contain "builtin source".

Let's restore historical behavior.  This might be annoying for users
who "install" with "ln -s target/debug/fish ~/bin/", but that hasn't
ever been recommended, and the historical behavior was in effect
until 4.1.0.

Fixes #11900
2025-10-07 15:24:01 +02:00
Johannes Altmanninger
bdca70bfb0 build.rs: extract function for overridable paths
Also get rid of cursed get_path().
2025-10-07 15:24:01 +02:00
Johannes Altmanninger
5e28f068ec build.rs: dedicated error for bad encoding in environment variables
We should probably not silently treat invalid Unicode the same as
"the variable is unset", even though it probably makes no difference
in practice.
2025-10-07 15:24:01 +02:00
Johannes Altmanninger
a0b22077a5 Extract constant for resolved build directory
Also use a different name than for the CMake variable, to reduce
confusion.
2025-10-07 11:59:45 +02:00
Daniel Rainer
1d36b04ea6 check.sh: export gettext extraction file variable
Some versions of `/bin/sh`, e.g. the one on FreeBSD, do not propagate
variables set on a command through shell functions. This results in
`FISH_GETTEXT_EXTRACTION_FILE` being set in the `cargo` function, but
not for the actual `cargo` process spawned from the function, which
breaks our localization scripts and tests. Exporting the variable
prevents that.

Fixes #11896

Closes #11899
2025-10-07 10:49:39 +02:00
Daniel Rainer
6829c9d678 printf-c: restore length modifiers
These were accidentally removed when semi-automatically removing length
modifiers from Rust code and shell scripts.

In C, the length modifiers are required.

Closes #11898
2025-10-07 07:52:26 +02:00
Ilya Grigoriev
e1f6ab8916 checks: make tmux-multiline-prompt less affected by less config
Fixes #11881 for me. Thanks, @krobelus, for the help with debugging
this!

The `-+X` is unrelated to the bug, strictly speaking, but makes sure the
test tests what it is intended to test.

I initially thought of also adding `LESS=` and something like
`--lesskey-content=""` to the command, but I decided against it since
`less` can also maybe be configured with `LESSOPEN` (?) and I don't know
how long the `--lesskey-content` option existed.

Closes #11891
2025-10-06 15:08:25 +02:00
Daniel Rainer
778baaecb5 sprintf: remove signed int size info
The size was used to keep track of the number of bits of the input type
the `Arg::SInt` variant was created from. This was only relevant for
arguments defined in Rust, since the `printf` command takes all
arguments as strings.

The only thing the size was used for is for printing negative numbers
with the `x` and `X` format specifiers. In these cases, the `i64` stored
in the `SInt` variant would be cast to a `u64`, but only the number of
bits present in the original argument would be kept, so `-1i8` would be
formatted as `ff` instead of `ffffffffffffffff`.

There are no users of this feature, so let's simplify the code by
removing it. While we're at it, also remove the unused `bool` returned
by `as_wrapping_sint`.

Closes #11889
2025-10-06 15:08:25 +02:00
Daniel Rainer
a189f79590 input: remove dead code
See https://github.com/fish-shell/fish-shell/pull/11874#discussion_r2404478880
> There is a comment saying `// Keep this function for debug purposes`
but I'm sure that's obsolete, since ReadlineCmd implements `Debug` now.

Closes #11887
2025-10-06 15:08:25 +02:00
Ada Magicat
6395644e8c doc: correct example of fish_should_add_to_history
Closes #11886
2025-10-06 15:08:25 +02:00
Johannes Altmanninger
a958f23f63 Fix regression on paste in non-interactive read
As reported in
https://github.com/fish-shell/fish-shell/issues/11836#issuecomment-3369973613,
running "fish -c read" and pasting something would result this error
from __fish_paste:

	commandline: Can not set commandline in non-interactive mode

Bisects to 32c36aa5f8 (builtins commandline/complete: allow handling
commandline before reader initialization, 2025-06-13).  That commit
allowed "commandline" to work only in interactive sessions, i.e. if
stdin is a TTY or if overridden with -i.

But this is not the only case where fish might read from the TTY.
The notable other case is builtin read, which also works in
noninteractive shells.
Let's allow "commandline" at least after we have initialized the TTY
for the first reader, which restores the relevant historical behavior
(which is weird, e.g. « fish -c 'read; commandline foo' »).
2025-10-06 15:08:25 +02:00
Johannes Altmanninger
ec8756d7a3 tests/checks/read: add test for non-interactive use of commandline 2025-10-06 15:04:19 +02:00
Xiretza
b7fabb11ac Make command run by __fish_echo output to TTY for color detection
Without this, e.g. Alt-L shows the directory entries one per line and
without colors.

Closes #11888
2025-10-06 13:39:30 +02:00
Johannes Altmanninger
74ba4e9a98 docker_builds: run only on fish-shell/fish-shell repo
Else this runs when people push to their master's forks, see
https://github.com/fish-shell/fish-shell/pull/11884#discussion_r2405618358
2025-10-06 13:29:08 +02:00
Johannes Altmanninger
b1e8fdfaa2 Revert "CI: use build_tools/check.sh in Cirrus CI"
I think we do want to stop using CMake on Cirrus but this should
first be tested in combination with all the other changes that made
it to master concurrently (test by pushing a temporary branch to the
fish-shell repo), to avoid confusion as to what exactly broke.

This reverts commit d167ab9376.

See #11884
2025-10-06 13:28:49 +02:00
Johannes Altmanninger
9eb439c01d Revert "Allow black to be missing in style.fish"
The root cause was that FISH_CHECK_LINT was not set. By
default, check.sh should fail if any tool is not installed, see
59b43986e9 (build_tools/style.fish: fail if formatters are not
available, 2025-07-24); like it does for rustfmt and clippy.

This reverts commit fbfd29d6d2.

See #11884
2025-10-06 13:27:47 +02:00
Peter Ammon
fbfd29d6d2 Allow black to be missing in style.fish
Stop failing BSD builds because black is missing.
2025-10-05 20:49:49 -07:00
Peter Ammon
f158a3ae3e Revert "Attempt to fix Cirrus builds harder"
This reverts commit d683769e1f.
2025-10-05 20:38:22 -07:00
Peter Ammon
7d59b4f4e2 Add an unnecessary_cast suppression
Continue to help fix BSD builds.
2025-10-05 20:12:55 -07:00
Peter Ammon
e99eca47c3 Add .claude to gitignore 2025-10-05 19:58:28 -07:00
Peter Ammon
d683769e1f Attempt to fix Cirrus builds harder
Install black
2025-10-05 19:58:02 -07:00
Peter Ammon
9ea328e43a Fix the BSD builds
These relied on constants that don't actually exist.
2025-10-05 19:30:33 -07:00
David Adam
f6d93f2fdb GitHub Actions: add workflow to build Docker images for CI 2025-10-06 09:50:56 +08:00
David Adam
b644fdbb04 docker: do not install recommended packages on Ubuntu
This should speed things up a bit, but various additional packages need
to be installed.
2025-10-06 09:50:56 +08:00
David Adam
7647d68b68 docker: fix Rust package name for jammy 2025-10-06 09:50:56 +08:00
David Adam
d167ab9376 CI: use build_tools/check.sh in Cirrus CI
08b03a733a removed CMake from the Docker images used for the
Cirrus builds.

It might be better to use fish_run_tests.sh in the Docker image, but
that requires some context which I'm not sure is set up properly in
Cirrus.
2025-10-06 09:50:55 +08:00
David Adam
e68bd2f980 build_tools/check.sh: add support for FISH_TEST_MAX_CONCURRENCY 2025-10-06 09:50:55 +08:00
Jesse Harwin
b5c17d4743 completions/bind: bug fixes, cleanup, and complete multiple functions
Revamped and renamed the __fish_bind_test2 function. Now has a
more explicit name, `__fish_bind_has_keys`  and allows for multiple
functions after the key-combo, doesn't offer function names after an
argument with a parameter (e.g. -M MODE).

Logic on the function is now more concise.

Closes #11864
2025-10-05 15:16:41 +02:00
Jesse Harwin
66ca7ac6d0 completions/bind: removed the unused __fish_bind_test1 function 2025-10-05 15:16:41 +02:00
Johannes Altmanninger
e97a616ffa completions/bind: don't suggest key names if --function-names is given
This combination makes no sense and should be an error.  (Also the
short options and --key-names were missing, so this was quite
inconsistent.)

See #11864
2025-10-05 15:16:41 +02:00
Ilya Grigoriev
061517cd14 completion/set: fix bug preventing showing history or fish_killring
Previously, `set -S fish_kill<TAB>` did not list `fish_killring`. This
was because `$1` wasn't sufficiently escaped, and so instead of
referring to a regex capture group, it was always empty.

Closes #11880
2025-10-05 15:16:41 +02:00
Johannes Altmanninger
6accc475c9 Don't use kitty keyboard protocol support to decide timeout
As reported in
https://github.com/fish-shell/fish-shell/discussions/11868, some
terminals advertise support for the kitty keyboard protocol despite
it not necessarily being enabled.

We use this flag in 30ff3710a0 (Increase timeout when reading
escape sequences inside paste/kitty kbd, 2025-07-24), to support
the AutoHotKey scenario on terminals that support the kitty keyboard
protocols.

Let's move towards the more comprehensive fix mentioned in abd23d2a1b
(Increase escape sequence timeout while waiting for query response,
2025-09-30), i.e. only apply a low timeout when necessary to actually
distinguish legacy escape.

Let's pick 30ms for now (which has been used successfully for similar
things historically, see 30ff3710a0); a higher timeout let alone
a warning on incomplete sequence seems risky for a patch relase,
and it's also not 100% clear if this is actually a degraded state
because in theory the user might legitimately type "escape [ 1"
(while kitty keyboard protocol is turned off, e.g. before the shell
regains control).

This obsoletes and hence reverts commit 623c14aed0 (Kitty keyboard
protocol is non-functional on old versions of Zellij, 2025-10-04).
2025-10-05 15:16:41 +02:00
Johannes Altmanninger
c2e2fd6432 fish_add_path: remove extra argument to printf 2025-10-05 15:16:41 +02:00
Daniel Rainer
83af5c91bd printf: remove all uses of length modifiers
Length modifiers are useless. This simplifies the code a bit, results in
more consistency, and allows removing a few PO messages which only
differed in the use of length modifiers.

Closes #11878
2025-10-05 15:16:41 +02:00
Peter Ammon
e9f5982147 Fix a clipply 2025-10-04 19:25:10 -07:00
Peter Ammon
50819666b1 Remove our own C bits
fish-shell itself no longer depends on a C compiler; however we still
use cc for feature detection. Removing that will have to wait for another day.
2025-10-04 18:56:11 -07:00
Peter Ammon
6ad13e35c0 Bravely define PATH_BSHELL
PATH_BSHELL is always "/bin/sh" except on Android where it's "/system/bin/sh".

This isn't exposed by Rust, so just define it ourselves.
2025-10-04 17:27:16 -07:00
Peter Ammon
39e2f1138b Bravely stop setting stdout to unbuffered
Issue #3748 made stdout (the C FILE*, NOT the file descriptor) unbuffered,
due to concerns about mixing output to the stdout FILE* with output output.

We no longer write to the C FILE* and Rust libc doesn't expose stdout, which may
be a macro. This code no longer looks useful. Bravely remove it.
2025-10-04 17:27:15 -07:00
Peter Ammon
cd37c71e29 Adopt Rust libc RLIMIT_* fields
Moving more stuff out of C.
2025-10-04 14:09:47 -07:00
Peter Ammon
c1f3d93b3b Adopt Rust libc::MNT_LOCAL
Note the ST_LOCAL usage on NetBSD is also covered by this.
2025-10-04 14:09:46 -07:00
Peter Ammon
0aa05032c4 Adopt rust _PC_CASE_SENSITIVE
fish no longer needs to expose this - the libc crate does the job.
2025-10-04 14:01:13 -07:00
Peter Ammon
174130fe2f Adopt Rust libc _CS_PATH
This is now supported directly by the libc crate - no need for fish
to expose this via C.
2025-10-04 14:01:11 -07:00
Peter Ammon
d06f7f01d2 Remove MB_CUR_MAX from our libc ffi
We no longer need this.
2025-10-04 13:39:22 -07:00
Peter Ammon
a04ddd9b17 Adopt get_is_multibyte_locale in the pager 2025-10-04 13:39:22 -07:00
Peter Ammon
12929fed74 Adopt get_is_multibyte_locale in decode_input_byte
Move away from MB_CUR_MAX
2025-10-04 13:39:22 -07:00
Peter Ammon
87bf580f68 Adopt get_is_multibyte_locale in wcs2string_callback
Move away from MB_CUR_MAX
2025-10-04 13:39:22 -07:00
Peter Ammon
66bab5e767 Early work aiming to remove MB_CUR_MAX from fish libc FFI
Start detecting multibyte locales in Rust.
2025-10-04 13:39:21 -07:00
Peter Ammon
4b12fb2887 Migrate invalidate_numeric_locale into fish_setlocale
Centralizes where locale information is recomputed.
2025-10-04 13:39:21 -07:00
Johannes Altmanninger
623c14aed0 Kitty keyboard protocol is non-functional on old versions of Zellij
try_readb() uses a high timeout when the kitty keyboard protocol is
enabled, because in that case it should basically never be necessary
to interpret \e as escape key, see 30ff3710a0 (Increase timeout when
reading escape sequences inside paste/kitty kbd, 2025-07-24).

Zellij before commit 0075548a (fix(terminal): support kitty keyboard
protocol setting with "=" (#3942), 2025-01-17) fails to enable kitty
keyboard protocol, so it sends the raw escape bytes, causing us to
wait 300ms.

Closes #11868
2025-10-04 07:17:34 +02:00
Johannes Altmanninger
7d83dc4758 Refresh TTY timestamps after firing focus events
Using a multi-line prompt with focus events on:

	tmux new-session fish -C '
		tmux set -g focus-events on
		set -g fish_key_bindings fish_vi_key_bindings
		function fish_prompt
		    echo (prompt_pwd)
		    echo -n "> "
		end
		tmux split
	'

switching to the fish pane and typing any key sometimes leads to our
two-line-prompt being redawn one line below it's actual place.

Reportedly, it bisects to d27f5a5 which changed when we print things.
I did not verify root cause, but
1. symptoms are very similar to other
   problems with TTY timestamps, see eaa837effa (Refresh TTY
   timestamps again in most cases, 2025-07-24).
2. this seems fixed if we refresh timestamps after
   running the focus events, which print some cursor shaping commands
   to stdout. So bravely do that.

Closes #11870
2025-10-03 22:35:31 +02:00
Johannes Altmanninger
493d0bca95 Update changelog for patch release 2025-10-03 22:01:39 +02:00
qianlongzt
983501ff8c zh_CN: fix vi case
Part of #11854
2025-10-03 20:51:57 +02:00
The0x539
20da9a2b51 ast: use macro_rules_attribute for the Acceptor trait
Closes #11867
2025-10-03 20:45:01 +02:00
The0x539
7aec6c55f9 ast: use macro_rules_attribute for Leaf trait
Part of #11867
2025-10-03 20:45:01 +02:00
The0x539
532f30e031 ast: use macro_rules_attribute for Node trait
Part of #11867
2025-10-03 20:45:01 +02:00
Daniel Rainer
1d7ab57e3a xgettext: remove --strict flag from msguniq
As with `msgmerge`, this introduces unwanted empty comment lines above
`#, c-format`
lines. We don't need this strict formatting, so we get rid of the flag
and the associated empty comment lines.

Closes #11863
2025-10-03 20:22:59 +02:00
Étienne Deparis
8adc598e90 web_config: Support long options separated with = from their value
Closes #11861
2025-10-03 20:18:38 +02:00
Étienne Deparis
c884c08257 web_config: Use None as default for underline style
Underline is no more a boolean and should be one of the accepted style,
or None. By keeping False as default value, web_config was generating
wrong --underline=False settings

Part of #11861
2025-10-03 20:18:38 +02:00
Daniel Rainer
66dc734c11 printf: remove useless length modifiers
Closes #11858
2025-10-03 20:14:20 +02:00
Daniel Rainer
77fee9acb9 printf: rename direc -> directive
The abbreviation is ambiguous, which makes the code unnecessarily hard
to read. (possible misleading expansions: direct, direction, director,
...)

Part of #11858
2025-10-03 20:14:20 +02:00
Daniel Rainer
6b66c2bc1d printf: use options for idiomatic code
The `have_foo: bool` + `foo: i64` combination is more idiomatically
represented as `foo: Option<i64>`. This change is applied for
`field_width` and `precision`.

In addition, the sketchy explicit cast from `i64` to `c_int`, and the
subsequent implicit cast from `c_int` to `i32` are avoided by using
`i64` consistently.

Part of #11858
2025-10-03 20:14:20 +02:00
Daniel Rainer
81b9f50dc2 printf: reformat doc comments
Part of #11858
2025-10-03 20:14:20 +02:00
Johannes Altmanninger
fcd246064b Stop requesting modifyOtherKeys on old Midnight Commander again
This upstream issue was fixed in 0ea77d2ec (Ticket #4597: fix CSI
parser, 2024-10-09); for old mc's we had worked around this but the
workaround was accidentally removed. Add it back for all the versions
that don't have that fix.

Fixes f0e007c439 (Relocate tty metadata and protocols and clean
it up, 2025-06-19) Turns out this was why the "Capability" enum was
added in 2d234bb676 (Only request keyboard protocols once we know
if kitty kbd is supported, 2025-01-26).

Fixes #11869
2025-10-03 20:14:20 +02:00
Johannes Altmanninger
86a0a348ee Harmonize temporary Midnight Commander workarounds a bit 2025-10-03 18:18:05 +02:00
Johannes Altmanninger
ed36e852d2 release.sh: add next patch milestone
This is still the common case.
2025-10-03 18:08:28 +02:00
Johannes Altmanninger
da5d93c1e2 release.sh: close milestone when done
Don't fail early if this doesn't exist, because it's not needed for
testing this on fish-shell forks that live on GitHub.
2025-10-03 18:08:28 +02:00
Johannes Altmanninger
7b59ae0d82 Unbreak hack to strip " (deleted)" suffix from executable path
Commit 49b88868df (Fix stripping of " (deleted)" from non-UTF8 paths
to fish, 2024-10-12) was wrong because Path::ends_with() considers
entire path components. Fix that.

Refs:
- https://matrix.to/#/!YLTeaulxSDauOOxBoR:matrix.org/$k2IQazfmztFUXrairmIQvx_seS1ZJ7HlFWhmNy479Dg
- https://matrix.to/#/!YLTeaulxSDauOOxBoR:matrix.org/$4pugfHejL9J9L89zuFU6Bfg41UMjA0y79orc3EaBego
2025-10-03 18:08:28 +02:00
Fabian Boehm
97acc12d62 Fix scp completions
Introduced when __fish_mktemp_relative was.

Fixes #11860
2025-10-02 18:20:41 +02:00
Johannes Altmanninger
db6a7d26cd update_translations.sh: add header to new files too
Fixes #11855
2025-10-01 18:30:38 +02:00
Johannes Altmanninger
6be03d7cc4 update_translations.sh: fix test invocation when passed a file in non-extant directory
Need to make sure test arguments are not empty lists.
2025-10-01 18:30:38 +02:00
Daniel Rainer
617a6edb13 Extract messages without building default features
Default features are not needed for message extraction, so there is no
need to spend any resources on them.

If a PO files contains a syntax error, extraction would fail if the
`localize-messages` feature is active. This is undesirable, because it
results in an unnecessary failure with worse error messages than if the
`msgmerge` invocation of the `update_translations.fish` script fails.

Closes #11849
2025-10-01 08:05:37 +02:00
王宇逸
31c85723e8 zh_CN: don't translate set command
Closes #11852
2025-10-01 08:02:38 +02:00
王宇逸
d22c905d9f zh_CN: fix typo
Part of #11852
2025-10-01 08:02:38 +02:00
Daniel Rainer
216dc2d473 Remove redundant variable declaration
Closes #11851
2025-10-01 08:00:21 +02:00
Daniel Rainer
918e7abe6b Hide output of msgfmt -h
This command is only used to determine availability of `msgfmt`. Without
these changes, the entire help output shows up if the code panics later
on, which adds useless bloat to the output, making it harder to analyze
what went wrong.

Closes #11848
2025-10-01 07:59:46 +02:00
Daniel Rainer
c145ee6df3 Check exit status of msgfmt
Prior to this, when `msgfmt` failed, this would be detected indirectly
by the parser, which would then panic due to it input being empty.

Explicit checking allows us to properly display `msgfmt`'s error
message.

Closes #11847
2025-10-01 07:59:21 +02:00
Johannes Altmanninger
62543b36a4 release.sh: sunset release announcement email
I'm not sure if our peer projects do this or if it's useful to have
on top of github releases (especially as most releases are patch
releases mainly).

We could certainly use "sphinx-build -b text" in
build_tools/release-notes.sh to extract a nice plaintext changelog
and send that, but that doesn't support links.  Not sure about HTML
email either.
2025-10-01 07:26:50 +02:00
Daniel Rainer
751aad5302 Refactor PO section marking
Use msgids to mark sections. In the PO format, comments are associated
with specific messages, which does not match the semantics for section
markers.
Furthermore, comments are not preserved by `msgmerge`, which required
quite convoluted handling to copy them over from the template.
By using msgids to mark sections, this problem is avoided.

This convoluted handling was also used for header comments. Header
comments are now handled in a simpler way. There is a fixed prefix,
identifying these comments, as well as a list variable containing the
lines which should be put into the header. When a PO file is generated,
all existing lines starting with the prefix are deleted, the and the
current version of the lines is prepended to the file.

Closes #11845
2025-09-30 19:45:31 +02:00
Daniel Rainer
efabab492a Remove useless comments
Most of them have been added automatically for no good reason.

Also remove outdated comments referring to source locations in pt_BR.po.

Closes #11844
2025-09-30 19:44:36 +02:00
Daniel Rainer
c7cdbe60cd Put general comments on top of empty msgid
These should not be comments on an actual message, since they apply
throughout the entire file, so the sensible location is as comments on
the empty msgid.

Closes #11843
2025-09-30 19:44:15 +02:00
Johannes Altmanninger
412149a5de Changelog update for 4.1.1 2025-09-30 19:22:41 +02:00
Johannes Altmanninger
abd23d2a1b Increase escape sequence timeout while waiting for query response
Running "fish -d reader" inside SSH inside Windows terminal sometimes
results in hangs on startup (or whenever we run "scrollback-push"),
because not all of the Primary DA response is available for reading
at once:

	reader: Incomplete escape sequence: \e\[?61\;4\;6\;7\;14\;21\;22\;23\;24\;28\;32

Work around this by increasing the read timeout while we're waiting
for query responses.

We should try to find a better (more comprehensive?) fix in future,
but for the patch release, this small change will do.

Fixes #11841
2025-09-30 19:00:40 +02:00
Johannes Altmanninger
b774c54a6f Changelog for translation fixes from #11833 2025-09-30 19:00:40 +02:00
王宇逸
e4b797405b zh_CN: fix tier 1 translations
Closes #11833
2025-09-30 18:41:44 +02:00
Johannes Altmanninger
81a89a5dec release-notes.sh: fix sphinx warning for patch release notes
Integration_4.1.1 fails to generate release notes with

	CHANGELOG.rst:9: WARNING: Bullet list ends without a blank
	line; unexpected unindent. [docutils].
2025-09-30 18:00:09 +02:00
Johannes Altmanninger
0da12a6b55 Primary Device Attribute is a proper noun
We don't care about any specific attributes but we do very much care
about the specific query and response format associated with VT100's
primary device attribute query. Use a proper noun to emphasize that
we want that one and no other.

Ref: https://github.com/fish-shell/fish-shell/pull/11833#discussion_r2385659040
2025-09-30 12:06:08 +02:00
Johannes Altmanninger
86ec8994e6 build.rs: fix MSRV (1.70) clippy 2025-09-30 12:06:08 +02:00
Johannes Altmanninger
caf426ddb2 po/de.po: copy-edit German translations
Go through all existing translations except for tier3.
2025-09-30 11:50:43 +02:00
Johannes Altmanninger
508ae410a6 builtin commandline: fix completion description 2025-09-30 11:47:26 +02:00
Johannes Altmanninger
993b977c9b builtin function: remove dead code 2025-09-30 11:47:26 +02:00
Johannes Altmanninger
a7f0138fc7 builtin function: fix a misleading error message
Issue introduced in 7914c92824 (replaced the functions '--rename'
option with '--copy'., 2010-09-09).
2025-09-30 11:47:26 +02:00
Johannes Altmanninger
ab3c932903 builtin set: fix regression in error message description 2025-09-30 11:47:26 +02:00
Johannes Altmanninger
ae0fdadcff Remove translations for some error messages that basically never happen
Executable path is empty only in contrived circumstances.

The regex error happens only when the user explicitly turns off a
feature flag.

The orphaned process error seems very unlikely, not worth translating.
2025-09-30 11:47:26 +02:00
Johannes Altmanninger
e3974989d8 Fix short description for builtin wait 2025-09-30 11:47:26 +02:00
Johannes Altmanninger
080b1e0e4f Translation update implied by parent commit
Part of #11833
2025-09-30 11:47:26 +02:00
Johannes Altmanninger
a5db91dd85 po: add section markers to indicate translation priority
Part of #11833
2025-09-30 11:47:26 +02:00
Johannes Altmanninger
b62f54631b Translation update implied by parent commit
Part of #11833
2025-09-30 11:47:26 +02:00
Johannes Altmanninger
d835c5252a Prepare to not localize private function descriptions
The overwhelming majority of localizable messages comes from
completions:

	$ ls share/completions/ | wc -l
	$ 1048

OTOH functions also contribute a small amount, mostly via their
descriptions (so usually just one per file).

	$ ls share/functions/ | wc -l
	$ 237

Most of these are private and almost never shown to the user, so it's
not worth bothering translators with them. So:

- Skip private (see the parent commit) and deprecated functions.
- Skip wrapper functions like grep (where the translation seems to
  be provided by apropos), and even the English description is not
  helpful.
  - Assume that most real systems have "seq", "realpath" etc.,
    so it's no use providing our own translations for our fallbacks.
- Mark fish's own functions as tier1, and some barely-used functiosn
  and completions as tier3, so we can order them that way in
  po/*.po. Most translators should only look at tier1 and tier2.
  In future we could disable localization for tier3.

See the explanation at the bottom of
tests/checks/message-localization-tier-is-declared.fish

Part of #11833
2025-09-30 11:47:26 +02:00
Johannes Altmanninger
a53db72564 Mark private functions that don't need localization
See the next commit.

Part of #11833
2025-09-30 11:47:26 +02:00
Johannes Altmanninger
61b0368dac functions/realpath: remove weird wrapping
Wrapping the same thing is redundant and wrapping grealpath is kinda
pointless since we only provide completions for realpath.
2025-09-30 11:47:26 +02:00
Integral
568b4a22f9 completions/help: correct the spelling of "redirection"
Closes #11839
2025-09-30 11:47:26 +02:00
Jiangqiu Shen
8abba8a089 Re-add translations for share/completions/cjpm.fish
As removed in the parent commit.
Cherry-picked from e4c55131c7 (Update translation, 2025-07-04)
2025-09-30 11:47:26 +02:00
王宇逸
b3b789cd68 zh_CN: bad translations are worse than none
Part of #11833
2025-09-30 11:47:26 +02:00
Johannes Altmanninger
425a166111 functions/seq: use early return 2025-09-30 10:37:11 +02:00
Johannes Altmanninger
1dcc290e29 tests/checks/check-all-fish-files.fish: follow naming convention 2025-09-30 10:37:11 +02:00
Johannes Altmanninger
863204dbfa build.rs: remove dead code 2025-09-30 10:37:11 +02:00
Sebastian Fleer
4b21e7c9c7 webconfig: Replace str.stripprefix with str.removeprefix
str.stripprefix doesn't exist in Python:
https://docs.python.org/3/library/stdtypes.html#str.removeprefix

Closes #11840
2025-09-30 10:37:06 +02:00
Johannes Altmanninger
df5230ff4a Reliably disable modifyOtherKeys on WezTerm
WezTerm allows applications to enable modifyOtherKeys by default.
Its implementation has issues on non-English or non-QWERTY layouts,
see https://github.com/wezterm/wezterm/issues/6087 and #11204.

fish 4.0.1 disabled modifyOtherKeys on WezTerm specifically
(7ee6d91ba0 (Work around keyboard-layout related bugs in WezTerm's
modifyOtherKeys, 2025-03-03)), fish 4.1.0 didn't, because at that
time, WezTerm would advertise support for the kitty keyboard protocol
(even if applications are not allowed to enable it) which would make
fish skip requesting the legacy modifyOtherKeys.

WezTerm no longer advertises that if config.enable_kitty_keyboard
is false.  Let's work around it in another way.
2025-09-30 10:33:03 +02:00
Johannes Altmanninger
7cd0943056 Tighten some screws for TTY-specific workarounds 2025-09-30 10:25:23 +02:00
Johannes Altmanninger
6f0532460a fish_config: fix for non-embedded builds
I only tested with embedded-builds; CMake tests were failing because
they use different code paths here.

fish_config could use some love.  Start by extracting common
functionality between "{theme,prompt} show", fixing the behavior.

Fixes 29a35a7951 (fish_config: fix "prompt/theme show" in embed-data
builds, 2025-09-28).
2025-09-28 12:29:18 +02:00
Johannes Altmanninger
29a35a7951 fish_config: fix "prompt/theme show" in embed-data builds
Fixes #11832
2025-09-28 10:59:53 +02:00
Johannes Altmanninger
dd0d45f88f fish_config prompt: remove dead code
Commit 2b74affaf0 (Add prompt selector, 2021-04-22)
intentionally added an unused code path that checks for
$__fish_data_dir/sample_prompts, see

https://github.com/fish-shell/fish-shell/pull/7958#discussion_r621320945

> (note: This was added in preparation of moving the sample_prompts directory out of web_config -
> because it no longer is web-exclusive)

Remove it.
2025-09-28 10:11:01 +02:00
Johannes Altmanninger
0ff0de7efe release workflow: install msgfmt for staticbuilds
This makes us actually embed localized messages.

Part of #11828
2025-09-28 09:56:57 +02:00
Johannes Altmanninger
092ef99551 macos CI: explicitly install gettext
We need msgfmt for embedding translations.

Part of #11828
2025-09-28 09:54:30 +02:00
Johannes Altmanninger
97ae05b69d build_tools/release.sh: actually add new docs
Not quite sure for which step this is actually needed.  While at it,
fix the errexit issue that caused this blunder.
2025-09-27 22:56:49 +02:00
Johannes Altmanninger
3d8eca178e start new cycle
Created by ./build_tools/release.sh 4.1.0
2025-09-27 22:41:54 +02:00
Johannes Altmanninger
29b80bbaf9 Release 4.1.0
Created by ./build_tools/release.sh 4.1.0
2025-09-27 22:20:11 +02:00
Johannes Altmanninger
29470358d4 make_macos_pkg: update CMake invocation for MSRV rustc+cargo
Since 205d80c75a (findrust: Simplify (#11328), 2025-03-30), we need
to set Rust_COMPILER and Rust_CARGO instead of Rust_TOOLCHAIN (which
is no longer used).  Adjust macOS builds accordingly.

When I set only one of the two, the error messages were pretty
unintelligible. But I guess few normal users need to override the
Rust version, and they can always look here.

While at it, enable localization.  AFAIK, the only reason why we didn't
do this on macOS were problems related to the gettext shared library /
dependency. We no longer need that, and it's already tested in CI.
2025-09-27 22:15:25 +02:00
Johannes Altmanninger
8fe68781be Release workflow fixups
(cherry picked from commit ce4aa7669d)
2025-09-27 22:06:26 +02:00
Johannes Altmanninger
aba4d26f95 Emphasize that "status {list-files,get-file}" are meant for internal use
I'm not aware of a lot of sensible use cases where users need to access
our files directly.  The one example we know about is zoxide overriding
exactly our version of "function cd", ignoring any user-provided cd.
I think this is already hacky. But I guess it's here to stay.

I think we should not recommend this for external use, or at least
ask users to tell us what they are using this for.

Given that we expect these to be used mainly/only internally,
get-file/list-files are fine as names.

The other issue is that one has to be careful to always do

	status list-files 2>/dev/null

to support non-embedded builds.

Closes #11555
2025-09-27 14:22:18 +02:00
Johannes Altmanninger
b964072c11 Move scrollback-push feature detection to fish script
A lot of terminals support CSI Ps S.  Currently we only allow them
to use scrollback-up if they advertise it via XTGETTCAP.  This seems
surprising; it's better to make visible in fish script  whether this
is supposed to be working.  The canonical place is in "bind ctrl-l"
output.

The downside here is that we need to expose something that's rarely
useful. But the namespace pollution is not so bad, and this gives
users a nice paper trail instead of having to look in the source code.
2025-09-27 14:22:18 +02:00
Johannes Altmanninger
06bbac8ed6 release-notes.sh: add stats, round off committer list
Instead of adding these to the Markdown directly, add it to the
fake CHANGELOG.rst source, which makes escaping easier, and allows
generating other formats than Markdown in future.
2025-09-27 14:22:18 +02:00
Johannes Altmanninger
aab22a453b Revert "Only load sphinx_markdown_builder extension if it's used"
sphinx-build fails with

	sphinx.errors.SphinxError: Builder name markdown not registered or available through entry point

Apparently this issue was hidden locally by caching, and not checked
in CI because of this error causing
tests/checks/sphinx-markdown-changelog.fish to be skipped.

	sphinx-build 7.2.6
	runner@runnervm3ublj:~/work/fish-shell/fish-shell$ python -c 'import sphinx_markdown_builder'
	Traceback (most recent call last):
	  File "<string>", line 1, in <module>
	  File "/home/runner/.local/lib/python3.12/site-packages/sphinx_markdown_builder/__init__.py", line 6, in <module>
	    from sphinx.util.typing import ExtensionMetadata
	ImportError: cannot import name 'ExtensionMetadata' from 'sphinx.util.typing' (/usr/lib/python3/dist-packages/sphinx/util/typing.py)


This reverts commit 7b495497d7.

While at it, fail the test earlier if something went wrong, because the
remaining check will likely also fail and confuse.
2025-09-27 14:22:18 +02:00
Johannes Altmanninger
4cc2d2ec30 release-notes.sh: remove line breaks from generated Markdown, for GitHub
GitHub-flavored Markdown translates line breaks to <br/>, which does
not match our intent. Work around that by joining lines when producing
Markdown output.
2025-09-27 14:22:18 +02:00
Johannes Altmanninger
6ed4e54c1e Remove some dead code and unnecessary allocations
complete --subcommand was added in a8e237f0f9 (Let `complete`
show completions for one command if just given `-c`, 2020-09-09)
but never used or documented.
2025-09-27 14:22:18 +02:00
Johannes Altmanninger
829d6bc8fb Move terminal name into status subcommand not variable
Forgot about that; less namespace pollution this way, and it's more
obvious that it's read-only.
2025-09-27 14:22:18 +02:00
Johannes Altmanninger
abae6157d9 Changelog: reduce verbosity a bit, add some more 2025-09-27 14:22:18 +02:00
Daniel Rainer
bce19e7265 Fix typos 2025-09-26 16:34:22 +02:00
Johannes Altmanninger
166b17701a Remove spurious terminal interaction from unit tests
Some of our integration tests require a reader for code execution
and testing cancellation etc., but they never actually read from the
terminal.  So they don't need to call reader_interactive_init(), and
doing so is a bit weird.  Let's stop that; this allows us to assert
that reader_push() is always called with an input file descriptor
that refers to a TTY.
2025-09-26 13:03:20 +02:00
Johannes Altmanninger
f8269be359 Move redundant instances of blocking_query into InputData
We duplicate the same member across all implementations.  Looks like
this never makes sense, so move it to the shared data bag.
2025-09-26 12:59:02 +02:00
Johannes Altmanninger
b907bc775a Use a low TTY query timeout only if first query failed
As mentioned in the comment, query timeouts can happen if either
1. the terminal doesn't support primary device attribute
2. there is extreme (network) latency

In the first case, we want to turn the timeout way down.  In the
second case, probably not, especially because the network latency
may be transient. Let's keep the high timeout in the second case.

Fixes 7ef4e7dfe7 (Time out terminal queries after a while,
2025-09-21).
2025-09-26 12:59:02 +02:00
Johannes Altmanninger
7e3fac561d Query terminal only just before reading from it
Commit 5e317497ef (Query terminal before reading config, 2025-05-17)
disabled the kitty keyboard protocol in "fish -c read".  This seems
surprising, and it's not actually necessary that we query before
reading config; we only need query results before we read from
the TTY for the first time (which is about the time we call
__fish_config_interactive). Let's do that, reverting parts of
5e317497ef.
2025-09-26 12:59:02 +02:00
Johannes Altmanninger
7713e90aeb Make query response handling more consistent 2025-09-26 12:58:10 +02:00
Johannes Altmanninger
08ad5c26ea Give scroll-forward a less confusing name
ECMA-48 calls CSI S "scroll up", so use something like that but try
to avoid ambiguity.
2025-09-26 12:52:28 +02:00
Johannes Altmanninger
310b7eca68 Only initialize kitty keyboard capability on startup
If the initial query is interrupted by ctrl-c, we leave it unset. A
later rogue "\e[?0u" reply would make us enable it, which seems
surprising. Fix that by always setting the capability if we're gonna
read from stdin.
2025-09-26 12:10:56 +02:00
Johannes Altmanninger
e9ae143bab Fix cursor position reports being ignored
When we receive a cursor position report, we only store the result;
we'll act on it only when we receive the primary DA reply.  Make sure
we don't discard the query state until then.

Fixes 06ede39ec9 (Degrade gracefully when failing to receive cursor
position report, 2025-09-23)
2025-09-26 12:10:04 +02:00
Johannes Altmanninger
74d3832610 Early return in reader_execute_readline_cmd() 2025-09-26 12:10:04 +02:00
yegorc
3c04a05ea4 Added documentation for &>>.
The `&>>` redirection was undocumented.

Closes #11824
2025-09-26 12:10:04 +02:00
Johannes Altmanninger
e6541c5c93 Enable sphinx parallelism also when building markdown release notes
We get a warning about sphinx_markdown_builder not being
parallelizable. Fix that.

Ref: https://github.com/liran-funaro/sphinx-markdown-builder/pull/38
2025-09-26 12:10:04 +02:00
Johannes Altmanninger
7b495497d7 Only load sphinx_markdown_builder extension if it's used
As is, building man pages or HTML docs while sphinx_markdown_builder
is installed, will result in unrelated warnings.  Remove those by
removing it from the extensions config.  Markdown building (only used
for changelog) seems to work without this just fine.
2025-09-26 12:08:51 +02:00
Johannes Altmanninger
6e90d9bd6f Fix markdown changelog generation test
System tests typically run outside the workspace directory, but they
still have read-only access to the workspace; fix it accordingly.
This test only works on git checkouts, not in tarballs, so skip it
if .git doesn't exist.
2025-09-26 12:08:51 +02:00
Stevie Alvarez
b8f704e3c4 fish_git_prompt: add diverged upstream char option
Currently, `__fish_git_prompt_char_upstream_diverged` can only be set to
a combination of `__fish_git_prompt_char_upstream_behind` and
`__fish_git_prompt_char_upstream_ahead`s plain-text options. Adding a
combination of the less-plain character options gives users more choice.

Closes #11817
2025-09-25 11:38:52 +02:00
Johannes Altmanninger
b37b57781b Disable sphinx-markdown-builder in tests again for now
Somehow I didn't realize this breaks the tests/checks/sphinx-* tests.
2025-09-24 16:30:21 +02:00
Fabian Boehm
e3a5725a46 Fix crash if there is no xtversion reply 2025-09-24 16:21:18 +02:00
Karel Balej
3d5a5b8740 completions: add basic completions for udevil
Add a set of basic completions for udevil, which is a program that
allows unpriviledged users to mount devices.

Each command has a corresponding long-option-like syntax variant
(sometimes even multiple ones), such as "udevil monitor" -> "udevil
--monitor", which are omitted here for simplicity.

The project unfortunately seems long abandoned and as such no attempt to
submit these completions upstream has been made.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
22ffc31b71 release workflow: credit contributors in release notes
While at it, do a 's/^--$/^---/' to fix Markdown syntax for horizontal
line for CommonMark-based parsers.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
127c02992d Test markdown changelog creation in CI
Extract a github action to install the same version used in the release
workflow.  In future we should probably migrate to requirements.txt
or similar.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
765ca54d59 release workflow: resolve relative references in changelog properly
Instead of having sphinx-build only build CHANGELOG.rst, build the
entire thing, so relative references (":doc:", ":ref:") can be resolved
properly.  Replace our sed-based hacks with 'markdown_http_base'.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
1519ea74be release workflow: extract script for generating markdown release notes 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
2895986465 tests/sphinx-man: man page building no longer requires fish_indent 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
e9e7ad24b5 Changelog: also extract changes to embed-data to its own section 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
4f8aa0a78c Show warnings from sphinx-build when building with cargo 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
f821d6dd7f Feature flag for turning off TTY querying
Experience with OSC 133 and kitty keyboard protocol enabling sequences
has shown that a lot of users are still on incompatible terminals.
It's not always easy to fix those terminals straight away. There
are probably some more environments where primary device attribute
queries are not answered.

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

When the terminal fails to respond to primary device attribute, we
already print an error pointing to "help terminal-compatibility".
Inside that document, inside the "primary device attribute" section,
point out this new feature flag.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
4c1da3b2d3 Parse Terminal.app version only if needed 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
a6f975a7e3 Add mark-prompt feature flag
(not sure if we should also include this in 4.1 but I guess better
safe than sorry)

So far, terminals that fail to parse OSC sequences are the only reason
for wanting to turn off OSC 133.  Let's allow to work around it by
adding a feature flag (which is implied to be temporary).

To use it, run this once, and restart fish:

    set -Ua fish_features no-mark-prompt

Tested with

    fish -i | string escape | grep 133 &&
    ! fish_features=no-mark-prompt fish -i | string escape | grep 133

Closes #11749
Also #11609

(cherry picked from commit 6900b89c82)
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
bbd7205de1 Document GNU screen incompatibily & workarounds
The problem described in 829709c9c4 (Replace synchronized update
workaround, 2025-04-25) doesn't seem too bad; let's document the
workaround.

We could probably also remove our $STY-based workaround.  I'm not
yet sure how many problems that one will cause.

Closes #11437
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
64c8d361b0 status.rst: link to feature flag documentation 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
c31e769f7d Use XTVERSION for terminal-specific workarounds
As mentioned in earlier commit
("Query terminal before reading config").

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

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

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

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

Do startup queries before reading config.

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

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

Unlike fish, fish_key_reader seems to ignore these failures, meaning
it will retry poll()/read() immediately.  This seems less correct,
so use fish's behavior for now.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
fadb2fac44 Fix some import conventions 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
1b01ac87fa Improve a tcsetpgrp error message 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
4abd390c84 test_driver.py: extend the list of terminal-specific workarounds 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
712bbf00ec fish_key_reader: remove things that don't belong in the builtin
While at it, remove a redundant call to initialize_tty_metadata();
that's done by initial_query().
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
76d1e8d4da fish_key_reader: reuse isatty helper 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
2d457adfec builtin read: use a better type 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
6fdce59572 Extract function for setting shell modes
We do the same thing in several places, with lots of small differences.
Extract the most reasonable behavior and use it everywhere.  Note that
we had an explictly-motivated ENOTTY check; the motivating issues
doesn't seem to reproduce anymore here though I did not bisect yet.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
21150726d3 Remove confused redundant mutex lock 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
37a0919fee Also check isatty(0) before querying
A process like "fish -i <somefile ..."  probably shouldn't query
because it's not gonna work.

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

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

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

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

But MC_TMPDIR needs to be checked before querying for XTVERSION.

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

	fish -C 'read'

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

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

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

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

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

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

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

Closes #11108
Closes #11117
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
06ede39ec9 Degrade gracefully when failing to receive cursor position report
Follow up the cursor position report query with a primary device
attribute one.  When that one arrives, any cursor position response
must have arrived too. This allows us to prevent a hang on terminals
that only support primary device attribute.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
1612576d1c Restructure query response handling logic
Instead of switching only on the response type, switch on the
combination of that and the expected response.  This helps the
following commits, which add more combinations (due to following up
cursor position report with a primary DA, and adding a timeout). No
behavior change here.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
2260465ed7 Rename enum variants to remove a name clash 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
3bd296ae3d Reuse variable in next_input_event() 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
064a45635c Document inter-dependencies between optional terminal features
- document that we currently require "cursor position report" if
  either of both click_events or XTGETTCAP+indn is implemented.
  One of the following patches will remove this requirement.
- document properly that scrollback-push currently only works
  when XTGETTCAP+indn is implemented. There are still a few terminals
  that don't support SCROLL UP, for example the Linux Console,
  and there is no better way to find out if it's supported.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
6f01c7b707 Changelog: minor edits 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
6f2a701f81 README: recommend latest release in embedded builds section 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
f79377e4b0 Revert "Temporary workaround for BSD WEXITSTATUS libc bug"
This reverts commit c1b460525c.

We have libc commit 7a7fe4683 (Apply modulo 256 to BSD WEXITSTATUS,
2024-12-19) now.
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
c3626a3031 builtin read: --tokenize-raw option
Users have tried to get a list of all tokens -- including operators
-- using "commandline --tokens-raw".  That one has been deprecated
by cc2ca60baa (commandline.rst: deprecate --tokens-raw option,
2025-05-05).  Part of the reason is that the above command is broken
for multi-line tokens.

Let's support this use case in a way that's less ambiguous.

Closes #11084
2025-09-24 15:51:32 +02:00
Johannes Altmanninger
f3b27e8d11 Changelog: link to command docs 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
5d71609ff7 Changelog: fix rst syntax for man page output 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
dbd79285cb Contributing: fix rst external link syntax 2025-09-24 15:51:32 +02:00
David Adam
dca571ac4c fixup! fish.spec: also drop %find stanza for lang files 2025-09-23 08:01:04 +08:00
Daniel Rainer
c8f31ceedb Fix release workflow syntax
The previous version results in an immediate workflow failure due to a
syntax error in the YAML. `workflow_dispatch` should be a dictionary
key, with its value being another dictionary (whose only key is `inputs`
at the moment).
2025-09-22 20:25:45 +02:00
Johannes Altmanninger
b1d1ef1b6e github release workflow: fix structure 2025-09-22 18:19:36 +02:00
Johannes Altmanninger
01361b9217 github release workflow: only run on explicit dispatch
Release automation can be tested on any GitHub fork, using

	build_tools/release.sh $version $repository_owner $git_remote

which should work perfectly except for macOS packages (which fail
unless provided GitHub secrets).

People might push tags to their forks, both non-release tags (which
would trigger an early failure in "is-release-tag") or replicas of
our actual release tags (which would create a draft release etc. and
only fail when building macOS packages).

Run on explicit workflow dispatch to make sure it's not triggered by
accident like that.

This means that we'll use the .github/workflows/release.yml from
the default branch (i.e. master), so try to make sure it matches the
version in the release, to prevent accidents.

Closes #11816
2025-09-22 18:15:21 +02:00
Johannes Altmanninger
dab8df1a18 github release workflow: use github context only if needed 2025-09-22 18:15:21 +02:00
Johannes Altmanninger
c771ff06d4 build_tools/make_macos_pkg.sh: fix inconsistent version computation
make_tarball.sh and others do it differently.
2025-09-22 18:15:21 +02:00
David Adam
e5cb1689c5 fish.spec: also drop %find stanza for lang files 2025-09-21 18:45:20 +08:00
David Adam
fa96df3a90 fish.spec: drop find_lang macro
All translations are now handled internally.
2025-09-21 18:11:01 +08:00
Karel Balej
67a9d08778 completions/mpc: conditionally enable file completion for add and insert
When connecting to MPD via a Unix socket, mpc add and insert accept
absolute paths to local files. Offer these in the completion if the
completed token starts with a slash after expansion.
2025-09-20 15:20:19 +02:00
Johannes Altmanninger
5ece9bec6c __fish_print_help: use man as-is
Since 0fea1dae8c (__fish_print_help: Make formatting more man-like,
2024-10-03), there is barely any difference left between "man abbr"
and "abbr -h".

The main difference is that it looks almost like man but is actually
nroff/mandoc and less.  This means it doesn't support environment
variables like MANWIDTH and possibly others.

Let's use full "man" for now.
This matches what "git foo --help" does so it's widely accepted.

Keep around a fallback for a while, in case users/packagers fail to
install the new "man" dependency.

In future, "abbr -h" (as opposed to "abbr --help") could show a more
concise version, not sure.

Closes #11786
2025-09-20 13:56:23 +02:00
Johannes Altmanninger
d422ad603e __fish_print_help: remove error message
__fish_print_help supports printing an error message above the
documentation.

This is currently only used by extremely rare edge cases, namely:

	eval "break"
	eval "continue --unknown"
	fish -c 'sleep 10&; bg %1'

Let's remove this feature to enable us to use man directly (#11786).
2025-09-20 13:56:23 +02:00
Johannes Altmanninger
26116b477e Fix crash on "bg" of non-job-controlled job
fish -c 'sleep 1 & bg %1' is supposed to fail because the job is not
under job control.

When we try to print the failure, we accidentally still
hold a borrow of the job list.  This blows up because we use
"builtin_print_help_error()" to print the failure message; that
function runs "job_reap()" which wants an exclusive borrow of the
job list. Let's drop our job list earlier.
2025-09-20 13:56:23 +02:00
Johannes Altmanninger
87c73b7fbf builtin break/continue: support -h/--help argument
These are not generic builtins because we check whether they're inside
a loop. There's no reason to not support "break -h" when we support
"if -h" etc.; do that.
2025-09-20 13:56:23 +02:00
Johannes Altmanninger
c77255aabc tests: consolidate __fish_print_help overrides 2025-09-20 13:56:23 +02:00
Johannes Altmanninger
0063992195 functions/man: consistent switch-case ordering
Match __fish_print_help
2025-09-20 13:56:23 +02:00
Johannes Altmanninger
1fdf37cc4a __fish_echo: fully overwrite lines
With upcoming multi-line autosuggestions, when I run

	$ function foo
	    true
	  end

and type "function", then I'll get a suggestion for the above command.
Now if press "alt-w", it will echo "function - create a function"
and rewdraw the prompt below.  But the previous autosuggestion is
not cleared, so it will look weird like:

	johannes@abc ~/g/fish-shell> function foo
	function - create a function     true

Let's erase these lines before writing them.

There's an issue remaining: the first line of the autosuggestion
(i.e. "foo") is not erased.  Fortunately this is less annoying,
but it shows that __fish_echo needs more support from core.
2025-09-20 13:56:23 +02:00
Johannes Altmanninger
c0c7b364fc builtin status: rename buildinfo to build-info
See: https://github.com/fish-shell/fish-shell/pull/11726#discussion_r2347389523
2025-09-20 13:56:23 +02:00
Johannes Altmanninger
64bbd41f81 build_tools/release.sh: approve macos-codesign github environment 2025-09-20 13:56:23 +02:00
Johannes Altmanninger
955345dd5b completions/sudo-rs: wrap sudo for now, closes #11800 2025-09-20 13:56:23 +02:00
Johannes Altmanninger
012b507128 Workaround for embed-data debug builds on Cygwin
When running a debug build, rust-embed always sources files from disk.
This is currently broken with on Cygwin.

As a temporary workaround, use the "debug-embed" feature to actually
embed the files into the binary, like we do for release builds.

We can probably fix the rust-embed issue fairly easily.
I haven't checked.  For now, I think this hack is preferrable to
not having an easy way to make debug builds on Cygwin.  (CMake
files would need some changes, and I also hit some problems with
installation). At least this would have helped with investigating
https://github.com/msys2/msys2-runtime/issues/308
2025-09-20 13:56:23 +02:00
Johannes Altmanninger
142cec3a96 github release workflow: use a consistent order for static builds 2025-09-20 13:56:23 +02:00
Johannes Altmanninger
cdc57b97a3 Changelog updates 2025-09-20 13:56:23 +02:00
Johannes Altmanninger
bb916f8d73 Fix regression breaking self-insert of kitty shifted codepoint
Commit 50a6e486a5 (Allow explicit shift modifier for non-ASCII
letters, fix capslock behavior, 2025-03-30) delayed handling of kitty
keyboard protocol's shifted codepoints.  It does handle shifted
codepoints when matching keys to mappings; but it fails to handle
them in the self-insert code paths where we want to insert the text
represented by CharEvent::Key.
Fix it by resolving the shifted key.

Fixes #11813
2025-09-20 13:56:23 +02:00
Johannes Altmanninger
8d12dfe065 Move key codepoint computation to key event
For the next commit.
2025-09-20 13:56:23 +02:00
Johannes Altmanninger
9bf58abcae Changelog: move gettext and argparse changes to dedicated sections
By giving them dedicated headlines, they should be easier for users to skip
over if not interested.
2025-09-20 13:56:23 +02:00
Johannes Altmanninger
b325ff6992 Changelog: syntax fixes and rewording 2025-09-20 13:56:23 +02:00
Daniel Rainer
397969ddcc Add message localization tests
These tests require building with the `localize-messages` feature.

If certain translations are updated, this test might fail, either
because a message was changed in the source, or because a translation of
a message was changed, or because a translation was added in a language
which previously did not have a translation for a particular message,
and we rely on that in the test. If any of these happen, the tests need
to be updated accordingly.

Closes #11726
2025-09-20 13:56:23 +02:00
Daniel Rainer
bee1e122f9 Remove unused locale path code
The locale path was used to tell GNU gettext where to look for MO files
at runtime. Since we now embed the message catalog data into the
executable, we no longer need a locale path.

Part of #11726
2025-09-20 13:56:23 +02:00
Daniel Rainer
ad323d03b6 Switch to builtin gettext implementation
This completely removes our runtime dependency on gettext. As a
replacement, we have our own code for runtime localization in
`src/wutil/gettext.rs`. It considers the relevant locale variables to
decide which message catalogs to take localizations from. The use of
locale variables is mostly the same as in gettext, with the notable
exception that we do not support "default dialects". If `LANGUAGE=ll` is
set and we don't have a `ll` catalog but a `ll_CC` catalog, we will use
the catalog with the country code suffix. If multiple such catalogs
exist, we use an arbitrary one. (At the moment we have at most one
catalog per language, so this is not particularly relevant.)

By using an `EnvStack` to pass variables to gettext at runtime, we now
respect locale variables which are not exported.
For early output, we don't have an `EnvStack` to pass, so we add an
initialization function which constructs an `EnvStack` containing the
relevant locale variables from the corresponding Environment variables.
Treat `LANGUAGE` as path variable. This add automatic colon-splitting.

The sourcing of catalogs is completely reworked. Instead of looking for
MO files at runtime, we create catalogs as Rust maps at build time, by
converting PO files into MO data, which is not stored, but immediately
parsed to extract the mappings. From the mappings, we create Rust source
code as a build artifact, which is then macro-included in the crate's
library, i.e. `crates/gettext-maps/src/lib.rs`. The code in
`src/wutil/gettext.rs` includes the message catalogs from this library,
resulting in the message catalogs being built into the executable.

The `localize-messages` feature can now be used to control whether to
build with gettext support. By default, it is enabled. If `msgfmt` is
not available at build time, and `gettext` is enabled, a warning will be
emitted and fish is built with gettext support, but without any message
catalogs, so localization will not work then.

As a performance optimization, for each language we cache a separate
Rust source file containing its catalog as a map. This allows us to
reuse parsing results if the corresponding PO files have not changed
since we cached the parsing result.

Note that this approach does not eliminate our build-time dependency on
gettext. The process for generating PO files (which uses `msguniq` and
`msgmerge`) is unchanged, and we still need `msgfmt` to translate from
PO to MO. We could parse PO files directly, but these are significantly
more complex to parse, so we use `msgfmt` to do it for us and parse the
resulting MO data.

Advantages of the new approach:
- We have no runtime dependency on gettext anymore.
- The implementation has the same behavior everywhere.
- Our implementation is significantly simpler than GNU gettext.
- We can have localization in cargo-only builds by embedding
  localizations into the code.
  Previously, localization in such builds could only work reliably as
  long as the binary was not moved from the build directory.
- We no longer have to take care of building and installing MO files in
  build systems; everything we need for localization to work happens
  automatically when building fish.
- Reduced overhead when disabling localization, both in compilation time
  and binary size.

Disadvantages of this approach:
- Our own runtime implementation of gettext needs to be maintained.
- The implementation has a more limited feature set (but I don't think
  it lacks any features which have been in use by fish).

Part of #11726
Closes #11583
Closes #11725
Closes #11683
2025-09-20 13:56:23 +02:00
Daniel Rainer
3a196c3a08 Use rsconf::warn! where appropriate
This is a more explicit variant than directly prefixing build script
output with `cargo:warning=`.

Part of #11726
2025-09-20 09:10:39 +02:00
Daniel Rainer
f34dfb8424 Allow 3-letter language codes
Some languages do not have a 2-letter code.
https://www.gnu.org/software/gettext/manual/html_node/Locale-Names.html

Part of #11726
2025-09-20 09:10:39 +02:00
Daniel Rainer
e2a9f0eb50 Make build_dir construction more readable
Part of #11726
2025-09-20 09:10:39 +02:00
Johannes Altmanninger
fb0f9842ae Update to Rust 1.90 2025-09-19 15:43:55 +02:00
Johannes Altmanninger
ff633bd744 github release workflow: make sure that last changelog entry isn't given spurious markup 2025-09-18 10:46:21 +02:00
Johannes Altmanninger
0d46c26988 github release workflow: work around trailing "---"-line in changelog
The extracted release notes trigger a sphinx warning

	/tmp/tmp.V6RGP92nc2/src/index.rst:6:Document may not end with a transition.

which we don't seem to get on the full CHANGELOG.rst.
Let's work around that for now.
2025-09-18 09:58:14 +02:00
Johannes Altmanninger
1840df96a2 build_tools/release.sh: relax assertion about changelog title
I'd like to move to a process where everything goes into master first,
and then flows downstream to any release branches (i.e. no merging
of Integration_* branches back into master).

The only thing we need to change for that is to add release notes for
patch releases eagerly on master.  That implies that we want to use
the actual version instead of ???.  (Only if something goes wrong
in the release process, we need to change this on both branches,
but that should happen too often.)
2025-09-18 09:37:44 +02:00
Johannes Altmanninger
3456b33050 build_tools/release.sh: push to the integration branch when all goes well
Also, ignore any "sendemail.to" Git config, and remove a temporary
statement in release notes.
2025-09-18 09:32:16 +02:00
Johannes Altmanninger
9a04c15894 Fix new-style bindings shadowing raw escape sequence bindings
Given

	bind up "echo user up, new notation"
	bind \e\[A "echo user up, legacy notation"

prior to b9d9e7edc6 (Match bindings with explicit shift
first, 2025-05-24), we prioritized the legacy notation because
input_mapping_insert_sorted() makes us try longer sequences first --
and "up" is only one key compared to the three-key legacy sequence.

This prioritization was broken in b9d9e7edc6, causing plugins that
update to the "bind up" notation to break users who haven't (#11803).

Even worse, it caused preset bindings to shadow user ones:

	bind --preset up "echo preset up, new notation"
	bind \e\[A "echo user up, legacy notation"

Restore backwards compatibility by treating matches against legacy
notation like exact matches again.
2025-09-18 09:28:57 +02:00
David Adam
63585a3e26 Merge branch 'Integration_4.0.6' 2025-09-17 14:38:19 +08:00
David Adam
aa1b64f955 build_tools: make tarball scripts use the build version 2025-09-17 13:56:22 +08:00
Piotr Kubaj
91ee45b0e1 path.rs: fix build on ARM / POWER
error[E0308]: mismatched types
   --> src/path.rs:749:13
    |
748 |         let remoteness = remoteness_via_statfs(
    |                          --------------------- arguments to this function are incorrect
749 |             libc::statfs,
    |             ^^^^^^^^^^^^ expected fn pointer, found fn item
    |
    = note: expected fn pointer `unsafe extern "C" fn(*const i8, _) -> _`
                  found fn item `unsafe extern "C" fn(*const u8, _) -> _ {libc::statfs}`
note: function defined here
   --> src/path.rs:712:12
    |
712 |         fn remoteness_via_statfs<StatFS, Flags>(
    |            ^^^^^^^^^^^^^^^^^^^^^
713 |             statfn: unsafe extern "C" fn(*const i8, *mut StatFS) -> libc::c_int,
    |             -------------------------------------------------------------------

error[E0308]: mismatched types
   --> src/path.rs:725:34
    |
725 |             if unsafe { (statfn)(path.as_ptr(), buf.as_mut_ptr()) } < 0 {
    |                         -------- ^^^^^^^^^^^^^ expected `*const i8`, found `*const u8`
    |                         |
    |                         arguments to this function are incorrect
    |
    = note: expected raw pointer `*const i8`
               found raw pointer `*const u8`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `fish` (lib) due to 2 previous errors
2025-09-16 20:03:40 +02:00
Johannes Altmanninger
f69c2a4d4a fish_config: silence error when compiled without embed-data 2025-09-13 15:14:13 +02:00
Johannes Altmanninger
3f7a576835 Support installing to nested subdirectory of CMAKE_BINARY_DIR
The only install directory that's not supported is
-DCMAKE_INSTALL_PREFIX=$CARGO_MANIFEST_DIR, but that's a bad idea
anyway since share/ is Git-tracked.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
75cedd8039 config_paths: extract variable 2025-09-13 15:12:23 +02:00
Johannes Altmanninger
a2c6e22d13 build.rs: don't define unused build environment variables
We set a lot of variables that are never used with
--feature=embed-data.  Remove them.  I don't think this change will
cause any problems with caching.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
aaf5ed7f11 build.rs: fix fallback PATH when embed-data is enabled
On a system where _CS_PATH is not defined (tested by removing that
code path), we get:

	$ cargo b && env -u PATH HOME=$PWD target/debug/fish -c 'set -S PATH'
	$PATH[1]: |.local//bin|
	$PATH[2]: |/usr/bin|
	$PATH[3]: |/bin|

The relative $PATH[1] makes no sense; probably it's an
accident. Restore the traditional $PATH[1]=/usr/local/bin.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
35d791ed49 config_paths: get rid of global variable, initialize locale predictably
Commit bf65b9e3a7 (Change `gettext` paths to be relocatable (#11195),
2025-03-30) made the locale directory (/usr/local/share/locale)
relocatable as well, so that "mv /usr/local /usr/local2" would not
break translations.

But this introduces a weird circular dependency, which was probably
the reason why the locale directory hadn't been relocatable:
1. option parsing might fail and print error messages, which should
   be localized, hence require detection of config paths
2. detection of config paths calls `FLOG`, which depends on options
   parsing (e.g. "-d config -o /tmp/log")

Since commit bf65b9e3a7, fish initializes the config paths
lazily, as soon as needed by translations.

When initializing config paths, we produce logs.  The logs are off by
default so its' fine in practical cases, but technically we should only
log things after we have handled things like FISH_DEBUG_OUTPUT.

Here's an example where the config directory initialization sneakily
injected by an error message's "wgettext_fmt!" causes logs to be
printed to the wrong file spuriously:

	$ FISH_DEBUG='*' FISH_DEBUG_OUTPUT=/tmp/log build/fish --unknown-arg
	config: exec_path: "./build/fish", argv[0]: "./build/fish"
	config: paths.sysconf: ./etc
	config: paths.bin: ./build
	config: paths.data: ./share
	config: paths.doc: ./user_doc/html
	config: paths.locale: ./share/locale
	fish: --unknown-arg: unknown option

Now we could handle "-d config", "-o", and FISH_DEBUG later, but
that would mean that in this example we don't get any logs at all,
which doesn't seem correct either.

Break the circular dependency by determining config paths earlier,
while making sure to log the config path locations only after parsing
options, because options might affect whether we want to log the
"config" category.

The global variable is only needed for locale, so use explicit argument
passing for everything else, as before.

While at it, make other binaries (fish_key_reader, fish_indent) use
the same localization logic as fish. This means that we need to tweak
the «ends_with("bin/fish")» check.

Closes #11785
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
a7f8e294ba config_paths: remove rogue use of std::env::args() 2025-09-13 15:12:23 +02:00
Johannes Altmanninger
01ae00c653 config_paths: remove weird and redundant path handling
This duplicates work already done in get_executable_path().  No idea
why this was added.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
ee8a88a101 config_paths: rework logging a little
The executable path is used for embedded builds too, so let's log
it always.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
b4697231d7 config_paths: remove a comment
I'm not sure if setting PROGRAM_NAME based on argv[0] is a good idea.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
6cddceb37a config_paths: use immutable/SSA style for determining config paths
This logic is extremely confusing because it creates a ConfigPaths
object only to throw it away later. Fix that.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
8640ce8148 config_paths: embed-data has no doc dir
This makes no sense:

	$ target/debug/fish -d config
	config: paths.doc: .local/share/doc/fish

so remove it.

While at it, group config paths by whether they can be embedded.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
f7a2d56046 config_paths: model embed-data dichotomy better
Rather than having every single config path be an Option<Path>,
clarify that we define either all or nothing.

If we want to decide this at runtime, we'd use an enum; but it's all
known at compile time, so we can give the reader even more information.

Unfortunately this means that compile errors in non-embed-data
code paths might go unnoticed for a while, and rust-analyzer often
doesn't work in those code paths. But that's a general problem with
the compile-time feature, it seems orthodox to ifdef away as much
as possible.

There are some odd data paths that don't follow the "all or nothing",
the next commits will fix this.

Note that this breaks localization for target/debug/fish built with
embed-data. But as soon as fish was moved out of the repo, that was
already broken.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
cacb9f50b8 config_paths: use early return 2025-09-13 15:12:23 +02:00
Johannes Altmanninger
cef60fe585 config_paths: remove obsolete installable vars
Same reason as the grandparent commit.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
04a2398c90 config_paths: separate ifdef'd path logic into functions
No functional change, only reduce the number of times we check for
presence of the embed-data feature.

While at it, move ConfigPaths?
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
f05ad46980 config_paths: remove vestiges of installable builds
Commit 3dc49d9d93 (Allow installable builds to be installed into a
specific path (#10923), 2024-12-22) added some ifdefs to use installed
config paths for installable builds that have already been installed.

The "installable" feature has been superseded by "embed-data"
which no longer uses config paths to retrieve files,
so remove those code paths. Further cleanup to follow.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
e9595d4817 config_paths: remove dead code trying to handle in-tree CMake builds
Assuming every in-tree build uses CMake, the source tree must
also be a valid build directory, so we already return in the
env!("FISH_BUILD_DIR") code path above.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
deabcf5478 config_paths: fix comment 2025-09-13 15:12:23 +02:00
Johannes Altmanninger
e73243b879 Reuse workspace_root() helper from fish-build-helper 2025-09-13 15:12:23 +02:00
Johannes Altmanninger
632b6582c5 builds.rs: canonicalize workspace root and build dir only when necessary
This means we can remove the unwrap() calls from fish-build-helper,
which paves the way for reusing it in the config_paths module.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
27db0e5fed Rename repo_root to workspace_root
This seems like a slightly better term
because I think it also applies to tarball.
Ref: https://github.com/fish-shell/fish-shell/pull/11785#discussion_r2335280389
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
33735f507a Fix regression causing build/fish to use wrong config paths
Commit 8b102f2571 (Stop using Cargo's OUT_DIR,
2025-06-22) accidentally removed canonicalization of
FISH_BUILD_DIR=${CMAKE_BINARY_DIR}.  This means that if the path to
${CMAKE_BINARY_DIR} includes a symlink, ${CMAKE_BINARY_DIR}/fish will
wrongly use /usr/share/fish instead of ${CARGO_MANIFEST_DIR}/share.
Fix this and reintroduce the comment.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
bde8a5aa40 path: fix inconsistency in default PATH
path_get_path does not use "getconf PATH", for no apparent reason.
Fix that.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
e0cd9e2e0a bin/fish.rs: remove dead argv fallback
I think argv[0] is guaranteed to be non-null.
2025-09-13 15:12:23 +02:00
Johannes Altmanninger
7e1123fb42 bin/fish.rs: remove needless clone 2025-09-13 15:12:23 +02:00
Johannes Altmanninger
f7648757f1 autoload: simplify conditional compilation 2025-09-13 15:12:23 +02:00
Johannes Altmanninger
78d46f4b47 fish_indent.rst: remove missing debug options 2025-09-13 15:11:35 +02:00
Johannes Altmanninger
443956b8e3 ci: run clippy without --features=embed-data too
That configuration is already tested, but not clippy-checked yet.
This sometimes causes things like unused imports linger on master.
Let's at least enable clippy for stable Rust.

Also do the same build_tools/check.sh; since that script already runs
"cargo test --no-default-features", this shouldn't add much work,
though I didn't check that.
2025-09-13 15:10:24 +02:00
Johannes Altmanninger
a58cd12c4d Merge pull request #11779 2025-09-13 15:10:24 +02:00
Johannes Altmanninger
89883b791d checks/po-files-up-to-date.fish: mention how to update translations
As pointed out in
https://github.com/fish-shell/fish-shell/issues/11610#issuecomment-3240489072
2025-09-13 15:10:24 +02:00
Johannes Altmanninger
7706ce2e82 Merge pull request #11794 2025-09-13 15:10:24 +02:00
Daniel Rainer
027ea88477 Use deterministic timestamps for embedded data
We do not need timestamps of embedded files, so setting them to 0
reduces the potential for unwanted changes to the binary, allowing for
better build reproducibility.
2025-09-12 16:43:55 +02:00
Daniel Rainer
6cbd655b3d Update to rust-embed 8.7.2
This is the most recent version, which allows using the
`deterministic-timestamps` feature.
2025-09-12 16:43:03 +02:00
Johannes Altmanninger
529f722d2f build_tools/release.sh: fixes for updating fish-site
Also check that "cd fish-site && make && make new-release" doesn't
leave behind untracked files we're not aware of.  This implies that
this script ought to refuse to run if there are untracked files,
at least in fish-site.
2025-09-12 12:49:29 +02:00
Johannes Altmanninger
7619fa316c Release 4.0.6
Created by ./build_tools/release.sh 4.0.6
2025-09-12 11:47:41 +02:00
Johannes Altmanninger
e2005c64b3 Backport release-script related changes from master
Will commit these to master momentarily (#10449).
2025-09-12 11:47:01 +02:00
Johannes Altmanninger
f9dbb4d419 github workflows: actually include the tarballs in the release 2025-09-12 11:46:24 +02:00
Johannes Altmanninger
5e658bf4e9 Release automation script
Things that are not currently happening in this workflow:
- No GPG-signature on the Git tag
- No *.asc signature file for the tarball (or for any other release assets)
- No GPG-signed Debian and other OBS packages

To-do:
- remove the corresponding entries from
  https://github.com/fish-shell/fish-shell/wiki/Release-checklist
  and link to this workflow.
- Maybe add some testing (for the Linux packages)?.
- Let's hope that this doesn't cause security issues.

Usage:
1. run "build_tools/release.sh $version"; this will create and push
   a tag, which kicks off .github/workflows/release.yml
2. wait for the draft release to be created at
   https://github.com/fish-shell/fish-shell/releases/tags/$version
3. publish the draft (manually, for now). This should unblock the
   last part of the workflow (website updates).

Closes #10449

Incremental usage example:

	version=4.0.3
	repository_owner=fish-shell
	remote=origin
	cd ../fish-shell-secondary-worktree
	git tag -d $version ||:
	git push $remote :$version ||:
	git reset --hard origin/Integration_$version
	for d in .github build_tools; do {
		rm -rf $d
		cp -r ../fish-shell/$d .
		git add $d
	} done
	git commit -m 'Backport CI/CD'
	echo "See https://github.com/$repository_owner/fish-shell/actions"
	echo "See the draft release at https://github.com/$repository_owner/fish-shell/releases/$version"
	../fish-shell/build_tools/release.sh $version $repository_owner $remote
2025-09-12 11:42:59 +02:00
Johannes Altmanninger
9ada3e6c16 Group changelog entries 2025-09-12 11:14:52 +02:00
Johannes Altmanninger
0c7e7e3fd9 Add sphinx-markdown-builder for generating release notes
Without this, Sphinx refuses to use the "-b markdown" builder (see next commit).
2025-09-12 10:48:21 +02:00
Johannes Altmanninger
ebcb2eac68 github workflows staticbuild: use stable Rust on Linux
We use the MSRV for CI checks, and for deploying to old macOS.
But for static Linux builds , there should be no reason to use an
old Rust version.  Let's track stable.
2025-09-12 10:48:21 +02:00
Johannes Altmanninger
a7d50c1a62 CHANGELOG: minor updates 2025-09-12 10:48:21 +02:00
Johannes Altmanninger
6056b54ddb Rename build_tools/make_pkg.sh
pkg is a pretty subtle name?
2025-09-11 14:17:40 +02:00
Johannes Altmanninger
bdba2c227d CHANGELOG: update 2025-09-11 13:55:30 +02:00
Johannes Altmanninger
2563adfee1 github workflows: mac_codesign: clean up 2025-09-10 12:41:51 +02:00
Johannes Altmanninger
acad6e6a92 github workflows autolabel: remove stale milestone code
We look for a milestone that no longer exists.
Remove this until we find a solution.
2025-09-10 12:41:51 +02:00
Johannes Altmanninger
9faf78d269 github actions: fix warning about unexpected inputs
CI runs show warnings like

	ubuntu-32bit-static-pcre2
	Unexpected input(s) 'targets', valid inputs are ['']

This is about the rust-toolchain action, which is a composite action, see
https://docs.github.com/en/actions/tutorials/create-actions/create-a-composite-action
not a full workflow
https://docs.github.com/en/actions/how-tos/reuse-automations/reuse-workflows

Looks like composite actions specify inputs at top level.
Also they should not need «on: ["workflow_call"]».

The unexpected inputs are still forwared, so it happens to work.
Fix the warnings.
2025-09-10 12:41:51 +02:00
Johannes Altmanninger
201882e72a CHANGELOG: fix inline literal RST syntax 2025-09-09 07:46:31 +02:00
The0x539
b0565edf85 reader: add case-insensitive history autosuggest
Resolves issue #3126

To match what I've been able to figure out about the existing design
philosophy, case-sensitive matches still always take priority,
but case-insensitive history suggestions precede case-insensitive
completion suggestions.
2025-09-08 22:26:02 -05:00
Johannes Altmanninger
80fff4439f Merge pull request #11784 2025-09-08 11:11:21 +02:00
Johannes Altmanninger
54c48e20d3 completions/ansible: update translations 2025-09-08 11:11:21 +02:00
Johannes Altmanninger
c1af3f2a8f Merge pull request #11783 2025-09-08 11:11:21 +02:00
Johannes Altmanninger
01d7f93917 Merge pull request #11782 2025-09-08 11:08:31 +02:00
Johannes Altmanninger
a9be7b0298 Merge pull request #11781 2025-09-08 11:07:56 +02:00
ookami
5d66e02412 create_manpage_completions.py: Fix options_parts_regex
Escape the dot before "RE".

Fix matching options containing "RE", e.g. ADDRESS.
2025-09-08 10:14:21 +08:00
Vincent Rischmann
18ed4f5f10 Add completion for become-password-file option 2025-09-07 00:51:36 +02:00
Lucas Garron
0f609e7054 build_tools/update_translations.fish --no-mo 2025-09-05 21:07:13 -07:00
Lucas Garron
b77ea98ee0 npm install completions: add --save-peer flag
It's unclear if there is a short flag, since `npm install --help` does not list all short flags.
2025-09-05 18:46:53 -07:00
Johannes Altmanninger
1db0ff9f77 Allow overriding __fish_update_cwd_osc to work around terminal bugs
See #11777

While at it, pull in the TERM=dumb check from master.

(cherry picked from commit 898cc3242b)
2025-09-05 09:39:57 +02:00
Johannes Altmanninger
898cc3242b Allow overriding __fish_update_cwd_osc to work around terminal bugs
See #11777
2025-09-05 09:38:25 +02:00
Nathan Chancellor
66940e8369 completions/systemctl: Handle --boot-loader-entry and --boot-loader-menu
systemd 242 added two new options to halt, poweroff, and reboot:

  --boot-loader-entry: Reboot to a specific boot loader entry
  --boot-loader-menu: Reboot into boot loader menu with specified
                      timeout

Add these to the systemctl completion so that it is easy to
interactively select available entries.

Signed-off-by: Nathan Chancellor <nathan@kernel.org>
2025-09-04 19:25:56 -07:00
Johannes Altmanninger
f98bf3d520 functions/man: use "command man", skipping functions
As reported in
https://github.com/fish-shell/fish-shell/issues/11767#issuecomment-3240198608,
the new "man" function uses "rm" which is sometimes overidden to do
"rm -i".

Same as d3dd9400e3 (Make sure the rm command and not a wrapper
function that could change its behaviour is used. 2006-12-12)

While at it, make sure that all users of __fish_mktemp_relative
1. return if mktemp fails
2. actually clean up their temporary directory -- except for help.fish
   which spawns an asynchronous browser window.
2025-08-31 17:33:24 +02:00
Johannes Altmanninger
90862c5c57 edit_command_buffer: remove dead code 2025-08-31 17:29:07 +02:00
Johannes Altmanninger
4abdc8716b share/functions/__fish_mktemp_relative: adopt argparse -u 2025-08-29 22:23:53 +02:00
Johannes Altmanninger
4ba070645d Merge pull request #11763 2025-08-29 22:23:53 +02:00
Johannes Altmanninger
4a12d57711 Merge pull request #11698 2025-08-29 22:23:52 +02:00
Johannes Altmanninger
8741e201de Install fish-terminal-compatibility man page
Not sure about whether "man fish-terminal-compatibility"; it's not
really meant for end-users, but it also doesn't hurt raise awareness
of the existence of this doc.

Either way, we should be consistent with embedded builds, where this
works since the parent commit.
2025-08-29 22:23:52 +02:00
Johannes Altmanninger
c3c3a9518c functions/man: fix for embedded fish-* man pages
"man abbr" works in embed-data builds,
but "man fish-faq" doesn't.

This is because it delegates to

	__fish_print_help fish-faq

which skips all lines until the NAME section:

	contains -- $name NAME; and set have_name 1

but the NAME section doesn't exist for this man pages, it only exists
for docs from doc_src/cmds/*.rst.

Let's use the "man" utility instead; this is also what the user
asked for.  Unfortunately we can't use "status get-file | man -l -"
because that's not supported on BSD man.  Note that man displays the
basename of the file, so make sure it looks good.
2025-08-29 22:23:52 +02:00
Johannes Altmanninger
0ebd41eb9f Extract function for calling mktemp
BSD mktemp doesn't support GNU mktemp's -t or --tmpdir option, so when
we want a named temporary file, we need to compute ${TMPDIR:-/tmp}
ourselves, see 5accc7c6c5 (Fix funced's tmpfile generation on OSX,
2016-05-23).

While at it, use template like "fish.XXXXXX"; seems like a good idea?

Take care to have edit_command_buffer use a pretty filename.
2025-08-29 22:23:27 +02:00
Johannes Altmanninger
35733a0f8d functions/man: allow "man !"
Analogous to the "! -h" code path.
2025-08-29 22:00:25 +02:00
Johannes Altmanninger
c1f6e0c03e CONTRIBUTING: mention sourcehut mailing list 2025-08-29 21:11:24 +02:00
Johannes Altmanninger
c36e7967e2 CONTRIBUTING: recommend build_tools/check.sh 2025-08-29 20:32:58 +02:00
Johannes Altmanninger
7f91e029fb CONTRIBUTING: remove mention of Coverity
Seems stale?
2025-08-29 20:32:58 +02:00
Johannes Altmanninger
ca131f7440 README: mention Python version also in build dependencies 2025-08-29 20:32:58 +02:00
Johannes Altmanninger
df3fc48a21 README: mention /bin/sh dependency
As reported in
https://matrix.to/#/!YLTeaulxSDauOOxBoR:matrix.org/$39FQId4CHJ6yT8B4S4smD4MDbxp4pT8Eio-cGP0KoEU
we currently require "sh -c" for some background tasks.  Document this.
2025-08-29 20:32:58 +02:00
Johannes Altmanninger
9258275fe6 config_paths: fix compiled-in locale dir for installed, non-embed builds
Commit bf65b9e3a7 (Change `gettext` paths to be relocatable (#11195),
2025-03-30) broke the locale path.

Commit c3740b85be (config_paths: fix compiled-in locale dir,
2025-06-12) fixed what it calls "case 4", but "case 2" is also
affected; fix that. Before/after:

	$ ~/.local/opt/fish/bin/fish -d config
	paths.locale: /home/johannes/.local/opt/fish/share/fish/locale
	$ ~/.local/opt/fish/bin/fish -d config
	paths.locale: /home/johannes/.local/opt/fish/share/locale

See https://github.com/fish-shell/fish-shell/issues/11683#issuecomment-3218190662

(cherry picked from commit 21a07f08a3)
2025-08-29 19:29:03 +02:00
Isaac Oscar Gariano
6149ac4e40 Added a -C/--center option to string pad.
The --center option does exactly what you'd expect. When a
perfectly centred result is not possible, this adds extra padding to
the left. If the --right option is also given, the extra padding is
added to the right.
2025-08-30 02:57:01 +10:00
Isaac Oscar Gariano
d82442991b Fix argparse documentation to make it clear that -n takes an argument. 2025-08-30 01:55:56 +10:00
Isaac Oscar Gariano
944cfd181e Added a -v/--validate option to fish_opt
This new flag causes fish_opt to generrate an option spec with !
(e.g. "fish_opt -s s -rv some code" will output "s=!some code").

Such validation scripts are not particular useful (they are highly limited as
they cannot access the values for other options, and must be quoted
appropriately so they can be passed to argparse). I merely added the option to
fish_opt so that it can now generate any valid option spec.
2025-08-30 01:55:56 +10:00
Isaac Oscar Gariano
007edac145 Make argparse reject supplying a validator for boolean flags
Specifically, this commit simply makes argparse issue an error if you use the !
syntax to define a validation script on an option that does not take any
arguments. For example, "argparse foo!exit -- --foo" is now an error. This was
previously accepted, despite that fact that the code after ! would never be
executed (the ! code is only executed when an option is given a value).

Alternatively, ! validation scripts could be made to execute even when no value
was provided, but this break existing code that uses them with flags that take
optional values.
2025-08-30 01:55:56 +10:00
Isaac Oscar Gariano
c403822fac Modified argparse to support one character long only options.
This fixes an issue noticed in the previous commit (the made the -s/--short
option optional to fish_opt): it was impossible to define a single character
long flag, unless you also provided a single-character short flag equivalent.

This commit works by allowing an option spec to start with a '/', treating the
subsequent alpha-numeric characters as a long flag name.

In detail, consider the following:
- s defines a -s short flag
- ss defines an --ss long flag
- /ss (new) also defines a --ss long flag
- s/s defines a -s short flag and an --s long flag
- s-s defines a --s long flag (if there's already an -s short flag, you'd have
    to change the first s, e.g. S-s)
- /s (new) defines a --s long flag
- s/ is an error (a long flag name must follow the /)

Note that without using --strict-longopts, a long flag --s can always be
abbreviated as -s, provided that -s isn't defined as a separate short flag.

This 'issue' fixed by this commit is relatively trivial, however it does allow
simplifying the documentation for fish_opt (since it no longer needs to mention
the restriction). In particular, this commit makes the --long-only flag to
fish_opt completely unnecessary (but it is kept for backwards compatibility).
2025-08-30 01:55:56 +10:00
Isaac Oscar Gariano
663430a925 Added support to fish_opt for defining a long flag with no short flag.
Specifically, this now makes the -s/--short option to fish_opt optional when the
-l/--long option is given. This commit does not modify argparse, as it already
supports defining long flags without a corresponding short flag, however
fish_opt would never take advantage of this feature.

Note that due to a limitation in argparse, fish_opt will give an error if you
try to define a one-character --long flag without also providing a --short
option.

For backwards compatibility, the --long-only flag is still included with
fish_opt, and when used with -s/--short, will behave as before (the short flag
is still defined, but argparse will fail if it is actually used by the parsed
arguments, moreover the _flag_ option variables will not be defined). This can
however be used to define a one character long flag.
2025-08-30 01:55:56 +10:00
Isaac Oscar Gariano
4db61ee117 Added argparse support for arguments with multiple optional values.
This commit fixes #8432 by adding put =* in an option spec to indicate that the
option takes an optional value, where subsequent uses of the option accumulate
the value (so the parsing behaviour is like =?, but the _flag_ variables are
appended to like =+). If the option didn't have a value, it appends an empty
string. As an example,. long=* -- --long=1 --long will execute
set -l _flag_long 1 '' (i.e. count $_flag_long is 2), whereas with =? instead,
you'd get set -l _flag_long (i.e. count $_flag_long is 0).

As a use case, I'm aware of git clone which has a
--recurse-submodules=[<pathspec>]: if you use it without a value, it operates on
all submodules, with a value, it operates on the given submodule.

The fish_opt function will generate an =* option spec when given both the
--optional-val and --multiple-vals options (previously, doing so was an error).
fish_opt now also accepts -m as an abbreviation for --multiple-vals, to go with
the pre-existing -o and -r abbreviations for --optional-val and --required-val.
2025-08-30 01:55:56 +10:00
Isaac Oscar Gariano
9d56cdbcbc Added a -U/--unknown-arguments option to argparse
The new -U/--unknown-arguments option takes either 'optional', 'required', or
'none', indicating how many arguments unknown options are assumed to take.
The default is optional, the same behaviour as before this commit, despite
most options in practice taking not taking any arguments. Using
--unknown-arguments=required and --unknown-arguments=none (but not
--unknown-arguments=optional) can give you parse errors if, respectively,
an unknown option has no argument (because it the option is at the end of the
argument list), or is given an argument (with the `--flag=<value> syntax).
See doc_src/cmds/argparse.rst for more details (specifically, the descritpion
of the --unknown-arguments flag and the example at the end
of the examples section).

As a convenience, -U/--unknown-arguments implies -u/--move-unknown.
However you can use it the deprecated -i/--ignore-unknown if you really want to.
2025-08-30 01:55:56 +10:00
Isaac Oscar Gariano
8ae685cf27 Refactored argparse code by removing ArgCardinality type.
This just uses an ArgType and 'accumulate_args' bool in place of the old
ArgCardinality. Curently only one of the three kinds of an ArgType can
have both a true and false accumulate_args. But this will be extended to
two of three in a future commit.
2025-08-30 01:55:56 +10:00
Isaac Oscar Gariano
24eeed65a2 Added an -S/--strict-longopts option to argparse.
This flag disables a very surprising and confusing feature I found in the code
of wgetopt.rs: the ability to abbreviate the names of long options and the
ability to parse long options with a single "-". This commit addresses #7341,
but unlike pull request #11220, it does so in a backwards compatible way: one
must use the new -S/--strict-longotps flag to disable the old legacy behaviour.

Unlike pull request #11220 however, this flag only applies to ``argparse``,
and not to any builtins used by fish.

Note that forcing the flag -S/--strict-longotps on (i.e. in  src/wgetopt.rs,
replacing both uses of `self.strict_long_opts` with `true`), does not cause any
of the current test cases to fail. However, third-party fish scripts may be
depending on the current behaviour.
2025-08-30 01:55:56 +10:00
Isaac Oscar Gariano
70dca1e136 Give a more helpful error when a boolean option has an argument.
This fixes an issue very similar to #6483,
For example, fish --login=root used to print this:
    fish: --login=root: unknown option
But `--login` is a valid option to fish.
So the above now prints:
    fish: --login=root: option does not take an argument

This is done by modifying WGetOpter so that it returns a ';' if it encountered
an option with an argument, but the option was not declared as taking an option
(this is only possible with the --long=value or legacy -long=value syntax).
All uses of WGetOpter (except echo, fish_indent, and some tests) are modified to
then print an appropriate error message when ';' is returned.
echo doesn't have any long options, so it will panic if gets a ';'.
fish_indent doesn't print any error messages for invalid options anyway, and I
wasn't sure if this was intentional or not.

Moreover, WGetOpter now always returns a ':' for options that are missing an
argument. And you can no longer prefix a your option spec with a ':' to enable
this (since every use of WGetOpter was doing this anyway, except
wgetopt::test_exchange and tests::wgetopt::test_wgetopt).
2025-08-30 01:55:56 +10:00
Isaac Oscar Gariano
c94eddaf4b Replaced all uses of argparse -i with argparse -u
This removes the functions/completions that were using the deprecated
--ignore-unknown option and replaces it with the new --move-unknown.

Although some of the code is now more verbose, it has improved the
functionality of two completions:
    the iwctl completion will now skip over options when detecting
        what subcommand is being used
    the ninja completion wil now handle -- correctly: if the completion
        internally invokes ninja, it will correctly interpret the users
        arguments as being arguments if they start with a - and occur
        after the --.
2025-08-30 01:10:03 +10:00
Isaac Oscar Gariano
51d16f017d Added a -u/--move-unknown option to argparse.
--move-unknown is like --ignore-unknown, but unknown options are instead moved
from $argv to $argv_opts, just like known ones. This allows unambiguously
parsing non-option arguments to other commands. For example if $argv contains
`--opt -- --file`, and we execute `argparse --move-unknown -- $argv`, we can
then call `cmd $argv_opts -- --another-file $argv`, which will correctly
interpret `--opt` as an option, but `--file` and `--some-file` as an argument.
This makes `--move-unknown` a better alternative to `--ignore-unknown`, so the
latter has been marked as deprecated, but kept for backwards compatibility.
2025-08-29 23:10:02 +10:00
Isaac Oscar Gariano
5a7e5dc743 Make argparse separate known and unknown options that occur in a group together.
For example, argparse --ignore-unknown h -- -ho will now set set $argv to -o and
$argv_opts to -h (i.e. -ho is split into -h and -o). Previously, it would set
$argv to -ho, and $argv_opts to empty. With this change, the "Limitations"
section of argparse's man page has been removed, and the examples merged into
the description of the -i/--ignore-unknown option. (Note: there was another
'limitation' mentioned in the 'limitations' section: that everything occuring
after an unknown option in a group was considered an argument to an option; the
documentation has been reworded to make it clear that this is intended
behaviour, as unknown options are always treated as taking optional arguments,
and modifying that behaviour would be a breaking change and not a bug fix).
2025-08-29 23:10:02 +10:00
Isaac Oscar Gariano
f780b01ac9 Added way to tell argparse to delete an option from $argv_opts.
The intention is that if you want to parse some of your options verbatim to
another command, but you want to modfy other options (e.g. change their value,
convert them to other options, or delete them entirely), you mark the options
you want to modify with an &, and argparse will not add them to argv_opts. You
can then call the other command with argv_opts together with any new/modified
options, ensuring that the other command doesn't set the pre-modified options.
As with other known options, & options will be removed from $argv, and have
their $_flag_ variables set.

The `&` goes at the end of the option spec, or if the option spec contains a
validation script, immediately before the `!`. There is also now a -d/--delete
flag to fish_opt that will generate such an option spec.

See the changes in doc_src/cmds/argparse.rst for more details and an example use
case.
2025-08-29 23:10:02 +10:00
Isaac Oscar Gariano
ddcb1813a7 Clarified & fixed documentation for fish_opt options.
The previous fish_opt synopsis was hard to parse, and was incorrect:
- it indicated that -s is optional
- it indicated that only one option could be provided
- it indicated that every option took a value
2025-08-29 23:10:02 +10:00
Isaac Oscar Gariano
e62abc460d Make argparse save parsed options in $argv_opts. (Fixes #6466)
Specifically, every argument (other than the first --, if any) that argparse
doesn't add to $argv is now added to a new local variable $argv_opts. This
allows you to make wrapper commands that modify non-option arguments, and then
forwards all arguments to another command. See the new example at the end of
doc_src/cmds/argparse.rst for a use case for this new variable.
2025-08-29 23:10:02 +10:00
Isaac Oscar Gariano
e6b4f0a696 Stopped argparse tests from leaking local variables.
By wrapping the various argparse tests in begin ... end blocs, it makes it much
easier to debug test failures and add new tests. In particular, each block is
independent, and shouldn't affect any subsequent tests. There's also now a check
at the end of the test file to ensure that the tests are no longer leaking local
variables.
2025-08-29 23:10:02 +10:00
Johannes Altmanninger
c0c9245d99 completions/java_home: fix version completion due to change of osascript default output
Apparently the last expression outputs to stdout.

Closes #11743
2025-08-27 10:01:51 +02:00
Isaac Oscar Gariano
f843d5bd34 Disable editor hard wrapping for .rst and .md files.
The .md and .rst files allready present do not hard wrap lines (the
style seems to be one line per paragraph, which could be a few hundred
characters long). So this makes those files have no line length limit,
instead of 100.
2025-08-27 10:01:51 +02:00
Johannes Altmanninger
6900b89c82 Add mark-prompt feature flag
So far, terminals that fail to parse OSC sequences are the only reason
for wanting to turn off OSC 133.  Let's allow to work around it by
adding a feature flag (which is implied to be temporary).

To use it, run this once, and restart fish:

    set -Ua fish_features no-mark-prompt

Tested with

    fish -i | string escape | grep 133 &&
    ! fish_features=no-mark-prompt fish -i | string escape | grep 133

See #11749
Also #11609
2025-08-27 09:17:35 +02:00
Johannes Altmanninger
4a9fd1bbbb completions/loginctl: accept alphabetic session names
systemd's session_id_valid accepts [a-zA-Z0-9], so allowing only
numbers is wrong.

Fixes #11754

While at it, correct the description; instead of
showing the leader PID, show the seat, which is probably
2025-08-27 08:34:16 +02:00
Daniel Rainer
7b8efe3105 Remove Optional annotation
The is inconsistent with the type annotation of `TestPass.duration_ms`.
The function is only called with `duration_ms` as an int, so there is no
need to declare it `Optional`.
2025-08-26 20:54:02 +08:00
Daniel Rainer
425d487de9 Remove redundant clones 2025-08-25 10:24:03 +02:00
Johannes Altmanninger
21a07f08a3 config_paths: fix compiled-in locale dir for installed, non-embed builds
Commit bf65b9e3a7 (Change `gettext` paths to be relocatable (#11195),
2025-03-30) broke the locale path.

Commit c3740b85be (config_paths: fix compiled-in locale dir,
2025-06-12) fixed what it calls "case 4", but "case 2" is also
affected; fix that. Before/after:

	$ ~/.local/opt/fish/bin/fish -d config
	paths.locale: /home/johannes/.local/opt/fish/share/fish/locale
	$ ~/.local/opt/fish/bin/fish -d config
	paths.locale: /home/johannes/.local/opt/fish/share/locale

Fixes https://github.com/fish-shell/fish-shell/issues/11683#issuecomment-3218190662
2025-08-25 10:11:01 +02:00
David Adam
67e8657109 debian packaging: don't remove Cargo.toml.orig files
Newer versions of cargo include the Cargo.toml.orig file when vendoring,
but dh_clean removes those by default. Try to disable this to fix the
package builds again.
2025-08-23 19:40:53 +08:00
Daniel Rainer
e96300b08e Build man pages in separate crate
Building man pages takes significant time due to Sphinx running for several
seconds, even when no updates are required. Previously, we added custom logic to
avoid calling `sphinx-build` if the inputs to `sphinx-build` had not changed
since a cached timestamp.

By moving this into its own crate, we can tell cargo to rebuild when the input
files changed and unrelated changes will have no effect on this crate. This
allows us to get rid of the custom code for tracking whether to recompile, while
keeping the effect of only calling `sphinx-build` when appropriate.

In order to avoid code duplication, a new `build-helper` crate is added,
which contains some functionality for use in `build.rs`.

Closes #11737
2025-08-22 09:14:56 +02:00
Daniel Rainer
32e5fa0c03 Stop depending on fish_indent for man pages
This allows building man pages without having `fish_indent` available, which is
useful because building man pages can happen during compilation of the fish
binaries, including `fish_indent`, resulting in an annoying cyclic dependency.

This change does not affect the generated man pages, at least with the current
config.
2025-08-22 09:12:10 +02:00
Daniel Rainer
e895f96f8a Do not rely on fish_indent for version in Sphinx
Depending on `fish_indent` when building docs is problematic because the docs
might get built before `fish_indent` is available. Furthermore, a version of
`fish_indent` which does not correspond to the current build might be used,
which would result in incorrect version information.

Use the `git_version_gen.sh` script instead to ensure up-to-date version
information without depending on build output.
2025-08-22 09:12:10 +02:00
Johannes Altmanninger
92f7063acb Use version=0.0.0 for unpublished crates
As discussed in #11737
2025-08-22 09:12:10 +02:00
Daniel Rainer
cd71801360 Use workspace dependencies exclusively
This allows us to track all dependencies in a single place and
automatically avoids using different versions of the same dependency in
different crates.

Sort dependencies alphabetically.

Closes #11751
2025-08-22 09:12:10 +02:00
Daniel Rainer
24898c61af Sync dependency versions of crates
This is done to allow merging all dependencies into workspace
dependencies.
2025-08-21 21:39:35 +02:00
Johannes Altmanninger
62b7c81052 Merge pull request #11742 2025-08-21 20:40:33 +02:00
Daniel Rainer
d32ce89889 Remove unnecessary rebuild paths
Cargo tracks normal Rust dependencies and automatically figures out if changes
to Rust source files, `Cargo.{toml,lock}`, and `build.rs` necessitate a rebuild,
and if so of what. In some cases these checks are smarter than just comparing
file modification times, so not specifying such paths explicitly can reduce the
amount of rebuilding which happens, without skipping necessary rebuilding.

ja: this reverts b2aaf1db52 (Rebuild if src changed, 2025-03-28) and
460b93a (Rebuild on changes relevant to build artifacts, 2025-03-30)
which tried to fix #11332 ("moving Git HEAD does not invalidate cargo
build results").  But that expectation is overbearing.  It's better
to only rebuild if something else of relevance to the build output
has changed.

Closes #11736
2025-08-21 20:40:33 +02:00
Henrik Hørlück Berg
16119377c3 Document --allow-empty, specify -r is only useful w/ -m
As noted in #11744
2025-08-21 00:06:24 +02:00
Kevin F. Konrad
5a45fec730 add completions for stackit CLI 2025-08-20 11:54:57 +02:00
Johannes Altmanninger
d26fd90efd Merge pull request #11741, closes #11741 2025-08-20 10:16:19 +02:00
Daniel Rainer
1c654f23af Put local dependencies in crates directory
With an increasing number of local dependencies, the repo root is getting
somewhat bloated. This commit moves the two current local dependencies into the
newly created `crates` directory, with the intention of using it for all future
local dependencies as well.

Some dependencies which are introduced by currently in-progress pull requests
will need modifications in order for relative paths to work correctly.
2025-08-20 10:16:19 +02:00
Johannes Altmanninger
b828822385 Merge pull request #11695 2025-08-20 10:14:34 +02:00
Johannes Altmanninger
e795ed4061 Merge pull request #11738 2025-08-20 10:14:27 +02:00
Johannes Altmanninger
3363d55ec4 staticbuild.yml: break up long line 2025-08-20 10:14:08 +02:00
Johannes Altmanninger
f8bfb7cb6b editorconfig: commit message line length is 72, not 80
This is more standard. Not sure how many people use editorconfig though.
2025-08-20 10:14:08 +02:00
Daniel Rainer
ce7733c75f Remove unnecessary parentheses
This was spotted by a lint on nightly Rust.
2025-08-19 00:26:21 +02:00
Johannes Altmanninger
feaec20de6 Run update_translations.fish 2025-08-18 14:10:46 +02:00
Daniel Rainer
514d34cc52 Add cargo feature for enabling gettext extraction
This allows having the proc macro crate as an optional dependency and speeds up
compilation in situations where `FISH_GETTEXT_EXTRACTION_FILE` changes, such as
the `build_tools/check.sh` script. Because we don't need to recompile on changes
to the environment variable when the feature is disabled, cargo can reuse
earlier compilation results instead of recompiling everything.
This speeds up the compilation work in `build_tools/check.sh` when no changes
were made which necessitate recompilation.
For such runs of `build_tools/check.sh`, these changes reduce the runtime on my
system by about 10 seconds, from 70 to 60, approximately.
The difference comes from the following two commands recompiling code without
the changes in this commit, but not with them:
- `cargo test --doc --workspace`
- `cargo doc --workspace`
2025-08-18 10:37:59 +02:00
Daniel Rainer
367696b346 Remove unnecessary into_iter call
Starting with Rust 1.90, this `into_iter` call triggers the
`clippy::useless_conversion` lint.
2025-08-17 20:20:09 -07:00
Daniel Rainer
70a9327858 Do not unwrap after checking with is_err
Doing so results in the `clippy::unnecessary_unwrap` lint starting with Rust
1.90 (in beta at the moment).
2025-08-17 20:20:09 -07:00
idealseal
a766c44a18 feat(comp): update networkctl completions 2025-08-17 20:17:00 -07:00
Peter Ammon
13d9aa0002 Changelog PR 11729 2025-08-16 11:22:45 -07:00
Bryce Berger
9273f352a0 allow psub --fifo --suffix ... 2025-08-15 15:02:44 -04:00
Johannes Altmanninger
79135c6c82 Merge pull request #11713, closes #11713 2025-08-15 18:11:07 +02:00
Daniel Rainer
5de43a4e86 Call fsync after appending to history file
This is an attempt to eliminate history file corruption as described in
https://github.com/fish-shell/fish-shell/issues/10300.
In particular, issues raised in
https://github.com/fish-shell/fish-shell/issues/10300#issuecomment-2567063654
can benefit from such synchronization.
2025-08-15 18:10:20 +02:00
Daniel Rainer
188282a27b Don't redundantly unlink() temporary file after renaming
There is an unlikely issue if two shells are concurrently rewriting the
history file:
- fish A runs rename("fish_history.DEADBEEF") (rewriting a history file with)
- fish B starts rewriting the history file; since "fish_history.DEADBEEF" no longer exists, it can in theory use that filename
- fish A runs wunlink("fish_history.DEADBEEF"), destroying fish B's work

Fix this by not calling wunlink() iff we successfully rename it.

[ja: add commit message and fix "!do_save" case]
2025-08-15 17:50:04 +02:00
Johannes Altmanninger
f59e5884bf Merge pull request #11714
Closes #11714
2025-08-15 17:37:34 +02:00
Johannes Altmanninger
51dcadf7c6 completions/makepkg: remove stale --pkg option
Closes #11724
2025-08-15 17:24:18 +02:00
Daniel Rainer
ba2500211b Call fsync and close tmpfile before rename
This is another attempt at eliminating file corruption, primarily in connection
to the history file, as observed in
https://github.com/fish-shell/fish-shell/issues/10300.

Calling `fsync` and closing the tmpfile should ensure that when the renaming
happens, the file content and metadata are already in persistent storage.

Some background can be found in the following links, which were pointed out in
https://github.com/fish-shell/fish-shell/issues/10300#issuecomment-2567063654:
https://www.comp.nus.edu.sg/~lijl/papers/ferrite-asplos16.pdf
https://archive.kernel.org/oldwiki/btrfs.wiki.kernel.org/index.php/FAQ.html#What_are_the_crash_guarantees_of_overwrite-by-rename.3F
2025-08-15 17:24:02 +02:00
Johannes Altmanninger
491ffb356b Merge pull request #11727 2025-08-15 16:48:34 +02:00
Daniel Rainer
b0ae11e769 Write only once when appending items
Building a buffer in advance and writing it once all items are serialized into
the buffer makes for simpler code, makes it easier to ensure that
`self.first_unwritten_new_item_index` is only updated if writing succeeded, and
it actually matches the previous behavior of the code in most realistic cases,
since previously there was only more than one `write_all` call if the serialized
items took up more than `HISTORY_OUTPUT_BUFFER_SIZE` bytes (64 * 1024), which
seems unlikely to occur during normal use, where mostly just a single item will
be appended.
2025-08-15 16:26:52 +02:00
Daniel Rainer
20e268a75c Clear file state earlier
This should not result in behavioral changes in the code, but it eliminates some
redundant variables and is a step in refactoring the function such that early
returns via `?` become sound.

Remove the `drop` since the lock will be dropped at this point anyway, there is
no need to be explicit about it.
2025-08-15 16:26:03 +02:00
Daniel Rainer
9c4c28da9d Use updated file id
This restores behavior from before f438e80f9b.
The file id changes when data is written to the file, so it needs to be updated
with data obtained after the updates to the file are completed.
2025-08-15 16:26:03 +02:00
Johannes Altmanninger
8219dd8af6 Merge pull request #11715 2025-08-15 16:19:03 +02:00
Johannes Altmanninger
781791c00c Revert "Add rust-toolchain.toml"
By default, we make every rustup user use our pinned version.  This might
not be ideal at this point, for a few reasons:
1. we don't have automatic Rust updates yet (see the parent commit),
   so this might unnecessarily install an old version. As a contributor,
   this feels irritating (newer versions are usually strictly better).
2. it will use more bandwidth and perhaps other resources during "git-bisect"
   scenarios
3. somehow rustup will download things redundantly; it will download "1.89.0"
   and "stable" even if they are identical. The user will need to clean
   those up at some point, even if they didn't add them explicitly.

See also
https://github.com/fish-shell/fish-shell/pull/11712#issuecomment-3165388330

Part of the motivation for rust-toolchain.toml is probably the regular
(every 6 weeks) failures due to the update check, but that failure has been
removed in the parent commit.

The other motivation ("fix the issue of local compiles running into lint
warnings from newer compilers") is a fair point but I think we should rather
fix warnings quickly.

Let's remove rust-toolchain.toml again until we have more agreement on what
we should do.

This reverts commits
* f806d35af8 (Ignore rust-toolchain.toml in CI, 2025-08-07)
* 9714b98262 (Explicitly use fully qualified rust version numbers, 2025-08-07)
* 921aaa0786 (Add rust-toolchain.toml, 2025-08-07)

Closes #11718
2025-08-15 16:10:18 +02:00
Johannes Altmanninger
d93fc5eded Revert "build_tools/check.sh: check that stable rust is up-to-date"
As reported in #11711 and #11712, the update-checks make check.sh automatically
fail every 6 weeks, so it pressures people into updating Rust, and (what's
worse), updating fish's pinned Rust version, even when that's not relevant
to their intent (which is to run `clippy -Dwarnings` and all other checks).

The update-checks were added as a "temporary" solution to make sure that
our pinned version doesn't lag too far behind stable, which gives us an
opportunity to fix new warnings before most contributors see them.

As suggested in #11584, reasonable solutions might be either of:
1. stop pinning stable Rust and rely on beta-nightlies to fix CI failures early
2. use renovatebot or similar to automate Rust updates

Until then, remove the update check to reduce friction.
I'll still run it on my machine.

This reverts commit 6d061daa91.
2025-08-15 15:08:48 +02:00
Daniel Rainer
b2dfb3fd6e Add completions for single-letter cargo aliases 2025-08-15 03:09:28 +02:00
Daniel Rainer
55122b127c Wrap renaming code in function
These changes are not intended to change any behavior. They are done to
facilitate closing the tmpfile before renaming, which is required for
correctness on some filesystems (at least btrfs). Using a `ScopeGuard` which
unlinks when the file is closed/dropped does not work in this context, so the
relevant code is wrapped in a function and the tmpfile is unlinked after the
function returns.
2025-08-12 19:50:53 +02:00
brennenputh
0adcbfa2ac Resolve review comments 2025-08-11 20:32:16 -04:00
Johannes Altmanninger
51fd00c98f Merge pull request #11717 2025-08-10 18:59:30 +02:00
Johannes Altmanninger
c123c4e866 Merge pull request #11691 2025-08-10 18:40:01 +02:00
Johannes Altmanninger
9003835452 Merge pull request #11701 2025-08-10 17:12:50 +02:00
Trevor Bender
cf044038e0 fix typo in completions.rst 2025-08-10 07:49:10 -04:00
brennenputh
6561a1d6ba Add distrobox completion script 2025-08-08 20:48:20 -04:00
Xiretza
894d4ecc53 Update to Rust 1.89, address newly added lints 2025-08-07 21:48:17 +00:00
Xiretza
f806d35af8 Ignore rust-toolchain.toml in CI
We set a specific default toolchain with dtolnay/rust-toolchain, we don't want
it to be overridden by the config intended for devs.
2025-08-07 21:47:36 +00:00
Xiretza
9714b98262 Explicitly use fully qualified rust version numbers
The action expands these internally, but then rust-toolchain.toml is interpreted
literally, and 1.88 is technically a different toolchain from 1.88.0.
2025-08-07 21:28:34 +00:00
Xiretza
921aaa0786 Add rust-toolchain.toml
This ensures that, by default, developers use the toolchain that is also tested
in CI, avoiding spurious warnings from lints added in new compiler versions.
2025-08-07 21:13:32 +00:00
Saeed M Rad
45bb8f535b funced: pretend copied functions are defined interactively
After #9542, the format for `functions -Dv` was changed for copied
functions.

```diff
-stdin
-n/a
+[path to copy location]
+[path to original definition]
 [and a few more lines]
```

Some components were (and perhaps are) still expecting the old format,
however. After a search, it looks like `funced` and `fish_config` are
the only two functions using `functions -D` and `functions -Dv` in this
repo (none are using `type -p`).

As noted in issue #11614, `funced` currently edits the file which
copies the given copied function. Another option was to make `funced`
edit the file which originally defined the function. Since the copied
function would not have been updated either way, I modified `funced` so
it would pretend that the copied function was defined interactively,
like it was before.

I did not modify `fish_config`, since it was only used for preset
prompts in the web config, none of which used `functions --copy`.
(Moreover, I believe it would have behaved correctly, since the preset
would not have had to define the function, only copy it.)

Fixes issue #11614
2025-08-07 10:19:42 +00:00
Bacal Mesfin
fa68770c16 Fix missed bottom right artifact 2025-08-04 13:01:19 -04:00
Bacal Mesfin
4b736fd92b Fix fish.png artifacts
The fish.png doc image has grey pixel artifacts in the
top-left, top-right, and bottom-right corners.

This patch takes the original image from 3a5b096
removes the artifacts, and compresses it with squoosh
to a similar file size.
2025-08-03 07:06:42 -04:00
Johannes Altmanninger
54e8ad7e90 Merge pull request #11623 2025-08-03 08:00:18 +02:00
Kid
944da7dba9 Fix copy paste error 2025-08-02 19:39:54 +08:00
Kid
c9945f3439 Use __fish_cache_sourced_completions 2025-08-02 18:05:46 +08:00
Kid
e8b2767dcc Add completion for pnpm 2025-08-02 18:00:23 +08:00
Johannes Altmanninger
5c216f48a3 Attempt to fix Opensuse Leap 15.6 nightlies
This has been failing because it uses Python 3.6.  Until we have figured
out when we can roll platform off (#11679), let's try to fix this.  Untested.
2025-08-02 11:07:22 +02:00
Johannes Altmanninger
320b8235ed Update README to reflect regained Cygwin/Msys2 support 2025-08-02 11:07:22 +02:00
Johannes Altmanninger
9c0086b7af Backport default alt-delete binding
This is standard on macOS and in chrome/firefox.

On master, this was sneakily added in
2bb5cbc959 (Default bindings for token movements v2, 2025-03-04)
and before that in
6af96a81a8 (Default bindings for token movement commands, 2024-10-05)

Ref: https://lobste.rs/s/ndlwoh/wizard_his_shell#c_qvhnvd
2025-08-02 09:53:22 +02:00
Johannes Altmanninger
f218cf2f38 Merge pull request #11686 2025-08-02 09:19:48 +02:00
Johannes Altmanninger
8158c227c3 Merge pull request #11672 2025-08-02 09:19:48 +02:00
Johannes Altmanninger
2549334bae Merge pull request #11693 2025-08-02 09:19:48 +02:00
ndrew222
ec60bf1898 added completions for glow 2025-07-30 13:25:12 +08:00
JJ
17e0f3d96f Add example of string manipulation to prompt_pwd 2025-07-28 19:55:18 -07:00
Branch Vincent
b4b0fc08da completions: add container 2025-07-26 12:13:05 -07:00
Johannes Altmanninger
e200abe39c __fish_seen_subcommand_from: fix regression causing false negatives given multiple arguments
Fixes 2bfa7db7bc (Restructure __fish_seen_subcommand_from, 2024-07-07)
Fixes #11685

(cherry picked from commit 4412164fd4)
2025-07-26 20:36:53 +02:00
Johannes Altmanninger
6d061daa91 build_tools/check.sh: check that stable rust is up-to-date
As suggested in
https://github.com/fish-shell/fish-shell/discussions/11584#discussioncomment-13674983
In future, we should probably make updates automatic again.
2025-07-26 20:36:03 +02:00
Johannes Altmanninger
4412164fd4 __fish_seen_subcommand_from: fix regression causing false negatives given multiple arguments
Fixes 2bfa7db7bc (Restructure __fish_seen_subcommand_from, 2024-07-07)
Fixes #11685
2025-07-26 17:08:15 +02:00
Johannes Altmanninger
0c8f1f4220 Fix async-signal safety in SIGTERM handler
Cherry-picked from
- 941701da3d (Restore some async-signal discipline to SIGTERM, 2025-06-15)
- 81d45caa76e (Restore terminal state on SIGTERM again, 2025-06-21)

Also, be more careful in terminal_protocols_disable_ifn about accessing
reader_current_data(), as pointed out in 65a4cb5245 (Revert "Restore terminal
state on SIGTERM again", 2025-07-19).

See #11597
2025-07-26 13:09:18 +02:00
Johannes Altmanninger
e593da1c2e Increase timeout when reading escape sequences inside paste/kitty kbd
Historically, fish has treated input bytes [0x1b, 'b'] as alt-b (rather than
"escape,b") if the second byte arrives within 30ms of the first.

Since we made builtin bind match key events instead of raw byte sequences,
we have another place where we do similar disambiguation: when we read keys
such as alt-left ("\e[1;3D"), we only consider bytes to be part of this
sequence if stdin is immediately readable (actually "readable after a 1ms
timeout" since e1be842 (Work around torn byte sequences in qemu kbd input
with 1ms timeout, 2025-03-04)).

This is technically wrong but has worked in practice (for Kakoune etc.).

Issue #11668 reports two issues on some Windows terminals feeding a remote
fish shell:
- the "bracketed paste finished" sequence may be split into multiple packets,
  which causes a delay of > 1ms between individual bytes being readable.
- AutoHotKey scripts simulating seven "left" keys result in sequence tearing
  as well.

Try to fix the paste case by increasing the timeout when parsing escape
sequences.

Also increase the timeout for terminals that support the kitty keyboard
protocol.  The user should only notice this new delay after pressing one of
escape,O, escape,P, escape,[, or escape,] **while the kitty keyboard protocol
is disabled** (e.g. while an external command is running).  In this case,
the fish_escape_delay_ms is also virtually increased; hopefully this edge
case is not ever relevant.

Part of #11668

(cherry picked from commit 30ff3710a0)
2025-07-25 18:29:19 +02:00
Johannes Altmanninger
30ff3710a0 Increase timeout when reading escape sequences inside paste/kitty kbd
Historically, fish has treated input bytes [0x1b, 'b'] as alt-b (rather than
"escape,b") if the second byte arrives within 30ms of the first.

Since we made builtin bind match key events instead of raw byte sequences,
we have another place where we do similar disambiguation: when we read keys
such as alt-left ("\e[1;3D"), we only consider bytes to be part of this
sequence if stdin is immediately readable (actually "readable after a 1ms
timeout" since e1be842 (Work around torn byte sequences in qemu kbd input
with 1ms timeout, 2025-03-04)).

This is technically wrong but has worked in practice (for Kakoune etc.).

Issue #11668 reports two issues on some Windows terminals feeding a remote
fish shell:
- the "bracketed paste finished" sequence may be split into multiple packets,
  which causes a delay of > 1ms between individual bytes being readable.
- AutoHotKey scripts simulating seven "left" keys result in sequence tearing
  as well.

Try to fix the paste case by increasing the timeout when parsing escape
sequences.

Also increase the timeout for terminals that support the kitty keyboard
protocol.  The user should only notice this new delay after pressing one of
escape,O, escape,P, escape,[, or escape,] **while the kitty keyboard protocol
is disabled** (e.g. while an external command is running).  In this case,
the fish_escape_delay_ms is also virtually increased; hopefully this edge
case is not ever relevant.

Part of #11668
2025-07-25 18:28:21 +02:00
Johannes Altmanninger
6666c8f1cd Block interrupts and uvar events while decoding key
readb() has only one caller that passes blocking=false: try_readb().
This function is used while decoding keys; anything but a successful read
is treated as "end of input sequence".

This means that key input sequences such as \e[1;3D
can be torn apart by
- signals (EINTR) which is more likely since e1be842 (Work around torn byte
  sequences in qemu kbd input with 1ms timeout, 2025-03-04).
- universal variable notifications (from other fish processes)

Fix this by blocking signals and not listening on the uvar fd.  We do something
similar when matching key sequences against bindings, so extract a function
and use it for key decoding too.

Ref: https://github.com/fish-shell/fish-shell/issues/11668#issuecomment-3101341081
(cherry picked from commit da96172739)
2025-07-25 18:21:27 +02:00
Johannes Altmanninger
3e61036911 Revert "Change readch() into try_readch()"
try_readch() was added to help a fuzzing harness, specifically to avoid a
call to `unreachable!()` in the NothingToRead case.  I don't know much about
that but it seems like we should find a better way to tell the fuzzer that
this can't happen.

Fortunately the next commit will get rid of readb()'s "blocking" argument,
along the NothingToRead enum variant. So we'll no longer need this.

This reverts commit b92830cb17.

(cherry picked from commit fb7ee0db74)
2025-07-25 18:21:27 +02:00
Johannes Altmanninger
f23a479b81 Reduce MaybeUninit lifetime
(cherry picked from commit 137f220225)
2025-07-25 18:21:27 +02:00
王宇逸
e274ff41d0 Use uninit instead of zeroed (src/input_common.rs)
(cherry picked from commit 7c2c7f5874)
2025-07-25 18:21:27 +02:00
Johannes Altmanninger
d2af306f3d Fix some unused-ControlFlow warnings 2025-07-25 18:11:04 +02:00
Johannes Altmanninger
da96172739 Block interrupts and uvar events while decoding key
readb() has only one caller that passes blocking=false: try_readb().
This function is used while decoding keys; anything but a successful read
is treated as "end of input sequence".

This means that key input sequences such as \e[1;3D
can be torn apart by
- signals (EINTR) which is more likely since e1be842 (Work around torn byte
  sequences in qemu kbd input with 1ms timeout, 2025-03-04).
- universal variable notifications (from other fish processes)

Fix this by blocking signals and not listening on the uvar fd.  We do something
similar when matching key sequences against bindings, so extract a function
and use it for key decoding too.

Ref: https://github.com/fish-shell/fish-shell/issues/11668#issuecomment-3101341081
2025-07-25 17:56:49 +02:00
Johannes Altmanninger
fb7ee0db74 Revert "Change readch() into try_readch()"
try_readch() was added to help a fuzzing harness, specifically to avoid a
call to `unreachable!()` in the NothingToRead case.  I don't know much about
that but it seems like we should find a better way to tell the fuzzer that
this can't happen.

Fortunately the next commit will get rid of readb()'s "blocking" argument,
along the NothingToRead enum variant. So we'll no longer need this.

This reverts commit b92830cb17.
2025-07-25 17:56:49 +02:00
Johannes Altmanninger
137f220225 Reduce MaybeUninit lifetime 2025-07-25 17:56:49 +02:00
Johannes Altmanninger
f8b9fa19e8 Merge pull request #11681 2025-07-25 17:56:39 +02:00
A2uria
4508b5b0db Handle cygwin select() returning -1 with errno 0 2025-07-25 22:45:01 +08:00
A2uria
d8e5821a3b Use \n as end of line on windows 2025-07-25 18:44:42 +08:00
A2uria
4162e0efad Fix path_remoteness on cygwin 2025-07-25 18:41:40 +08:00
Johannes Altmanninger
0e7c7f1745 Remove unused import
(cherry picked from commit 07ff4e7df0)
2025-07-25 11:57:21 +02:00
Johannes Altmanninger
07ff4e7df0 Remove unused import 2025-07-25 11:55:10 +02:00
Johannes Altmanninger
3fada80553 Fix regression causing \e[ to be interpreted as ctrl-[
Fixes 3201cb9f01 (Stop parsing invalid CSI/SS3 sequences as alt-[/alt-o,
2024-12-30).

(cherry picked from commit 43d583d991)
2025-07-25 11:49:07 +02:00
Johannes Altmanninger
43d583d991 Fix regression causing \e[ to be interpreted as ctrl-[
Fixes 3201cb9f01 (Stop parsing invalid CSI/SS3 sequences as alt-[/alt-o,
2024-12-30).
2025-07-25 11:47:18 +02:00
Johannes Altmanninger
d69886efe0 completions/protontricks: coding style, translations 2025-07-25 11:15:05 +02:00
Johannes Altmanninger
bd8cc6d317 Merge pull request #11375 2025-07-25 11:10:38 +02:00
Johannes Altmanninger
9c5b3f3d57 Merge pull request #11674 2025-07-25 11:03:53 +02:00
Johannes Altmanninger
5970f34a60 Retry some writes on EINTR again
I guess?
Fixes f0e007c439 (Relocate tty metadata and protocols and clean it up,
2025-06-19).
2025-07-25 10:23:48 +02:00
Johannes Altmanninger
eaa837effa Refresh TTY timestamps again in most cases
See commit 081c3282b7 (Refresh TTY timestamps also in some rare cases,
2025-01-15) and others.
Fixes d27f5a5293 (Adopt TtyHandoff in remaining places, 2025-06-21)
Fixes #11671
2025-07-25 10:23:48 +02:00
Johannes Altmanninger
e52cf2f6a7 Try to restore TTY protocols more reliably after SIGTERM
We might
1. set TTY_PROTOCOLS_ACTIVE to false
2. receive `SIGTERM`
3. due to 1 fail to disable TTY protocols

Fix this by making sure that the disabling of protocols happens-before we
set TTY_PROTOCOLS_ACTIVE to false.

See 37c04745e6 (Avoid potential contention on SIGTERM while enabling terminal
protocols, 2024-10-08).
Fixes d27f5a5293 (Adopt TtyHandoff in remaining places, 2025-06-21)
2025-07-25 10:23:48 +02:00
Johannes Altmanninger
8c7568c0cb Reapply "Disable focus reporting on non-tmux again for now" 2024-04-18
Reapply bdd478bbd0. Amendment to f0e007c439 (Relocate tty metadata and
protocols and clean it up, 2025-06-19).
2025-07-25 10:23:48 +02:00
Johannes Altmanninger
07979782a6 Fix iTerm2 detection on non-iTerm2 terminals
Fixes f0e007c439 (Relocate tty metadata and protocols and clean it up,
2025-06-19).
2025-07-25 10:23:48 +02:00
Johannes Altmanninger
59b43986e9 build_tools/style.fish: fail if formatters are not available
build_tools/check.sh is supposed to fail on formatting violations.  I don't
think we have a good reason for running build_tools/style.fish outside
check.sh.

black is the only formatter not versioned in CI -- but we can probably
satisfy all realistic versions.

Ref: https://github.com/fish-shell/fish-shell/pull/11608#discussion_r2173176621
2025-07-25 10:23:48 +02:00
phisonate
51f3722e02 Fix funced to not expand or execute function name when interactive
Due to unnecessary quotes in the prompt command given to `read` by `funced` when
editing a function interactively (using `-i`), the name of the function to edit
would be evaluated, expanded and even executed (when using command substitution
for example), which is at least annoying when using unusual but valid and
allowed function names like '*' or 'head (cat)'. This commit delays the function
name expansion so that this should no longer happen.
2025-07-24 01:03:02 +02:00
Daniel Rainer
2c11bfa532 Avoid running sphinx-build if possible
Despite the caching in `sphinx-build`, it takes several seconds to run
`sphinx-build` when no rebuilding is necessary, which slows down build times
significantly.

Add custom logic to `build.rs` to avoid calling `sphinx-build` if deemed
unnecessary based on the mtime of the source files.
This is done by writing the most recent timestamp of the source files into a
dedicated file, and only calling `sphinx-build` (and updating the timestamp)
when a cached timestamp is older than the most recent source file mtime.
2025-07-23 18:37:07 +02:00
Daniel Rainer
eae633c4af Extract SPHINX_DOC_SOURCES and handle rebuilds
The sources for Sphinx documentation builds include `CHANGELOG.rst` and
`CONTRIBUTING.rst`. Use `SPHINX_DOC_SOURCES` to clarify this and avoid
repetition.

Rebuilds should happen for debug builds as well, since rebuilding is required
for updating the man files.
2025-07-23 18:28:54 +02:00
Daniel Rainer
ecc004a122 Rebuild if gettext-extraction changed 2025-07-23 18:28:54 +02:00
Peter Ammon
db0f9c1d53 Minor refactoring of make_wait_handle 2025-07-20 13:40:22 -07:00
Peter Ammon
c9901398ed Switch DISOWNED_PIDS from MainThread to Mutex
Preparation for concurrent execution. These can be reaped on any thread.
2025-07-19 17:56:58 -07:00
Peter Ammon
6181ba3b56 Fix an 1.70 clippy 2025-07-19 16:49:51 -07:00
Peter Ammon
d27f5a5293 Adopt TtyHandoff in remaining places
This adopts the tty handoff in remaining places. The idea is to rationalize
when we enable and disable tty protocols (such as CSI-U).

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

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

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

This commit moves code around and does some cleanup; it doesn't actually
pull the trigger on centralizing the logic though. Next commit will do that.
2025-07-19 16:04:13 -07:00
Peter Ammon
65a4cb5245 Revert "Restore terminal state on SIGTERM again"
This reverts commit 1d6fa258f6.

This reintroduces commit 941701da3d, which was then reverted in
941701da3d8; this commit reverts the revert to reintroduce 941701da3d.

The reason is that the existing logic in terminal_protocols_disable_ifn does a
bunch of stuff for which nobody has thought about its signal safety, such as
accessing the reader stack (clearly not async signal safe).
Even functions which happen to be safe now may become unsafe in the future.

This is just the nature of signal handling code. We must ensure that only
async-signal safe syscalls are run, and only functions which are themselves
async-signal safe, which we (try) to designate with the "safe_" prefix.
2025-07-19 15:37:00 -07:00
rosavi
c7262d6c05 Add completions for cpan and t-rec
Closes #11647
2025-07-19 22:15:24 +02:00
Johannes Altmanninger
f3c264722d Merge pull request #11666 2025-07-19 22:15:24 +02:00
Johannes Altmanninger
39742cafa0 Merge pull request #11663 2025-07-19 22:15:24 +02:00
Johannes Altmanninger
295d2bd218 update translations for completions/fish_indent 2025-07-19 22:15:24 +02:00
Johannes Altmanninger
3588b41744 Merge pull request #11662 2025-07-19 22:15:24 +02:00
Johannes Altmanninger
82e3311756 Merge pull request #11659 2025-07-19 22:15:24 +02:00
Johannes Altmanninger
b611c96cdd Merge pull request #11654 2025-07-19 22:15:24 +02:00
Johannes Altmanninger
1d6fa258f6 Restore terminal state on SIGTERM again
Commit 941701da3d (Restore some async-signal discipline to SIGTERM,
2025-06-15) made two changes
1. removed a mutex lock in signal handler (regression from 55fd43d86c
   (Port reader, 2023-12-22))
2. removed some SIGTERM cleanup

I'm not sure what's the reason for 2, so let's revert it I guess.  This code
path already uses FLOG_SAFE for async-signal safety.

There is an avoidable panic when `Outputter::stdoutput()` is already
borrowed. Fix that.

Closes #11597
2025-07-19 22:15:24 +02:00
Johannes Altmanninger
6312b1dbd8 Format test_driver.py 2025-07-19 22:15:24 +02:00
Peter Ammon
5fa2f62536 test_driver: increase open file limit
Prevent failures due to file handle exhaustion.
2025-07-19 11:14:37 -07:00
Peter Ammon
bbf7568ebd test_driver.py: Properly report exceptions
If a test fails by throwing an exception (in this case, "Too many open files")
then that exception would propagate, be uncaught, and then the remaining tests
would not be await'ed, leading to a hang.

Fix this by properly catching and reporting exceptions.
2025-07-19 11:01:48 -07:00
Rhidian De Wit
72347517b2 Fix missing bool to string cast causing errors 2025-07-17 20:17:52 +02:00
may
95475c35ff update translation files 2025-07-16 16:36:09 +02:00
may
560d21cd86 complete git rebase --keep-base 2025-07-16 15:49:38 +02:00
nick
bef453f69b fish_indent -c/--check completions
Manpage `fish_indent(1)` documents the `-c/--check` option, which checks
if a file is already indented as `fish_indent` would. This option is now
included in the completions for `fish_indent`.
2025-07-15 15:44:36 -05:00
vcalv
fa832ead65 add suspend-then-hibernate to systemctl.fish
suspend-then-hibernate was missing
2025-07-14 18:09:32 -04:00
Azamat Dinaev
eb7afd2a9c Update __fish_print_hostnames.fish
There was an issue in autocomplete of ssh. 

When you put in ~/.ssh/config line like this:

"Include Include ${HRL_SSH}/onprem_config"

and then trying to use fish complete for ssh, for example:

"ssh -J" and press key <Tab> it throughs an error that fish cannot understand ${HRL_SSH} with brackets.
2025-07-12 14:18:43 +03:00
Johannes Altmanninger
f4ddcfa694 Merge pull request #11652 2025-07-11 12:11:17 +02:00
Johannes Altmanninger
1605d8d6ce Merge pull request #11644 2025-07-11 12:10:49 +02:00
Johannes Altmanninger
a7559a62c4 completions/cjpm: format with fish_indent 2025-07-11 12:09:58 +02:00
Johannes Altmanninger
e9327d234d Merge pull request #11641 2025-07-11 12:08:37 +02:00
Daniel Rainer
a3d03fc0fb Avoid duplicate flag values
Both `SKIP_CMDSUBST` and `NO_SPACE_FOR_UNCLOSED_BRACE` used `1 << 14` as their
value accidentally, resulting from `SKIP_CMDSUBST` not being sorted correctly.
Resolve this by using the next (and last in u16) unused bit for `SKIP_CMDSUBST`
and moving it to the end.

Fixes #11651.
2025-07-10 18:52:10 +02:00
Daniel Rainer
1e981a9827 Support upper/lower casing selection
Fixes #11639.
2025-07-10 16:57:17 +02:00
Daniel Rainer
770f4ce6d1 Add docs for casing shortcuts in normal mode 2025-07-06 20:15:25 +02:00
Daniel Rainer
aa782bdad7 Fix vi mode docs for moving to beginning of line 2025-07-06 20:10:21 +02:00
Jiangqiu Shen
e4c55131c7 Update translation 2025-07-04 18:31:39 -04:00
Jiangqiu Shen
e6ad78cda7 update 2025-07-04 00:30:28 -04:00
Jiangqiu Shen
578e162f35 Add completion for Cangjie programing language
add completion of cjpm
2025-07-04 00:29:11 -04:00
Johannes Altmanninger
e9bb150a41 Merge pull request #11633 2025-07-03 15:19:51 +02:00
Johannes Altmanninger
b5eccdf9f6 Merge pull request #11632 2025-07-03 15:19:51 +02:00
Johannes Altmanninger
75716bd6b0 Merge pull request #11631 2025-07-03 15:19:51 +02:00
Johannes Altmanninger
9e628995da Merge pull request #11629 ("Add completions for tmuxp") 2025-07-03 15:19:51 +02:00
Daniel Müller
5b39efc96d Support incrementing/decrementing the number below the cursor
Vim supports incrementing & decrementing the number below the cursor (or
after it) via Ctrl-a and Ctrl-x, respectively. Given fish's Vi mode
support, it makes sense to provide similar functionality when working on
the command line, to provide a more natural environment for Vim users.
With this change we add the necessary functionality.

Closes: #8320
Closes #11570
2025-07-03 14:38:42 +02:00
Johannes Altmanninger
b5bb50d742 builtin commandline: apply commandline+cursor to first top-level reader
Historically, `fish -C "commandline echo"` was silently ignored.  Make it do
the expected thing.  This won't affect subsequent readers because we only do
it for top-level ones, and reader_pop() will clear the commandline state again.

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

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

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

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

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

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

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

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

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

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

Co-authored-by: Johannes Altmanninger <aclopte@gmail.com>
2025-07-03 14:16:18 +02:00
merceyz
fc37d8d5a8 feat: add completions for volta 2025-06-29 16:23:49 +02:00
merceyz
8d361b4290 feat: add completions for k9s 2025-06-29 16:18:41 +02:00
merceyz
9789e6b731 feat: add completions for cilium and hubble 2025-06-29 16:09:27 +02:00
Johannes Altmanninger
c7d4acbef8 Update changelog 2025-06-29 16:01:44 +02:00
Johannes Altmanninger
e204a4c126 Add ctrl-alt-h compatibility binding
Historically, ctrl-i sends the same code as tab, ctrl-h sends backspace and
ctrl-j and ctrl-m behave like enter.

Even for terminals that send unambiguous encodings (via the kitty keyboard
protocol), we have kept bindings like ctrl-h, to support existing habits.

We forgot that pressing alt-ctrl-h would behave like alt-backspace (and can
be easier to reach) so maybe we should add that as well.

Don't add ctrl-shift-i because at least on Linux, that's usually intercepted
by the terminal emulator.

Technically there are some more such as "ctrl-2" (which used to do the same as
"ctrl-space") but I don't think anyone uses that over "ctrl-space".

Closes #https://github.com/fish-shell/fish-shell/discussions/11548

(cherry picked from commit 4d67ca7c58)
2025-06-28 14:20:03 +02:00
Johannes Altmanninger
4d67ca7c58 Add ctrl-alt-h compatibility binding
Historically, ctrl-i sends the same code as tab, ctrl-h sends backspace and
ctrl-j and ctrl-m behave like enter.

Even for terminals that send unambiguous encodings (via the kitty keyboard
protocol), we have kept bindings like ctrl-h, to support existing habits.

We forgot that pressing alt-ctrl-h would behave like alt-backspace (and can
be easier to reach) so maybe we should add that as well.

Don't add ctrl-shift-i because at least on Linux, that's usually intercepted
by the terminal emulator.

Technically there are some more such as "ctrl-2" (which used to do the same as
"ctrl-space") but I don't think anyone uses that over "ctrl-space".

Closes #https://github.com/fish-shell/fish-shell/discussions/11548
2025-06-28 14:19:10 +02:00
Patryk Bratkowski
fbe5a53dc9 Add completions for tmuxp 2025-06-28 14:58:37 +03:00
Johannes Altmanninger
dcd93e4c52 Fix compatibility with Python 3.6 for OpenSUSE Leap 15.6
Nightlies for opensuse/leap:15.6 are failing because their /bin/python3
is Python 3.6 (the "python311" package creates only /bin/python311).
Python3.6 has been EOL for 3.5 years but OpenSuse leap is not even EOL.
Given that we don't write a lot of Python, let's support this for now.
2025-06-28 13:36:31 +02:00
Johannes Altmanninger
7acd20dc7e Fix opensuse docker file file extension 2025-06-28 13:27:30 +02:00
Johannes Altmanninger
9af33802ec Use statvfs on NetBSD again to fix build
From commit ba00d721f4 (Correct statvfs call to statfs, 2025-06-19):

> This was missed in the Rust port

To elaborate:

- ec176dc07e (Port path.h, 2023-04-09) didn't change this (as before,
 `statvfs` used `ST_LOCAL` and `statfs` used `MNT_LOCAL`)
- 6877773fdd (Fix build on NetBSD (#10270), 2024-01-28) changed the `statvfs`
  call to `statfs`, presumably due to the libc-wrapper for
  `statvfs` being missing on NetBSD.  This change happens
  to work fine on NetBSD because they do [`#define ST_LOCAL
  MNT_LOCAL`](https://github.com/fish-shell/fish-shell/pull/11486#discussion_r2092408952)
  But it was wrong on others like macOS and FreeBSD, which was fixed by
  ba00d721f4 (but that broke the build on NetBSD).
- 7228cb15bf (Include sys/statvfs.h for the definition of ST_LOCAL (Rust
  port regression), 2025-05-16)
  fixed a code clone left behind by the above commit (incorrectly assuming
  that the clone had always existed.)

Fix the NetBSD build specifically by using statfs on that platform.

Note that this still doesn't make the behavior equivalent to commit LastC++11.
That one used ST_LOCAL if defined, and otherwise MNT_LOCAL if defined.

If we want perfect equivalence, we could detect both flags in `src/build.rs`.
Then we would also build on operating systems that define neither. Not sure.

Closes #11596

(cherry picked from commit 6644cc9b0e)
2025-06-28 11:46:25 +02:00
王宇逸
eecf0814a1 Use uninit instead of zeroed (cherry-pikcked only the change to src/path.rs)
(cherry picked from commit 7c2c7f5874)
2025-06-28 11:46:25 +02:00
Johannes Altmanninger
1d893b77d3 Also flag MSRV rustc/clippy warnings in CI 2025-06-28 11:04:29 +02:00
Johannes Altmanninger
b451650faa Flag stable rustc/clippy warnings in CI
Today, when a change introduces warnings, the change author might not see
them. Fix that by making clippy fail on warnings.

AFAICT, "clippy --deny-warnings" will also fail on rustc warnings.
I'd imagine this is what most respectable Rust projects do.

Pin stable rust so we won't get unrelated failures. Alternatively, we could
keep using "dtolnay/rust-toolchain@stable", that should be fine too (as long
as we have the capacity to quickly silence/iron out clippy failures).

While at it, remove some unneeded dependencies. Keep gettext because that
one might enable some cfg-directives (?).
Other cfgs like feature="benchmark" and target_os != "linux" are not yet checked in CI.

See #11584
2025-06-28 11:02:19 +02:00
Johannes Altmanninger
3e0a53ae4f Resolve an uninlined_format_args warning 2025-06-28 11:02:19 +02:00
Johannes Altmanninger
e01aafab1c Extract github actions for interesting rust toolchain versions
Extract a github action to reduce the number of references to our MSRV and
stable (to be pinned in the next commit).

While at it, use the MSRV for macOS builds; this means that we'll be less
like accidentally to break the macOS build when bumping the MSRV.  I don't
think there is a reason for using 1.73 specifically, other than "it's the
highest we can use on old macOS", so using an even older one should be fine.
2025-06-28 09:50:18 +02:00
Johannes Altmanninger
fd0fba83b9 Fix inconsistency in docker file lint override 2025-06-28 09:50:18 +02:00
Johannes Altmanninger
6644cc9b0e Use statvfs on NetBSD again to fix build
From commit ba00d721f4 (Correct statvfs call to statfs, 2025-06-19):

> This was missed in the Rust port

To elaborate:

- ec176dc07e (Port path.h, 2023-04-09) didn't change this (as before,
 `statvfs` used `ST_LOCAL` and `statfs` used `MNT_LOCAL`)
- 6877773fdd (Fix build on NetBSD (#10270), 2024-01-28) changed the `statvfs`
  call to `statfs`, presumably due to the libc-wrapper for
  `statvfs` being missing on NetBSD.  This change happens
  to work fine on NetBSD because they do [`#define ST_LOCAL
  MNT_LOCAL`](https://github.com/fish-shell/fish-shell/pull/11486#discussion_r2092408952)
  But it was wrong on others like macOS and FreeBSD, which was fixed by
  ba00d721f4 (but that broke the build on NetBSD).
- 7228cb15bf (Include sys/statvfs.h for the definition of ST_LOCAL (Rust
  port regression), 2025-05-16)
  fixed a code clone left behind by the above commit (incorrectly assuming
  that the clone had always existed.)

Fix the NetBSD build specifically by using statfs on that platform.

Note that this still doesn't make the behavior equivalent to commit LastC++11.
That one used ST_LOCAL if defined, and otherwise MNT_LOCAL if defined.

If we want perfect equivalence, we could detect both flags in `src/build.rs`.
Then we would also build on operating systems that define neither. Not sure.

Closes #11596
2025-06-28 09:50:18 +02:00
adamanteye
f5370e6f22 Add colorful disassembler output completion for objdump
As desribed in objdump(1), --disassembler-color can be applied to
enable or disable the use of syntax highlighting in disassembly
output.

The options are:

--disassembler-color=off
--disassembler-color=terminal
--disassembler-color=on|color|colour
--disassembler-color=extened|extended-color|extened-colour

Signed-off-by: adamanteye <ada@adamanteye.cc>

Closes #11615
2025-06-28 09:50:18 +02:00
Patryk Bratkowski
d62fb9cc74 Add 'ollama stop' completions.
- Added the '__fish_ollama_ps' function to list running models.
- Added the 'stop' subcommand to ollama completions.
- Added running models as arguments to 'stop'.
2025-06-28 09:50:18 +02:00
Johannes Altmanninger
88ab024d7d Merge pull request #11627 2025-06-28 09:29:11 +02:00
Johannes Altmanninger
1cc900ab7f Merge pull request #11625 2025-06-28 09:28:58 +02:00
Dezhi Wu
144725e947 fix(echo): handle overflow in octal/hex escape parsing
Use wrapping arithmetic when parsing octal and hex escapes in echo to
prevent panics on overflow and ensure consistent behavior with other
shells. This change allows echo to process escape sequences like \5555
without crashing, keeping the same behavior as 3.7.1.

```
$ ./fish --version
fish, version 3.7.1
$ ./fish -c 'echo -e "\5555"'
m5
```
2025-06-28 11:15:42 +08:00
Dezhi Wu
d969577f0b fix(fmt): Resolve Rust 1.88 clippy warnings
Update formatting macros to use the new inline variable syntax as
recommended by Rust 1.88 clippy.
2025-06-28 09:10:27 +08:00
Johannes Altmanninger
1ceebdf580 Fix some CSI commands being sent to old midnight commander
Commit 97581ed20f (Do send bracketed paste inside midnight commander,
2024-10-12) accidentally started sending CSI commands such as "CSI >5;0m",
which we intentionally didn't do for some old versions of Midnight Commander,
which fail to parse them. Fix that.

Fixes #11617
2025-06-25 13:36:03 +02:00
Johannes Altmanninger
3e098249a5 docker/fish_run_tests.sh: add check command to bash history
Closes #11600
2025-06-24 12:52:35 +02:00
Johannes Altmanninger
7cd7f31a93 build_tools/check.sh: ignore modifications to the running script
I sometimes want to run this script in multiple docker containers concurrently,
and possibly modify it while another instance is already running.  The behavior
after modification is unpredictable; let's change it to read the whole script
up-front (like Python/fish do).
2025-06-24 12:52:35 +02:00
Johannes Altmanninger
5e12d4e99c Use sync::OnceCell for terminal modes, fixing memory leak
My

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

shows a lot of complaints about

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

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

It doesn't feel great to change implementation code to accomodate tests but
I think for this specific issue that's what we usually do.  Alternatively,
we could add to lsan_suppressions.txt.
2025-06-24 12:52:35 +02:00
Johannes Altmanninger
f98d1779dd build_tools/check.sh: respect inherited RUSTFLAGS/RUSTDOCFLAGS
No particular motivation. Seems better?
Also, use long options I guess.
2025-06-24 12:52:35 +02:00
Johannes Altmanninger
6f18a1b314 Also namespace target/man -> target/fish-man
If cargo ever wants to write to "target/man", it would collide with our
use of this path.  Let's make this less likely by prefixing the name with
"fish-".  This also makes it more obvious that this is fish's invention.
2025-06-24 12:52:35 +02:00
Johannes Altmanninger
8b102f2571 Stop using Cargo's OUT_DIR
(Note: this commit should technically have preceded the "Fix config paths
for disjoint build-dirs and in-tree installs" one, to make that one easier
to follow, but I wasn't 100% sure if this commit is right.)

From https://doc.rust-lang.org/cargo/reference/environment-variables.html

> OUT_DIR — If the package has a build script, this is set to the folder
> where the build script should place its output. See below for more
> information. (Only set during compilation.)

so OUT_DIR is something like "target/debug/build/fish-41da27d587f48978".
Whenever build.rs is re-run, we get a new one.

I don't think we need this flexibility anywhere.  It wouldn't protect
concurrent "cargo test" from interfering with each other - that's handled
by a file lock taken by Cargo.

Use "target/" instead (or CMAKE_BINARY_DIR if set).
Namespace the files better, so we don't create weird paths like

	target/test/complete_test/...
	target/fish_root/
2025-06-24 12:52:35 +02:00
Johannes Altmanninger
514eebb002 build_tools/update_translations.fish: move po/template.po to /tmp
With the upcoming tests/checks/gettext.fish test from #11583, my

	sudo docker/docker_run_tests.sh --shell-after docker/focal.Dockerfile

fails writing to "po/template.po" because "/fish-source" is mounted as
read-only.  (There should be no need for tests to write to the source tree.)

Since commit 6239cba1e4 (Add dry-run mode to update_translations.fish,
2025-05-30), "build_tools/update_translations.fish" always removes that
template file when done, even without "--dry-run".

I'm not sure if we still have a need for keeping around "po/template.po".
To add a new translation, you can run "build_tools/update_translations
po/xy.po". It could serve as a cache but that would only work if we integrated
it into a build system.

Move it to /tmp, fixing the docker tests.
2025-06-24 12:52:35 +02:00
Johannes Altmanninger
41eb0a2fd0 build_tools/update_translations.fish: protect against universally-set $tmpdir 2025-06-24 12:51:17 +02:00
Johannes Altmanninger
290d957ab6 build_tools/update_translations.fish: remove forward reference
cleanup_exit references variables that are only defined later.  Fix that.
Also move the definition one block up, to help the next commit.
2025-06-24 12:33:28 +02:00
Johannes Altmanninger
08b03a733a docker_run_tests.sh: stop using cmake
Use test_driver directly instead of CMake in the docker tests.

Deal with the read-only "/fish-source" by exporting
"CARGO_TARGET_DIR=$HOME/fish-build".  It seems correct to also inject this
environment variable into the interactive debugging shells.  Add some logging
to make this override more obvious to the user.

Adopt "build_tools/check.sh", because that defines the full set of checks
that we (eventually) want to run in CI.

In particular, this will also run "tests/checks/po-files-up-to-date.fish"
which "cargo b && cargo t && tests/test_driver.py" does not, due to the
REQUIRES clause.

Since most docker images have some lints/warnings today, disable those for
now. Use "docker_run_tests.sh --lint" to override. The default may be changed
in future.
2025-06-24 12:32:42 +02:00
Johannes Altmanninger
19a17fa981 Fix docker warning by using "ENV key=value" syntax
- LegacyKeyValueFormat: "ENV key=value" should be used instead of
	legacy "ENV key value" format (line 4)
2025-06-24 12:32:42 +02:00
Johannes Altmanninger
13c00c9f79 Fix config paths for disjoint build-dirs and in-tree installs
Commit 89282fd9bc (Use CARGO_MANIFEST_DIR to see if we're running from
build dir, 2024-01-20) did

    -if exec_path.starts_with(OUT_DIR)
    +if exec_path.starts_with(CARGO_MANIFEST_DIR)

where OUT_DIR is the cmake build directory ("./build")
and CARGO_MANIFEST_DIR is our top level source tree.

This allowed "target/debug/fish" to work, but it broke
1. CMake build directories outside the source tree, e.g. "docker/docker_run_tests.sh".
   Those incorrectly fall back to the compiled-in-path (/usr/local/share etc)
2. Installations iside the source tree, e.g.
   "mkdir build && cd build && cmake .. -DCMAKE_INSTALL_PREFIX=$PWD/../install".
   These installations incorrectly use "share/" etc. from the source tree.

Fix this by
1. respecting the CMake-specific FISH_BUILD_DIR, ad
2. if that's not set, use $CARGO_MANIFEST_DIR/target
2025-06-24 12:32:42 +02:00
Johannes Altmanninger
19eceff3bc bulid.rs respect CARGO_TARGET_DIR in man output 2025-06-24 12:32:42 +02:00
Johannes Altmanninger
6a4d3a59ab build.rs: extract constant for cargo manifest dir 2025-06-24 12:32:42 +02:00
Johannes Altmanninger
3b0d5c342b build.rs: extract function for canonicalizing paths
This probably means we should enable Rust backtraces.. not sure though.
2025-06-24 12:32:42 +02:00
Johannes Altmanninger
884a2d100c build.rs: remove redundant include dir
This include was added for config.h in 618834c4b5 (Port
UVAR_FILE_SET_MTIME_HACK, 2023-09-15), but that file no longer exists.
Remove it.
2025-06-24 12:04:57 +02:00
Johannes Altmanninger
a4d355634d docker_run_tests: fix failed build exiting prematurely
My bad; the "set +e" is only active inside the subshell.
The outer shell uses "set -e", which means that it will
exit upon seeing the subshell fail.
2025-06-24 12:04:57 +02:00
Johannes Altmanninger
3c620f56ee test_driver.py: fix compatibility with Python 3.8 / Ubuntu Focal 2025-06-24 12:04:57 +02:00
Johannes Altmanninger
7679be3126 test_driver.py: fix confusing help output 2025-06-24 12:02:13 +02:00
Johannes Altmanninger
49926cfbac Standardize shell script indent level
We have a mixture of 2 and 4 space indent.

    4 benchmarks/driver.sh
    2 build_tools/check.sh
    4 build_tools/git_version_gen.sh
    4 build_tools/mac_notarize.sh
    2 build_tools/make_pkg.sh
    2 build_tools/make_tarball.sh
    2 build_tools/make_vendor_tarball.sh
    4 docker/docker_run_tests.sh
    4 osx/install.sh
    2 tests/test_functions/sphinx-shared.sh

Our editorconfig file specifies 2, with no explicit reason.
Our fish and Python scripts use 4, so let's use that.
2025-06-24 12:02:13 +02:00
Johannes Altmanninger
963c3425a3 Merge pull request #11593 2025-06-24 12:02:13 +02:00
ndrew222
55bddac90a added completion for uv and uvx
Closes #11601
2025-06-24 12:02:13 +02:00
Illia Ostapyshyn
20c67692e1 completions/journalctl: Add --pager-end
Closes #11604
2025-06-24 12:02:13 +02:00
Johannes Altmanninger
1e7088fb6b Merge pull request #11605 2025-06-24 12:02:13 +02:00
Johannes Altmanninger
0f75df7c35 history: remove flush()/fsync() and don't write after each appended item
Commit 5c0fddae70 (Refactor history flushing, 2025-03-28) made three changes:
1. call fsync() when we are finished writing the history file.
2. when appending to (as opposed to vacuuming) history, call write(2)
   (followed by flush() and sync()) for each item. Previously, we'd only
   call write(2) if our 64k buffer was full, or after processing the last
   history item.
3. actually check the return value of flush() (which would retry when flushing
   fails -- but std::fs::File::flush() never fails!).

The motivation was to potentially fix #10300 which didn't succeed (see
https://github.com/fish-shell/fish-shell/issues/10300#issuecomment-2876718382).

As for 1 and 2, I don't think the way we use fsync really helps, and flushing
eagerly should not make a difference.

As for 3, there are some explanations in comments, commit message and a [PR
comment](https://github.com/fish-shell/fish-shell/pull/11330#discussion_r2020171339).
To summarize, 5c0fddae70 wants to address the scenario where file.flush()
fails. Prior to that commit we would ostensibly carry on with a corrupted
"first_unwritten_new_item_index" (corrupted because it doesn't match what's
written to disk), which can cause various issues.  However this doesn't
ever happen because std::fs::File::flush() never fails because it doesn't
do anything -- std::fs::File::write() does not buffer writes, it always
delegates to write(2).

There can definitely be scenarios like the one described in
https://github.com/fish-shell/fish-shell/pull/11330#discussion_r2020171339
where the disk is full. In that case, either write(2) fails, which we
already check.  Or close(3p) fails with EIO, which we have never checked. We
should probably check that.

Undo all three changes for now.

Closes #11495
2025-06-24 12:02:13 +02:00
Asuka Minato
edb0617d13 Update git.fish
Closes #11606
2025-06-24 12:02:12 +02:00
Johannes Altmanninger
d2f7d238f3 Remove obsolete translation entries
I doubt these are very helpful; most of them won't be used again.  We can
still find them in history with "git log -Gpart.of.msgid".
2025-06-24 11:44:46 +02:00
Johannes Altmanninger
335f91babd completions/git: fix spurious error when no subcommand is in $PATH
Systems like NixOS might not have "git-receive-pack" or any other "git-*"
executable in in $PATH -- instead they patch git to use absolute paths.
This is weird. But no reason for us to fail. Silence the error.

Fixes #11590

(cherry picked from commit 4f46d369c4)
2025-06-24 11:59:57 +08:00
Volodymyr Chernetskyi
a7bed39c1e Add info on formatting fish_git_prompt output 2025-06-23 18:46:54 +02:00
Johannes Altmanninger
06646998db fixup! alias: fix indentation 2025-06-23 14:18:15 +02:00
Johannes Altmanninger
096f225579 alias: fix indentation
Fixes #11602
2025-06-23 13:53:43 +02:00
Johannes Altmanninger
ebec8c15ab Merge pull request #11599 2025-06-23 13:46:34 +02:00
Daniel Rainer
92d9646631 Simplify CMake Tests
Remove dependency on CTest. Parallel execution is handled by `test_driver.py`
internally now, so CTest is no longer relevant for performance.

This also removes CMake targets for single tests. As a replacement,
`test_driver.py` can be called directly with the path to the build directory as
the first argument and the path to the desired test as the second argument.
Ensuring that the executables in the build directory are up to date needs to be
done separately.
For a pure cargo build, an example of running a single test would be:
`cargo b && tests/test_driver.py target/debug tests/checks/abbr.fish`

The recommended way of running tests is `build_tools/check.sh`, which runs more
extensive tests and does not depend on CMake. That script does not work in CI
yet, so CMake testing is retained for now.

Update CI config to use the new `FISH_TEST_MAX_CONCURRENCY`.
Also update the FreeBSD version, since the previous one is outdated and does not
support the semaphore logic in `test_driver.py`.
2025-06-22 22:44:46 +02:00
Daniel Rainer
ab1307c63b Do not mmap with len 0
`mmap` should fail when the length argument is 0. Checking this in advance
allows returning early, without performing unnecessary syscalls.

This also fixes an issue observed on FreeBSD 13.2 where `mmap` does not always
fail when the length is 0, resulting in the `assert!(len > 0)` in
`MmapRegion::new` failing.
https://github.com/fish-shell/fish-shell/issues/11595
2025-06-21 22:19:27 +02:00
Johannes Altmanninger
ec66749369 __fish_complete_list: only unescape "$(commandline -t)"
Commit cd3da62d24 (fix(completion): unescape strings for __fish_complete_list,
2024-09-17) bravely addressed an issue that exists in a lot of completions.
It did so only for __fish_complete_list. Fair enough.

Unfortunately it unescaped more than just "$(commandline -t)".

This causes the problem described at
https://github.com/fish-shell/fish-shell/issues/11508#issuecomment-2889088934
where completion descriptions containing a backslash followed by "n" are
interpreted as newlines, breaking the completion parser.  Fix that.

(cherry picked from commit 60881f1195)
2025-06-21 18:58:33 +02:00
Johannes Altmanninger
6e9e33d81d __fish_complete_list: strip "--foo=" prefix from replacing completions
Given a command line like

	foo --foo=bar=baz=qux\=argument

(the behavior is the same if '=' is substituted with ':').

fish completes arguments starting from the last unescaped separator, i.e.

	foo --foo=bar=baz=qux\=argument
			 ^
__fish_complete_list provides completions like

	printf %s\n (commandline -t)(printf %s\n choice1 choice2 ...)

This means that completions include the "--foo=bar=baz=" prefix.

This is wrong. This wasn't a problem until commit f9febba (Fix replacing
completions with a -foo prefix, 2024-12-14), because prior to that, replacing
completions would replace the entire token.
This made it too hard to writ ecompletions like

	complete -c foo -s s -l long -xa "hello-world goodbye-friend"

that would work with "foo --long fri" as well as "foo --long=frie".
Replacing the entire token would only work if the completion included that
prefix, but the above command is supposed to just work.
So f9febba made us replace only the part after the separator.

Unfortunately that caused the earlier problem.  Work around this.  The change
is not pretty, but it's a compromise until we have a better way of telling
which character fish considers to be the separator.

Fixes #11508

(cherry picked from commit 320ebb6859)
2025-06-21 18:58:17 +02:00
Johannes Altmanninger
bebf3c129f Merge pull request #11594 2025-06-21 18:54:22 +02:00
Johannes Altmanninger
60881f1195 __fish_complete_list: only unescape "$(commandline -t)"
Commit cd3da62d24 (fix(completion): unescape strings for __fish_complete_list,
2024-09-17) bravely addressed an issue that exists in a lot of completions.
It did so only for __fish_complete_list. Fair enough.

Unfortunately it unescaped more than just "$(commandline -t)".

This causes the problem described at
https://github.com/fish-shell/fish-shell/issues/11508#issuecomment-2889088934
where completion descriptions containing a backslash followed by "n" are
interpreted as newlines, breaking the completion parser.  Fix that.
2025-06-21 18:53:50 +02:00
Johannes Altmanninger
320ebb6859 __fish_complete_list: strip "--foo=" prefix from replacing completions
Given a command line like

	foo --foo=bar=baz=qux\=argument

(the behavior is the same if '=' is substituted with ':').

fish completes arguments starting from the last unescaped separator, i.e.

	foo --foo=bar=baz=qux\=argument
			 ^
__fish_complete_list provides completions like

	printf %s\n (commandline -t)(printf %s\n choice1 choice2 ...)

This means that completions include the "--foo=bar=baz=" prefix.

This is wrong. This wasn't a problem until commit f9febba (Fix replacing
completions with a -foo prefix, 2024-12-14), because prior to that, replacing
completions would replace the entire token.
This made it too hard to writ ecompletions like

	complete -c foo -s s -l long -xa "hello-world goodbye-friend"

that would work with "foo --long fri" as well as "foo --long=frie".
Replacing the entire token would only work if the completion included that
prefix, but the above command is supposed to just work.
So f9febba made us replace only the part after the separator.

Unfortunately that caused the earlier problem.  Work around this.  The change
is not pretty, but it's a compromise until we have a better way of telling
which character fish considers to be the separator.

Fixes #11508
2025-06-21 18:53:50 +02:00
Daniel Rainer
9bf6112b60 Add option to limit concurrency of tests
The main purpose of this is avoiding timeouts in CI.

Passing `--max-concurrency=n` to `test_driver.py` will result in at most `n`
tests running concurrently, where `n` is a positive integer.
Not specifying the argument preserves the old behavior of running all tests
concurrently without a limit.
2025-06-21 02:36:06 +02:00
Daniel Rainer
ef1a6aba26 Remove unused NEVER_MMAP 2025-06-20 00:44:50 +02:00
Peter Ammon
f3ebc68d5d Correct statvfs call to statfs
This was missed in the Rust port - C++ had statfs for MNT_LOCAL and not statvfs.
The effect of this is that fish never thought its filesystem was local on macOS
or BSDs (Linux was OK). This caused history race tests to fail, and also could
in rare cases result in history items being dropped with multiple concurrent
sessions.

This fixes the history race tests under macOS and FreeBSD - we weren't locking
because we thought the history was a remote file.

Cherry-picked from ba00d721f4
2025-06-19 15:36:19 -07:00
Peter Ammon
ba00d721f4 Correct statvfs call to statfs
This was missed in the Rust port - C++ had statfs for MNT_LOCAL and not statvfs.
The effect of this is that fish never thought its filesystem was local on macOS
or BSDs (Linux was OK). This caused history race tests to fail, and also could
in rare cases result in history items being dropped with multiple concurrent
sessions.

This fixes the history race tests under macOS and FreeBSD - we weren't locking
because we thought the history was a remote file.
2025-06-19 15:30:36 -07:00
Peter Ammon
fe8909e8f2 Fix an off-by-one error in reporting dropped history items
This was introduced in the Rust port. The original C++ was pretty gnarly to be
fair.
2025-06-19 11:08:50 -07:00
Lorenzo Albano
d369614ad9 Use $GIT_COMMON_DIR for stashes detection.
Within a linked worktree, `$GIT_DIR` and `$GIT_COMMON_DIR` have different
values (see [git-worktree docs](https://git-scm.com/docs/git-worktree#_details)).
The two serve different purposes, in case of stashes `$GIT_COMMON_DIR`
should be used, this way stash detection in the git prompt works also
when inside a `git worktree`.

Closes #11591
2025-06-19 11:13:09 +02:00
Johannes Altmanninger
4f4d941760 Merge pull request #11588 2025-06-19 11:13:09 +02:00
Johannes Altmanninger
9b19db5c5f Merge pull request #11492 2025-06-19 11:13:09 +02:00
Johannes Altmanninger
4f46d369c4 completions/git: fix spurious error when no subcommand is in $PATH
Systems like NixOS might not have "git-receive-pack" or any other "git-*"
executable in in $PATH -- instead they patch git to use absolute paths.
This is weird. But no reason for us to fail. Silence the error.

Fixes #11590
2025-06-19 11:13:09 +02:00
Daniel Rainer
977459949f Obtain history file path in HistoryImpl::save
Eliminates some code duplication between the two different saving
implementations. These changes are based on
https://github.com/fish-shell/fish-shell/pull/11492#discussion_r2149438316.

One extra change is included here, namely the early return on an empty file
name, indicating private mode. Without this, `history_path.unwrap()` fails in
some tests. Returning early is probably what we want in such situations anyway.
2025-06-18 21:05:29 +02:00
Daniel Rainer
8cbcfc0b3a Stop caching whether to lock
The cached information might become outdated. It is important that all fish
processes use the same mutual exclusion logic (either `flock`-based or the
fallback), because the two methods do not provide mutual exclusion from one
another.
Avoiding caching makes the behavior independent on previous system states,
resulting in fish instances performing file operations at the same time to use
the same locking logic.

More detailed discussion in
https://github.com/fish-shell/fish-shell/pull/11492#discussion_r2134543447
2025-06-18 21:05:00 +02:00
Daniel Rainer
77738fd646 Remove obsolete comments
If arguments always have the same value they do not need to be arguments.
For `HistoryImpl::add`, the comments are incorrect (assuming they should
indicate the value of the argument), since both arguments can be true or false
independently.
`History::add` is only called at one location in a test with a constant value of
`false` for `pending`. This might mean that the parameter could be deleted, or
maybe even the entire function, if testing can work without it.
2025-06-18 20:54:38 +02:00
Daniel Rainer
f438e80f9b Use shared file locking logic
Both history files and universal variables files are accessed by multiple
processes, which need a way to synchronize their accesses.
This synchronization logic is mixed in with the logic for reading and updating
the files' contents, which results in messy code, duplicated locking
implementations, and inconsistencies.
Moreover, the existing implementations are flawed which has resulted in file
corruption (e.g. https://github.com/fish-shell/fish-shell/issues/10300).

The new approach separates the synchronization logic from the rest.

There are two approaches to synchronization.
- The primary one is using `flock(2)` to lock the directory containing the file
  which requires synchronized access. We do not lock the file holding the data
  directly because this file might be replaced, which can result in locking
  succeeding when it should block. Locking the directory solves this problem.
  To avoid inconsistent file states, changes are first written to a temporary
  file, which is then renamed into place while still holding the lock.
- In some situations `flock` locks are unavailable or take a long time. This
  mostly applies to remote file systems. If we think that a directory is located
  on a remote file system, we do not attempt to use `flock`.
  As a fallback, we have a lockless approach, which uses file metadata (device,
  inode, size, ctime, mtime) to identify file versions.
  We then read from the file, write the updated data to a temporary file,
  check if the file id for the path is the same as before we started reading,
  and if so we rename the temporary file such that it replaces the old file.
  Note that races are possible between the file id check and the rename syscall.
  If we detect a file id mismatch, we retry, up to a predetermined number of
  attempts.

The operations which should be performed are passed to the functions handling
synchronization as `Fn`s. Because we might have to run these operations
repeatedly when retrying, they should be executable arbitrarily often without
causing side-effects relevant to the program. This requires some changes to
functions accessing these files. In many cases, they have to work with
non-mutable references, which requires that they return the data which should be
updated in the program state, instead of directly assigning to the appropriate
location.

Locking via `O_EXLOCK`, which was used for the universal variable file, is no
longer supported. That version of locking locks the file operated on directly,
instead of its directory. According to the man pages of {Open,Free,Net}BSD
and macOS, these locks have flock semantics. So if flock is available, we can
use it, and if it is not, the `O_EXLOCK` flag does not help.
2025-06-18 20:48:27 +02:00
Daniel Rainer
ccb9c8225f Remove CHAOS_MODE
This is dead code. It is never set to true.
2025-06-18 20:31:28 +02:00
Daniel Rainer
33b651ad91 Get remoteness of correct directory
The history file is stored in the data dir, not the config dir, so the
remoteness of the former is the one which matters.
2025-06-18 20:31:28 +02:00
Daniel Rainer
a20712b51d Avoid mutable references for mmap creation
This is preparatory work for refactoring the file synchronization approach.

`read_exact` will fail if the file length does not match the expected one, which
means zero padding is useless. (On any reasonable OS it was also useless before,
because anonymous memory mapping would be zero-pages.)
2025-06-18 20:31:28 +02:00
Daniel Rainer
da426a1b03 Improve error handling in src/history/file.rs
Use `std::io::Result` instead of `Option` as the return type where appropriate,
to allow for better-informed error handling.

Remove explicit checks about the length to be mapped being 0.
From `mmap(3)`:
> If len is zero, mmap() shall fail and no mapping shall be established.
2025-06-18 20:31:28 +02:00
Daniel Rainer
fbb2fcdb06 Improve error handling in src/history.rs
This allows for more informative error handling.

In some cases, the `?` operator can now be applied sensibly instead of more
verbose local error handling.
2025-06-18 20:31:28 +02:00
Daniel Rainer
537b1c3dd5 Extract code for creating temporary files
There is no need for separate implementations for history and uvar file
handling. Having a shared implementation ensures that creating temporary files
is handled consistently and removes clutter from the source files which want to
create temporary files.
2025-06-18 20:31:28 +02:00
Daniel Rainer
b4d0538892 Reduce state mutation in load_from_file
This is another step towards making the `load_from_file` function callable
without modifying internal data of `self`. Instead, the required updates for
a successful load should be returned.

For now, `self.last_read_file_id` is still modified
within `load_from_file`, which means it still needs a mutable reference to
`self`.
2025-06-18 20:31:28 +02:00
Daniel Rainer
9222381769 Update self.vars outside of acquire_variables
This is done as a step towards enabling loading from the variables file without
affecting internal variables, such that retrying becomes possible without
issues.
2025-06-18 20:31:28 +02:00
Daniel Rainer
f2f54919af Remove unnecessary mut specifier 2025-06-18 20:31:28 +02:00
Daniel Rainer
f40e31f675 Return callbacks via return value
Callbacks are generated by
`src/env_universal_common.rs:generate_callbacks_and_update_exports`.
This function only pushes to the `Vec`, so the content of the `Vec` passed to it
does not matter within this function.
The generated callbacks are used in `src/env/environment.rs:universal_sync`,
which calls `generate_callbacks_and_update_exports` via
`EnvUniversal::sync`,
(optionally `EnvUniversal::load_from_path_narrow`),
`EnvUniversal::load_from_file`.

The only other code making use of these callbacks is in tests.
Because the only real use passes an empty `Vec`, there is no need to to pass the
`Vec` as a mutable reference to the function at all. Instead, we create the `Vec`
in `generate_callbacks_and_update_exports` and return it from there, which is
what the new code does.

This change is made because we want loading from a file to be performed without
mutating data which does not come directly from the file.
Then, we can safely retry loading from the file as many times as we want,
without worrying about side-effects on our data structures.
We want to be able to do this in the case where we cannot properly lock the file
and fall back to lockless reading, where we check file metadata before and after
reading to detect modifications, and retry if modifications are detected.

This fallback logic is not in place yet, and further changes are required for
side-effect free loading.
2025-06-18 20:31:28 +02:00
Fabio José Bohnenberger
43fbfa9a0f Extract __fish_cached function (#11526)
- extract __fish_cached function
- add cache for winetricks verb completion
2025-06-18 10:29:00 +02:00
Daniel Rainer
f6dbf17446 Decode WString in FLOG
This allows converting non-UTF-8-conforming bytes from their PUA encoding back
to the original bytes before outputting.

Due to the way Rust handles trait implementations, we cannot use
`impl<T: std::fmt::Display> FloggableDisplay for T`
anymore, as that would result in a conflicting implementation for the types
which get a custom implementation.
Instead, explicitly implement the trait for all types which need it.
2025-06-17 23:37:59 +02:00
Johannes Altmanninger
06578bd03d Merge pull request #11565 2025-06-16 12:25:08 +02:00
Johannes Altmanninger
6491518b97 Merge pull request #11571 2025-06-16 12:23:39 +02:00
Johannes Altmanninger
7e03bebf97 Merge pull request #11582 2025-06-16 12:23:21 +02:00
Peter Ammon
941701da3d Restore some async-signal discipline to SIGTERM
The changes to enable terminal protocols (CSI-U, etc) also attempts to
re-disable these when fish exits. In particular it attempts to disable these
from a SIGTERM handler.

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

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

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

Bravely remove these, except in the case where we got SIGHUP and we don't want
to trigger SIGTTIN or SIGTTOU.
2025-06-15 18:09:53 -07:00
Daniel Rainer
a317a6acd9 Unconditionally include gettext message
Definitions of localizable strings should not be guarded by `cfg`, because then
they might not end up being exported, depending on the compilation config.
2025-06-15 23:43:31 +02:00
Daniel Rainer
d3f287c520 Retain attributes in localizable_consts
These attributes (such as doc comments) should be retained.
2025-06-15 23:43:31 +02:00
Daniel Rainer
80131acff2 Add PO file update check to tests
The PO file updates can now run in a normal test, eliminating the need for
special handling.

Rename the `check-translations.fish` script, to clarify which part of the checks
happens in it.
2025-06-15 23:43:31 +02:00
Daniel Rainer
85fb937a4d Add --use-existing-template argument
This is intended to allow translation updates in contexts where building within
the `fish_xgettext.fish` script is undesirable.

Specifically, this allows checking for PO file updates in the tests run by
`test_driver.py`. Because these use a tmpdir for `$HOME`, building within such a
test requires installing the entire Rust toolchain and doing a clean build,
which is a waste of resources.
With this argument, it is possible to build the template before running the
tests and passing the file path into the script.
2025-06-15 23:43:31 +02:00
Daniel Rainer
1e571263a0 Ensure translation script is CWD-independent 2025-06-15 23:43:31 +02:00
Daniel Rainer
1a0a6f544d Do not export test strings
When including the tests in the build, string literals passed to `wgettext!`
would be included in the gettext template file, which we do not want here,
because the strings should not be localized for this test.
2025-06-15 23:43:31 +02:00
Daniel Rainer
413ce9fdb3 Call cursor_position() lazily
This function does not need to run when the pager is not focused. Calling it
lazily eliminates the overhead of calling it when it is not needed.
This code runs on each keypress when entering a command, so it makes sense to
keep it as lean as possible. (At the very least it avoids spam when trying to
debug/analyze gettext behavior.)
2025-06-15 23:16:22 +02:00
Daniel Rainer
03e3d0aa3f Split up sphinx HTML and MAN file generation
This test is the one with the longest runtime. Splitting the two targets into
separate tests allows them to run in parallel, which can speed up the tests.
2025-06-15 18:44:58 +02:00
Peter Ammon
00c528c13f Remove parser.assert_can_execute
We don't need this; Rust's Send safety means that a Parser never changes its
thread.
2025-06-14 16:05:54 -07:00
Peter Ammon
60dbb9c8ba Fix a misleading comment. 2025-06-14 15:18:18 -07:00
Peter Ammon
2104f8a18a Remove Rc from Parser's vars
This will help enable Parser to be Send, which will be important for
concurrent execution.
2025-06-14 13:59:17 -07:00
Peter Ammon
6f18a362e6 Remove the process argument to ProcPerformer
Processes can't cross threads, so this doesn't belong here.
Preparation for concurrent execution.
2025-06-14 13:34:04 -07:00
Peter Ammon
d67fdd1f02 Make Job store boxed process list instead of Vec 2025-06-14 12:04:52 -07:00
Creeperxie
d92bb57418 Add description for gzip -d completion 2025-06-14 10:59:44 -07:00
Peter Ammon
eb4cec1fe2 Clean up pgroup assignment and tighten up some post-fork code 2025-06-14 10:34:33 -07:00
Peter Ammon
ef2e30cdc1 Clean up pids
Use OnceLock more often in place of atomics. Tighten up async signal safety.
2025-06-14 10:34:27 -07:00
Peter Ammon
294d589d2f Clean up ProcStatus
Don't need all of these atomics.

In particular use OnceLock in InternalProc as this doesn't doesn't need
locking if there's only a single writer.
2025-06-14 10:34:20 -07:00
Peter Ammon
415232631a Continued refactoring of some exec functions 2025-06-14 10:34:15 -07:00
Peter Ammon
e10a12c0f2 Rationalize certain edge cases for function execution
This concerns edge cases when executing a function. Historically, when we parse
fish script, we identify early whether or not it's a function; but only record
the function's name and not its properties (i.e. not its source).

This means that the function can change between parsing and execution. Example:

    function foo; echo alpha; end
    foo (function foo; echo beta; end)

This has historically output "beta" because the function is replaced as part of
its own arguments.

Worse is if the function is deleted:

    function foo; echo alpha; end
    foo (functions --erase foo)

This outputs an error but in an awkward place; that's OK since it's very rare.

Let's codify this behavior since someone might be depending on it.
2025-06-14 10:34:10 -07:00
Peter Ammon
6fffb76937 Move Process's block node into its Type
Previously, for Processes which were BlockNodes, we stored the node separately
in the Process via an Option; just promote this to a real field of the
ProcessType::BlockNode.

No user visible changes expected.
2025-06-14 10:34:05 -07:00
Peter Ammon
36e385e1fb Make ProcessType not Eq or PartialEq
We are going to give it fields shortly.
2025-06-14 10:33:58 -07:00
Peter Ammon
e598010020 Clean up some Node representations
Use NodeRef more pervasively, leading to simplifications.
2025-06-14 10:33:53 -07:00
Peter Ammon
eba4c906ae Minor cleanup of process creation
Remove some useless types and unnecessary boxing.
2025-06-14 10:33:48 -07:00
Peter Ammon
fbb4a8d853 Adopt Rust naming conventions in JobControl and ProcessType 2025-06-14 10:33:46 -07:00
Peter Ammon
10e525c49c Fix rustdoc warnings on macOS 2025-06-14 10:32:41 -07:00
Johannes Altmanninger
ec27b418e4 test_driver: support Python 3.8 for now
Our docker tests are currently broken since we no longer seem to install
"build_root".  When I sidestep this issue for now and run

	sudo docker/docker_run_tests.sh --shell-before docker/focal.Dockerfile
	cd /fish-source
	CARGO_TARGET_DIR=$HOME/out
	tests/test_driver.py ~/out/debug

the test driver fails in various ways.
This is because Ubuntu Focal provides Python 3.8.

Fix some of the typing incompatibilities.  Fix a hang in tests like
"tests/checks/init-command.fish"; apparently fish believes it's interactive
because Python 3.8's create_subprocess_shell() makes the fish child believe
that stdin is a TTY, so it's implicitly interactive.
2025-06-13 15:04:31 +02:00
Johannes Altmanninger
535a09a5b3 Silence error in check-all-fish-files
Our docker/docker_run_tests.sh script runs tests in a container with the fish
source tree mounted as read-only.  We have a hack to speed up repeated runs
of the check-all-fish-files test that assumes the source tree is writable.
Paper over this by silencing the error for now.

  There were no remaining checks left to match stderr:1:
    touch: cannot touch '/fish-source/tests/.last-check-all-files': Read-only file system
2025-06-13 14:56:02 +02:00
Johannes Altmanninger
4be17bfefb fixup! Extract config path module. NFC
Fix cargo (non-cmake) build.
2025-06-13 12:17:10 +02:00
Johannes Altmanninger
6fd0025f38 Make LOCALEDIR relocatable as well
As explained in c3740b85be (config_paths: fix compiled-in locale dir,
2025-06-12), fish is "relocatable", i.e. "mv /usr/ /usr2/" will leave
"/usr2/bin/fish" fully functional.

There is one exception: for LOCALEDIR we always use the path determined at
compile time.
This seems wrong; let's use the same relocatable-logic as for other paths.

Inspired by bf65b9e3a7 (Change `gettext` paths to be relocatable (#11195),
2025-03-30).
2025-06-13 11:52:46 +02:00
Johannes Altmanninger
052fc18db9 Extract config path module. NFC
This is the "code movement" part of bf65b9e3 ("Change `gettext` paths to be
relocatable (#11195)").

While at it, fix some warnings.
2025-06-13 11:52:46 +02:00
Johannes Altmanninger
d584f36f5d Remove support for elf-patching LOCALEDIR
Commit bf65b9e3a7 (Change `gettext` paths to be relocatable (#11195),
2025-03-30) is difficult to follow because it combines code movement with
two behavior changes. Our parent commit fixed the first behavior change.

The second behavior change made us tolerate trailing NUL bytes in LOCALEDIR.
This was motivated because conda wants to use binary patching to change
the effective prefix at runtime, presumably so the user can move around the
"fish" executable program arbitraily.

This turned out to be unnecessary because fish is already "relocatable"
(see our parent commit).

Let's remove the special case for LOCALEDIR. Treat it like other paths.

Closes #11574
Closes #11474
2025-06-13 11:22:15 +02:00
Johannes Altmanninger
c3740b85be config_paths: fix compiled-in locale dir
Fish uses this logic to find paths to functions etc.:

1. if $(status fish-path) starts with $CARGO_MANIFEST_DIR,
   we use $CARGO_MANIFEST_DIR/share etc.

   Aside: this also has the unintended effect that "cmake
   -DCMAKE_INSTALL_PREFIX=$PWD/prefix" will not use "$PWD/prefix/share" but
   "$PWD/share", at least since eacbd6156d (Port and adopt main written in
   Rust, 2023-08-18).

2. Else if $(status fish-path) ends with "bin/fish",
   and $(status fish-path)/../share/fish exists, we use that, since 4912967eab
   (Large set of changes related to making fish relocatable, and improving
   the build and install story, 2012-07-08)

3. Else if $(status fish-path) ends in "fish" (which is very likely),
   and $(status fish-path)/share exists, we use that, since
   c2a8de4873 (Make fish find config directories in source tree, 2016-09-23).
   I think this is for running (without installing) in-tree builds ("cmake .");
   this is not recommended but it is used, see
   https://github.com/fish-shell/fish-shell/pull/10330

4. If none of the above worked, either because the fish binary has been
   moved into a weird directory, or if we fail to get $(status fish-path)
   (e.g. on OpenBSD, where "argv[0]" is not available),
   then we fall back to reasonable default paths determined at compiled
   time.

   These paths include data_dir=$PREFIX/share.
   We recently added locale_dir too in bf65b9e3a7 (Change `gettext` paths
   to be relocatable (#11195), 2025-03-30).

In case 1, we use               locale: manifest_dir.join("share/locale"),
In case 2 and 3, we use         locale: data_dir.join("locale"),
In case 4, we use               locale: data_dir.join("share"),

The last one seems wrong (there is not "/usr/share/share"). Fix that.

Alternatively, we could revert bf65b9e3a7 (and redo the parts we want to keep).
2025-06-13 11:22:15 +02:00
Johannes Altmanninger
d840fd9a7f config_paths: remove code clone
Another evident problem from bf65b9e3a7 (Change `gettext` paths to be
relocatable (#11195), 2025-03-30). How disrespectful.
2025-06-13 11:22:15 +02:00
Johannes Altmanninger
592b059c30 config_paths: inline function 2025-06-12 11:20:56 +02:00
Johannes Altmanninger
86b9a0b876 test_driver: use isinstance chain to replace Python 3.10's match statement
We still want to support Python 3.9 (debian-oldstable); it's still used by
our (outdated) FreeBSD CI, and, more importantly by (nightly) builders on OBS.

Ref: https://github.com/fish-shell/fish-shell/pull/11560#discussion_r2134556573
2025-06-12 11:20:55 +02:00
Johannes Altmanninger
6822a772fb Merge pull request #11573 2025-06-12 10:29:11 +02:00
Daniel Rainer
912d93c99d Remove tmpdirs at the end of tests 2025-06-11 22:33:32 +02:00
Johannes Altmanninger
74f4742565 Merge pull request #11567 2025-06-11 14:02:07 +02:00
Johannes Altmanninger
a47dcad1ea Clean up staticbuild CI action
- This action does not use CMake or CTest, so remove associated environment variables.
- Remove a seemingly unnecessary check

Tested with "gh --repo krobelus/fish-shell workflow run staticbuild.yml --ref=tmp", see
https://github.com/krobelus/fish-shell/actions/runs/15583255106/job/43883294589
2025-06-11 13:22:09 +02:00
Integral
63a08e53e5 Replace some PathBuf with Path avoid unnecessary heap allocation (#10929)
(cherry picked from commit b19a467ea6)
2025-06-11 11:38:53 +02:00
Erick Howard
62ac23453e Code cleanup in src/bin/fish.rs to make it more idiomatic (#10975)
Code cleanup in `src/bin/fish.rs` to make it more idiomatic

(cherry picked from commit 967c4b2272)
2025-06-11 11:31:05 +02:00
Johannes Altmanninger
69bd7cc9a5 Merge pull request #11561 2025-06-11 11:14:56 +02:00
Daniel Rainer
b56ee16aa9 Parallelize test_driver.py
This uses Python's `asyncio` to run tests in parallel, which speeds up test
execution significantly.

The timeout is removed. It would be possible to add a timeout to
`asyncio.as_completed()` if we want that.
2025-06-10 20:11:43 +02:00
Daniel Rainer
679cef9c0e Add async versions of littlecheck tests
These will be used by a parallelized version of `test_driver.py`.

The old, non-async versions are kept, but just a wrappers around the async
versions.
2025-06-10 20:11:43 +02:00
Daniel Rainer
01b623efce Do not modify env vars in test_driver.py
Such modifications would break tests when they run concurrently, as they will
when this script is parallelized using `asyncio`.
2025-06-10 20:11:36 +02:00
Daniel Rainer
aa627ea935 Allow specifying env for running test
This can be used instead of the default operating system variables.
Useful for running multiple tests which need different environments.
2025-06-10 19:37:47 +02:00
Daniel Rainer
c15a900f31 Fail on compilation failure 2025-06-10 19:37:47 +02:00
Daniel Rainer
1136e656e0 Remove --cachedir for test_driver.py
This was only used to cache `fish_test_driver`, which can be built in a few tens
of milliseconds on most systems.

This avoids using outdated binaries for testing and simplifies the code and its
usage.
2025-06-10 19:37:47 +02:00
Daniel Rainer
456c9254fa Annotate more run_test parameters
This helps with static analysis.

Rename the second argument to clarify what it refers to.
2025-06-10 19:37:47 +02:00
Daniel Rainer
ebc16e51d7 Build fish_test_helper at most once
If no `--cachedir` is specifed for `test_driver.py`, it would build
`fish_test_helper` once per test it runs. This is unnecessary. Instead, build it
once in the beginning before running any tests and then use the binary in all
tests.
2025-06-10 19:37:41 +02:00
Johannes Altmanninger
c052beb4dd Fix build on Rust 1.70 2025-06-10 17:56:21 +02:00
Daniel Rainer
2d7a6063b9 Remove unnecessary trailing space 2025-06-10 17:08:44 +02:00
Daniel Rainer
7c57b746d0 Use Path for some paths
The added type annotations help with static analysis and constructing paths gets
some syntactic sugar.
2025-06-10 17:07:19 +02:00
Johannes Altmanninger
e0cabacdaa kitty keyboard protocol: fall back to base layout key
On terminals that do not implement the kitty keyboard protocol "ctrl-ц" on
a Russian keyboard layout generally sends the same byte as "ctrl-w". This
is because historically there was no standard way to encode "ctrl-ц",
and the "ц" letter happens to be in the same position as "w" on the PC-101
keyboard layout.

Users have gotten used to this, probably because many of them are switching
between a Russian (or Greek etc.) and an English layout.

Vim/Emacs allow opting in to this behavior by setting the "input method"
(which probably means "keyboard layout").

Match key events that have the base layout key set against bindings for
that key.

Closes #11520

---

Alternatively, we could add the relevant preset bindings (for "ctrl-ц" etc.)
but
1. this will be wrong if there is a disagreement on the placement of "ц" between two layouts
2. there are a lot of them
3. it won't work for user bindings (for better or worse)

(cherry picked from commit 7a79728df3)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
59b9f57802 fish_key_reader: unopinionated description for bind notation variants
As explained in the parent commit, "alt-+" is usually preferred over
"alt-shift-=" but both have their moments. We communicate this via a comment
saying "# recommended notation". This is not always true and not super helpful,
especially as we add a third variant for #11520 (physical key), which is
the recommended one for users who switch between English and Cyrillic layouts.

Only explain what each variant does. Based on this the user may figure out
which one to use.

(cherry picked from commit 4cbd1b83f1)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
65fc2b539c Fix some invalid assertions parsing keys
For example the terminal sending « CSI 55296 ; 5 u » would crash fish.

(cherry picked from commit c7391d1026)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
3f1add9e21 Sanitize some inputs in CSI parser
This was copied from C++ code but we have overflow checks, which
forces us to actually handle errors.

While at it, add some basic error logging.

Fixes #11092

(cherry picked from commit 4c28a7771e)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
68d2cafa6e input: remove unnecessary check in bracketed paste code path
When "self.paste_is_buffering()" is true, "parse_escape_sequence()" explicitly
returns "None" instead of "Some(Escape)".  This is irrelevant because this
return value is never read, as long as "self.paste_is_buffering()" remains
true until "parse_escape_sequence()" returns, because the caller will return
early in that case. Paste buffering only ends if we actually read a complete
escape sequence (for ending bracketed paste).

Remove this extra branch.

(cherry picked from commit e5fdd77b09)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
b9d9e7edc6 Match bindings with explicit shift first
The new key notation canonicalizes aggressively, e.g.  these two bindings
clash:

	bind ctrl-shift-a something
	bind shift-ctrl-a something else

This means that key events generally match at most one active binding that
uses the new syntax.

The exception -- two coexisting new-syntax binds that match the same key
event -- was added by commit 50a6e486a5 (Allow explicit shift modifier for
non-ASCII letters, fix capslock behavior, 2025-03-30):

	bind ctrl-A 'echo A'
	bind ctrl-shift-a 'echo shift-a'

The precedence was determined by definition order.
This doesn't seem very useful.

A following patch wants to resolve #11520 by matching "ctrl-ц" events against
"ctrl-w" bindings. It would be surprising if a "ctrl-w" binding shadowed a
"ctrl-ц" one based on something as subtle as definition order.  Additionally,
definition order semantics (which is an unintended cause of the implementation)
is not really obvious.  Reverse definition order would make more sense.

Remove the ambiguity by always giving precedence to bindings that use
explicit shift.

Unrelated to this, as established in 50a6e486a5, explicit shift is still
recommended for bicameral letters but not typically for others -- e.g. alt-+
is typically preferred over alt-shift-= because the former also works on a
German keyboard.

See #11520

(cherry picked from commit 08c8afcb12)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
6b17ec7dae Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")

This has problems:

A. Our bind notation canonicalization only works for 26 English letters.
   For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
   We could try to fix that but this depends on the keyboard layout.
   For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
   layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
   This is due a quirk in the kitty keyboard protocol[^1].  This means that
   fish_key_reader's canonicalization doesn't work (unless we call toupper()
   ourselves).

I think we want to support both notations.

It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".

	1. bind ctrl-shift-i do-something
	2. bind ctrl-shift-I do-something
	3. bind ctrl-I do-something
	4. bind ctrl-i do-something

Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.

For keys that are not affected by capslock, problem B does not apply.  In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.

Example output for some key events:

	$ fish_key_reader -cV
	# decoded from: \e\[61:43\;4u
	bind alt-+ 'do something' # recommended notation
	bind alt-shift-= 'do something'
	# decoded from: \e\[61:43\;68u
	bind alt-+ 'do something' # recommended notation
	bind alt-shift-= 'do something'

	# decoded from: \e\[105:73\;6u
	bind ctrl-I 'do something'
	bind ctrl-shift-i 'do something' # recommended notation
	# decoded from: \e\[105\;70u
	bind ctrl-shift-i 'do something'

Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key.  We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.

The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.

Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".

[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>

(cherry picked from commit 50a6e486a5)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
08d796890a Stop accepting "bind shift-A"
This notation doesn't make sense, use either A or shift-a.  We accept it
for ASCII letters only -- things like "bind shift-!" or "bind shift-Ä"
do not work as of today, we don't tolerate extra shift modifiers yet.
So let's remove it for consistency.

Note that the next commit will allow the shift-A notation again, but it will
not match shift-a events.

(cherry picked from commit 7f25d865a9)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
4f98ef36f6 Extract KeyEvent type
The be used in the grandchild commit.

(cherry picked from commit 855a1f702e)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
a09c78491f Extract function for creating key event with modifiers
(cherry picked from commit fabbbba037)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
02932d6b8c Extract constant for the number of function keys
Switch to fish_wcstoul because we want the constant to be unsigned.
It's u32 because most callers of function_key() want that.

(cherry picked from commit e9d1cdfe87)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
7cca98bda2 Fix regression decoding function keys
Commit 109ef88831 (Add menu and printscreen keys, 2025-01-01)
accidentally broke an assumption by inverting f1..f12.  Fix that.

Fixes #11098

(cherry picked from commit d2b2c5286a)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
b77fc28692 Add menu and printscreen keys
These aren't typically used in the terminal but they are present on
many keyboards.

Also reorganize the named key constants a bit.  Between F500 and
ENCODE_DIRECT_BASE (F600) we have space for 256 named keys.

(cherry picked from commit 109ef88831)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
3475531ef7 Also ignore invalid recursive escape sequences
We parse "\e\e[x" as alt-modified "Invalid" key.  Due to this extra
modifier, we accidentally add it to the input queue, instead of
dropping this invalid key.

We don't really want to try to extract some valid keys from this
invalid sequence, see also the parent commit.

This allows us to remove misplaced validation that was added by
e8e91c97a6 (fish_key_reader: ignore sentinel key, 2024-04-02) but
later obsoleted by 66c6e89f98 (Don't add collateral sentinel key to
input queue, 2024-04-03).

(cherry picked from commit 84f19a931d)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
8222ed891b Stop parsing invalid CSI/SS3 sequences as alt-[/alt-o
This situation can be triggered in practice inside a terminal like tmux
3.5 by running

	tmux new-session fish -C 'sleep 2' -d reader -o log-file

and typing "alt-escape x"

The log shows that we drop treat this as alt-[ and drop  the x on the floor.

	reader: Read char alt-\[ -- Key { modifiers: Modifiers { ctrl: false,
	alt: true, shift: false }, codepoint: '[' } -- [27, 91, 120]

This input ("\e[x") is ambiguous.

It looks like it could mean "alt-[,x".  However that conflicts with a
potential future CSI code, so it makes no sense to try to support this.

Returning "None" from parse_csi() causes this weird behavior of
returning "alt-[" and dropping the rest of the parsed sequence.
This is too easy; it has even crept into a bunch of places
where the input sequence is actually valid like "VT200 button released"
but where we definitely don't want to report any key.

Fix the default: report no key for all unknown sequences and
intentionally-suppressed sequences.  Treat it at "alt-[" only when
there is no input byte available, which is more or less unambiguous,
hence a strong enough signal that this is a actually "alt-[".

(cherry picked from commit 3201cb9f01)
2025-06-10 16:50:00 +02:00
Johannes Altmanninger
f787e6858c Fix tests/checks/autoload.fish
Apparently this test runs with "build/tests" as CWD when run with cmake.
2025-06-10 16:50:00 +02:00
Daniel Rainer
64442cb464 Use with syntax for tmpdir root 2025-06-10 16:20:56 +02:00
Lorenzo Albano
07ead04890 Improve fish_git_prompt stashes detection.
When the informative status is disabled, the stashstate variable was
set to 1 if the $git_dir/logs/refs/stash file existed and was readable,
even if the file itself was empty (i.e., no stashes). Now the stashstate
variable is set only if the file is NOT empty.
2025-06-09 13:16:24 +02:00
Johannes Altmanninger
5346d3d491 check-all-fish-files: don't glob the entire worktree
Commit 22c0054c1e (Add check to test all fish files with -n, 2020-02-17)
added a test that runs "fish --no-execute" on all fish files in share/**.fish

Commit 329cd7d429 (Make functions, completions and tests resilient to
running on an embed-data fish, 2025-03-25) change this to run it on **.fish.

Evidently "the tests are exempt because they contain syntax errors" is no
longer true - this is because we have since changed those files to recursively
run 'fish -c "syntax-error"', which makes it easier to test for multiple
syntax-errors in a single test file. Remove the comment.

Globbing everything seems a bit crass, and there's no explicit
motivation.

	$ time find -name '*.fish' >/dev/null
	Executed in  431.93 millis
	$ time find * -name '*.fish' >/dev/null
	Executed in   39.98 millis

Let's go back to testing only directories where we currently have Git-tracked
fish files.  This makes uncached "check-all-fish-files.fish" go from 26
seconds to 5 seconds.
2025-06-09 10:28:49 +02:00
Johannes Altmanninger
138c6c4c40 check-all-fish-files: remove unneeded fallback
According to commit 8a07db8e8f (Revert "Revert "Speed up check-all-fish-files
when executed locally"", 2021-03-06), we can assume "find -newer" is supported.
2025-06-09 10:28:49 +02:00
Johannes Altmanninger
f8751a4f97 check-all-fish-files: fix caching logic
check-all-fish-files takes a long time (>20 seconds here); which is why we
have a (hacky) optimization to avoid checking files we already checked.
This optimization hasn't worked since commit e96b6e157c (Remove TMPDIR
dependency from tests/, 2021-07-30) which started each test invocation in
a private tmpdir. Fix that.

The optimization is useful because this test is, by far, the bottleneck
for parallel test execution (#11561):

	$ cargo b && time tests/test_driver.py target/debug
	...
	checks/tmux-complete.fish                      PASSED    8465 ms
	checks/check-completions.fish                  PASSED   10948 ms
	checks/check-sphinx.fish                       PASSED   12949 ms
	checks/check-all-fish-files.fish               PASSED   29828 ms
	200 / 200 passed (0 skipped)
	________________________________________________________
	Executed in   31.00 secs    fish           external
	   usr time   81.02 secs  462.00 micros   81.02 secs
	   sys time   26.41 secs  272.00 micros   26.41 secs

A cache miss for check-all-fish-files.fish takes 24 seconds (though the
grandchild commit will speed this up), a cache hit 0.5 seconds.
2025-06-09 10:28:49 +02:00
Johannes Altmanninger
cdc2db5eae Update translations for git completions 2025-06-08 14:07:17 +02:00
Johannes Altmanninger
aea9cd6165 completions/git: sort stash completions after branches and others
Completions like "stash@{1}" don't give a lot of information, unlike local
branches which are sorted by recency so let's put those first.
2025-06-08 13:42:01 +02:00
Johannes Altmanninger
d0a490d76b completions/git: extract logic 2025-06-08 13:42:01 +02:00
Johannes Altmanninger
b4392f6f7d completions/git: extract function for adding arbitrary-revision-completion
Most Git commands take arbitrary revisions.  AFAICT, we usually want the same
order, e.g. list local branches before remote branches before commit IDs etc.
I think there is no particular reason why this order is inconsistent between
various subcommands.

Let's extract a function. This standardizes the order and adds various
revision-types that were missing for some subcommands.
2025-06-08 13:42:01 +02:00
Johannes Altmanninger
7f2f5bb2f4 completions/git: remove code clone 2025-06-08 13:26:12 +02:00
Johannes Altmanninger
c47ecf9677 completions/git: rebase --onto requires a revision argument 2025-06-08 13:21:55 +02:00
Johannes Altmanninger
cd45a8c5cc completions/git: add more special refs
These are things like .git/HEAD, i.e. the ones that are typically not
namespaced under .git/refs.  The list is taken from gitrevisions(7).
2025-06-08 13:21:55 +02:00
Johannes Altmanninger
99f78fb0b1 completions/git: fix copy-paste error
This variable is never defined. It was copied from Git's
contrib/completion/git-completion.bash where $match is probably equivalent
to $(commandline -t). I could not measure a significant speedup from passing
this filter to "git for-each-ref", so let's remove it for now.
2025-06-08 13:21:55 +02:00
Johannes Altmanninger
235108e2df CI rust_checks: cargo --workspace obsoletes --all
This flag exists since 1.39, see
https://doc.rust-lang.org/nightly/cargo/CHANGELOG.html#cargo-139-2019-11-07

While at it, remove the "--all" option from "cargo fmt",
since it's not needed for formatting the entire workspace,
and because it would also format path-dependencies, see
https://github.com/fish-shell/fish-shell/pull/11550#discussion_r2133674330
2025-06-08 13:21:12 +02:00
Johannes Altmanninger
e66b13ac3d Merge pull request #11560 2025-06-08 12:09:41 +02:00
Johannes Altmanninger
0c211cbffb Merge pull request #11564 2025-06-08 11:27:01 +02:00
Johannes Altmanninger
da5a394178 Merge pull request #11566 2025-06-08 11:25:41 +02:00
Johannes Altmanninger
59bfd3ba6c Merge pull request #11563 2025-06-08 11:25:32 +02:00
Daniel Rainer
631bde1081 Revert "Hide some calls to localize() via Deref/AsRef"
This reverts commit 894139933d.

Rationale in this comment thread:
https://github.com/fish-shell/fish-shell/pull/11547#discussion_r2133625349
2025-06-08 02:52:01 +02:00
cyclopentane
383d2aa3e9 Fix behaviour upon repeating a vi-mode t/T jump 2025-06-08 01:03:06 +02:00
Daniel Rainer
cac3d0ef16 Build before running style.fish
This makes the latest versions of fish_indent (and fish) available to
`style.fish`.
2025-06-07 20:21:29 +02:00
Daniel Rainer
4721ffe512 Remove unused variables 2025-06-07 19:31:06 +02:00
Daniel Rainer
df097b114c Put test tmpdirs under common root tmpdir
This is done to prepare for running the tests in parallel.
With this approach the root tmpdir can be created before any test starts, each
test can create its home dir under the root tmpdir,
and when all tests are done the root tmpdir can be deleted.
Deletion of per-test dirs is more difficult in an async context.
2025-06-07 19:30:44 +02:00
Daniel Rainer
2ebe3134cf Extract function for running tests
This is done to prepare for running tests concurrently.

Align output and prevent flushing stdout between test name and result.
2025-06-07 19:28:58 +02:00
David Adam
d663f553df document alt-s binding programs more clearly 2025-06-07 22:47:56 +08:00
Shayan
dcde198c94 completions/adb: add listing device files for exec-out subcommand 2025-06-07 22:44:57 +08:00
Daniel Rainer
b5c393dc39 Add simple script for running checks
This is intended as a way to run all available checks with a single command.

The script can be used locally and in CI. It is intended to replace
`cmake/Tests.cmake` (but this script also runs checks not present there).
At the moment, `ctest` is not used, which could be added to speed up tests.

Address and thread sanitizers are not run by this script.
2025-06-07 15:34:53 +02:00
Daniel Rainer
f0a54510c3 Format files using build_tools/style.fish 2025-06-07 15:34:53 +02:00
Daniel Rainer
d18d414745 Improve style.fish
Add flags to control behavior.
- `--check` to fail if changes would be made by formatters
- `--force` to skip the prompt about uncommitted changes

Fix behavior when `--all` is not specified. It used to operate on `$files`,
which did not get set in that case.

Not all fish files are considered, mainly because some tests might test how fish
behaves on weirdly formatted files.

For Rust files, `cargo fmt` is used when `--all` is specified.
The `--check` flag for `cargo fmt` is used when appropriate.

Do not try to build `fish_indent`. `make fish_indent` does not work anymore. Let
the user handle building and installing/setting `$PATH`.
2025-06-07 15:34:53 +02:00
Johannes Altmanninger
ac44b3da91 build_tools/fish_xgettext.fish: fix formatting 2025-06-07 11:15:44 +02:00
Johannes Altmanninger
0903e7a8f2 Merge pull request #11557 2025-06-07 10:20:30 +02:00
Johannes Altmanninger
894139933d Hide some calls to localize() via Deref/AsRef
As suggested in https://github.com/fish-shell/fish-shell/pull/11547#discussion_r2133625349
AsRef is needed for OutputStream::append which has this signature:

	pub fn append<Str: AsRef<wstr>>(&mut self, s: Str) -> bool
2025-06-07 10:20:30 +02:00
Johannes Altmanninger
1aec1d3955 Merge pull request #11547
Supersedes previous approaches:
Closes #11543
Closes #11536
2025-06-07 10:20:30 +02:00
Daniel Rainer
a138bc328b Fix eager cloning
https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
2025-06-07 00:22:52 +02:00
Daniel Rainer
b9583bb16a Document call sites of from_external_source
This is intended to provide information to programmers where localizations might
be coming from, and potentially help with analyzing issues with localizations.
2025-06-07 00:10:10 +02:00
Daniel Rainer
80033adcf5 Use LocalizableString for gettext
This new wrapper type can be constructed via macros which invoke the
`gettext_extract` proc macro to extract the string literals for PO file
generation.

The type checking enabled by this wrapper should prevent trying to obtain
translations for a string for which none exist.

Because some strings (e.g. for completions) are not defined in Rust, but rather
in fish scripts, the `LocalizableString` type can also be constructed from
non-literals, in which case no extraction happens.
In such cases, it is the programmer's responsibility to only construct the type
for strings which are available for localization.

This approach is a replacement for the `cargo-expand`-based extraction.

When building with the `FISH_GETTEXT_EXTRACTION_FILE` environment variable set,
the `gettext_extract` proc macro will write the messages marked for extraction
to a file in the directory specified by the variable.

Updates to the po files:
- This is the result of running the `update_translations.fish` script using the
  new proc_macro extraction. It finds additional messages compared to the
  `cargo-expand` based approach.
- Messages IDs corresponding to paths are removed. The do not have localizations
  in any language and localizing paths would not make sense. I have not
  investigated how they made it into the po files in the first place.
- Some messages are reordered due to `msguniq` sorting differing from `sort`.

Remove docs about installing `cargo-expand`
These are no longer needed due to the switch to our extraction macro.
2025-06-07 00:10:05 +02:00
Daniel Rainer
51a57870eb Make help error message localizable 2025-06-07 00:02:31 +02:00
Daniel Rainer
98b3ba5e8e Remove concat! macro from localized strings
This is done in preparation for a proc macro which extracts strings which are
passed to `gettext`. Because the `concat!` macro would get expanded after the
proc macro, the proc macro would still see the `concat!`, which it cannot
handle.
2025-06-07 00:02:14 +02:00
Johannes Altmanninger
08bf5c92a9 Merge pull request #11545 2025-06-06 12:54:55 +02:00
Johannes Altmanninger
a84048511e Merge pull request #11542 2025-06-06 11:54:45 +02:00
Fabian Boehm
a014166795 completions/nmcli: Complete at runtime
This used to get all the interfaces and ssids when the completions
were loaded. That's obviously wrong, given that ssids especially can, you know, change

(cherry picked from commit 9116c61736)

cherry-picking since this easy to trigger
(seen again in https://github.com/fish-shell/fish-shell/pull/11549)
2025-06-06 11:52:17 +02:00
Johannes Altmanninger
46e8f12dbf Merge pull request #11554 2025-06-05 14:28:36 +02:00
Dennis Huang
7fe92be405 Add --all option to path
- Add --all option to path
- Add tests
- Add doc
2025-06-05 14:10:47 +02:00
Chinmay Dalal
366034940f Add run0 to alt-s commands
https://www.freedesktop.org/software/systemd/man/devel/run0.html
Since everyone using a systemd distro will have this, it's added
at the end so that it's tried last
2025-06-04 20:00:52 +05:30
Daniel Rainer
6239cba1e4 Add dry-run mode to update_translations.fish
This mode is intended for testing if the PO files are up-to-date and
well-formed.

At the moment, we only check translations in CI, where this is not particularly
relevant. Once we no longer need `cargo-expand`
(e.g. via https://github.com/fish-shell/fish-shell/pull/11536)
we can extend the `check_translations.fish` test to run
`update_translations.fish --dry-run` and fail if the exit status is nonzero.
2025-06-02 03:18:25 +02:00
Daniel Rainer
75d243faaa Check rustdocs in CI
Setting `RUSTDOCFLAGS='-D warnings'` is needed to fail on warnings.
For `cargo test --doc` no equivalent option seems to exist.
See https://github.com/rust-lang/cargo/issues/14802.
2025-05-30 21:32:40 +02:00
Daniel Rainer
ba86028aaa Fix rustdoc warning 2025-05-30 21:32:40 +02:00
Johannes Altmanninger
f7e639504a completions/git: improve idempotency in case of double load
As mentioned in the previous few commits and in #11535, running
"set fish_complete_path ..."  and "complete -C 'git ...'"  may result in
"share/completions/git.fish" being loaded multiple times.

This is usually fine because fish internally erases all cached completions
whenever fish_complete_path changes.

Unfortunately there is at least global variable that grows each time git.fish
is sourced. This doesn't make a functional difference but it does slow
down completions.  Fix that by resetting the variable at load time.

(cherry picked from commit 4b5650ee4f)
2025-05-29 18:01:22 +02:00
Johannes Altmanninger
028b60cad6 Fix "set fish_complete_path" accidentally disabling autoloading
Commit 5918bca1eb (Make "complete -e" prevent completion autoloading,
2024-08-24) makes "complete -e foo" add a tombstone for "foo", meaning we
will never again load completions for "foo".

Due to an oversight, the same tombstone is added when we clear cached
completions after changing "fish_complete_path", preventing completions from
being loaded in that case.  Fix this by restoring the old behavior unless
the user actually used "complete -e".

(cherry picked from commit a7c04890c9)
2025-05-29 18:01:04 +02:00
Johannes Altmanninger
b11e22d905 Fix uvar file mtime force-update (Rust port regression)
When two fish processes rewrite the uvar file concurrent, they rely on the
uvar file's mtime (queried after taking a lock, if locking is supported) to
tell us whether their view of the uvar file is still up-to-date.  If it is,
they proceed to move it into place atomically via rename().

Since the observable mtime only updates on every OS clock tick, we call
futimens() manually to force-update that, to make sure that -- unless both
fish conincide on the same *nanosecond* -- other fish will notice that the
file changed.

Unfortunately, commit 77aeb6a2a8 (Port execution, 2023-10-08) accidentally
made us call futimens() only if clock_gettime() failed, instead of when
it succeeded. This means that we need to wait for the next clock tick to
observe a change in mtime.
Any resulting false negatives might have caused us to drop universal variable updates.

Reported in https://github.com/fish-shell/fish-shell/pull/11492#discussion_r2098948362

See #10300

(cherry picked from commit 8617964d4d)
2025-05-23 08:50:17 +02:00
Johannes Altmanninger
33f8415785 Fixup history file EINTR loop to actually loop
Fixes d84e68dd4f (Retry history file flock() on EINTR, 2025-05-20).

(cherry picked from commit 3867163193)
2025-05-20 17:19:12 +02:00
Johannes Altmanninger
5ccd155177 Retry history file flock() on EINTR
When locking the uvar file, we retry whenever flock() fails with EINTR
(e.g. due to ctrl-c).

But not when locking the history file.  This seems wrong; all other libc
functions in the "history_file" code path do retry.

Fix that. In future we should extract a function.

Note that there are other inconsistencies; flock_uvar_file() does not
shy away from remote file systems and does not respect ABANDONED_LOCKING.
This means that empirically probably neither are necessary; let's make things
consistent in future.

See https://github.com/fish-shell/fish-shell/pull/11492#discussion_r2095096200
Might help #10300

(cherry picked from commit 4d84e68dd4)
2025-05-20 15:32:56 +02:00
Johannes Altmanninger
0f8d3a5174 Revert "Temporarily enable history_file debug category by default"
Commit f906a949cf (Temporarily enable history_file debug category by default,
2024-10-09) enabled the "history_file" debug category by default to gather
more data.

Judging from
https://github.com/fish-shell/fish-shell/issues/10300#issuecomment-2876718382
the logs didn't help, or were at least not visible when logging to stderr
(due to reboot).

Let's disable "history_file" logs again to remove potential
noise if the file system is read-only, disk is full etc., see
https://github.com/fish-shell/fish-shell/pull/11492#discussion_r2094781120

See #10300

(cherry picked from commit 285a810814)
2025-05-20 15:31:57 +02:00
Johannes Altmanninger
c4a26cb2b1 builtin status: remove spurious newline from current-command (Rust port regression)
WHen "status current-command" is called outside a function it always returns
"fish". An extra newline crept in, fix that.

Fixes 77aeb6a2a8 (Port execution, 2023-10-08).
Fixes #11503

(cherry picked from commit e26b585ce5)
2025-05-20 12:33:01 +02:00
Johannes Altmanninger
7228cb15bf Include sys/statvfs.h for the definition of ST_LOCAL (Rust port regression)
See https://man.netbsd.org/statvfs.5.
According to https://github.com/NetBSD/src/blob/trunk/sys/sys/statvfs.h#L135,
NetBSD has "#define ST_LOCAL MNT_LOCAL".  So this commit likely makes no
difference on existing systems.

While at it
- comment include statements
- remove a code clone

See #11486

(cherry picked from commit d68f8bdd3b)
2025-05-16 08:21:56 +02:00
Alan Somers
d5b46d6535 Fix remote filesystem detection on FreeBSD
Need an extra include to get the definition of MNT_LOCAL

Fixes #11483

(cherry picked from commit 7f4998ad9b)
2025-05-16 08:21:25 +02:00
Johannes Altmanninger
d4b4d44f14 Fix Vi mode glitch when replacing at last character
Another regression from d51f669647 (Vi mode: avoid placing cursor beyond last
character, 2024-02-14) "Unfortunately Vi mode sometimes needs to temporarily
select past end". So do the replace_one mode bindings which were forgotten.

Fix this.

This surfaces a tricky problem: when we use something like

	bind '' self-insert some-command

When key event "x" matches this generic binding, we insert both "self-insert"
and "some-command" at the front of the queue, and do *not* consume "x",
since the binding is empty.

Since there is a command (that might call "exit"), we insert a check-exit
event too, after "self-insert some-command" but _before_ "x".

The check-exit event makes "self-insert" do nothing. I don't think there's a
good reason for this; self-insert can only be triggered by a key event that
maps to self-insert; so there must always be a real key available for it to
consume. A "commandline -f self-insert" is a nop. Skip check-exit here.

Fixes #11484

(cherry picked from commit 107e4d11de)
2025-05-13 00:31:22 +02:00
Johannes Altmanninger
b8cfd6d12b Fix typo causing wrong cursor position after Vi mode paste
Regressed in d51f669647 (Vi mode: avoid placing cursor beyond last character,
2024-02-14).

(cherry picked from commit 50500ec5b9)
2025-05-13 00:31:09 +02:00
Johannes Altmanninger
6fb22a4fd1 Fix regression causing crash indenting commandline with "$()"
Commit b00899179f (Don't indent multi-line quoted strings; do indent inside
(), 2024-04-28) changed how we compute indents for string tokens with command
substitutions:

	echo "begin
	not indented
	end $(
	begin
	    indented
	end)"(
	begin
	    indented
	end
	)

For the leading quoted part of the string, we compute indentation only for
the first character (the opening quote), see 4c43819d32 (Fix crash indenting
quoted suffix after command substitution, 2024-09-28).

The command substitutions, we do indent as usual.

To implement the above, we need to separate quoted from non-quoted
parts. This logic crashes when indent_string_part() is wrongly passed
is_double_quoted=true.

This is because, given the string "$()"$(), parse_util_locate_cmdsub calls
quote_end() at index 4 (the second quote). This is wrong because that function
should only be called at opening quotes; this is a closing quote. The opening
quote is virtual here. Hack around this.

Fixes #11444

(cherry picked from commit 48704dc612)
2025-05-12 21:41:10 +02:00
Johannes Altmanninger
35849c57dc Explicit type for "$()" hack in parse_util_locate_cmdsub
(cherry picked from commit 8abab0e2cc)
2025-05-12 21:41:10 +02:00
Johannes Altmanninger
27504658ce Remove code clone in parse_util_locate_cmdsub
(cherry picked from commit bd178c8ba8)
2025-05-12 21:41:10 +02:00
Johannes Altmanninger
db323348c7 Set transient command line in custom completions (Rust port regression)
Commit df3b0bd89f (Fix commandline state for custom completions with variable
overrides, 2022-01-26) made us push a transient command line for custom
completions based on a tautological null-pointer check ("var_assignments").

Commit 77aeb6a2a8 (Port execution, 2023-10-08) turned the null pointer into
a reference and replaced the check with "!ad.var_assignments.is_empty()".
This broke scenarios that relied on the transient commandline.  In particular
the attached test cases rely on the transient commandline implicitly placing
the cursor at the end, irrespective of the cursor in the actual commandline.

I'm not sure if there is an easy way to identify these scenarios.

Let's restore historical behavior by always pushing the transient command line.

Fixes #11423

(cherry picked from commit 97641c7bf6)
2025-05-12 21:39:42 +02:00
Johannes Altmanninger
edb1b5f333 Share alt-{b,f} with Vi mode, to work around Terminal.app/Ghostty more
Commit f4503af037 (Make alt-{b,f} move in directory history if commandline is
empty, 2025-01-06) had the intentional side effect of making alt-{left,right}
(move in directory history) work in Terminal.app and Ghostty without other,
less reliable workarounds.
That commit says "that [workaround] alone should not be the reason for
this change."; maybe this was wrong.

Extend the workaround to Vi mode.  The intention here is to provide
alt-{left,right} in Vi mode.  This also adds alt-{b,f} which is odd but
mostly harmless (?) because those don't do anything else in Vi mode.
It might be confusing when studying "bind" output but that one already has
almost 400 lines for Vi mode.

Closes #11479

(cherry picked from commit 3081d0157b)
2025-05-12 21:35:47 +02:00
Daniel Rainer
2d8d377ddc Make printf unicode-aware
Specifically, the width and precision format specifiers are interpreted as
referring to the width of the grapheme clusters rather than the byte count of
the string. Note that grapheme clusters can differ in width.

If a precision is specified for a string, meaning its "maximum number of
characters", we consider this to limit the width displayed.
If there is a grapheme cluster whose width is greater than 1,
it might not be possible to get precisely the desired width.
In such cases, this last grapheme cluster is excluded from the output.

Note that the definitions used here are not consistent with the `string length`
builtin at the moment, but this has already been the case.

(cherry picked from commit 09eae92888)
2025-05-12 21:33:40 +02:00
Lucas Melo
3cbb5e384b Shorten and format completions for protontricks, protontricks-launch 2025-05-12 08:36:07 -03:00
Lucas Melo
90b35335ee Add completions for protontricks 2025-05-12 08:24:26 -03:00
Peter Ammon
057dd930b4 Changelog fix for #11354 2025-05-08 18:42:57 -07:00
Cuichen Li
25b944e3e6 Revert "Work around $PATH issues under WSL (#10506)"
This reverts commit 3374692b91.
2025-05-08 18:41:02 -07:00
610 changed files with 58974 additions and 70067 deletions

View File

@@ -1,7 +1,6 @@
image: alpine/edge
packages:
- cargo
- clang17-libclang
- cmake
- ninja
- pcre2-dev
@@ -24,4 +23,4 @@ tasks:
ninja
- test: |
cd fish-shell/build
ninja test
ninja fish_run_tests

View File

@@ -20,4 +20,4 @@ tasks:
ninja
- test: |
cd fish/build
ninja test
ninja fish_run_tests

View File

@@ -5,7 +5,6 @@ packages:
- gettext
- gmake
- llvm
- terminfo-db
- ninja
- pcre2
- py311-pexpect
@@ -27,4 +26,4 @@ tasks:
ninja
- test: |
cd fish-shell/build
ninja test
ninja fish_run_tests

29
.builds/openbsd.yml Normal file
View File

@@ -0,0 +1,29 @@
image: openbsd/latest
packages:
- cmake
- gcc
- gettext
- gmake
- llvm
- ninja
- pcre2
- py311-pexpect
- python
- rust
- tmux
sources:
- https://github.com/fish-shell/fish-shell
tasks:
- build: |
cd fish-shell
mkdir build
cd build
cmake -GNinja .. \
-DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_INSTALL_DATADIR=share \
-DCMAKE_INSTALL_DOCDIR=share/doc/fish \
-DCMAKE_INSTALL_SYSCONFDIR=/etc
ninja
- test: |
cd fish-shell/build
ninja fish_run_tests

View File

@@ -1,3 +1,5 @@
skip: $CIRRUS_REPO_OWNER == 'fish-shell' && $CIRRUS_BRANCH == 'master'
env:
CIRRUS_CLONE_DEPTH: 100
CI: 1
@@ -6,27 +8,19 @@ linux_task:
matrix:
- name: alpine
container: &step
image: ghcr.io/krobelus/fish-ci/alpine:latest
image: ghcr.io/fish-shell/fish-ci/alpine:latest
memory: 4GB
- name: jammy
container:
<<: *step
image: ghcr.io/krobelus/fish-ci/jammy:latest
# - name: jammy-asan
# container:
# <<: *step
# image: ghcr.io/krobelus/fish-ci/jammy-asan:latest
# - name: focal-32bit
# container:
# <<: *step
# image: ghcr.io/krobelus/fish-ci/focal-32bit:latest
image: ghcr.io/fish-shell/fish-ci/jammy:latest
tests_script:
# cirrus at times gives us 32 procs and 2 GB of RAM
# Unrestriced parallelism results in OOM
- lscpu || true
- (cat /proc/meminfo | grep MemTotal) || true
- mkdir build && cd build
- cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCTEST_PARALLEL_LEVEL=6 ..
- FISH_TEST_MAX_CONCURRENCY=6 cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
- ninja -j 6 fish
- ninja fish_run_tests
only_if: $CIRRUS_REPO_OWNER == 'fish-shell'
@@ -36,35 +30,24 @@ linux_arm_task:
- name: focal-arm64
arm_container:
image: ghcr.io/fish-shell/fish-ci/focal-arm64
- name: jammy-armv7-32bit
arm_container:
image: ghcr.io/fish-shell/fish-ci/jammy-armv7-32bit
tests_script:
# cirrus at times gives us 32 procs and 2 GB of RAM
# Unrestriced parallelism results in OOM
- lscpu || true
- (cat /proc/meminfo | grep MemTotal) || true
- mkdir build && cd build
- cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCTEST_PARALLEL_LEVEL=6 ..
- FISH_TEST_MAX_CONCURRENCY=6 cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
- ninja -j 6 fish
- file ./fish
- ninja fish_run_tests
# CI task disabled during RIIR transition
only_if: false && $CIRRUS_REPO_OWNER == 'fish-shell'
only_if: $CIRRUS_REPO_OWNER == 'fish-shell'
freebsd_task:
matrix:
# - name: FreeBSD 14
# freebsd_instance:
# image_family: freebsd-14-0-snap
- name: FreeBSD 13
- name: FreeBSD 14
freebsd_instance:
image: freebsd-13-2-release-amd64
# - name: FreeBSD 12.3
# freebsd_instance:
# image: freebsd-12-3-release-amd64
image: freebsd-14-3-release-amd64-ufs
tests_script:
- pkg install -y cmake-core devel/pcre2 devel/ninja misc/py-pexpect git-lite terminfo-db
- 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
# BSDs have the following behavior: root may open or access files even if
@@ -77,15 +60,7 @@ freebsd_task:
- mkdir build && cd build
- chown -R fish-user ..
- sudo -u fish-user -s whoami
# FreeBSD's pkg currently has rust 1.66.0 while we need rust 1.70.0+. Use rustup to install
# the latest, but note that it only installs rust per-user.
- sudo -u fish-user -s fetch -qo - https://sh.rustup.rs > rustup.sh
- sudo -u fish-user -s sh ./rustup.sh -y --profile=minimal
# `sudo -s ...` does not invoke a login shell so we need a workaround to make sure the
# rustup environment is configured for subsequent `sudo -s ...` commands.
# For some reason, this doesn't do the job:
# - sudo -u fish-user sh -c 'echo source \$HOME/.cargo/env >> $HOME/.cshrc'
- sudo -u fish-user -s cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCTEST_PARALLEL_LEVEL=1 ..
- sudo -u fish-user sh -c '. $HOME/.cargo/env; ninja -j 6 fish'
- sudo -u fish-user sh -c '. $HOME/.cargo/env; ninja fish_run_tests'
- sudo -u fish-user -s FISH_TEST_MAX_CONCURRENCY=1 cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
- sudo -u fish-user -s ninja -j 6 fish
- sudo -u fish-user -s ninja fish_run_tests
only_if: $CIRRUS_REPO_OWNER == 'fish-shell'

View File

@@ -13,10 +13,14 @@ max_line_length = 100
indent_style = tab
[*.{md,rst}]
max_line_length = unset
trim_trailing_whitespace = false
[*.{sh,ac}]
indent_size = 2
[*.sh]
indent_size = 4
[build_tools/release.sh]
max_line_length = 72
[Dockerfile]
indent_size = 2
@@ -24,5 +28,5 @@ indent_size = 2
[share/{completions,functions}/**.fish]
max_line_length = unset
[{COMMIT_EDITMSG,git-revise-todo}]
max_line_length = 80
[{COMMIT_EDITMSG,git-revise-todo,*.jjdescription}]
max_line_length = 72

4
.gitattributes vendored
View File

@@ -1,7 +1,5 @@
# normalize newlines
* text=auto
*.fish text
*.bat eol=crlf
* text=auto eol=lf
# let git show off diff hunk headers, help git diff -L:
# https://git-scm.com/docs/gitattributes

View File

@@ -0,0 +1,43 @@
name: Install dependencies for system tests
inputs:
include_sphinx:
description: Whether to install Sphinx
required: true
default: false
include_pcre:
description: Whether to install the PCRE library
required: false
default: true
permissions:
contents: read
runs:
using: "composite"
steps:
- shell: bash
env:
include_sphinx: ${{ inputs.include_sphinx }}
include_pcre: ${{ inputs.include_pcre }}
run: |
set -x
: "optional dependencies"
sudo apt install \
gettext \
$(if $include_pcre; then echo libpcre2-dev; fi) \
$(if $include_sphinx; then echo python3-sphinx; fi) \
;
: "system test dependencies"
sudo apt install \
diffutils $(: "for diff") \
git \
gettext \
less \
$(if ${{ inputs.include_pcre }}; then echo libpcre2-dev; fi) \
python3-pexpect \
tmux \
wget \
;
- uses: ./.github/actions/install-sphinx-markdown-builder
if: ${{ inputs.include_sphinx == 'true' }}

View File

@@ -0,0 +1,14 @@
name: Install sphinx-markdown-builder
permissions:
contents: read
runs:
using: "composite"
steps:
- shell: bash
run: |
set -x
commit=b259de1dc97573a71470a1d71c3d83535934136b
pip install git+https://github.com/krobelus/sphinx-markdown-builder@"$commit"
python -c 'import sphinx_markdown_builder'

View File

@@ -0,0 +1,41 @@
name: Rust Toolchain
inputs:
toolchain_channel:
description: Either "stable" or "msrv"
required: true
targets:
description: Comma-separated list of target triples to install for this toolchain
required: false
components:
description: Comma-separated list of components to be additionally installed
required: false
permissions:
contents: read
runs:
using: "composite"
steps:
- name: Set toolchain
env:
toolchain_channel: ${{ inputs.toolchain_channel }}
shell: bash
run: |
set -x
toolchain=$(
case "$toolchain_channel" in
(stable) echo 1.90 ;;
(msrv) echo 1.70 ;;
(*)
printf >&2 "error: unsupported toolchain channel %s" "$toolchain_channel"
exit 1
;;
esac
)
printf 'TOOLCHAIN=%s\n' "$toolchain" >>"$GITHUB_ENV"
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.TOOLCHAIN }}
targets: ${{ inputs.targets }}
components: ${{ inputs.components }}

View File

@@ -0,0 +1,21 @@
name: Oldest Supported Rust Toolchain
inputs:
targets:
description: Comma-separated list of target triples to install for this toolchain
required: false
components:
description: Comma-separated list of components to be additionally installed
required: false
permissions:
contents: read
runs:
using: "composite"
steps:
- uses: ./.github/actions/rust-toolchain
with:
toolchain_channel: "msrv"
targets: ${{ inputs.targets }}
components: ${{ inputs.components }}

View File

@@ -0,0 +1,21 @@
name: Stable Rust Toolchain
inputs:
targets:
description: Comma-separated list of target triples to install for this toolchain
required: false
components:
description: Comma-separated list of components to be additionally installed
required: false
permissions:
contents: read
runs:
using: "composite"
steps:
- uses: ./.github/actions/rust-toolchain
with:
toolchain_channel: "stable"
targets: ${{ inputs.targets }}
components: ${{ inputs.components }}

View File

@@ -8,16 +8,12 @@ jobs:
label-and-milestone:
runs-on: ubuntu-latest
steps:
# - name: Checkout repository
# uses: actions/checkout@v2
- name: Set label and milestone
id: set-label-milestone
uses: actions/github-script@v7
with:
script: |
const completionsLabel = 'completions';
const completionsMilestone = 'fish next-3.x';
// Get changed files in the pull request
const prNumber = context.payload.pull_request.number;
@@ -41,26 +37,4 @@ jobs:
labels: [completionsLabel],
});
console.log(`PR ${prNumber} assigned label "${completionsLabel}"`);
// Get the list of milestones
const { data: milestones } = await github.rest.issues.listMilestones({
owner: context.repo.owner,
repo: context.repo.repo,
});
// Find the milestone id
const milestone = milestones.find(milestone => milestone.title === completionsMilestone);
if (milestone) {
// Set the milestone for the PR
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
milestone: milestone.number
});
console.log(`PR ${prNumber} assigned milestone "${completionsMilestone}"`);
} else {
console.error(`Milestone "${completionsMilestone}" not found`);
}
}

View File

@@ -0,0 +1,126 @@
name: Build Docker test images
on:
push:
branches:
- master
workflow_dispatch:
concurrency:
group: docker-builds
env:
REGISTRY: ghcr.io
NAMESPACE: fish-ci
ONLY_FOR_REPO_OWNER: fish-shell
jobs:
check-docker-changes:
if: github.repository_owner == env.ONLY_FOR_REPO_OWNER
runs-on: ubuntu-latest
outputs:
docker-changed: ${{ steps.changes.outputs.docker }}
steps:
- uses: actions/checkout@v5
- uses: dorny/paths-filter@v3
id: changes
with:
filters: |
docker:
- 'docker/**'
docker-build:
needs: check-docker-changes
if: github.repository_owner == env.ONLY_FOR_REPO_OWNER && needs.check-docker-changes.outputs.docker-changed == 'true'
permissions:
contents: read
packages: write
attestations: write
id-token: write
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: alpine
- os: ubuntu-latest
target: centos9
- os: ubuntu-latest
target: fedora
- os: ubuntu-latest
target: focal-32bit
- os: ubuntu-24.04-arm
target: focal-arm64
- os: ubuntu-latest
target: focal
- os: ubuntu-24.04-arm
target: jammy-armv7-32bit
- os: ubuntu-latest
target: jammy-asan
- os: ubuntu-latest
target: jammy-tsan
- os: ubuntu-latest
target: jammy
- os: ubuntu-latest
target: noble
- os: ubuntu-latest
target: opensuse-tumbleweed
runs-on: ${{ matrix.os }}
steps:
-
name: Checkout
uses: actions/checkout@v5
-
name: Login to Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.NAMESPACE }}/${{ matrix.target }}
flavor: |
latest=true
-
name: Build and push
uses: docker/build-push-action@v6
with:
context: docker/context
push: true
file: docker/${{ matrix.target }}.Dockerfile
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
trigger-cirrus:
needs: [check-docker-changes, docker-build]
if: always() && github.repository_owner == env.ONLY_FOR_REPO_OWNER
runs-on: ubuntu-latest
steps:
- name: Trigger Cirrus CI
env:
CIRRUS_TOKEN: ${{ secrets.CIRRUS_TOKEN }}
run: |
set -x
# N.B. push-triggered workflows are usually from master.
branch=${{ github.ref_name }}
repository_id=${{ github.repository_id }}
curl -X POST \
-H "Authorization: Bearer $CIRRUS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation {
createBuild(input: {
repositoryId: \"$repository_id\",
branch: \"$branch\"
})
{ build { id } }
}"
}' \
https://api.cirrus-ci.com/graphql

59
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,59 @@
name: Lint
on: [push, pull_request]
permissions:
contents: read
jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/rust-toolchain@stable
with:
components: rustfmt
- name: install dependencies
run: pip install ruff
- name: build fish
run: cargo build
- name: check format
run: PATH="target/debug:$PATH" build_tools/style.fish --all --check
clippy:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- rust_version: "stable"
features: ""
- rust_version: "stable"
features: "--no-default-features"
- rust_version: "msrv"
features: ""
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/rust-toolchain
with:
toolchain_channel: ${{ matrix.rust_version }}
components: clippy
- name: Install deps
run: |
sudo apt install gettext
- name: cargo clippy
run: cargo clippy --workspace --all-targets ${{ matrix.features }} -- --deny=warnings
rustdoc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/rust-toolchain@stable
- name: Install deps
run: |
sudo apt install gettext
- name: cargo doc
run: |
RUSTDOCFLAGS='-D warnings' cargo doc --workspace
- name: cargo doctest
run: |
cargo test --doc --workspace

View File

@@ -12,6 +12,7 @@ permissions:
jobs:
lock:
if: github.repository_owner == 'fish-shell'
permissions:
issues: write # for dessant/lock-threads to lock issues
pull-requests: write # for dessant/lock-threads to lock PRs

View File

@@ -1,42 +0,0 @@
name: macOS build and codesign
on:
workflow_dispatch: # Enables manual trigger from GitHub UI
jobs:
build-and-code-sign:
runs-on: macos-latest
environment: macos-codesign
steps:
- uses: actions/checkout@v4
- name: Install Rust 1.73.0
uses: dtolnay/rust-toolchain@1.73.0
with:
targets: x86_64-apple-darwin
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-darwin
- name: build-and-codesign
run: |
cargo install apple-codesign
mkdir -p "$FISH_ARTEFACT_PATH"
echo "$MAC_CODESIGN_APP_P12_BASE64" | base64 --decode > /tmp/app.p12
echo "$MAC_CODESIGN_INSTALLER_P12_BASE64" | base64 --decode > /tmp/installer.p12
echo "$MACOS_NOTARIZE_JSON" > /tmp/notarize.json
./build_tools/make_pkg.sh -s -f /tmp/app.p12 -i /tmp/installer.p12 -p "$MAC_CODESIGN_PASSWORD" -n -j /tmp/notarize.json
rm /tmp/installer.p12 /tmp/app.p12 /tmp/notarize.json
env:
MAC_CODESIGN_APP_P12_BASE64: ${{ secrets.MAC_CODESIGN_APP_P12_BASE64 }}
MAC_CODESIGN_INSTALLER_P12_BASE64: ${{ secrets.MAC_CODESIGN_INSTALLER_P12_BASE64 }}
MAC_CODESIGN_PASSWORD: ${{ secrets.MAC_CODESIGN_PASSWORD }}
MACOS_NOTARIZE_JSON: ${{ secrets.MACOS_NOTARIZE_JSON }}
# macOS runners keep having issues loading Cargo.toml dependencies from git (GitHub) instead
# of crates.io, so give this a try. It's also sometimes significantly faster on all platforms.
CARGO_NET_GIT_FETCH_WITH_CLI: true
FISH_ARTEFACT_PATH: /tmp/fish-built
- uses: actions/upload-artifact@v4
with:
name: macOS Artefacts
path: /tmp/fish-built/*
if-no-files-found: error

191
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,191 @@
name: Create a new release
on:
workflow_dispatch:
inputs:
version:
description: 'Version to release (tag name)'
required: true
type: string
permissions:
contents: write
jobs:
is-release-tag:
name: Pre-release checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Workaround for https://github.com/actions/checkout/issues/882
ref: ${{ inputs.version }}
- name: Check if the pushed tag looks like a release
run: |
set -x
commit_subject=$(git log -1 --format=%s)
tag=$(git describe)
[ "$commit_subject" = "Release $tag" ]
source-tarball:
needs: [is-release-tag]
name: Create the source tarball
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
tarball-name: ${{ steps.version.outputs.tarball-name }}
steps:
- uses: actions/checkout@v4
with:
# Workaround for https://github.com/actions/checkout/issues/882
ref: ${{ inputs.version }}
- name: Install dependencies
run: sudo apt install cmake gettext ninja-build python3-pip python3-sphinx
- uses: ./.github/actions/install-sphinx-markdown-builder
- name: Create tarball
run: |
set -x
mkdir /tmp/fish-built
FISH_ARTEFACT_PATH=/tmp/fish-built ./build_tools/make_tarball.sh
relnotes=/tmp/fish-built/release-notes.md
# Need history since the last release (i.e. tag) for stats.
git fetch --tags
git fetch --unshallow
sh -x ./build_tools/release-notes.sh >"$relnotes"
# Delete title
sed -n 1p "$relnotes" | grep -q "^## fish .*"
sed -n 2p "$relnotes" | grep -q '^$'
sed -i 1,2d "$relnotes"
- name: Upload tarball artifact
uses: actions/upload-artifact@v4
with:
name: source-tarball
path: |
/tmp/fish-built/fish-${{ inputs.version }}.tar.xz
/tmp/fish-built/release-notes.md
if-no-files-found: error
packages-for-linux:
needs: [is-release-tag]
name: Build single-file fish for Linux (experimental)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Workaround for https://github.com/actions/checkout/issues/882
ref: ${{ inputs.version }}
- name: Install Rust Stable
uses: ./.github/actions/rust-toolchain@stable
with:
targets: x86_64-unknown-linux-musl,aarch64-unknown-linux-musl
- name: Install dependencies
run: sudo apt install crossbuild-essential-arm64 gettext musl-tools python3-sphinx
- name: Build statically-linked executables
run: |
set -x
cargo build --release --target x86_64-unknown-linux-musl --bin fish
CFLAGS="-D_FORTIFY_SOURCE=2" \
CC=aarch64-linux-gnu-gcc \
RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc -C link-arg=-lgcc -C link-arg=-D_FORTIFY_SOURCE=0" \
cargo build --release --target aarch64-unknown-linux-musl --bin fish
- name: Compress
run: |
set -x
for arch in x86_64 aarch64; do
tar -cazf fish-$(git describe)-linux-$arch.tar.xz \
-C target/$arch-unknown-linux-musl/release fish
done
- uses: actions/upload-artifact@v4
with:
name: Static builds for Linux
path: fish-${{ inputs.version }}-linux-*.tar.xz
if-no-files-found: error
create-draft-release:
needs:
- is-release-tag
- source-tarball
- packages-for-linux
name: Create release draft
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Workaround for https://github.com/actions/checkout/issues/882
ref: ${{ inputs.version }}
- name: Download all artifacts
uses: actions/download-artifact@v4
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
with:
tag_name: ${{ inputs.version }}
name: fish ${{ inputs.version }}
body_path: /tmp/artifacts/release-notes.md
draft: true
files: |
/tmp/artifacts/fish-${{ inputs.version }}.tar.xz
/tmp/artifacts/fish-${{ inputs.version }}-linux-*.tar.xz
packages-for-macos:
needs: [is-release-tag, create-draft-release]
name: Build packages for macOS
runs-on: macos-latest
environment: macos-codesign
steps:
- uses: actions/checkout@v4
with:
# Workaround for https://github.com/actions/checkout/issues/882
ref: ${{ inputs.version }}
- name: Install Rust
uses: ./.github/actions/rust-toolchain@oldest-supported
with:
targets: x86_64-apple-darwin
- name: Install Rust Stable
uses: ./.github/actions/rust-toolchain@stable
with:
targets: aarch64-apple-darwin
- name: Install dependencies
run: brew install gettext
- name: Build and codesign
run: |
die() { echo >&2 "$*"; exit 1; }
[ -n "$MAC_CODESIGN_APP_P12_BASE64" ] || die "Missing MAC_CODESIGN_APP_P12_BASE64"
[ -n "$MAC_CODESIGN_INSTALLER_P12_BASE64" ] || die "Missing MAC_CODESIGN_INSTALLER_P12_BASE64"
[ -n "$MAC_CODESIGN_PASSWORD" ] || die "Missing MAC_CODESIGN_PASSWORD"
[ -n "$MACOS_NOTARIZE_JSON" ] || die "Missing MACOS_NOTARIZE_JSON"
set -x
export FISH_ARTEFACT_PATH=/tmp/fish-built
# macOS runners keep having issues loading Cargo.toml dependencies from git (GitHub) instead
# of crates.io, so give this a try. It's also sometimes significantly faster on all platforms.
export CARGO_NET_GIT_FETCH_WITH_CLI=true
cargo install apple-codesign
mkdir -p "$FISH_ARTEFACT_PATH"
echo "$MAC_CODESIGN_APP_P12_BASE64" | base64 --decode >/tmp/app.p12
echo "$MAC_CODESIGN_INSTALLER_P12_BASE64" | base64 --decode >/tmp/installer.p12
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
version=$(git describe)
[ -f "${FISH_ARTEFACT_PATH}/fish-$version.app.zip" ]
[ -f "${FISH_ARTEFACT_PATH}/fish-$version.pkg" ]
rm /tmp/installer.p12 /tmp/app.p12 /tmp/notarize.json
env:
MAC_CODESIGN_APP_P12_BASE64: ${{ secrets.MAC_CODESIGN_APP_P12_BASE64 }}
MAC_CODESIGN_INSTALLER_P12_BASE64: ${{ secrets.MAC_CODESIGN_INSTALLER_P12_BASE64 }}
MAC_CODESIGN_PASSWORD: ${{ secrets.MAC_CODESIGN_PASSWORD }}
MACOS_NOTARIZE_JSON: ${{ secrets.MACOS_NOTARIZE_JSON }}
- name: Add macOS packages to the release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
version=$(git describe)
gh release upload $version \
/tmp/fish-built/fish-$version.app.zip \
/tmp/fish-built/fish-$version.pkg

View File

@@ -1,41 +0,0 @@
name: Rust checks
on: [push, pull_request]
permissions:
contents: read
jobs:
rustfmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- name: cargo fmt
run: cargo fmt --check --all
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- name: Install deps
run: |
sudo apt install gettext libpcre2-dev
- name: cmake
run: |
cmake -B build
- name: cargo clippy
# This used to have --deny=warnings, but that turns rust release day
# into automatic CI failure day, so we don't do that.
run: cargo clippy --workspace --all-targets
# Disabling for now because it also checks "advisories",
# making CI fail for reasons unrelated to the patch
# cargo-deny:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - uses: EmbarkStudios/cargo-deny-action@v1

View File

@@ -1,80 +0,0 @@
name: staticbuilds
on:
# release:
# types: [published]
# schedule:
# - cron: "14 13 * * *"
workflow_dispatch:
env:
CTEST_PARALLEL_LEVEL: "1"
CMAKE_BUILD_PARALLEL_LEVEL: "4"
jobs:
staticbuilds-linux:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: dtolnay/rust-toolchain@1.70
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Prepare
run: |
sudo apt install python3-sphinx
rustup target add x86_64-unknown-linux-musl
rustup target add aarch64-unknown-linux-musl
sudo apt install musl-tools crossbuild-essential-arm64 python3-pexpect tmux -y
- name: Build
run: |
CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2" CMAKE_WITH_GETTEXT=0 CC=aarch64-linux-gnu-gcc RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc -C link-arg=-lgcc -C link-arg=-D_FORTIFY_SOURCE=0" cargo build --release --target aarch64-unknown-linux-musl --bin fish
cargo build --release --target x86_64-unknown-linux-musl
- name: Test
run: |
test -e tests/test_driver.py && tests/test_driver.py -f /tmp target/x86_64-unknown-linux-musl/release/
- name: Compress
run: |
tar -cazf fish-static-x86_64-$(git describe).tar.xz -C target/x86_64-unknown-linux-musl/release/ fish
tar -cazf fish-static-aarch64-$(git describe).tar.xz -C target/aarch64-unknown-linux-musl/release/ fish
- uses: actions/upload-artifact@v4
with:
name: fish-static-linux
path: |
fish-*.tar.xz
retention-days: 14
staticbuilds-macos:
runs-on: macos-latest
permissions:
contents: read
steps:
- uses: dtolnay/rust-toolchain@1.70
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Prepare
run: |
sudo pip3 install --break-system-packages sphinx
rustup target add x86_64-apple-darwin
rustup target add aarch64-apple-darwin
- name: Build
run: |
PCRE2_SYS_STATIC=1 cargo build --release --target aarch64-apple-darwin --bin fish
PCRE2_SYS_STATIC=1 cargo build --release --target x86_64-apple-darwin --bin fish
- name: Compress
run: |
tar -cazf fish-macos-aarch64.tar.xz -C target/aarch64-apple-darwin/release/ fish
tar -cazf fish-macos-x86_64.tar.xz -C target/x86_64-apple-darwin/release/ fish
- uses: actions/upload-artifact@v4
with:
name: fish-static-macos
path: |
fish-macos-*.tar.xz
retention-days: 14

View File

@@ -1,9 +1,9 @@
name: make fish_run_tests
name: Test
on: [push, pull_request]
env:
CTEST_PARALLEL_LEVEL: "4"
FISH_TEST_MAX_CONCURRENCY: "4"
CMAKE_BUILD_PARALLEL_LEVEL: "4"
permissions:
@@ -11,16 +11,16 @@ permissions:
jobs:
ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.70
- uses: ./.github/actions/rust-toolchain@oldest-supported
- name: Install deps
uses: ./.github/actions/install-dependencies
with:
include_sphinx: true
- name: Generate a locale that uses a comma as decimal separator.
run: |
sudo apt install gettext libpcre2-dev python3-pexpect python3-sphinx tmux
# Generate a locale that uses a comma as decimal separator.
sudo locale-gen fr_FR.UTF-8
- name: cmake
run: |
@@ -32,31 +32,30 @@ jobs:
- name: make fish_run_tests
run: |
make -C build VERBOSE=1 fish_run_tests
- uses: dtolnay/rust-toolchain@stable
- name: translation updates
run: |
# Required for our custom xgettext implementation.
cargo install --locked --version 1.0.106 cargo-expand
# Generate PO files. This should not result it a change in the repo if all translations are
# up to date.
# Ensure that fish is available as an executable.
PATH="$PWD/build:$PATH" build_tools/update_translations.fish --no-mo
PATH="$PWD/build:$PATH" build_tools/update_translations.fish
# Show diff output. Fail if there is any.
git --no-pager diff --exit-code || { echo 'There are uncommitted changes after regenerating the gettext PO files. Make sure to update them via `build_tools/update_translations.fish --no-mo` after changing source files.'; exit 1; }
git --no-pager diff --exit-code || { echo 'There are uncommitted changes after regenerating the gettext PO files. Make sure to update them via `build_tools/update_translations.fish` after changing source files.'; exit 1; }
ubuntu-32bit-static-pcre2:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.70
- uses: ./.github/actions/rust-toolchain@oldest-supported
with:
targets: "i586-unknown-linux-gnu" # rust-toolchain wants this comma-separated
targets: "i586-unknown-linux-gnu"
- name: Install deps
uses: ./.github/actions/install-dependencies
with:
include_pcre: false
include_sphinx: false
- name: Install g++-multilib
run: |
sudo apt update
sudo apt install gettext python3-pexpect g++-multilib tmux
sudo apt install g++-multilib
- name: cmake
env:
CFLAGS: "-m32"
@@ -71,7 +70,6 @@ jobs:
make -C build VERBOSE=1 fish_run_tests
ubuntu-asan:
runs-on: ubuntu-latest
env:
# Rust has two different memory sanitizers of interest; they can't be used at the same time:
@@ -81,7 +79,6 @@ jobs:
#
RUSTFLAGS: "-Zsanitizer=address"
# RUSTFLAGS: "-Zsanitizer=memory -Zsanitizer-memory-track-origins"
steps:
- uses: actions/checkout@v4
# All -Z options require running nightly
@@ -91,8 +88,11 @@ jobs:
# this is comma-separated
components: rust-src
- name: Install deps
uses: ./.github/actions/install-dependencies
with:
include_sphinx: false
- name: Install llvm
run: |
sudo apt install gettext libpcre2-dev python3-pexpect tmux
sudo apt install llvm # for llvm-symbolizer
- name: cmake
env:
@@ -121,58 +121,53 @@ jobs:
export LSAN_OPTIONS="$LSAN_OPTIONS:suppressions=$PWD/build_tools/lsan_suppressions.txt"
make -C build VERBOSE=1 fish_run_tests
# Our clang++ tsan builds are not recognizing safe rust patterns (such as the fact that Drop
# cannot be called while a thread is using the object in question). Rust has its own way of
# running TSAN, but for the duration of the port from C++ to Rust, we'll keep this disabled.
# ubuntu-threadsan:
#
# runs-on: ubuntu-latest
#
# steps:
# - uses: actions/checkout@v4
# - uses: dtolnay/rust-toolchain@1.70
# - name: Install deps
# run: |
# sudo apt install gettext libpcre2-dev python3-pexpect tmux
# - name: cmake
# env:
# FISH_CI_SAN: 1
# CC: clang
# run: |
# mkdir build && cd build
# cmake ..
# - name: make
# run: |
# make
# - name: make fish_run_tests
# run: |
# make -C build fish_run_tests
macos:
runs-on: macos-latest
env:
# macOS runners keep having issues loading Cargo.toml dependencies from git (GitHub) instead
# 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: dtolnay/rust-toolchain@1.70
- uses: ./.github/actions/rust-toolchain@oldest-supported
- name: Install deps
run: |
# --break-system-packages because homebrew has now declared itself "externally managed".
# this is CI so we don't actually care.
sudo pip3 install --break-system-packages pexpect
brew install tmux
brew install gettext tmux
- name: cmake
run: |
mkdir build && cd build
cmake -DWITH_GETTEXT=NO -DCMAKE_BUILD_TYPE=Debug ..
cmake -DCMAKE_BUILD_TYPE=Debug ..
- name: make
run: |
make -C build VERBOSE=1
- name: make fish_run_tests
run: |
make -C build VERBOSE=1 fish_run_tests
windows:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
update: true
msystem: MSYS
- name: Install deps
# Not using setup-msys2 `install` option to make it easier to copy/paste
run: |
pacman --noconfirm -S --needed git rust
- name: cargo build
run: |
cargo build
- name: smoketest
# We can't use `cargo test` yet, there are just too many failures
# so this is just a quick check to make sure that fish can swim
run: |
set -x
[ "$(target/debug/fish.exe -c 'echo (math 1 + 1)')" = 2 ]

6
.gitignore vendored
View File

@@ -38,8 +38,6 @@ Desktop.ini
Thumbs.db
ehthumbs.db
po/template.po
*.mo
.directory
.fuse_hidden*
@@ -78,6 +76,7 @@ __pycache__
/share/__fish_build_paths.fish
/share/pkgconfig
/tests/*.tmp.*
/tests/.last-check-all-files
# xcode
## Build generated
@@ -106,3 +105,6 @@ target/
# JetBrains editors.
.idea/
# AI slop
.claude/

View File

@@ -1,92 +1,276 @@
fish 4.1.0 (released ???)
fish ?.?.? (released ???)
=========================
.. 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
fish 4.1.3 (released ???)
=========================
This release fixes the following regressions identified in 4.1.0:
- Crash on invalid :doc:`function <cmds/function>` command (:issue:`11912`).
as well as the following regressions identified in 4.0.0:
- Crash when passing negative PIDs to :doc:`wait <cmds/wait>` (:issue:`11929`).
fish 4.1.2 (released October 7, 2025)
=====================================
This release fixes the following regressions identified in 4.1.0:
- Fixed spurious error output when completing remote file paths for ``scp`` (:issue:`11860`).
- Fixed the :kbd:`alt-l` binding not formatting ``ls`` output correctly (one entry per line, no colors) (:issue:`11888`).
- Fixed an issue where focus events (currently only enabled in ``tmux``) would cause multiline prompts to be redrawn in the wrong line (:issue:`11870`).
- Stopped printing output that would cause a glitch on old versions of Midnight Commander (:issue:`11869`).
- Added a fix for some configurations of Zellij where :kbd:`escape` key processing was delayed (:issue:`11868`).
- Fixed a case where the :doc:`web-based configuration tool <cmds/fish_config>` would generate invalid configuration (:issue:`11861`).
- Fixed a case where pasting into ``fish -c read`` would fail with a noisy error (:issue:`11836`).
- Fixed a case where upgrading fish would break old versions of fish that were still running.
In general, fish still needs to be restarted after it is upgraded,
except for `standalone builds <https://github.com/fish-shell/fish-shell/?tab=readme-ov-file#building-fish-with-embedded-data-experimental>`__.
fish 4.1.1 (released September 30, 2025)
========================================
This release fixes the following regressions identified in 4.1.0:
- Many of our new Chinese translations were more confusing than helpful; they have been fixed or removed (:issue:`11833`).
Note that you can work around this type of issue by configuring fish's :doc:`message localization <cmds/_>`:
if your environment contains something like ``LANG=zh_CN.UTF-8``,
you can use ``set -g LC_MESSAGES en`` to use English messages inside fish.
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`).
- 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 now be abbreviated using braces (``{ echo1; echo 2 }``), like in other shells.
- Fish now supports transient prompts: if :envvar:`fish_transient_prompt` is set to 1, fish will reexecute prompt functions with the ``--final-rendering`` argument before running a commandline (:issue:`11153`).
- When tab completion results are truncated, any common directory name is omitted. E.g. if you complete "share/functions", and it includes the files "foo.fish" and "bar.fish",
the completion pager will now show "…/foo.fish" and "…/bar.fish". This will make the candidates shorter and allow for more to be shown at once (:issue:`11250`).
- The self-installing configuration introduced in fish 4.0 has been changed.
Now fish built with embedded data will just read the data straight from its own binary or write it out when necessary, instead of requiring an installation step on start.
That means it is now possible to build fish as a single file and copy it to a compatible system, including as a different user, without extracting any files.
As before this is the default when building via `cargo`, and disabled when building via `cmake`, and for packagers we continue to recommend cmake.
Note: When fish is built like this, the `$__fish_data_dir` variable will be empty because that directory no longer has meaning. If you need to load files from there,
use `status get-file` or find alternatives (like loading completions for "foo" via `complete -C"foo "`).
We're considering making data embedding mandatory in future releases because it has a few advantages even for installation from a package (like making file conflicts with other packages impossible). (:issue:`11143`)
- Compound commands (``begin; echo 1; echo 2; end``) can now be written using braces (``{ echo1; echo 2 }``), like in other shells.
- fish now supports transient prompts: if :envvar:`fish_transient_prompt` is set to 1, fish will reexecute prompt functions with the ``--final-rendering`` argument before running a commandline (:issue:`11153`).
- Tab completion results are truncated up to the common directory path, instead of somewhere inside that path. E.g. if you complete "share/functions", and it includes the files "foo.fish" and "bar.fish",
the completion pager will now show "…/foo.fish" and "…/bar.fish" (:issue:`11250`).
- Self-installing builds as created by e.g. ``cargo install`` no longer install other files, see :ref:`below <changelog-4.1-embedded>`.
- Our gettext-based message-localization has been reworked,
adding translations to self-installing builds; see :ref:`below <changelog-4.1-gettext>`.
Deprecations and removed features
---------------------------------
- Tokens like ``{echo,echo}`` or ``{ echo, echo }`` in command position are no longer interpreted as brace expansion but as compound command.
- Terminfo-style key names (``bind -k``) are no longer supported. They had been superseded by the native notation since 4.0,
and currently they would map back to information from terminfo, which does not match what terminals would send with the kitty keyboard protocol (:issue:`11342`).
- fish no longer reads the terminfo database, so its behavior is no longer affected by the :envvar:`TERM` environment variable (:issue:`11344`).
For the time being, this can be turned off via the "ignore-terminfo" feature flag::
- ``set_color --background=COLOR`` no longer implicitly activates bold mode.
If your theme is stored in universal variables (the historical default), some bold formatting might be lost.
To fix this, we suggest updating to the latest version of our theme, to explicitly activate bold mode,
for example use ``fish_config theme save "fish default"``.
- ``{echo,echo}`` or ``{ echo, echo }`` are no longer interpreted as brace expansion tokens but as :doc:`compound commands <cmds/begin>`.
- Terminfo-style key names (``bind -k nul``) are no longer supported. They had been superseded by fish's :doc:`own key names <cmds/bind>` since 4.0 (:issue:`11342`).
- fish no longer reads the terminfo database, so its behavior is generally no longer affected by the :envvar:`TERM` environment variable (:issue:`11344`).
For the time being, this change can be reversed via the ``ignore-terminfo`` :ref:`feature flag <featureflags>`.
To do so, run the following once and restart fish::
set -Ua fish_features no-ignore-terminfo
- The ``--install`` option when fish is built as self-installable was removed. If you need to write out fish's data you can use the new ``status list-files`` and ``status get-file`` subcommands, but it should no longer be necessary. (:issue:`11143`)
- RGB colors (``set_color ff0000``) now default to using 24-bit RGB true-color commands, even if $COLORTERM is unset, because that is often lost e.g. over ssh (:issue:`11372`)
- To go back to using the nearest match from the 256-color palette, use ``set fish_term24bit 0`` or set $COLORTERM to a value that is not "24bit" or "truecolor".
To make the nearest-match logic use the 16 color palette instead, use ``set fish_term256 0``.
- Inside macOS Terminal.app, fish makes an attempt to still use the palette colors.
If that doesn't work, use ``set fish_term24bit 0``.
- ``set_color --background=COLOR`` no longer implicitly activates bold mode.
To mitigate this change on existing installations that use a default theme, update your theme with ``fish_config theme choose`` or ``fish_config theme save``.
- The ``--install`` option when fish is built as self-installing is removed, see :ref:`below <changelog-4.1-embedded>`.
- ``set_color ff0000`` now outputs 24-bit RGB true-color even if :envvar:`COLORTERM` is unset.
One can override this by setting :envvar:`fish_term24bit` to 0 (:issue:`11372`).
- fish now requires the terminal to respond to queries for the :ref:`Primary Device Attribute <term-compat-primary-da>`.
For now, this can be reversed via a :ref:`feature flag <featureflags>`,
by running (once) ``set -Ua fish_features no-query-term`` and restarting fish.
- Users of GNU screen may experience :ref:`minor glitches <term-compat-dcs-gnu-screen>` when starting fish.
Scripting improvements
----------------------
- The :doc:`argparse <cmds/argparse>` builtin has seen many improvements, see :ref:`below <changelog-4.1-argparse>`.
- The :doc:`string pad <cmds/string-pad>` command now has a ``-C/--center`` option.
- The :doc:`psub <cmds/psub>` command now allows combining ``--suffix`` with ``--fifo`` (:issue:`11729`).
- The :doc:`read <cmds/read>` builtin has learned the ``--tokenize-raw`` option to tokenize without quote removal (:issue:`11084`).
Interactive improvements
------------------------
- Autosuggestions are now also provided in multi-line command lines. Like `ctrl-r`, autosuggestions operate only on the current line.
- Autosuggestions used to not suggest multi-line commandlines from history; now autosuggestions include individual lines from multi-line command lines.
- The history search now preserves ordering between :kbd:`ctrl-s` forward and :kbd:`ctrl-r` backward searches.
- Left mouse click (as requested by `click_events <terminal-compatibility.html#click-events>`__) can now select pager items (:issue:`10932`).
- Instead of flashing all the text to the left of the cursor, fish now flashes the matched token during history token search, the completed token during completion (:issue:`11050`), the autosuggestion when deleting it, and the full command line in all other cases.
- Pasted commands are now stripped of any ``$`` prefix.
- Autosuggestions are now also provided in multi-line command lines. Like :kbd:`ctrl-r`, these operate only on the current line.
- Autosuggestions used to not suggest multi-line command-lines from history; now autosuggestions include individual lines from multi-line command-lines.
- The history pager search now preserves ordering between :kbd:`ctrl-s` forward and :kbd:`ctrl-r` backward searches.
- Instead of highlighting events by flashing *all text to the left of the cursor*,
failing history token search (:kbd:`alt-.`) flashes the associated token,
failing tab-completion flashes the to-be-completed token (:issue:`11050`),
deleting an autosuggestion (:kbd:`shift-delete`) flashes the suggestion,
and all other scenarios flash the full command line.
- Pasted commands are now stripped of any :code:`$\ ` command prefixes, to help pasting code snippets.
- Builtin help options (e.g. ``abbr --help``) now use ``man`` directly, meaning that variables like :envvar:`MANWIDTH` are respected (:issue:`11786`).
- ``funced`` will now edit copied functions directly, instead of the file where ``function --copy`` was invoked. (:issue:`11614`)
- Added a simple ``fish_jj_prompt`` which reduces visual noise in the prompt inside `Jujutsu <https://jj-vcs.github.io/jj/latest/>`__ repositories that are colocated with Git.
New or improved bindings
^^^^^^^^^^^^^^^^^^^^^^^^
- On non-macOS systems, :kbd:`alt-left`, :kbd:`alt-right`, :kbd:`alt-backspace`, :kbd:`alt-delete` no longer operate on punctuation-delimited words but on whole arguments, possibly including special characters like ``/`` and quoted spaces.
- On non-macOS systems, :kbd:`alt-left`, :kbd:`alt-right`, :kbd:`alt-backspace` and :kbd:`alt-delete` no longer operate on punctuation-delimited words but on whole arguments, possibly including special characters like ``/`` and quoted spaces.
On macOS, the corresponding :kbd:`ctrl-` prefixed keys operate on whole arguments.
Word operations are still available via the other respective modifier, same as in the browser.
Word operations are still available via the other respective modifier, just like in most web browsers.
- :kbd:`ctrl-z` (undo) after executing a command will restore the previous cursor position instead of placing the cursor at the end of the command line.
- The OSC 133 prompt marking feature has learned about kitty's ``click_events=1`` flag, which allows moving fish's cursor by clicking.
- :kbd:`ctrl-l` now pushes all text located above the prompt to the terminal's scrollback, before clearing and redrawing the screen (via a new special input function ``scrollback-push``).
For compatibility with terminals that do not provide the scroll-forward command,
this is only enabled by default if the terminal advertises support for the ``indn`` capability via XTGETTCAP.
- Bindings using shift with non-ASCII letters (such as :kbd:`ctrl-shift-ä`) are now supported.
If there is any modifier other than shift, this is the recommended notation (as opposed to :kbd:`ctrl-Ä`).
- The :kbd:`alt-s` binding will now also use ``run0`` if available.
- Some mouse support has been added: the OSC 133 prompt marking feature has learned about kitty's ``click_events=1`` flag, which allows moving fish's cursor by clicking in the command line,
and selecting pager items (:issue:`10932`).
- Before clearing the screen and redrawing, :kbd:`ctrl-l` now pushes all text located above the prompt to the terminal's scrollback,
via a new special input function :ref:`scrollback-push <special-input-functions-scrollback-push>`.
For compatibility with terminals that do not implement ECMA-48's :ref:`SCROLL UP <term-compat-indn>` command,
this function is only used if the terminal advertises support for that via :ref:`XTGETTCAP <term-compat-xtgettcap>`.
- Vi mode has learned :kbd:`ctrl-a` (increment) and :kbd:`ctrl-x` (decrement) (:issue:`11570`).
Completions
^^^^^^^^^^^
- ``git`` completions now show the remote url as a description when completing remotes.
- ``systemctl`` completions no longer print escape codes if ``SYSTEMD_COLORS`` is set (:issue:`11465`).
- ``git`` completions now show the remote URL as description when completing remotes.
- ``systemctl`` completions no longer print escape codes if ``SYSTEMD_COLORS`` happens to be set (:issue:`11465`).
- Added and improved many completion scripts, notably ``tmux``.
Improved terminal support
^^^^^^^^^^^^^^^^^^^^^^^^^
- Support for double, curly, dotted and dashed underlines in `fish_color_*` variables and :doc:`set_color <cmds/set_color>` (:issue:`10957`).
- Support for double, curly, dotted and dashed underlines, for use in ``fish_color_*`` variables and the :doc:`set_color builtin <cmds/set_color>` (:issue:`10957`).
- Underlines can now be colored independent of text (:issue:`7619`).
- New documentation page `Terminal Compatibility <terminal-compatibility.html>`_ (also accessible via ``man fish-terminal-compatibility``) lists required and optional terminal control sequences used by fish.
- New documentation page :doc:`Terminal Compatibility <terminal-compatibility>` (also accessible via ``man fish-terminal-compatibility``) lists the terminal control sequences used by fish.
Other improvements
------------------
- ``fish_indent`` and ``fish_key_reader`` are now available as builtins, and if fish is called with that name it will act like the given tool (as a multi-call binary).
This allows truly distributing fish as a single file. (:issue:`10876`)
- Updated Chinese and German translations.
- ``fish_indent --dump-parse-tree`` now emits simple metrics about the tree including its memory consumption.
- We added some tools to improve development workflows, for example ``build_tools/{check,update_translations,release}.sh`` and ``tests/test_driver.py``.
In conjunction with ``cargo``, these enable almost all day-to-day development tasks without using CMake.
For distributors
----------------
- ``fish_indent`` and ``fish_key_reader`` are still built as separate binaries for now, but can also be replaced with a symlink if you want to save disk space (:issue:`10876`).
- The CMake system was simplified and no longer second-guesses rustup. It will run rustc and cargo via $PATH or in ~/.cargo/bin/.
If that doesn't match your setup, set the Rust_COMPILER and Rust_CARGO cmake variables (:issue:`11328`).
- Cygwin support has been reintroduced, since rust gained a Cygwin target (https://github.com/rust-lang/rust/pull/134999, :issue:`11238`).
- Builtin commands that support the ``--help`` option now require the ``man`` program.
The direct dependency on ``mandoc`` and ``nroff`` has been removed.
- fish no longer uses gettext MO files, see :ref:`below <changelog-4.1-gettext>`.
If you have use cases which are incompatible with our new approach, please let us know.
- The :doc:`fish_indent <cmds/fish_indent>` and :doc:`fish_key_reader <cmds/fish_key_reader>` programs are now also available as builtins.
If fish is invoked via e.g. a symlink with one of these names,
it will act like the given tool (i.e. it's a multi-call binary).
This allows truly distributing fish as a single file (:issue:`10876`).
- The CMake build configuration has been simplified and no longer second-guesses rustup.
It will run rustc and cargo via :envvar:`PATH` or in ~/.cargo/bin/.
If that doesn't match your setup, set the Rust_COMPILER and Rust_CARGO CMake variables (:issue:`11328`).
- Cygwin support has been reintroduced, since `Rust gained a Cygwin target <https://github.com/rust-lang/rust/pull/134999>`__ (:issue:`11238`).
- CMake 3.15 is now required.
.. _changelog-4.1-embedded:
Changes to self-installing builds
---------------------------------
The self-installing build type introduced in fish 4.0 has been changed (:issue:`11143`).
Now fish built with embedded data will just read the data straight from its own binary or write it out to temporary files when necessary, instead of requiring an installation step on start.
That means it is now possible to build fish as a single file and copy it to any system with a compatible CPU architecture, including as a different user, without extracting any files.
As before, this is the default when building via ``cargo``, and disabled when building via CMake.
For packagers we continue to recommend CMake.
Note: When fish is built like this, the :envvar:`__fish_data_dir` variable will be empty because that directory no longer has meaning.
You should generally not need these files.
For example, if you want to make sure that completions for "foo" are loaded, use ``complete -C"foo " >/dev/null`` instead).
The raw files are still exposed via :ref:`status subcommands <status-get-file>`, mainly for fish's internal use, but you can also use them as a last resort.
Remaining benefits of a full installation (as currently done by CMake) are:
- man pages like ``fish(1)`` in standard locations, easily accessible from outside fish.
- a local copy of the HTML documentation, typically accessed via the :doc:`help <cmds/help>` function.
In builds with embedded data, ``help`` will redirect to e.g. `<https://fishshell.com/docs/current/>`__
- ``fish_indent`` and ``fish_key_reader`` as separate files, making them easily accessible outside fish
- an (empty) ``/etc/fish/config.fish`` as well as empty directories ``/etc/fish/{functions,completions,conf.d}``
- ``$PREFIX/share/pkgconfig/fish.pc``, which defines directories for configuration-snippets, like ``vendor_completions.d``
.. _changelog-4.1-gettext:
Changes to gettext localization
-------------------------------
We replaced several parts of the gettext functionality with custom implementations (:issue:`11726`).
Most notably, message extraction, which should now work reliably, and the runtime implementation, where we no longer dynamically link to gettext, but instead use our own implementation, whose behavior is similar to GNU gettext, with some :doc:`minor deviations <cmds/_>`.
Our implementation now fully respects fish variables, so locale variables do not have to be exported for fish localizations to work.
They still have to be exported to inform other programs about language preferences.
The :envvar:`LANGUAGE` environment variable is now treated as a path variable, meaning it is an implicitly colon-separated list.
While we no longer have any runtime dependency on gettext, we still need gettext tools for building, most notably ``msgfmt``.
When building without ``msgfmt`` available, localization will not work with the resulting executable.
Localization data is no longer sourced at runtime from MO files on the file system, but instead built into the executable.
This is always done, independently of the other data embedding, so all fish executables will have access to all message catalogs, regardless of the state of the file system.
Disabling our new ``localize-messages`` cargo feature will cause fish to be built without localization support.
CMake builds can continue to use the ``WITH_GETTEXT`` option, with the same semantics as the ``localize-messages`` feature.
The current implementation does not provide any configuration options for controlling which language catalogs are built into the executable (other than disabling them all).
As a workaround, you can delete files in the ``po`` directory before building to exclude unwanted languages.
.. _changelog-4.1-argparse:
Changes to the :doc:`argparse <cmds/argparse>` builtin
------------------------------------------------------
- ``argparse`` now saves recognised options, including option-arguments in :envvar:`argv_opts`, allowing them to be forwarded to other commands (:issue:`6466`).
- ``argparse`` options can now be marked to be deleted from :envvar:`argv_opts` (by adding a ``&`` at the end of the option spec, before a ``!`` if present). There is now also a corresponding ``-d`` / ``--delete`` option to ``fish_opt``.
- ``argparse --ignore-unknown`` now removes preceding known short options from groups containing unknown options (e.g. when parsing ``-abc``, if ``a`` is known but ``b`` is not, then :envvar:`argv` will contain ``-bc``).
- ``argparse`` now has an ``-u`` / ``--move-unknown`` option that works like ``--ignore-unknown`` but preserves unknown options in :envvar:`argv`.
- ``argparse`` now has an ``-S`` / ``--strict-longopts`` option that forbids abbreviating long options or passing them with a single dash (e.g. if there is a long option called ``foo``, ``--fo`` and ``--foo`` won't match it).
- ``argparse`` now has a ``-U`` / ``--unknown-arguments`` option to specify how to parse unknown option's arguments.
- ``argparse`` now allows specifying options that take multiple optional values by using ``=*`` in the option spec (:issue:`8432`).
In addition, ``fish_opt`` has been modified to support such options by using the ``--multiple-vals`` together with ``-o`` / ``--optional-val``; ``-m`` is also now acceptable as an abbreviation for ``--multiple-vals``.
- ``fish_opt`` no longer requires you give a short flag name when defining options, provided you give it a long flag name with more than one character.
- ``argparse`` option specifiers for long-only options can now start with ``/``, allowing the definition of long options with a single letter. Due to this change, the ``--long-only`` option to ``fish_opt`` is now no longer necessary and is deprecated.
- ``fish_opt`` now has a ``-v`` / ``--validate`` option you can use to give a fish script to validate values of the option.
--------------
fish 4.0.9 (released September 27, 2025)
========================================
This release fixes:
- a regression in 4.0.6 causing shifted keys to not be inserted on some terminals (:issue:`11813`).
- a regression in 4.0.6 causing the build to fail on systems where ``char`` is unsigned (:issue:`11804`).
- a regression in 4.0.0 causing a crash on an invalid :doc:`bg <cmds/bg>` invocation.
--------------
fish 4.0.8 (released September 18, 2025)
========================================
This release fixes a regression in 4.0.6 that caused user bindings to be shadowed by either fish's or a plugin's bindings (:issue:`11803`).
--------------
fish 4.0.6 (released September 12, 2025)
========================================
This release of fish fixes a number of issues identified in fish 4.0.2:
- fish now properly inherits $PATH under Windows WSL2 (:issue:`11354`).
- Remote filesystems are detected properly again on non-Linux systems.
- the :doc:`printf <cmds/printf>` builtin no longer miscalculates width of multi-byte characters (:issue:`11412`).
- For many years, fish has been "relocatable" -- it was possible to move the entire ``CMAKE_INSTALL_PREFIX`` and fish would use paths relative to its binary.
Only gettext locale paths were still determined purely at compile time, which has been fixed.
- the :doc:`commandline <cmds/commandline>` builtin failed to print the commandline set by a ``commandline -C`` invocation, which broke some completion scripts.
This has been corrected (:issue:`11423`).
- To work around terminals that fail to parse Operating System Command (OSC) sequences, a temporary feature flag has been added.
It allows you to disable prompt marking (OSC 133) by running (once) ``set -Ua fish_features no-mark-prompt`` and restarting fish (:issue:`11749`).
- The routines to save history and universal variables have seen some robustness improvements.
- builtin :doc:`status current-command <cmds/status>` no longer prints a trailing blank line.
- A crash displaying multi-line quoted command substitutions has been fixed (:issue:`11444`).
- Commands like ``set fish_complete_path ...`` accidentally disabled completion autoloading, which has been corrected.
- ``nmcli`` completions have been fixed to query network information dynamically instead of only when completing the first time.
- Git completions no longer print an error when no `git-foo` executable is in :envvar:`PATH`.
- Custom completions like ``complete foo -l long -xa ...`` that use the output of ``commandline -t``.
on a command-line like ``foo --long=`` have been invalidated by a change in 4.0; the completion scripts have been adjusted accordingly (:issue:`11508`).
- Some completions were misinterpreted, which caused garbage to be displayed in the completion list. This has been fixed.
- fish no longer interprets invalid control sequences from the terminal as if they were :kbd:`alt-[` or :kbd:`alt-o` key strokes.
- :doc:`bind <cmds/bind>` has been taught about the :kbd:`printscreen` and :kbd:`menu` keys.
- :kbd:`alt-delete` now deletes the word right of the cursor.
- :kbd:`ctrl-alt-h` erases the last word again (:issue:`11548`).
- :kbd:`alt-left` :kbd:`alt-right` were misinterpreted because they send unexpected sequences on some terminals; a workaround has been added. (:issue:`11479`).
- Key bindings like ``bind shift-A`` are no longer accepted; use ``bind shift-a`` or ``bind A``.
- Key bindings like ``bind shift-a`` take precedence over ``bind A`` when the key event included the shift modifier.
- Bindings using shift with non-ASCII letters (such as :kbd:`ctrl-shift-ä`) are now supported.
- Bindings with modifiers such as ``bind ctrl-w`` work again on non-Latin keyboard layouts such as a Russian one.
This is implemented by allowing key events such as :kbd:`ctrl-ц` to match bindings of the corresponding Latin key, using the kitty keyboard protocol's base layout key (:issue:`11520`).
- Vi mode: The cursor position after pasting via :kbd:`p` has been corrected.
- Vi mode: Trying to replace the last character via :kbd:`r` no longer replaces the last-but-one character (:issue:`11484`).
--------------
@@ -99,7 +283,7 @@ This release of fish fixes a number of issues identified in fish 4.0.1:
- The warning when the terminfo database can't be found has been downgraded to a log message. fish will act as if the terminal behaves like xterm-256color, which is correct for the vast majority of cases (:issue:`11277`, :issue:`11290`).
- Key combinations using the super (Windows/command) key can now (actually) be bound using the :kbd:`super-` prefix (:issue:`11217`). This was listed in the release notes for 4.0.1 but did not work correctly.
- :doc:`function <cmds/function>` is stricter about argument parsing, rather than allowing additional parameters to be silently ignored (:issue:`11295`).
- Using parentheses in the :doc:`test <cmds/test>` builtin works correctly, following a regression in 4.0.0 where they were not recognized (:issue:`11387`).
- Using parentheses in the :doc:`test <cmds/test>` builtin works correctly, following a regression in 4.0.0 where they were not recognized (:issue:`11387`).
- :kbd:`delete` in Vi mode when Num Lock is active will work correctly (:issue:`11303`).
- Abbreviations cannot alter the command-line contents, preventing a crash (:issue:`11324`).
- Improvements to various completions, including new completions for ``wl-randr`` (:issue:`11301`), performance improvements for ``cargo`` completions by avoiding network requests (:issue:`11347`), and other improvements for ``btrfs`` (:issue:`11320`), ``cryptsetup`` (:issue:`11315`), ``git`` (:issue:`11319`, :issue:`11322`, :issue:`11323`), ``jj`` (:issue:`11046`), and ``systemd-analyze`` (:issue:`11314`).
@@ -149,7 +333,7 @@ Notable backwards-incompatible changes
- As part of a larger binding rework, ``bind`` gained a new key notation.
In most cases the old notation should keep working, but in rare cases you may have to change a ``bind`` invocation to use the new notation.
See :ref:`below <changelog-new-bindings>` for details.
See :ref:`below <changelog-4.0-new-bindings>` for details.
- :kbd:`ctrl-c` now calls a new bind function called ``clear-commandline``. The old behavior, which leaves a "^C" marker, is available as ``cancel-commandline`` (:issue:`10935`)
- ``random`` will produce different values from previous versions of fish when used with the same seed, and will work more sensibly with small seed numbers.
The seed was never guaranteed to give the same result across systems,
@@ -171,7 +355,7 @@ Notable backwards-incompatible changes
Notable improvements and fixes
------------------------------
.. _changelog-new-bindings:
.. _changelog-4.0-new-bindings:
- fish now requests XTerm's ``modifyOtherKeys`` keyboard encoding and `kitty keyboard protocol's <https://sw.kovidgoyal.net/kitty/keyboard-protocol/>`_ progressive enhancements (:issue:`10359`).
Depending on terminal support, this allows to binding more key combinations, including arbitrary combinations of modifiers :kbd:`ctrl`, :kbd:`alt` and :kbd:`shift`, and distinguishing (for example) :kbd:`ctrl-i` from :kbd:`tab`.
@@ -302,7 +486,7 @@ Interactive improvements
New or improved bindings
^^^^^^^^^^^^^^^^^^^^^^^^
- When the cursor is on a command that resolves to an executable script, :kbd:`alt-o` will now open that script in your editor (:issue:`10266`).
- During up-arrow history search, :kbd:`shift-delete` will delete the current search item and move to the next older item. Previously this was only supported in the history pager.
- During up-arrow history search, :kbd:`shift-delete` will delete the current search item and move to the next older item. Previously this was only supported in the history pager.
- :kbd:`shift-delete` will also remove the currently-displayed autosuggestion from history, and remove it as a suggestion.
- :kbd:`ctrl-Z` (also known as :kbd:`ctrl-shift-z`) is now bound to redo.
- Some improvements to the :kbd:`alt-e` binding which edits the command line in an external editor:
@@ -314,7 +498,7 @@ New or improved bindings
- Bindings like :kbd:`alt-l` that print output in between prompts now work correctly with multiline commandlines.
- :kbd:`alt-d` on an empty command line lists the directory history again. This restores the behavior of version 2.1.
- ``history-prefix-search-backward`` and ``-forward`` now maintain the cursor position, instead of moving the cursor to the end of the command line (:issue:`10430`).
- The following keys have refined behavior if the terminal supports :ref:`the new keyboard encodings <changelog-new-bindings>`:
- The following keys have refined behavior if the terminal supports :ref:`the new keyboard encodings <changelog-4.0-new-bindings>`:
- :kbd:`shift-enter` now inserts a newline instead of executing the command line.
- :kbd:`ctrl-backspace` now deletes the last word instead of only one character (:issue:`10741`).
@@ -373,7 +557,7 @@ Improved terminal support
Other improvements
------------------
- ``status`` gained a ``buildinfo`` subcommand, to print information on how fish was built, to help with debugging (:issue:`10896`).
- ``status`` gained a ``build-info`` subcommand, to print information on how fish was built, to help with debugging (:issue:`10896`).
- ``fish_indent`` will now collapse multiple empty lines into one (:issue:`10325`).
- ``fish_indent`` now preserves the modification time of files if there were no changes (:issue:`10624`).
- Performance in launching external processes has been improved for many cases (:issue:`10869`).

View File

@@ -15,8 +15,6 @@ endif()
# Set up standard directories.
include(GNUInstallDirs)
include(cmake/gettext.cmake)
# Set up PCRE2
# This sets an environment variable that needs to be available before the Rust stanzas
include(cmake/PCRE2.cmake)
@@ -54,8 +52,8 @@ function(CREATE_TARGET target)
$<$<CONFIG:RelWithDebInfo>:--profile=release-with-debug>
--target ${Rust_CARGO_TARGET}
--no-default-features
--features=${FISH_CARGO_FEATURES}
${CARGO_FLAGS}
${FEATURES_ARG}
&&
"${CMAKE_COMMAND}" -E
copy "${rust_target_dir}/${rust_profile}/${target}" "${CMAKE_CURRENT_BINARY_DIR}"

View File

@@ -11,10 +11,17 @@ Contributions are welcome, and there are many ways to contribute!
Whether you want to change some of the core Rust source, enhance or add a completion script or function,
improve the documentation or translate something, this document will tell you how.
Getting Set Up
==============
Fish is developed on Github, at https://github.com/fish-shell/fish-shell.
Mailing List
============
Send patches to the public mailing list: mailto:~krobelus/fish-shell@lists.sr.ht.
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.
First, you'll need an account there, and you'll need a git clone of fish.
Fork it on Github and then run::
@@ -29,7 +36,7 @@ For that, you'll require:
- Rust - when in doubt, try rustup
- CMake
- PCRE2 (headers and libraries) - optional, this will be downloaded if missing
- gettext (headers and libraries) - optional, for translation support
- gettext (only the msgfmt tool) - optional, for translation support
- Sphinx - optional, to build the documentation
Of course not everything is required always - if you just want to contribute something to the documentation you'll just need Sphinx,
@@ -43,7 +50,7 @@ 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 (including ``make fish_run_tests`` and ``build_tools/style.fish``)
- Use automated tools to help you (``build_tools/check.sh``)
Contributing completions
========================
@@ -103,7 +110,7 @@ before committing your change. That will run our autoformatters:
- ``rustfmt`` for Rust
- ``fish_indent`` (shipped with fish) for fish script
- ``black`` for python
- ``ruff format`` for python
If youve already committed your changes thats okay since it will then
check the files in the most recent commit. This can be useful after
@@ -219,12 +226,10 @@ Or you can run them on a fish, without involving cmake::
cargo build
cargo test # for the unit tests
tests/test_driver.py --cachedir=/tmp target/debug # for the script and interactive tests
tests/test_driver.py target/debug # for the script and interactive tests
Here, the first argument to test_driver.py refers to a directory with ``fish``, ``fish_indent`` and ``fish_key_reader`` in it.
In this example we're in the root of the git repo and have run ``cargo build`` without ``--release``, so it's a debug build.
The ``--cachedir /tmp`` argument means it will keep the fish_test_helper binary in /tmp instead of recompiling it for every test.
This saves some time, but isn't strictly necessary.
Git hooks
---------
@@ -245,51 +250,42 @@ One possibility is a pre-push hook script like this one:
# Git gives us lines like "refs/heads/frombranch SOMESHA1 refs/heads/tobranch SOMESHA1"
# We're only interested in the branches
isprotected=false
while read from _ to _; do
if [ "x$to" = "xrefs/heads/$protected_branch" ]; then
isprotected=1
if [ "$to" = "refs/heads/$protected_branch" ]; then
isprotected=true
fi
done
if [ "x$isprotected" = x1 ]; then
echo "Running tests before push to master"
make fish_run_tests
RESULT=$?
if [ $RESULT -ne 0 ]; then
echo "Tests failed for a push to master, we can't let you do that" >&2
exit 1
fi
if "$isprotected"; then
echo "Running checks before push to master"
build_tools/check.sh
fi
exit 0
This will check if the push is to the master branch and, if it is, only
allow the push if running ``make fish_run_tests`` succeeds. In some circumstances
allow the push if running ``build_tools/check.sh`` succeeds. In some circumstances
it may be advisable to circumvent this check with
``git push --no-verify``, but usually that isnt necessary.
To install the hook, place the code in a new file
``.git/hooks/pre-push`` and make it executable.
Coverity Scan
-------------
We use Coveritys static analysis tool which offers free access to open
source projects. While access to the tool itself is restricted,
fish-shell organization members should know that they can login
`here <https://scan.coverity.com/projects/fish-shell-fish-shell?tab=overview>`__
with their GitHub account. Currently, tests are triggered upon merging
the ``master`` branch into ``coverity_scan_master``. Even if you are not
a fish developer, you can keep an eye on our statistics there.
Contributing Translations
=========================
Fish uses the GNU gettext library to translate messages from English to
other languages.
Fish uses GNU gettext to translate messages from English to other languages.
We use custom tools for extracting messages from source files and to localize at runtime.
This means that we do not have a runtime dependency on the gettext library.
It also means that some features are not supported, such as message context and plurals.
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 ``LANG.po``, where ``LANG`` is the
two letter ISO 639-1 language code of the target language (e.g. ``de`` for
German). A region specifier can also be used (e.g. ``pt_BR`` for Brazilian Portuguese).
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
(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.
These are the files you will interact with when adding translations.
Adding translations for a new language
--------------------------------------
@@ -297,29 +293,38 @@ Adding translations for a new language
Creating new translations requires the Gettext tools.
More specifically, you will need ``msguniq`` and ``msgmerge`` for creating translations for a new
language.
In addition, the ``cargo-expand`` tool is required.
If you have ``cargo`` installed, run::
cargo install --locked --version 1.0.106 cargo-expand
to install ``cargo-expand`` (Note that other versions might not work correctly with our scripts).
To create a new translation, run::
build_tools/update_translations.fish po/LANG.po
build_tools/update_translations.fish po/ll_CC.po
By default, this also creates ``mo`` files, which contain the information from the ``po`` files in a
binary format.
Fish uses these files for translating at runtime.
They are not tracked in version control, but they can help translators check if their translations
show up correctly.
If you build fish locally (``cargo build``), and then run the resulting binary,
it will make use of the ``mo`` files generated by the script.
Use the ``LANG`` environment variable to tell fish which language to use, e.g.::
This will create a new PO file containing all messages available for translation.
If the file already exists, it will be updated.
LANG=pt_BR.utf8 target/debug/fish
After modifying a PO file, you can recompile fish, and it will integrate the modifications you made.
This requires that the ``msgfmt`` utility is installed (comes as part of ``gettext``).
It is important that the ``localize-messages`` cargo feature is enabled, which it is by default.
You can explicitly enable it using::
If you do not care about the ``mo`` files you can pass the ``--no-mo`` flag to the
``update_translations.fish`` script.
cargo build --features=localize-messages
Use environment variables to tell fish which language to use, e.g.::
LANG=pt_BR.utf8 fish
or within the running fish shell::
set LANG pt_BR.utf8
For more options regarding how to choose languages, see
`the corresponding gettext documentation
<https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html>`__.
One neat thing you can do is set a list of languages to check for translations in the order defined
using the ``LANGUAGE`` variable, e.g.::
set LANGUAGE pt_BR de_DE
to try to translate messages to Portuguese, if that fails try German, and if that fails too you will
see the English version defined in the source code.
Modifying existing translations
-------------------------------
@@ -327,13 +332,8 @@ Modifying existing translations
If you want to work on translations for a language which already has a corresponding ``po`` file, it
is sufficient to edit this file. No other changes are necessary.
To see your translations in action you can run::
build_tools/update_translations.fish --only-mo po/LANG.po
to update the binary ``mo`` used by fish. Check the information for adding new languages for a
description on how you can get fish to use these files.
Running this script requires a fish executable and the gettext ``msgfmt`` tool.
After recompiling fish, you should be able to see your translations in action. See the previous
section for details.
Editing PO files
----------------
@@ -341,20 +341,20 @@ 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 ``po/sv.po``, and you'll see something like::
msgid "%ls: No suitable job\n"
msgstr ""
msgid "%s: No suitable job\n"
msgstr ""
The ``msgid`` here is the "name" of the string to translate, typically the English string to translate.
The second line (``msgstr``) is where your translation goes.
For example::
msgid "%ls: No suitable job\n"
msgstr "%ls: Inget passande jobb\n"
msgid "%s: No suitable job\n"
msgstr "%s: Inget passande jobb\n"
Any ``%s`` / ``%ls`` or ``%d`` are placeholders that fish will use for formatting at runtime. It is important that they match - the translated string should have the same placeholders in the same order.
Any ``%s`` or ``%d`` are placeholders that fish will use for formatting at runtime. It is important that they match - the translated string should have the same placeholders in the same order.
Also any escaped characters, like that ``\n`` newline at the end, should be kept so the translation has the same behavior.
@@ -367,7 +367,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 ``#~``).
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``).
This is only relevant for developers modifying the source files of fish or fish scripts.
@@ -381,7 +381,7 @@ macros:
::
streams.out.append(wgettext_fmt!("%ls: There are no jobs\n", argv[0]));
streams.out.append(wgettext_fmt!("%s: There are no jobs\n", argv[0]));
All messages in fish script must be enclosed in single or double quote
characters for our message extraction script to find them.
@@ -390,15 +390,15 @@ that the following are **not** valid:
::
echo (_ hello)
_ "goodbye"
echo (_ hello)
_ "goodbye"
Above should be written like this instead:
::
echo (_ "hello")
echo (_ "goodbye")
echo (_ "hello")
echo (_ "goodbye")
You can use either single or double quotes to enclose the
message to be translated. You can also optionally include spaces after

150
Cargo.lock generated
View File

@@ -42,9 +42,9 @@ dependencies = [
[[package]]
name = "cfg-if"
version = "1.0.0"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "cfg_aliases"
@@ -97,20 +97,35 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "fish"
version = "4.1.0-alpha0"
version = "4.1.0-snapshot"
dependencies = [
"bitflags",
"cc",
"cfg-if",
"errno",
"fish-build-helper",
"fish-build-man-pages",
"fish-gettext-extraction",
"fish-gettext-maps",
"fish-gettext-mo-file-parser",
"fish-printf",
"libc",
"lru",
"macro_rules_attribute",
"nix",
"num-traits",
"once_cell",
"pcre2",
"phf 0.12.1",
"phf_codegen 0.12.1",
"portable-atomic",
"rand",
"rsconf",
@@ -121,6 +136,43 @@ dependencies = [
"widestring",
]
[[package]]
name = "fish-build-helper"
version = "0.0.0"
dependencies = [
"rsconf",
]
[[package]]
name = "fish-build-man-pages"
version = "0.0.0"
dependencies = [
"fish-build-helper",
"rsconf",
]
[[package]]
name = "fish-gettext-extraction"
version = "0.0.0"
dependencies = [
"proc-macro2",
]
[[package]]
name = "fish-gettext-maps"
version = "0.0.0"
dependencies = [
"fish-build-helper",
"fish-gettext-mo-file-parser",
"phf 0.12.1",
"phf_codegen 0.12.1",
"rsconf",
]
[[package]]
name = "fish-gettext-mo-file-parser"
version = "0.0.0"
[[package]]
name = "fish-printf"
version = "0.2.1"
@@ -204,6 +256,22 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "macro_rules_attribute"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520"
dependencies = [
"macro_rules_attribute-proc_macro",
"paste",
]
[[package]]
name = "macro_rules_attribute-proc_macro"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30"
[[package]]
name = "memchr"
version = "2.7.4"
@@ -276,6 +344,12 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pcre2"
version = "0.2.9"
@@ -302,7 +376,16 @@ version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
dependencies = [
"phf_shared",
"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",
]
[[package]]
@@ -311,8 +394,18 @@ version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
dependencies = [
"phf_generator",
"phf_shared",
"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",
]
[[package]]
@@ -321,10 +414,20 @@ version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared",
"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"
dependencies = [
"fastrand",
"phf_shared 0.12.1",
]
[[package]]
name = "phf_shared"
version = "0.11.3"
@@ -334,6 +437,15 @@ dependencies = [
"siphasher",
]
[[package]]
name = "phf_shared"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981"
dependencies = [
"siphasher",
]
[[package]]
name = "pkg-config"
version = "0.3.31"
@@ -348,18 +460,18 @@ checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
[[package]]
name = "proc-macro2"
version = "1.0.92"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.38"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
@@ -399,9 +511,9 @@ dependencies = [
[[package]]
name = "rust-embed"
version = "8.5.0"
version = "8.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0"
checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
@@ -410,9 +522,9 @@ dependencies = [
[[package]]
name = "rust-embed-impl"
version = "8.5.0"
version = "8.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478"
checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c"
dependencies = [
"proc-macro2",
"quote",
@@ -423,9 +535,9 @@ dependencies = [
[[package]]
name = "rust-embed-utils"
version = "8.5.0"
version = "8.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d"
checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594"
dependencies = [
"sha2",
"walkdir",
@@ -532,8 +644,8 @@ checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662"
dependencies = [
"fnv",
"nom",
"phf",
"phf_codegen",
"phf 0.11.3",
"phf_codegen 0.11.3",
]
[[package]]

View File

@@ -1,41 +1,25 @@
[workspace]
resolver = "2"
members = ["printf"]
members = ["crates/*"]
[workspace.package]
# To build revisions that use Corrosion (those before 2024-01), use CMake 3.19, Rustc 1.78 and Rustup 1.27.
rust-version = "1.70"
edition = "2021"
[profile.release]
overflow-checks = true
lto = true
[profile.release-with-debug]
inherits = "release"
debug = true
[package]
name = "fish"
version = "4.1.0-alpha0"
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"
repository = "https://github.com/fish-shell/fish-shell"
homepage = "https://fishshell.com"
readme = "README.rst"
[dependencies]
pcre2 = { git = "https://github.com/fish-shell/rust-pcre2", tag = "0.2.9-utf32", default-features = false, features = [
"utf32",
] }
[workspace.dependencies]
bitflags = "2.5.0"
cc = "1.0.94"
cfg-if = "1.0.3"
errno = "0.3.0"
libc = "0.2"
fish-build-helper = { path = "crates/build-helper" }
fish-build-man-pages = { path = "crates/build-man-pages" }
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"] }
libc = "0.2.155"
# 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.
@@ -48,31 +32,90 @@ nix = { version = "0.30.1", default-features = false, features = [
] }
num-traits = "0.2.19"
once_cell = "1.19.0"
fish-printf = { path = "./printf", features = ["widestring"] }
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" }
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"] }
widestring = "1.2.0"
rsconf = "0.2.2"
rust-embed = { version = "8.7.2", features = ["deterministic-timestamps"] }
serial_test = { version = "3", default-features = false }
# We need 0.9.0 specifically for some crash fixes.
terminfo = "0.9.0"
rust-embed = { version = "8.2.0", optional = true }
widestring = "1.2.0"
unicode-segmentation = "1.12.0"
unicode-width = "0.2.0"
unix_path = "1.0.1"
[profile.release]
overflow-checks = true
lto = true
[profile.release-with-debug]
inherits = "release"
debug = true
[package]
name = "fish"
version = "4.1.0-snapshot"
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"
homepage = "https://fishshell.com"
readme = "README.rst"
[dependencies]
bitflags.workspace = true
cfg-if.workspace = true
errno.workspace = true
fish-build-helper.workspace = true
fish-build-man-pages = { workspace = true, optional = true }
fish-gettext-extraction = { workspace = true, optional = true }
fish-gettext-maps = { workspace = true, optional = true }
fish-printf.workspace = true
libc.workspace = true
lru.workspace = true
macro_rules_attribute = "0.2.2"
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
[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
portable-atomic = { version = "1", default-features = false, features = [
"fallback",
] }
portable-atomic.workspace = true
[target.'cfg(windows)'.dependencies]
rust-embed = { workspace = true, optional = true, features = ["deterministic-timestamps", "debug-embed"] }
[target.'cfg(not(windows))'.dependencies]
rust-embed = { workspace = true, optional = true, features = ["deterministic-timestamps"] }
[dev-dependencies]
serial_test = { version = "3", default-features = false }
serial_test.workspace = true
[build-dependencies]
cc = "1.0.94"
rsconf = "0.2.2"
cc.workspace = true
fish-build-helper.workspace = true
fish-gettext-mo-file-parser.workspace = true
phf_codegen = { workspace = true, optional = true }
rsconf.workspace = true
[target.'cfg(windows)'.build-dependencies]
unix_path = "1.0.1"
unix_path.workspace = true
[lib]
crate-type = ["rlib"]
@@ -91,9 +134,17 @@ name = "fish_key_reader"
path = "src/bin/fish_key_reader.rs"
[features]
default = ["embed-data"]
default = ["embed-data", "localize-messages"]
benchmark = []
embed-data = ["dep:rust-embed"]
embed-data = ["dep:rust-embed", "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"]
# 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
# `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 = []

View File

@@ -66,7 +66,8 @@ Windows
for Linux with the instructions for the appropriate distribution
listed above under “Packages for Linux”, or from source with the
instructions below.
- fish (4.0 on and onwards) cannot be installed in Cygwin, due to a lack of Rust support.
- Fish can also be installed on all versions of Windows using
`Cygwin <https://cygwin.com/>`__ or `MSYS2 <https://github.com/Berrysoft/fish-msys2>`__.
Building from source
~~~~~~~~~~~~~~~~~~~~
@@ -89,17 +90,14 @@ Running fish requires:
- some common \*nix system utilities (currently ``mktemp``), in
addition to the basic POSIX utilities (``cat``, ``cut``, ``dirname``,
``file``, ``ls``, ``mkdir``, ``mkfifo``, ``rm``, ``sort``, ``tee``, ``tr``,
``file``, ``ls``, ``mkdir``, ``mkfifo``, ``rm``, ``sh``, ``sort``, ``tee``, ``tr``,
``uname`` and ``sed`` at least, but the full coreutils plus ``find`` and
``awk`` is preferred)
- The gettext library, if compiled with
translation support
The following optional features also have specific requirements:
- builtin commands that have the ``--help`` option or print usage
messages require ``nroff`` or ``mandoc`` for
display
messages require ``man`` for display
- automated completion generation from manual pages requires Python 3.5+
- the ``fish_config`` web configuration tool requires Python 3.5+ and a web browser
- system clipboard integration (with the default Ctrl-V and Ctrl-X
@@ -124,13 +122,13 @@ Compiling fish requires:
- 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
- gettext (headers and libraries) - optional, for translation support
- gettext (only the msgfmt tool) - optional, for translation support
- an Internet connection, as other dependencies will be downloaded automatically
Sphinx is also optionally required to build the documentation from a
cloned git repository.
Additionally, running the full test suite requires Python 3, tmux, and the pexpect package.
Additionally, running the full test suite requires diff, git, Python 3.5+, pexpect, less, tmux and wget.
Building from source with CMake
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -164,7 +162,7 @@ In addition to the normal CMake build options (like ``CMAKE_INSTALL_PREFIX``), f
- 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).
- 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 build with gettext support for translations.
- WITH_GETTEXT=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 embedded data (experimental)
@@ -179,19 +177,20 @@ Fish will then read these right from its own binary, and print them out when nee
To install fish with embedded files, just use ``cargo``, like::
cargo install --path /path/to/fish # if you have a git clone
cargo install --git https://github.com/fish-shell/fish-shell --tag 4.0.0 # to build from git with a specific version
cargo install --git https://github.com/fish-shell/fish-shell # to build the current development snapshot without cloning
cargo install --git https://github.com/fish-shell/fish-shell --tag "$(curl -s https://api.github.com/repos/fish-shell/fish-shell/releases/latest | jq -r .tag_name)" # to build the latest release
cargo install --git https://github.com/fish-shell/fish-shell # to build the latest development snapshot
This will place the binaries in ``~/.cargo/bin/``, but you can place them wherever you want.
This build won't have the HTML docs (``help`` will open the online version) or translations.
This will place the standalone binaries in ``~/.cargo/bin/``, but you can place them wherever you want.
This build won't have the HTML docs (``help`` will open the online version).
It will try to build the man pages with sphinx-build. If that is not available and you would like to include man pages, you need to install it and retrigger the build script, e.g. by setting FISH_BUILD_DOCS=1::
FISH_BUILD_DOCS=1 cargo install --path .
Setting it to "0" disables the inclusion of man pages.
To disable translations, disable the ``localize-messages`` feature by passing ``--no-default-features --features=embed-data`` to cargo.
You can also link this build statically (but not against glibc) and move it to other computers.
Contributing Changes to the Code

339
build.rs
View File

@@ -1,9 +1,13 @@
#![allow(clippy::uninlined_format_args)]
use rsconf::{LinkType, Target};
use fish_build_helper::{env_var, fish_build_dir, workspace_root};
use rsconf::Target;
use std::env;
use std::error::Error;
use std::path::Path;
use std::path::{Path, PathBuf};
fn canonicalize<P: AsRef<Path>>(path: P) -> PathBuf {
std::fs::canonicalize(path).unwrap()
}
fn main() {
setup_paths();
@@ -11,28 +15,24 @@ fn main() {
// Add our default to enable tools that don't go through CMake, like "cargo test" and the
// language server.
// FISH_BUILD_DIR is set by CMake, if we are using it.
// OUT_DIR is set by Cargo when the build script is running (not compiling)
let default_build_dir = env::var("OUT_DIR").unwrap();
let build_dir = option_env!("FISH_BUILD_DIR").unwrap_or(&default_build_dir);
let build_dir = std::fs::canonicalize(build_dir).unwrap();
let build_dir = build_dir.to_str().unwrap();
rsconf::set_env_value("FISH_BUILD_DIR", build_dir);
rsconf::set_env_value(
"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(),
);
// 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",
std::fs::canonicalize(env!("CARGO_MANIFEST_DIR"))
.unwrap()
.as_path()
.to_str()
.unwrap(),
canonicalize(workspace_root()).to_str().unwrap(),
);
// Some build info
rsconf::set_env_value("BUILD_TARGET_TRIPLE", &env::var("TARGET").unwrap());
rsconf::set_env_value("BUILD_HOST_TRIPLE", &env::var("HOST").unwrap());
rsconf::set_env_value("BUILD_PROFILE", &env::var("PROFILE").unwrap());
rsconf::set_env_value("BUILD_TARGET_TRIPLE", &env_var("TARGET").unwrap());
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,
@@ -41,37 +41,16 @@ fn main() {
std::env::set_var("FISH_BUILD_VERSION", version);
let cman = std::fs::canonicalize(env!("CARGO_MANIFEST_DIR")).unwrap();
let targetman = cman.as_path().join("target").join("man");
#[cfg(feature = "embed-data")]
#[cfg(not(clippy))]
{
build_man(&targetman);
}
#[cfg(any(not(feature = "embed-data"), clippy))]
{
let sec1dir = targetman.join("man1");
let _ = std::fs::create_dir_all(sec1dir.to_str().unwrap());
}
rsconf::rebuild_if_paths_changed(&["src", "printf", "Cargo.toml", "Cargo.lock", "build.rs"]);
// 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(not(debug_assertions))]
rsconf::rebuild_if_paths_changed(&["doc_src", "share"]);
#[cfg(any(windows, not(debug_assertions)))]
rsconf::rebuild_if_path_changed("share");
cc::Build::new()
.file("src/libc.c")
.include(build_dir)
.compile("flibc.a");
#[cfg(feature = "gettext-extract")]
rsconf::rebuild_if_env_changed("FISH_GETTEXT_EXTRACTION_FILE");
let mut build = cc::Build::new();
// Add to the default library search path
build.flag_if_supported("-L/usr/local/lib/");
rsconf::add_library_search_path("/usr/local/lib");
let build = cc::Build::new();
let mut target = Target::new_from(build).unwrap();
// Keep verbose mode on until we've ironed out rust build script stuff
target.set_verbose(true);
@@ -98,54 +77,44 @@ 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.
(
"",
&(|_: &Target| Ok(false)) as &dyn Fn(&Target) -> Result<bool, Box<dyn Error>>,
),
("", &(|_: &Target| false) as &dyn Fn(&Target) -> bool),
("apple", &detect_apple),
("bsd", &detect_bsd),
("cygwin", &detect_cygwin),
("gettext", &have_gettext),
("small_main_stack", &has_small_stack),
// See if libc supports the thread-safe localeconv_l(3) alternative to localeconv(3).
("localeconv_l", &|target| {
Ok(target.has_symbol("localeconv_l"))
target.has_symbol("localeconv_l")
}),
("FISH_USE_POSIX_SPAWN", &|target| {
Ok(target.has_header("spawn.h"))
target.has_header("spawn.h")
}),
("HAVE_PIPE2", &|target| {
Ok(target.has_symbol("pipe2"))
target.has_symbol("pipe2")
}),
("HAVE_EVENTFD", &|target| {
// FIXME: NetBSD 10 has eventfd, but the libc crate does not expose it.
if cfg!(target_os = "netbsd") {
Ok(false)
false
} else {
Ok(target.has_header("sys/eventfd.h"))
target.has_header("sys/eventfd.h")
}
}),
("HAVE_WAITSTATUS_SIGNAL_RET", &|target| {
Ok(target.r#if("WEXITSTATUS(0x007f) == 0x7f", &["sys/wait.h"]))
target.r#if("WEXITSTATUS(0x007f) == 0x7f", &["sys/wait.h"])
}),
] {
match handler(target) {
Err(e) => {
rsconf::warn!("{}: {}", name, e);
rsconf::declare_cfg(name, false);
},
Ok(enabled) => rsconf::declare_cfg(name, enabled),
}
rsconf::declare_cfg(name, handler(target))
}
}
fn detect_apple(_: &Target) -> Result<bool, Box<dyn Error>> {
Ok(cfg!(any(target_os = "ios", target_os = "macos")))
fn detect_apple(_: &Target) -> bool {
cfg!(any(target_os = "ios", target_os = "macos"))
}
fn detect_cygwin(_: &Target) -> Result<bool, Box<dyn Error>> {
fn detect_cygwin(_: &Target) -> bool {
// Cygwin target is usually cross-compiled.
Ok(std::env::var("CARGO_CFG_TARGET_OS").unwrap() == "cygwin")
env_var("CARGO_CFG_TARGET_OS").unwrap() == "cygwin"
}
/// Detect if we're being compiled for a BSD-derived OS, allowing targeting code conditionally with
@@ -154,13 +123,14 @@ fn detect_cygwin(_: &Target) -> Result<bool, Box<dyn Error>> {
/// 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) -> Result<bool, Box<dyn Error>> {
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 = std::env::var("TARGET").unwrap();
let mut target = env_var("TARGET").unwrap();
if !target.chars().all(|c| c.is_ascii_lowercase()) {
target = target.to_ascii_lowercase();
}
#[allow(clippy::let_and_return)] // for old clippy
let is_bsd = target.ends_with("bsd") || target.ends_with("dragonfly");
#[cfg(any(
target_os = "dragonfly",
@@ -169,52 +139,7 @@ fn detect_bsd(_: &Target) -> Result<bool, Box<dyn Error>> {
target_os = "openbsd",
))]
assert!(is_bsd, "Target incorrectly detected as not BSD!");
Ok(is_bsd)
}
/// Detect libintl/gettext and its needed symbols to enable internationalization/localization
/// support.
fn have_gettext(target: &Target) -> Result<bool, Box<dyn Error>> {
// The following script correctly detects and links against gettext, but so long as we are using
// C++ and generate a static library linked into the C++ binary via CMake, we need to account
// for the CMake option WITH_GETTEXT being explicitly disabled.
rsconf::rebuild_if_env_changed("CMAKE_WITH_GETTEXT");
if let Some(with_gettext) = std::env::var_os("CMAKE_WITH_GETTEXT") {
if with_gettext.eq_ignore_ascii_case("0") {
return Ok(false);
}
}
// In order for fish to correctly operate, we need some way of notifying libintl to invalidate
// its localizations when the locale environment variables are modified. Without the libintl
// symbol _nl_msg_cat_cntr, we cannot use gettext even if we find it.
let mut libraries = Vec::new();
let mut found = 0;
let symbols = ["gettext", "_nl_msg_cat_cntr"];
for symbol in &symbols {
// Historically, libintl was required in order to use gettext() and co, but that
// functionality was subsumed by some versions of libc.
if target.has_symbol(symbol) {
// No need to link anything special for this symbol
found += 1;
continue;
}
for library in ["intl", "gettextlib"] {
if target.has_symbol_in(symbol, &[library]) {
libraries.push(library);
found += 1;
continue;
}
}
}
match found {
0 => Ok(false),
1 => Err(format!("gettext found but cannot be used without {}", symbols[1]).into()),
_ => {
rsconf::link_libraries(&libraries, LinkType::Default);
Ok(true)
}
}
is_bsd
}
/// Rust sets the stack size of newly created threads to a sane value, but is at at the mercy of the
@@ -223,13 +148,13 @@ fn have_gettext(target: &Target) -> Result<bool, Box<dyn Error>> {
///
/// 0.5 MiB is small enough that we'd have to drastically reduce MAX_STACK_DEPTH to less than 10, so
/// we instead use a workaround to increase the main thread size.
fn has_small_stack(_: &Target) -> Result<bool, Box<dyn Error>> {
fn has_small_stack(_: &Target) -> bool {
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "netbsd")))]
return Ok(false);
return false;
// NetBSD 10 also needs this but can't find pthread_get_stacksize_np.
#[cfg(target_os = "netbsd")]
return Ok(true);
return true;
#[cfg(any(target_os = "ios", target_os = "macos"))]
{
@@ -244,87 +169,79 @@ fn has_small_stack(_: &Target) -> Result<bool, Box<dyn Error>> {
// Modern macOS versions default to an 8 MiB main stack but legacy OS X have a 0.5 MiB one.
let stack_size = unsafe { pthread_get_stacksize_np(pthread_self()) };
const TWO_MIB: usize = 2 * 1024 * 1024 - 1;
match stack_size {
0..=TWO_MIB => Ok(true),
_ => Ok(false),
}
stack_size <= TWO_MIB
}
}
fn setup_paths() {
#[cfg(unix)]
use std::path::PathBuf;
#[cfg(windows)]
use unix_path::{Path, PathBuf};
fn get_path(name: &str, default: &str, onvar: &Path) -> PathBuf {
let mut var = PathBuf::from(env::var(name).unwrap_or(default.to_string()));
if var.is_relative() {
var = onvar.join(var);
fn overridable_path(env_var_name: &str, f: impl FnOnce(Option<String>) -> PathBuf) -> PathBuf {
rsconf::rebuild_if_env_changed(env_var_name);
let path = f(env_var(env_var_name));
rsconf::set_env_value(env_var_name, path.to_str().unwrap());
path
}
fn join_if_relative(parent_if_relative: &Path, path: String) -> PathBuf {
let path = PathBuf::from(path);
if path.is_relative() {
parent_if_relative.join(path)
} else {
path
}
var
}
let (prefix_from_home, prefix) = if let Ok(pre) = env::var("PREFIX") {
(false, PathBuf::from(pre))
} else {
(true, PathBuf::from(".local/"))
};
let prefix = overridable_path("PREFIX", |env_prefix| {
PathBuf::from(env_prefix.unwrap_or("/usr/local".to_string()))
});
// If someone gives us a $PREFIX, we need it to be absolute.
// Otherwise we would try to get it from $HOME and that won't really work.
if !prefix_from_home && prefix.is_relative() {
panic!("Can't have relative prefix");
}
rsconf::rebuild_if_env_changed("PREFIX");
rsconf::set_env_value("PREFIX", prefix.to_str().unwrap());
let datadir = get_path("DATADIR", "share/", &prefix);
rsconf::set_env_value("DATADIR", datadir.to_str().unwrap());
let datadir = join_if_relative(&prefix, env_var("DATADIR").unwrap_or("share/".to_string()));
rsconf::rebuild_if_env_changed("DATADIR");
#[cfg(not(feature = "embed-data"))]
rsconf::set_env_value("DATADIR", datadir.to_str().unwrap());
let datadir_subdir = if prefix_from_home {
"fish/install"
} else {
"fish"
};
rsconf::set_env_value("DATADIR_SUBDIR", datadir_subdir);
overridable_path("SYSCONFDIR", |env_sysconfdir| {
join_if_relative(
&datadir,
env_sysconfdir.unwrap_or(
// Embedded builds use "/etc," not "./share/etc".
if cfg!(feature = "embed-data") {
"/etc/"
} else {
"etc/"
}
.to_string(),
),
)
});
let bindir = get_path("BINDIR", "bin/", &prefix);
rsconf::set_env_value("BINDIR", bindir.to_str().unwrap());
rsconf::rebuild_if_env_changed("BINDIR");
let sysconfdir = get_path(
"SYSCONFDIR",
// If we get our prefix from $HOME, we should use the system's /etc/
// ~/.local/share/etc/ makes no sense
if prefix_from_home { "/etc/" } else { "etc/" },
&datadir,
);
rsconf::set_env_value("SYSCONFDIR", sysconfdir.to_str().unwrap());
rsconf::rebuild_if_env_changed("SYSCONFDIR");
let localedir = get_path("LOCALEDIR", "locale/", &datadir);
rsconf::set_env_value("LOCALEDIR", localedir.to_str().unwrap());
rsconf::rebuild_if_env_changed("LOCALEDIR");
let docdir = get_path("DOCDIR", "doc/fish", &datadir);
rsconf::set_env_value("DOCDIR", docdir.to_str().unwrap());
rsconf::rebuild_if_env_changed("DOCDIR");
#[cfg(not(feature = "embed-data"))]
{
overridable_path("BINDIR", |env_bindir| {
join_if_relative(&prefix, env_bindir.unwrap_or("bin/".to_string()))
});
overridable_path("LOCALEDIR", |env_localedir| {
join_if_relative(&datadir, env_localedir.unwrap_or("locale/".to_string()))
});
overridable_path("DOCDIR", |env_docdir| {
join_if_relative(&datadir, env_docdir.unwrap_or("doc/fish".to_string()))
});
}
}
fn get_version(src_dir: &Path) -> String {
use std::fs::read_to_string;
use std::process::Command;
if let Ok(var) = std::env::var("FISH_BUILD_VERSION") {
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.to_string();
return strver;
}
let args = &["describe", "--always", "--dirty=-dirty"];
@@ -351,8 +268,9 @@ fn get_version(src_dir: &Path) -> String {
// 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 gitdir = Path::new(env!("CARGO_MANIFEST_DIR")).join(".git");
let jjdir = Path::new(env!("CARGO_MANIFEST_DIR")).join(".jj");
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");
@@ -389,74 +307,3 @@ fn get_git_hash() -> Result<String, Box<dyn std::error::Error>> {
get_git_hash().expect("Could not get a version. Either set $FISH_BUILD_VERSION or install git.")
}
#[cfg(feature = "embed-data")]
// disable clippy because otherwise it would panic without sphinx
#[cfg(not(clippy))]
fn build_man(build_dir: &Path) {
use std::process::Command;
let mandir = build_dir;
let sec1dir = mandir.join("man1");
let docsrc_path = std::fs::canonicalize(env!("CARGO_MANIFEST_DIR"))
.unwrap()
.as_path()
.join("doc_src");
let docsrc = docsrc_path.to_str().unwrap();
let args = &[
"-j",
"auto",
"-q",
"-b",
"man",
"-c",
docsrc,
// doctree path - put this *above* the man1 dir to exclude it.
// this is ~6M
"-d",
mandir.to_str().unwrap(),
docsrc,
sec1dir.to_str().unwrap(),
];
let _ = std::fs::create_dir_all(sec1dir.to_str().unwrap());
rsconf::rebuild_if_env_changed("FISH_BUILD_DOCS");
if env::var("FISH_BUILD_DOCS") == Ok("0".to_string()) {
println!("cargo:warning=Skipping man pages because $FISH_BUILD_DOCS is set to 0");
return;
}
// We run sphinx to build the man pages.
// Every error here is fatal so cargo doesn't cache the result
// - if we skipped the docs with sphinx not installed, installing it would not then build the docs.
// That means you need to explicitly set $FISH_BUILD_DOCS=0 (`FISH_BUILD_DOCS=0 cargo install --path .`),
// which is unfortunate - but the docs are pretty important because they're also used for --help.
match Command::new("sphinx-build").args(args).spawn() {
Err(x) if x.kind() == std::io::ErrorKind::NotFound => {
if env::var("FISH_BUILD_DOCS") == Ok("1".to_string()) {
panic!("Could not find sphinx-build to build man pages.\nInstall sphinx or disable building the docs by setting $FISH_BUILD_DOCS=0.");
}
println!("cargo:warning=Cannot find sphinx-build to build man pages.");
println!("cargo:warning=If you install it now you need to run `cargo clean` and rebuild, or set $FISH_BUILD_DOCS=1 explicitly.");
}
Err(x) => {
// Another error - permissions wrong etc
panic!("Error starting sphinx-build to build man pages: {:?}", x);
}
Ok(mut x) => match x.wait() {
Err(err) => {
panic!(
"Error waiting for sphinx-build to build man pages: {:?}",
err
);
}
Ok(out) => {
if out.success() {
// Success!
return;
} else {
panic!("sphinx-build failed to build the man pages.");
}
}
},
}
}

70
build_tools/check.sh Executable file
View File

@@ -0,0 +1,70 @@
#!/bin/sh
{
set -ex
lint=true
if [ "$FISH_CHECK_LINT" = false ]; then
lint=false
fi
cargo_args=$FISH_CHECK_CARGO_ARGS
target_triple=$FISH_CHECK_TARGET_TRIPLE
if [ -n "$target_triple" ]; then
cargo_args="$cargo_args --target=$FISH_CHECK_TARGET_TRIPLE"
fi
cargo() {
subcmd=$1
shift
# shellcheck disable=2086
command cargo "$subcmd" $cargo_args "$@"
}
cleanup () {
if [ -n "$template_file" ] && [ -e "$template_file" ]; then
rm "$template_file"
fi
}
trap cleanup EXIT INT TERM HUP
if $lint; then
export RUSTFLAGS="--deny=warnings ${RUSTFLAGS}"
export RUSTDOCFLAGS="--deny=warnings ${RUSTDOCFLAGS}"
fi
workspace_root="$(dirname "$0")/.."
target_dir=${CARGO_TARGET_DIR:-$workspace_root/target}
if [ -n "$target_triple" ]; then
target_dir="$target_dir/$target_triple"
fi
# The directory containing the binaries produced by cargo/rustc.
# Currently, all builds are debug builds.
build_dir="$target_dir/debug"
if [ -n "$FISH_TEST_MAX_CONCURRENCY" ]; then
export RUST_TEST_THREADS="$FISH_TEST_MAX_CONCURRENCY"
export CARGO_BUILD_JOBS="$FISH_TEST_MAX_CONCURRENCY"
fi
template_file=$(mktemp)
(
export FISH_GETTEXT_EXTRACTION_FILE="$template_file"
cargo build --workspace --all-targets --features=gettext-extract
)
if $lint; then
PATH="$build_dir:$PATH" "$workspace_root/build_tools/style.fish" --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
cargo test --doc --workspace
if $lint; then
cargo doc --workspace
fi
FISH_GETTEXT_EXTRACTION_FILE=$template_file "$workspace_root/tests/test_driver.py" "$build_dir"
exit
}

View File

@@ -5,7 +5,8 @@
#
# Usage: ./diff_profiles.fish profile1.log profile2.log > profile_diff.log
if test (count $argv) -ne 2;
if test (count $argv) -ne 2
echo "Incorrect number of arguments."
echo "Usage: "(status filename)" profile1.log profile2.log"
exit 1

View File

@@ -2,6 +2,10 @@
#
# Tool to generate gettext messages template file.
# Writes to stdout.
# Intended to be called from `update_translations.fish`.
argparse use-existing-template= -- $argv
or exit $status
begin
# Write header. This is required by msguniq.
@@ -15,48 +19,38 @@ begin
echo ""
end
set -l cargo_expanded_file (mktemp)
# This is a gigantic crime.
# We use cargo-expand to get all our wgettext invocations.
# This might be replaced once we have a tool which properly handles macro expansions.
begin
cargo expand --lib
for f in fish fish_indent fish_key_reader
cargo expand --bin $f
end
end >$cargo_expanded_file
set -g workspace_root (path resolve (status dirname)/..)
set -l rust_string_file (mktemp)
set -l rust_extraction_file
if set -l --query _flag_use_existing_template
set rust_extraction_file $_flag_use_existing_template
else
set rust_extraction_file (mktemp)
# 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
or exit 1
end
# Extract any gettext call
grep -A1 wgettext_static_str <$cargo_expanded_file |
grep 'widestring::internals::core::primitive::str =' |
string match -rg '"(.*)"' |
string match -rv '^%ls$|^$' |
# escaping difference between gettext and cargo-expand: single-quotes
string replace -a "\'" "'" >$rust_string_file
function mark_section
set -l section_name $argv[1]
echo 'msgid "fish-section-'$section_name'"'
echo 'msgstr ""'
echo ''
end
# Extract any constants
grep -Ev 'BUILD_VERSION:|PACKAGE_NAME' <$cargo_expanded_file |
grep -E 'const [A-Z_]*: &str = "(.*)"' |
sed -E -e 's/^.*const [A-Z_]*: &str = "(.*)".*$/\1/' -e "s_\\\'_'_g" >>$rust_string_file
mark_section tier1-from-rust
rm $cargo_expanded_file
# Get rid of duplicates and sort.
msguniq --no-wrap --sort-output $rust_extraction_file
or exit 1
# Sort the extracted strings and remove duplicates.
# Then, transform them into the po format.
# If a string contains a '%' it is considered a format string and marked with a '#, c-format'.
# This allows msgfmt to identify issues with translations whose format string does not match the
# original.
sort -u $rust_string_file |
sed -E -e '/%/ i\
#, c-format
' -e 's/^(.*)$/msgid "\1"\nmsgstr ""\n/'
rm $rust_string_file
function extract_fish_script_messages --argument-names regex
if not set -l --query _flag_use_existing_template
rm $rust_extraction_file
end
function extract_fish_script_messages_impl
set -l regex $argv[1]
set -e argv[1]
# Using xgettext causes more trouble than it helps.
# This is due to handling of escaping in fish differing from formats xgettext understands
# (e.g. POSIX shell strings).
@@ -75,22 +69,74 @@ begin
# 5. Double quotes are escaped, such that they are not interpreted as the start or end of
# a msgid.
# 6. We transform the string into the format expected in a PO file.
cat share/config.fish share/completions/*.fish share/functions/*.fish |
cat $argv |
string replace --filter --regex $regex '$1' |
string unescape |
sort -u |
sed -E -e 's_\\\\_\\\\\\\\_g' -e 's_"_\\\\"_g' -e 's_^(.*)$_msgid "\1"\nmsgstr ""\n_'
end
# This regex handles explicit requests to translate a message. These are more important to translate
# than messages which should be implicitly translated.
set -l explicit_regex '.*\( *_ (([\'"]).+?(?<!\\\\)\\2) *\).*'
extract_fish_script_messages $explicit_regex
function extract_fish_script_messages
set -l tier $argv[1]
set -e argv[1]
if not set -q argv[1]
return
end
# This regex handles explicit requests to translate a message. These are more important to translate
# than messages which should be implicitly translated.
set -l explicit_regex '.*\( *_ (([\'"]).+?(?<!\\\\)\\2) *\).*'
mark_section "$tier-from-script-explicitly-added"
extract_fish_script_messages_impl $explicit_regex $argv
# This regex handles descriptions for `complete` and `function` statements. These messages are not
# particularly important to translate. Hence the "implicit" label.
set -l implicit_regex '^(?:\s|and |or )*(?:complete|function).*? (?:-d|--description) (([\'"]).+?(?<!\\\\)\\2).*'
extract_fish_script_messages $implicit_regex
# This regex handles descriptions for `complete` and `function` statements. These messages are not
# particularly important to translate. Hence the "implicit" label.
set -l implicit_regex '^(?:\s|and |or )*(?:complete|function).*? (?:-d|--description) (([\'"]).+?(?<!\\\\)\\2).*'
mark_section "$tier-from-script-implicitly-added"
extract_fish_script_messages_impl $implicit_regex $argv
end
set -g share_dir $workspace_root/share
set -l tier1 $share_dir/config.fish
set -l tier2
set -l tier3
for file in $share_dir/completions/*.fish $share_dir/functions/*.fish
# set -l tier (string match -r '^# localization: .*' <$file)
set -l tier (string replace -rf -m1 \
'^# localization: (.*)$' '$1' <$file)
if set -q tier[1]
switch "$tier"
case tier1 tier2 tier3
set -a $tier $file
case 'skip*'
case '*'
echo >&2 "$file:1 unexpected localization tier: $tier"
exit 1
end
continue
end
set -l dirname (path basename (path dirname $file))
set -l command_name (path basename --no-extension $file)
if test $dirname = functions &&
string match -q -- 'fish_*' $command_name
set -a tier1 $file
continue
end
if test $dirname != completions
echo >&2 "$file:1 missing localization tier for function file"
exit 1
end
if test -e $workspace_root/doc_src/cmds/$command_name.rst
set -a tier1 $file
else
set -a tier3 $file
end
end
extract_fish_script_messages tier1 $tier1
extract_fish_script_messages tier2 $tier2
extract_fish_script_messages tier3 $tier3
end |
# At this point, all extracted strings have been written to stdout,
# starting with the ones taken from the Rust sources,

177
build_tools/make_macos_pkg.sh Executable file
View File

@@ -0,0 +1,177 @@
#!/usr/bin/env bash
# Script to produce an OS X installer .pkg and .app(.zip)
usage() {
echo "Build macOS packages, optionally signing and notarizing them."
echo "Usage: $0 options"
echo "Options:"
echo " -s Enables code signing"
echo " -f <APP_KEY.p12> Path to .p12 file for application signing"
echo " -i <INSTALLER_KEY.p12> Path to .p12 file for installer signing"
echo " -p <PASSWORD> Password for the .p12 files (necessary to access the certificates)"
echo " -e <entitlements file> (Optional) Path to an entitlements XML file"
echo " -n Enables notarization. This will fail if code signing is not also enabled."
echo " -j <API_KEY.JSON> Path to JSON file generated with \`rcodesign encode-app-store-connect-api-key\` (required for notarization)"
echo
exit 1
}
set -x
set -e
SIGN=
NOTARIZE=
ARM64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=11.0'
X86_64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=10.9'
# As of this writing, the most recent Rust release supports macOS back to 10.12.
# The first supported version of macOS on arm64 is 10.15, so any Rust is fine for arm64.
# We wish to support back to 10.9 on x86-64; the last version of Rust to support that is
# version 1.73.0.
RUST_VERSION_X86_64=1.70.0
while getopts "sf:i:p:e:nj:" opt; do
case $opt in
s) SIGN=1;;
f) P12_APP_FILE=$(realpath "$OPTARG");;
i) P12_INSTALL_FILE=$(realpath "$OPTARG");;
p) P12_PASSWORD="$OPTARG";;
e) ENTITLEMENTS_FILE=$(realpath "$OPTARG");;
n) NOTARIZE=1;;
j) API_KEY_FILE=$(realpath "$OPTARG");;
\?) usage;;
esac
done
if [ -n "$SIGN" ] && { [ -z "$P12_APP_FILE" ] || [ -z "$P12_INSTALL_FILE" ] || [ -z "$P12_PASSWORD" ]; }; then
usage
fi
if [ -n "$NOTARIZE" ] && [ -z "$API_KEY_FILE" ]; then
usage
fi
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
echo "Version is $VERSION"
PKGDIR=$(mktemp -d)
echo "$PKGDIR"
SRC_DIR=$PWD
OUTPUT_PATH=${FISH_ARTEFACT_PATH:-~/fish_built}
mkdir -p "$PKGDIR/build_x86_64" "$PKGDIR/build_arm64" "$PKGDIR/root" "$PKGDIR/intermediates" "$PKGDIR/dst"
# Build and install for arm64.
# Pass FISH_USE_SYSTEM_PCRE2=OFF because a system PCRE2 on macOS will not be signed by fish,
# and will probably not be built universal, so the package will fail to validate/run on other systems.
# Note CMAKE_OSX_ARCHITECTURES is still relevant for the Mac app.
{ cd "$PKGDIR/build_arm64" \
&& cmake \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \
-DRust_CARGO_TARGET=aarch64-apple-darwin \
-DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \
-DFISH_USE_SYSTEM_PCRE2=OFF \
"$SRC_DIR" \
&& env $ARM64_DEPLOY_TARGET make VERBOSE=1 -j 12 \
&& 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.
# Set RUST_VERSION_X86_64 to the last version of Rust that supports macOS 10.9.
{ cd "$PKGDIR/build_x86_64" \
&& cmake \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \
-DRust_TOOLCHAIN="$RUST_VERSION_X86_64" \
-DRust_CARGO_TARGET=x86_64-apple-darwin \
-DRust_COMPILER="$(rustup +$RUST_VERSION_X86_64 which rustc)" \
-DRust_CARGO="$(rustup +$RUST_VERSION_X86_64 which cargo)" \
-DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \
-DFISH_USE_SYSTEM_PCRE2=OFF "$SRC_DIR" \
&& 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
if test -n "$SIGN"; then
echo "Signing executables"
ARGS=(
--p12-file "$P12_APP_FILE"
--p12-password "$P12_PASSWORD"
--code-signature-flags runtime
--for-notarization
)
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
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"
productbuild --package-path "$PKGDIR/intermediates" --distribution "$SRC_DIR/build_tools/osx_distribution.xml" --resources "$SRC_DIR/build_tools/osx_package_resources/" "$OUTPUT_PATH/fish-$VERSION.pkg"
if test -n "$SIGN"; then
echo "Signing installer"
ARGS=(
--p12-file "$P12_INSTALL_FILE"
--p12-password "$P12_PASSWORD"
--code-signature-flags runtime
--for-notarization
)
(set +x; rcodesign sign "${ARGS[@]}" "$OUTPUT_PATH/fish-$VERSION.pkg")
fi
# Make the app
(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.
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
if test -n "$SIGN"; then
echo "Signing app"
ARGS=(
--p12-file "$P12_APP_FILE"
--p12-password "$P12_PASSWORD"
--code-signature-flags runtime
--for-notarization
)
if [ -n "$ENTITLEMENTS_FILE" ]; then
ARGS+=(--entitlements-xml-file "$ENTITLEMENTS_FILE")
fi
(set +x; rcodesign sign "${ARGS[@]}" "fish.app")
fi
cp -R "fish.app" "$OUTPUT_PATH/fish-$VERSION.app"
cd "$OUTPUT_PATH"
# Maybe notarize.
if test -n "$NOTARIZE"; then
echo "Notarizing"
rcodesign notarize --staple --wait --max-wait-seconds 1800 --api-key-file "$API_KEY_FILE" "$OUTPUT_PATH/fish-$VERSION.pkg"
rcodesign notarize --staple --wait --max-wait-seconds 1800 --api-key-file "$API_KEY_FILE" "$OUTPUT_PATH/fish-$VERSION.app"
fi
# Zip it up.
zip -r "fish-$VERSION.app.zip" "fish-$VERSION.app" && rm -Rf "fish-$VERSION.app"
rm -rf "$PKGDIR"

View File

@@ -1,183 +0,0 @@
#!/usr/bin/env bash
# Script to produce an OS X installer .pkg and .app(.zip)
usage() {
echo "Build macOS packages, optionally signing and notarizing them."
echo "Usage: $0 options"
echo "Options:"
echo " -s Enables code signing"
echo " -f <APP_KEY.p12> Path to .p12 file for application signing"
echo " -i <INSTALLER_KEY.p12> Path to .p12 file for installer signing"
echo " -p <PASSWORD> Password for the .p12 files (necessary to access the certificates)"
echo " -e <entitlements file> (Optional) Path to an entitlements XML file"
echo " -n Enables notarization. This will fail if code signing is not also enabled."
echo " -j <API_KEY.JSON> Path to JSON file generated with \`rcodesign encode-app-store-connect-api-key\` (required for notarization)"
echo
exit 1
}
set -x
set -e
SIGN=
NOTARIZE=
ARM64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=11.0'
X86_64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=10.9'
# As of this writing, the most recent Rust release supports macOS back to 10.12.
# The first supported version of macOS on arm64 is 10.15, so any Rust is fine for arm64.
# We wish to support back to 10.9 on x86-64; the last version of Rust to support that is
# version 1.73.0.
RUST_VERSION_X86_64=1.73.0
while getopts "sf:i:p:e:nj:" opt; do
case $opt in
s) SIGN=1;;
f) P12_APP_FILE=$(realpath "$OPTARG");;
i) P12_INSTALL_FILE=$(realpath "$OPTARG");;
p) P12_PASSWORD="$OPTARG";;
e) ENTITLEMENTS_FILE=$(realpath "$OPTARG");;
n) NOTARIZE=1;;
j) API_KEY_FILE=$(realpath "$OPTARG");;
\?) usage;;
esac
done
if [ -n "$SIGN" ] && { [ -z "$P12_APP_FILE" ] || [ -z "$P12_INSTALL_FILE" ] || [ -z "$P12_PASSWORD" ]; }; then
usage
fi
if [ -n "$NOTARIZE" ] && [ -z "$API_KEY_FILE" ]; then
usage
fi
VERSION=$(git describe --always --dirty 2>/dev/null)
if test -z "$VERSION" ; then
echo "Could not get version from git"
if test -f version; then
VERSION=$(cat version)
fi
fi
echo "Version is $VERSION"
PKGDIR=$(mktemp -d)
echo "$PKGDIR"
SRC_DIR=$PWD
OUTPUT_PATH=${FISH_ARTEFACT_PATH:-~/fish_built}
mkdir -p "$PKGDIR/build_x86_64" "$PKGDIR/build_arm64" "$PKGDIR/root" "$PKGDIR/intermediates" "$PKGDIR/dst"
# Build and install for arm64.
# Pass FISH_USE_SYSTEM_PCRE2=OFF because a system PCRE2 on macOS will not be signed by fish,
# and will probably not be built universal, so the package will fail to validate/run on other systems.
# Note CMAKE_OSX_ARCHITECTURES is still relevant for the Mac app.
{ cd "$PKGDIR/build_arm64" \
&& cmake \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \
-DWITH_GETTEXT=OFF \
-DRust_CARGO_TARGET=aarch64-apple-darwin \
-DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \
-DFISH_USE_SYSTEM_PCRE2=OFF \
"$SRC_DIR" \
&& env $ARM64_DEPLOY_TARGET make VERBOSE=1 -j 12 \
&& 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.
# Set RUST_VERSION_X86_64 to the last version of Rust that supports macOS 10.9.
{ cd "$PKGDIR/build_x86_64" \
&& cmake \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \
-DWITH_GETTEXT=OFF \
-DRust_TOOLCHAIN="$RUST_VERSION_X86_64" \
-DRust_CARGO_TARGET=x86_64-apple-darwin \
-DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \
-DFISH_USE_SYSTEM_PCRE2=OFF "$SRC_DIR" \
&& 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
if test -n "$SIGN"; then
echo "Signing executables"
ARGS=(
--p12-file "$P12_APP_FILE"
--p12-password "$P12_PASSWORD"
--code-signature-flags runtime
--for-notarization
)
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
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"
productbuild --package-path "$PKGDIR/intermediates" --distribution "$SRC_DIR/build_tools/osx_distribution.xml" --resources "$SRC_DIR/build_tools/osx_package_resources/" "$OUTPUT_PATH/fish-$VERSION.pkg"
if test -n "$SIGN"; then
echo "Signing installer"
ARGS=(
--p12-file "$P12_INSTALL_FILE"
--p12-password "$P12_PASSWORD"
--code-signature-flags runtime
--for-notarization
)
(set +x; rcodesign sign "${ARGS[@]}" "$OUTPUT_PATH/fish-$VERSION.pkg")
fi
# Make the app
(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.
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
if test -n "$SIGN"; then
echo "Signing app"
ARGS=(
--p12-file "$P12_APP_FILE"
--p12-password "$P12_PASSWORD"
--code-signature-flags runtime
--for-notarization
)
if [ -n "$ENTITLEMENTS_FILE" ]; then
ARGS+=(--entitlements-xml-file "$ENTITLEMENTS_FILE")
fi
(set +x; rcodesign sign "${ARGS[@]}" "fish.app")
fi
cp -R "fish.app" "$OUTPUT_PATH/fish-$VERSION.app"
cd "$OUTPUT_PATH"
# Maybe notarize.
if test -n "$NOTARIZE"; then
echo "Notarizing"
rcodesign notarize --staple --wait --max-wait-seconds 1800 --api-key-file "$API_KEY_FILE" "$OUTPUT_PATH/fish-$VERSION.pkg"
rcodesign notarize --staple --wait --max-wait-seconds 1800 --api-key-file "$API_KEY_FILE" "$OUTPUT_PATH/fish-$VERSION.app"
fi
# Zip it up.
zip -r "fish-$VERSION.app.zip" "fish-$VERSION.app" && rm -Rf "fish-$VERSION.app"
rm -rf "$PKGDIR"

1
build_tools/make_pkg.sh Symbolic link
View File

@@ -0,0 +1 @@
make_macos_pkg.sh

View File

@@ -9,38 +9,38 @@
# Exit on error
set -e
# We wil generate a tarball with a prefix "fish-VERSION"
# 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 paralellises
# 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"
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
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
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 from git-describe
VERSION=$(git describe --dirty 2>/dev/null)
# Get the version
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
# The name of the prefix, which is the directory that you get when you untar
prefix="fish-$VERSION"
@@ -63,7 +63,7 @@ cmake -G "$BUILD_GENERATOR" -DCMAKE_BUILD_TYPE=Debug "$wd"
$BUILD_TOOL doc
TAR_APPEND="$TAR --append --file=$path --mtime=now --owner=0 --group=0 \
--mode=g+w,a+rX --transform s/^/$prefix\//"
--mode=g+w,a+rX --transform s/^/$prefix\//"
$TAR_APPEND --no-recursion user_doc
$TAR_APPEND user_doc/html user_doc/man
$TAR_APPEND version

View File

@@ -11,22 +11,22 @@ 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
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
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=$(git describe --dirty 2>/dev/null)
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
# The name of the prefix, which is the directory that you get when you untar
prefix="fish-$VERSION"

104
build_tools/release-notes.sh Executable file
View File

@@ -0,0 +1,104 @@
#!/bin/sh
set -e
workspace_root=$(dirname "$0")/..
relnotes_tmp=$(mktemp -d)
mkdir -p "$relnotes_tmp/fake-workspace" "$relnotes_tmp/out"
(
cd "$workspace_root"
cp -r doc_src CONTRIBUTING.rst README.rst "$relnotes_tmp/fake-workspace"
)
version=$(sed 's,^fish \(\S*\) .*,\1,; 1q' "$workspace_root/CHANGELOG.rst")
previous_version=$(
cd "$workspace_root"
awk <CHANGELOG.rst '
( /^fish \S*\.\S*\.\S* \(released .*\)$/ &&
NR > 1 &&
# Skip tags that have not been created yet..
system("git rev-parse --verify >/dev/null --quiet refs/tags/"$2) == 0 \
) {
print $2; ok = 1; exit
}
END { exit !ok }
'
)
minor_version=${version%.*}
previous_minor_version=${previous_version%.*}
{
sed -n 1,2p <"$workspace_root/CHANGELOG.rst"
ListCommitters() {
comm "$@" "$relnotes_tmp/committers-then" "$relnotes_tmp/committers-now"
}
(
cd "$workspace_root"
git log "$previous_version" --format="%aN" | sort -u >"$relnotes_tmp/committers-then"
git log "$previous_version".. --format="%aN" | sort -u >"$relnotes_tmp/committers-now"
ListCommitters -13 >"$relnotes_tmp/committers-new"
ListCommitters -12 >"$relnotes_tmp/committers-returning"
)
if [ "$minor_version" != "$previous_minor_version" ]; then
(
cd "$workspace_root"
num_commits=$(git log --no-merges --format=%H "$previous_version".. | wc -l)
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," \
" contributed by $num_authors authors, $num_new_authors of which are new committers."
echo
echo
)
fi
printf '%s\n' "$(awk <"$workspace_root/CHANGELOG.rst" '
NR <= 2 || /^\.\. ignore / { next }
/^===/ { exit }
{ print }
' | sed '$d')" |
sed -e '$s/^----*$//' # Remove spurious transitions at the end of the document.
if [ "$minor_version" != "$previous_minor_version" ]; then {
JoinEscaped() {
sed 's/\S/\\&/g' |
awk '
NR != 1 { printf ",\n" }
{ printf "%s", $0 }
END { printf "\n" }
'
}
echo ""
echo "---"
echo ""
echo "Thanks to everyone who contributed through issue discussions, code reviews, or code changes."
echo
printf "Welcome our new committers: "
JoinEscaped <"$relnotes_tmp/committers-new"
echo
printf "Welcome back our returning committers: "
JoinEscaped <"$relnotes_tmp/committers-returning"
} fi
echo
echo "---"
echo
echo "*Download links: To download the source code for fish, we suggest the file named \"fish-$version.tar.xz\". The file downloaded from \"Source code (tar.gz)\" will not build correctly.*"
echo
echo "*The files called fish-$version-linux-\*.tar.xz are experimental packages containing a single standalone ``fish`` binary for any Linux with the given CPU architecture.*"
} >"$relnotes_tmp/fake-workspace"/CHANGELOG.rst
sphinx-build >&2 -j auto \
-W -E -b markdown -c "$workspace_root/doc_src" \
-d "$relnotes_tmp/doctree" "$relnotes_tmp/fake-workspace/doc_src" "$relnotes_tmp/out" \
-D markdown_http_base="https://fishshell.com/docs/$minor_version" \
-D markdown_uri_doc_suffix=".html" \
-D markdown_github_flavored=1 \
"$@"
# Skip changelog header
sed -n 1p "$relnotes_tmp/out/relnotes.md" | grep -Fxq "# Release notes"
sed -n 2p "$relnotes_tmp/out/relnotes.md" | grep -Fxq ''
sed 1,2d "$relnotes_tmp/out/relnotes.md"
rm -r "$relnotes_tmp"

253
build_tools/release.sh Executable file
View File

@@ -0,0 +1,253 @@
#!/bin/sh
{
set -ex
version=$1
repository_owner=fish-shell
remote=origin
if [ -n "$2" ]; then
set -u
repository_owner=$2
remote=$3
set +u
[ $# -eq 3 ]
fi
[ -n "$version" ]
for tool in \
bundle \
gh \
jq \
ruby \
timeout \
; do
if ! command -v "$tool" >/dev/null; then
echo >&2 "$0: missing command: $1"
exit 1
fi
done
repo_root="$(dirname "$0")/.."
fish_site=$repo_root/../fish-site
fish_site_repo=git@github.com:$repository_owner/fish-site
for path in . "$fish_site"
do
if ! git -C "$path" diff HEAD --quiet ||
git ls-files --others --exclude-standard | grep .; then
echo >&2 "$0: index and worktree must be clean"
exit 1
fi
done
(
cd "$fish_site"
[ "$(git rev-parse HEAD)" = \
"$(git ls-remote "$fish_site_repo" refs/heads/master |
awk '{print $1}')" ]
)
if git tag | grep -qxF "$version"; then
echo >&2 "$0: tag $version already exists"
exit 1
fi
integration_branch=$(
git for-each-ref --points-at=HEAD 'refs/heads/Integration_*' \
--format='%(refname:strip=2)'
)
[ -n "$integration_branch" ] ||
git merge-base --is-ancestor $remote/master HEAD
sed -n 1p CHANGELOG.rst | grep -q '^fish .*(released .*)$'
sed -n 2p CHANGELOG.rst | grep -q '^===*$'
changelog_title="fish $version (released $(date +'%B %d, %Y'))"
sed -i \
-e "1c$changelog_title" \
-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
Created by ./build_tools/release.sh $version"
}
CommitVersion "$version" "Release $version"
# N.B. this is not GPG-signed.
git tag --annotate --message="Release $version" $version
git push $remote $version
TIMEOUT=
gh() {
command ${TIMEOUT:+timeout $TIMEOUT} \
gh --repo "$repository_owner/fish-shell" "$@"
}
gh workflow run release.yml --ref="$version" \
--raw-field="version=$version"
run_id=
while [ -z "$run_id" ] && sleep 5
do
run_id=$(gh run list \
--json=databaseId --jq=.[].databaseId \
--workflow=release.yml --limit=1 \
--commit="$(git rev-parse "$version^{commit}")")
done
# Update fishshell.com
tag_oid=$(git rev-parse "$version")
tmpdir=$(mktemp -d)
# TODO This works on draft releases only if "gh" is configured to
# have write access to the fish-shell repository. Unless we are fine
# publishing the release at this point, we should at least fail if
# "gh" doesn't have write access.
while ! \
gh release download "$version" --dir="$tmpdir" \
--pattern="fish-$version.tar.xz"
do
TIMEOUT=30 gh run watch "$run_id" ||:
sleep 5
done
actual_tag_oid=$(git ls-remote "$remote" |
awk '$2 == "refs/tags/'"$version"'" { print $1 }')
[ "$tag_oid" = "$actual_tag_oid" ]
( cd "$tmpdir" && tar xf fish-$version.tar.xz )
CopyDocs() {
rm -rf "$fish_site/site/docs/$1"
cp -r "$tmpdir/fish-$version/user_doc/html" "$fish_site/site/docs/$1"
git -C $fish_site add "site/docs/$1"
}
minor_version=${version%.*}
CopyDocs "$minor_version"
latest_release=$(
releases=$(git tag | grep '^[0-9]*\.[0-9]*\.[0-9]*.*' |
sed $(: "De-prioritize release candidates (1.2.3-rc0)") \
's/-/~/g' | LC_ALL=C sort --version-sort)
printf %s\\n "$releases" | tail -1
)
if [ "$version" = "$latest_release" ]; then
CopyDocs current
fi
rm -rf "$tmpdir"
(
cd "$fish_site"
make
git add -u
git add docs
if git ls-files --others --exclude-standard | grep .; then
exit 1
fi
git commit --message="$(printf %s "\
| Release $version (docs)
|
| Created by ../fish-shell/build_tools/release.sh
" | sed 's,^\s*| \?,,')"
)
gh_api_repo() {
path=$1
shift
command gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/$repository_owner/fish-shell/$path" \
"$@"
}
# Approve macos-codesign
# TODO what if current user can't approve?
gh_pending_deployments() {
gh_api_repo "actions/runs/$run_id/pending_deployments" "$@"
}
while {
environment_id=$(gh_pending_deployments | jq .[].environment.id)
[ -z "$environment_id" ]
}
do
sleep 5
done
echo '
{
"environment_ids": ['"$environment_id"'],
"state": "approved",
"comment": "Approved via ./build_tools/release.sh"
}
' |
gh_pending_deployments --method POST --input=-
# Await completion.
gh run watch "$run_id"
while {
! draft=$(gh release view "$version" --json=isDraft --jq=.isDraft) \
|| [ "$draft" = true ]
}
do
sleep 20
done
(
cd "$fish_site"
make new-release
git add -u
git add docs
if git ls-files --others --exclude-standard | grep .; then
exit 1
fi
git commit --message="$(printf %s "\
| Release $version (release list update)
|
| Created by ../fish-shell/build_tools/release.sh
" | sed 's,^\s*| \?,,')"
# 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
)
if [ -n "$integration_branch" ]; then {
git push $remote "$version^{commit}":refs/heads/$integration_branch
} else {
changelog=$(cat - CHANGELOG.rst <<EOF
fish ?.?.? (released ???)
=========================
EOF
)
printf %s\\n "$changelog" >CHANGELOG.rst
CommitVersion ${version}-snapshot "start new cycle"
git push $remote HEAD:master
} fi
milestone_number=$(
gh_api_repo milestones?state=open |
jq '.[] | select(.title == "fish '"$version"'") | .number'
)
gh_api_repo milestones/$milestone_number --method PATCH \
--raw-field state=closed
next_patch_version=$(
echo "$version" | awk -F. '
NF == 3 && $3 ~ /[0-9]+/ {
printf "%s.%s.%s", $1, $2, $3+1
}
'
)
if [ -n "$next_patch_version" ]; then
gh_api_repo milestones --method POST \
--raw-field title="fish $next_patch_version"
fi
exit
}

View File

@@ -1,38 +1,53 @@
#!/usr/bin/env fish
#
# This runs C++ files and fish scripts (*.fish) through their respective code
# formatting programs.
# 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
if test "$argv[1]" = --all
argparse all check force -- $argv
or exit $status
if set -l -q _flag_all
set all yes
set -e argv[1]
if set -q argv[1]
echo "Unexpected arguments: '$argv'"
exit 1
end
end
if set -q argv[1]
echo "Unexpected arguments: '$argv'"
exit 1
end
set -l workspace_root (status dirname)/..
if test $all = yes
set -l files (git status --porcelain --short --untracked-files=all | sed -e 's/^ *[^ ]* *//')
if set -q files[1]
echo
echo 'You have uncommitted changes. Are you sure you want to restyle?'
read -P 'y/N? ' -n1 -l ans
if not string match -qi y -- $ans
exit 1
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 share/**.fish
set python_files {doc_src,share,tests}/**.py
set rust_files fish-rust/src/**.rs
set fish_files $workspace_root/{benchmarks,build_tools,etc,share}/**.fish
set python_files $workspace_root
else
# Extract just the fish files.
# 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)
@@ -40,37 +55,70 @@ end
set -l red (set_color red)
set -l green (set_color green)
set -l blue (set_color blue)
set -l yellow (set_color yellow)
set -l normal (set_color normal)
# Run the fish reformatter if we have any fish files.
if set -q fish_files[1]
if not type -q fish_indent
make fish_indent
set PATH . $PATH
echo
echo $yellow'Could not find `fish_indent` in `$PATH`.'$normal
exit 127
end
echo === Running "$green"fish_indent"$normal"
fish_indent -w -- $fish_files
if set -l -q _flag_check
if not fish_indent --check -- $fish_files
echo $red"Fish files are not formatted correctly."$normal
exit 1
end
else
fish_indent -w -- $fish_files
end
end
if set -q python_files[1]
if not type -q black
echo
echo Please install "`black`" to style python
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
if not ruff format --check $python_files
echo $red"Python files are not formatted correctly."$normal
exit 1
end
else
echo === Running "$blue"black"$normal"
black $python_files
ruff format $python_files
end
end
if set -q rust_files[1]
if not type -q rustfmt
echo
echo Please install "`rustfmt`" to style rust
echo
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
echo === Running "$green"rustfmt"$normal"
if set -l -q _flag_check
if set -l -q _flag_all
if not cargo fmt --all --check
echo $red"Rust files are not formatted correctly."$normal
exit 1
end
else
echo === Running "$blue"rustfmt"$normal"
rustfmt $rust_files
if set -q rust_files[1]
if not rustfmt --check --files-with-diff $rust_files
echo $red"Rust files are not formatted correctly."
exit 1
end
end
end
else
if set -l -q _flag_all
cargo fmt --all
else
if set -q rust_files[1]
rustfmt $rust_files
end
end
end

View File

@@ -1,36 +1,42 @@
#!/usr/bin/env fish
# Updates the files used for gettext translations.
# By default, the whole xgettext, msgmerge, msgfmt pipeline runs,
# By default, the whole xgettext + msgmerge pipeline runs,
# which extracts the messages from the source files into $template_file,
# updates the PO files for each language from that
# (changed line numbers, added messages, removed messages),
# and finally generates a machine-readable MO file for each language,
# which is stored in share/locale/$LANG/LC_MESSAGES/fish.mo (relative to the repo root).
# and updates the PO files for each language from that.
#
# Use cases:
# For developers:
# - Run with args `--no-mo` to update all PO files after making changes to Rust/fish
# sources.
# - Run with no args to update all PO files after making changes to Rust/fish sources.
# For translators:
# - Run with `--no-mo` first, to ensure that the strings you are translating are up to date.
# - 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.
# 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
# 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.
# It can be obtained by running:
# set -l FILE (mktemp)
# FISH_GETTEXT_EXTRACTION_FILE=$FILE 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 template_file $build_tools/../po/template.po
set -l po_dir $build_tools/../po
set -l extract
set -l po
set -l mo
argparse --exclusive 'no-mo,only-mo' 'no-mo' 'only-mo' -- $argv
argparse dry-run use-existing-template= -- $argv
or exit $status
if test -z $argv[1]
@@ -39,8 +45,8 @@ if test -z $argv[1]
else
set -l po_dir_id (stat --format='%d:%i' -- $po_dir)
for arg in $argv
set -l arg_dir_id (stat --format='%d:%i' -- (dirname $arg))
if test $po_dir_id != $arg_dir_id
set -l arg_dir_id (stat --format='%d:%i' -- (dirname $arg) 2>/dev/null)
if test $po_dir_id != "$arg_dir_id"
echo "Argument $arg is not a file in the directory $(realpath $po_dir)."
echo "Non-option arguments must specify paths to files in this directory."
echo ""
@@ -50,7 +56,7 @@ else
echo "So valid filenames are of the shape 'll.po' or 'll_CC.po'."
exit 1
end
if not basename $arg | grep -qE '^[a-z]{2}(_[A-Z]{2})?\.po$'
if not basename $arg | grep -qE '^[a-z]{2,3}(_[A-Z]{2})?\.po$'
echo "Filename does not match the expected format ('ll.po' or 'll_CC.po')."
exit 1
end
@@ -58,31 +64,94 @@ else
set -g po_files $argv
end
if set -l --query _flag_no_mo
set -l --erase mo
end
if set -l --query _flag_only_mo
set -l --erase extract
set -l --erase po
set -g template_file (mktemp)
# Protect from externally set $tmpdir leaking into this script.
set -g tmpdir
function cleanup_exit
set -l exit_status $status
rm $template_file
if set -g --query tmpdir[1]
rm -r $tmpdir
end
exit $exit_status
end
if set -l --query extract
$build_tools/fish_xgettext.fish >$template_file
or exit 1
set -l xgettext_args
if set -l --query _flag_use_existing_template
set xgettext_args --use-existing-template=$_flag_use_existing_template
end
$build_tools/fish_xgettext.fish $xgettext_args >$template_file
or cleanup_exit
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.
set -g tmpdir (mktemp -d)
# Ensure tmpdir has the same initial state as the po dir.
cp -r $po_dir/* $tmpdir
end
# This is used to identify lines which should be set here via $header_lines.
# Make sure that this prefix does not appear elsewhere in the file and only contains characters
# without special meaning in a sed pattern.
set -g header_prefix "# fish-note-sections: "
function print_header
set -l header_lines \
"Translations are divided into sections, each starting with a fish-section-* pseudo-message." \
"The first few sections are more important." \
"Ignore the tier3 sections unless you have a lot of time."
for line in $header_lines
printf '%s%s\n' $header_prefix $line
end
end
function merge_po_files --argument-names template_file po_file
msgmerge --no-wrap --update --no-fuzzy-matching --backup=none --quiet \
$po_file $template_file
or cleanup_exit
set -l new_po_file (mktemp) # TODO Remove on failure.
# Remove obsolete messages instead of keeping them as #~ entries.
and msgattrib --no-wrap --no-obsolete -o $new_po_file $po_file
or cleanup_exit
begin
print_header
# Paste PO file without old header lines.
sed '/^'$header_prefix'/d' $new_po_file
end >$po_file
rm $new_po_file
end
for po_file in $po_files
if set --query tmpdir[1]
set po_file $tmpdir/(basename $po_file)
end
if set -l --query po
if test -e $po_file
msgmerge --update --no-fuzzy-matching --no-wrap --backup=none $po_file $template_file
merge_po_files $template_file $po_file
else
cp $template_file $po_file
begin
print_header
cat $template_file
end >$po_file
end
end
if set -l --query mo
set -l locale_dir $build_tools/../share/locale
set -l out_dir $locale_dir/(basename $po_file .po)/LC_MESSAGES
mkdir -p $out_dir
msgfmt --check-format --output-file=$out_dir/fish.mo $po_file
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
cleanup_exit
end
end
cleanup_exit

View File

@@ -37,10 +37,12 @@ set(MANUALS ${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish.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)
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-faq.1
)
# Determine which man page we don't want to install.
# On OS X, don't install a man page for open, since we defeat fish's open
@@ -154,16 +156,6 @@ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/user_doc/html/ # Trailing slash is
DESTINATION ${docdir} OPTIONAL)
install(FILES CHANGELOG.rst DESTINATION ${docdir})
# These files are built by cmake/gettext.cmake, but using GETTEXT_PROCESS_PO_FILES's
# INSTALL_DESTINATION leads to them being installed as ${lang}.gmo, not fish.mo
# The ${languages} array comes from cmake/gettext.cmake
if(GETTEXT_FOUND)
foreach(lang ${languages})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${lang}.gmo DESTINATION
${CMAKE_INSTALL_LOCALEDIR}/${lang}/LC_MESSAGES/ RENAME fish.mo)
endforeach()
endif()
# Group install targets into a InstallTargets folder
set_property(TARGET build_fish_pc CHECK-FISH-BUILD-VERSION-FILE
PROPERTY FOLDER cmake/InstallTargets)

View File

@@ -24,7 +24,7 @@ add_executable(fish_macapp EXCLUDE_FROM_ALL
# Compute the version. Note this is done at generation time, not build time,
# so cmake must be re-run after version changes for the app to be updated. But
# generally this will be run by make_pkg.sh which always re-runs cmake.
# 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 cut -d- -f1
@@ -32,7 +32,7 @@ execute_process(
OUTPUT_STRIP_TRAILING_WHITESPACE)
# Note CMake appends .app, so the real output name will be fish.app.
# Note CMake appends .app, so the real output name will be fish.app.
# This target does not include the 'base' resource.
set_target_properties(fish_macapp PROPERTIES OUTPUT_NAME "fish")

View File

@@ -5,11 +5,11 @@ set(FISH_RUST_BUILD_DIR "${CMAKE_BINARY_DIR}/cargo/build")
if(DEFINED ASAN)
list(APPEND CARGO_FLAGS "-Z" "build-std")
list(APPEND FISH_CRATE_FEATURES "asan")
list(APPEND FISH_CARGO_FEATURES_LIST "asan")
endif()
if(DEFINED TSAN)
list(APPEND CARGO_FLAGS "-Z" "build-std")
list(APPEND FISH_CRATE_FEATURES "tsan")
list(APPEND FISH_CARGO_FEATURES_LIST "tsan")
endif()
if (Rust_CARGO_TARGET)
@@ -21,32 +21,24 @@ endif()
set(rust_profile $<IF:$<CONFIG:Debug>,debug,$<IF:$<CONFIG:RelWithDebInfo>,release-with-debug,release>>)
set(rust_debugflags "$<$<CONFIG:Debug>:-g>$<$<CONFIG:RelWithDebInfo>:-g>")
# Temporary hack to propagate CMake flags/options to build.rs. We need to get CMake to evaluate the
# truthiness of the strings if they are set.
set(CMAKE_WITH_GETTEXT "1")
if(DEFINED WITH_GETTEXT AND NOT "${WITH_GETTEXT}")
set(CMAKE_WITH_GETTEXT "0")
option(WITH_GETTEXT "Build with gettext localization support. Requires `msgfmt` to work." ON)
# Enable gettext feature unless explicitly disabled.
if(NOT DEFINED WITH_GETTEXT OR "${WITH_GETTEXT}")
list(APPEND FISH_CARGO_FEATURES_LIST "localize-messages")
endif()
if(FISH_CRATE_FEATURES)
set(FEATURES_ARG ${FISH_CRATE_FEATURES})
list(PREPEND FEATURES_ARG "--features")
endif()
list(JOIN FISH_CARGO_FEATURES_LIST , FISH_CARGO_FEATURES)
# Tell Cargo where our build directory is so it can find Cargo.toml.
set(VARS_FOR_CARGO
"FISH_BUILD_DIR=${CMAKE_BINARY_DIR}"
"PREFIX=${CMAKE_INSTALL_PREFIX}"
# Temporary hack to propagate CMake flags/options to build.rs.
"CMAKE_WITH_GETTEXT=${CMAKE_WITH_GETTEXT}"
# Cheesy so we can tell cmake was used to build
"CMAKE=1"
"DOCDIR=${CMAKE_INSTALL_FULL_DOCDIR}"
"DATADIR=${CMAKE_INSTALL_FULL_DATADIR}"
"SYSCONFDIR=${CMAKE_INSTALL_FULL_SYSCONFDIR}"
"BINDIR=${CMAKE_INSTALL_FULL_BINDIR}"
"LOCALEDIR=${CMAKE_INSTALL_FULL_LOCALEDIR}"
"CARGO_TARGET_DIR=${FISH_RUST_BUILD_DIR}"
"CARGO_BUILD_RUSTC=${Rust_COMPILER}"
"${FISH_PCRE2_BUILDFLAG}"

View File

@@ -1,92 +1,33 @@
# This adds ctest support to the project
enable_testing()
# By default, ctest runs tests serially
# Support CTEST_PARALLEL_LEVEL as an environment variable in addition to a CMake variable
if(NOT CTEST_PARALLEL_LEVEL)
set(CTEST_PARALLEL_LEVEL $ENV{CTEST_PARALLEL_LEVEL})
if(NOT CTEST_PARALLEL_LEVEL)
include(ProcessorCount)
ProcessorCount(CORES)
math(EXPR halfcores "${CORES} / 2")
set(CTEST_PARALLEL_LEVEL ${halfcores})
endif()
endif()
# Put in a tests folder to reduce the top level targets in IDEs.
set(CMAKE_FOLDER tests)
# We will use 125 as a reserved exit code to indicate that a test has been skipped, i.e. it did not
# pass but it should not be considered a failed test run, either.
set(SKIP_RETURN_CODE 125)
# Even though we are using CMake's ctest for testing, we still define our own `make fish_run_tests` target
# rather than use its default for many reasons:
# * CMake doesn't run tests in-proc or even add each tests as an individual node in the ninja
# dependency tree, instead it just bundles all tests into a target called `test` that always just
# shells out to `ctest`, so there are no build-related benefits to not doing that ourselves.
# * CMake devs insist that it is appropriate for `make fish_run_tests` to never depend on `make all`, i.e.
# running `make fish_run_tests` does not require any of the binaries to be built before testing.
# * It is not possible to set top-level CTest options/settings such as CTEST_PARALLEL_LEVEL from
# within the CMake configuration file.
# * The only way to have a test depend on a binary is to add a fake test with a name like
# "build_fish" that executes CMake recursively to build the `fish` target.
# * Circling back to the point about individual tests not being actual Makefile targets, CMake does
# not offer any way to execute a named test via the `make`/`ninja`/whatever interface; the only
# way to manually invoke test `foo` is to to manually run `ctest` and specify a regex matching
# `foo` as an argument, e.g. `ctest -R ^foo$`... which is really crazy.
# The top-level test target is "fish_run_tests".
add_custom_target(fish_run_tests
COMMAND env CTEST_PARALLEL_LEVEL=${CTEST_PARALLEL_LEVEL} FISH_FORCE_COLOR=1
${CMAKE_CTEST_COMMAND} --force-new-ctest-process # --verbose
--output-on-failure --progress
DEPENDS fish fish_indent fish_key_reader fish_test_helper
USES_TERMINAL
)
# CMake being CMake, you can't just add a DEPENDS argument to add_test to make it depend on any of
# your binaries actually being built before `make fish_run_tests` is executed (requiring `make all` first),
# and the only dependency a test can have is on another test. So we make building fish
# prerequisites to our entire top-level `test` target.
function(add_test_target NAME)
string(REPLACE "/" "-" NAME ${NAME})
add_custom_target("test_${NAME}" COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -R "^${NAME}$$"
DEPENDS fish fish_indent fish_key_reader fish_test_helper USES_TERMINAL)
endfunction()
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)
get_filename_component(CHECK ${CHECK} NAME_WE)
add_test(NAME ${CHECK_NAME}
COMMAND ${CMAKE_SOURCE_DIR}/tests/test_driver.py --cachedir=${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}
checks/${CHECK}.fish
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
)
set_tests_properties(${CHECK_NAME} PROPERTIES SKIP_RETURN_CODE ${SKIP_RETURN_CODE})
set_tests_properties(${CHECK_NAME} PROPERTIES ENVIRONMENT FISH_FORCE_COLOR=1)
add_test_target("${CHECK_NAME}")
endforeach(CHECK)
FILE(GLOB PEXPECTS CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/tests/pexpects/*.py)
foreach(PEXPECT ${PEXPECTS})
get_filename_component(PEXPECT ${PEXPECT} NAME)
add_test(NAME ${PEXPECT}
COMMAND ${CMAKE_SOURCE_DIR}/tests/test_driver.py --cachedir=${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}
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
)
set_tests_properties(${PEXPECT} PROPERTIES SKIP_RETURN_CODE ${SKIP_RETURN_CODE})
set_tests_properties(${PEXPECT} PROPERTIES ENVIRONMENT FISH_FORCE_COLOR=1)
add_test_target("${PEXPECT}")
endforeach(PEXPECT)
set(cargo_test_flags)
# Rust stuff.
set(cargo_test_flags)
if(DEFINED ASAN)
# Rust w/ -Zsanitizer=address requires explicitly specifying the --target triple or else linker
# errors pertaining to asan symbols will ensue.
@@ -107,10 +48,17 @@ if(DEFINED Rust_CARGO_TARGET)
list(APPEND cargo_test_flags "--lib")
endif()
add_test(
NAME "cargo-test"
COMMAND env ${VARS_FOR_CARGO} cargo test --no-default-features ${CARGO_FLAGS} --workspace --target-dir ${rust_target_dir} ${cargo_test_flags}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
set(max_concurrency_flag)
if(DEFINED ENV{FISH_TEST_MAX_CONCURRENCY})
list(APPEND max_concurrency_flag "--max-concurrency" $ENV{FISH_TEST_MAX_CONCURRENCY})
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} 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
)
set_tests_properties("cargo-test" PROPERTIES SKIP_RETURN_CODE ${SKIP_RETURN_CODE})
add_test_target("cargo-test")

View File

@@ -1,22 +0,0 @@
set(languages de en fr pl pt_BR sv zh_CN)
include(FeatureSummary)
option(WITH_GETTEXT "translate messages if gettext is available" ON)
if(WITH_GETTEXT)
find_package(Gettext)
endif()
add_feature_info(gettext GETTEXT_FOUND "translate messages with gettext")
# Define translations
if(GETTEXT_FOUND)
# Group pofile targets into their own folder, as there's a lot of them.
set(CMAKE_FOLDER pofiles)
foreach(lang ${languages})
# Our translations aren't set up entirely as CMake expects, so installation is done in
# cmake/Install.cmake instead of using INSTALL_DESTINATION
gettext_process_po_files(${lang} ALL
PO_FILES po/${lang}.po)
endforeach()
set(CMAKE_FOLDER)
endif()

View File

@@ -0,0 +1,12 @@
[package]
name = "fish-build-helper"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
[dependencies]
rsconf.workspace = true
[lints]
workspace = true

View File

@@ -0,0 +1,48 @@
use std::{borrow::Cow, env, os::unix::ffi::OsStrExt, path::Path};
pub fn env_var(name: &str) -> Option<String> {
let err = match env::var(name) {
Ok(p) => return Some(p),
Err(err) => err,
};
use env::VarError::*;
match err {
NotPresent => None,
NotUnicode(os_string) => {
panic!(
"Environment variable {name} is not valid Unicode: {:?}",
os_string.as_bytes()
)
}
}
}
pub fn workspace_root() -> &'static Path {
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
manifest_dir.ancestors().nth(2).unwrap()
}
fn cargo_target_dir() -> Cow<'static, Path> {
option_env!("CARGO_TARGET_DIR")
.map(|d| Cow::Borrowed(Path::new(d)))
.unwrap_or(Cow::Owned(workspace_root().join("target")))
}
pub fn fish_build_dir() -> Cow<'static, Path> {
// This is set if using CMake.
option_env!("FISH_BUILD_DIR")
.map(|d| Cow::Borrowed(Path::new(d)))
.unwrap_or(cargo_target_dir())
}
// 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());
}
// TODO Move this to rsconf
pub fn rebuild_if_paths_changed<P: AsRef<Path>, I: IntoIterator<Item = P>>(paths: I) {
for path in paths {
rsconf::rebuild_if_path_changed(path.as_ref().to_str().unwrap());
}
}

View File

@@ -0,0 +1,13 @@
[package]
name = "fish-build-man-pages"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
[build-dependencies]
fish-build-helper.workspace = true
rsconf.workspace = true
[lints]
workspace = true

View File

@@ -0,0 +1,95 @@
#[cfg(not(clippy))]
use std::path::Path;
fn main() {
let mandir = fish_build_helper::fish_build_dir().join("fish-man");
let sec1dir = mandir.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(sec1dir.to_str().unwrap());
#[cfg(not(clippy))]
build_man(&mandir);
}
#[cfg(not(clippy))]
fn build_man(man_dir: &Path) {
use std::process::{Command, Stdio};
use fish_build_helper::{env_var, workspace_root};
let workspace_root = workspace_root();
let man_str = man_dir.to_str().unwrap();
let sec1_dir = man_dir.join("man1");
let sec1_str = sec1_dir.to_str().unwrap();
let docsrc_dir = workspace_root.join("doc_src");
let docsrc_str = docsrc_dir.to_str().unwrap();
let sphinx_doc_sources = [
workspace_root.join("CHANGELOG.rst"),
workspace_root.join("CONTRIBUTING.rst"),
docsrc_dir.clone(),
];
fish_build_helper::rebuild_if_paths_changed(sphinx_doc_sources);
let args = &[
"-j", "auto", "-q", "-b", "man", "-c", docsrc_str,
// doctree path - put this *above* the man1 dir to exclude it.
// this is ~6M
"-d", man_str, docsrc_str, sec1_str,
];
let _ = std::fs::create_dir_all(sec1_str);
rsconf::rebuild_if_env_changed("FISH_BUILD_DOCS");
if env_var("FISH_BUILD_DOCS") == Some("0".to_string()) {
rsconf::warn!("Skipping man pages because $FISH_BUILD_DOCS is set to 0");
return;
}
// We run sphinx to build the man pages.
// Every error here is fatal so cargo doesn't cache the result
// - if we skipped the docs with sphinx not installed, installing it would not then build the docs.
// That means you need to explicitly set $FISH_BUILD_DOCS=0 (`FISH_BUILD_DOCS=0 cargo install --path .`),
// which is unfortunate - but the docs are pretty important because they're also used for --help.
let sphinx_build = match Command::new("sphinx-build")
.args(args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.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 to build man pages.\nInstall sphinx or disable building the docs by setting $FISH_BUILD_DOCS=0.");
}
rsconf::warn!("Cannot find sphinx-build to build man pages.");
rsconf::warn!("If you install it now you need to run `cargo clean` and rebuild, or set $FISH_BUILD_DOCS=1 explicitly.");
return;
}
Err(e) => {
// Another error - permissions wrong etc
panic!("Error starting sphinx-build to build man pages: {:?}", e);
}
Ok(sphinx_build) => sphinx_build,
};
match sphinx_build.wait_with_output() {
Err(err) => {
panic!(
"Error waiting for sphinx-build to build man pages: {:?}",
err
);
}
Ok(out) => {
if !out.stderr.is_empty() {
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.");
}
}
}
}

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,16 @@
[package]
name = "fish-gettext-extraction"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
description = "proc-macro for extracting strings for gettext translation"
[lib]
proc-macro = true
[dependencies]
proc-macro2.workspace = true
[lints]
workspace = true

View File

@@ -0,0 +1,107 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use std::{ffi::OsString, fs::OpenOptions, io::Write};
fn unescape_multiline_rust_string(s: String) -> String {
if !s.contains('\n') {
return s;
}
let mut unescaped = String::new();
enum State {
Ground,
Escaped,
ContinuationLineLeadingWhitespace,
}
use State::*;
let mut state = Ground;
for c in s.chars() {
match state {
Ground => match c {
'\\' => state = Escaped,
_ => {
unescaped.push(c);
}
},
Escaped => match c {
'\\' => {
unescaped.push('\\');
state = Ground
}
'\n' => state = ContinuationLineLeadingWhitespace,
_ => panic!("Unsupported escape sequence '\\{c}' in message string '{s}'"),
},
ContinuationLineLeadingWhitespace => match c {
' ' | '\t' => (),
_ => {
unescaped.push(c);
state = Ground
}
},
}
}
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}"));
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}'")
}
// Crude check for format strings. This might result in false positives.
let format_string_annotation = if message_string.contains('%') {
"#, c-format\n"
} else {
""
};
let po_entry = format!("{format_string_annotation}msgid {message_string}\nmsgstr \"\"\n\n");
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,
/// 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
/// 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") {
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.")
}
if let proc_macro2::TokenTree::Group(group) = first_token {
let mut group_tokens = group.stream().into_iter();
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.")
}
if let proc_macro2::TokenTree::Literal(_) = first_group_token {
append_po_entry_to_file(&message, &file_path);
} else {
panic!("Expected literal in gettext_extract, but got: {first_group_token:?}");
}
} else {
panic!("Expected group in gettext_extract, but got: {first_token:?}");
}
}
message
}

View File

@@ -0,0 +1,18 @@
[package]
name = "fish-gettext-maps"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
[dependencies]
phf.workspace = true
[build-dependencies]
fish-build-helper.workspace = true
fish-gettext-mo-file-parser.workspace = true
phf_codegen.workspace = true
rsconf.workspace = true
[lints]
workspace = true

View File

@@ -0,0 +1,153 @@
use std::{
ffi::OsStr,
path::{Path, PathBuf},
process::{Command, Stdio},
};
use fish_build_helper::env_var;
fn main() {
let cache_dir =
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"));
}
fn embed_localizations(cache_dir: &Path) {
use fish_gettext_mo_file_parser::parse_mo_file;
use std::{
fs::File,
io::{BufWriter, Write},
};
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();
let localization_map_path =
Path::new(&env_var("OUT_DIR").unwrap()).join("localization_maps.rs");
let mut localization_map_file = BufWriter::new(File::create(&localization_map_path).unwrap());
// This will become a map which maps from language identifiers to maps containing localizations
// for the respective language.
let mut catalogs = phf_codegen::Map::new();
match Command::new("msgfmt")
.arg("-h")
.stdout(Stdio::null())
.status()
{
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
rsconf::warn!(
"Cannot find msgfmt to build gettext message catalogs. Localization will not work."
);
rsconf::warn!(
"If you install it now you need to trigger a rebuild to get localization support."
);
rsconf::warn!(
"One way to achieve that is running `touch po` followed by the build command."
);
}
Err(e) => {
panic!("Error when trying to run `msgfmt -h`: {e:?}");
}
Ok(_) => {
for dir_entry_result in 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")) {
continue;
}
let lang = po_file_path
.file_stem()
.expect("All entries in the po directory must be regular files.");
let language = lang.to_str().unwrap().to_owned();
// Each language gets its own static map for the mapping from message in the source code to
// the localized version.
let map_name = format!("LANG_MAP_{language}");
let cached_map_path = cache_dir.join(lang);
// Include the file containing the map for this language in the main generated file.
writeln!(
&mut localization_map_file,
"include!(\"{}\");",
cached_map_path.display()
)
.unwrap();
// Map from the language identifier to the map containing the localizations for this
// language.
catalogs.entry(language, format!("&{map_name}"));
if let Ok(metadata) = std::fs::metadata(&cached_map_path) {
// Cached map file exists, but might be outdated.
let cached_map_mtime = metadata.modified().unwrap();
let po_mtime = dir_entry.metadata().unwrap().modified().unwrap();
if cached_map_mtime > po_mtime {
// Cached map file is considered up-to-date.
continue;
};
}
// Generate the map file.
// Try to create new MO data and load it into `mo_data`.
let output = Command::new("msgfmt")
.arg("--check-format")
.arg("--output-file=-")
.arg(&po_file_path)
.output()
.unwrap();
if !output.status.success() {
panic!(
"msgfmt failed:\n{}",
String::from_utf8(output.stderr).unwrap()
);
}
let mo_data = output.stdout;
// Extract map from MO data.
let language_localizations = parse_mo_file(&mo_data).unwrap();
// This file will contain the localization map for the current language.
let mut cached_map_file = File::create(&cached_map_path).unwrap();
let mut single_language_localization_map = phf_codegen::Map::new();
// The values will be written into the source code as is, meaning escape sequences and
// double quotes in the data will be interpreted by the Rust compiler, which is undesirable.
// Converting them to raw strings prevents this. (As long as no input data contains `"###`.)
fn to_raw_str(s: &str) -> String {
assert!(!s.contains("\"###"));
format!("r###\"{s}\"###")
}
for (msgid, msgstr) in language_localizations {
single_language_localization_map.entry(
String::from_utf8(msgid.into()).unwrap(),
to_raw_str(&String::from_utf8(msgstr.into()).unwrap()),
);
}
writeln!(&mut cached_map_file, "#[allow(non_upper_case_globals)]").unwrap();
write!(
&mut cached_map_file,
"static {}: phf::Map<&'static str, &'static str> = {}",
&map_name,
single_language_localization_map.build()
)
.unwrap();
writeln!(&mut cached_map_file, ";").unwrap();
}
}
}
write!(
&mut localization_map_file,
"pub static CATALOGS: phf::Map<&str, &phf::Map<&str, &str>> = {}",
catalogs.build()
)
.unwrap();
writeln!(&mut localization_map_file, ";").unwrap();
}

View File

@@ -0,0 +1 @@
include!(concat!(env!("OUT_DIR"), "/localization_maps.rs"));

View File

@@ -0,0 +1,9 @@
[package]
name = "fish-gettext-mo-file-parser"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
[lints]
workspace = true

View File

@@ -0,0 +1,131 @@
use std::collections::HashMap;
const U32_SIZE: usize = std::mem::size_of::<u32>();
fn read_le_u32(bytes: &[u8]) -> u32 {
u32::from_le_bytes(bytes[..U32_SIZE].try_into().unwrap())
}
fn read_be_u32(bytes: &[u8]) -> u32 {
u32::from_be_bytes(bytes[..U32_SIZE].try_into().unwrap())
}
fn get_u32_reader_from_magic_number(magic_number: &[u8]) -> std::io::Result<fn(&[u8]) -> u32> {
match magic_number {
[0x95, 0x04, 0x12, 0xde] => Ok(read_be_u32),
[0xde, 0x12, 0x04, 0x95] => Ok(read_le_u32),
_ => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"First 4 bytes of MO file must correspond to magic number 0x950412de, either big or little endian.",
)),
}
}
/// Returns an error if an unknown major revision is detected.
/// There are no relevant differences between supported revisions.
fn check_if_revision_is_supported(revision: u32) -> std::io::Result<()> {
// From the reference:
// A program seeing an unexpected major revision number should stop reading the MO file entirely;
// whereas an unexpected minor revision number means that the file can be read
// but will not reveal its full contents,
// when parsed by a program that supports only smaller minor revision numbers.
let major_revision = revision >> 16;
match major_revision {
0 | 1 => {
// At time of writing, these are the only major revisions which exist.
// There is no documented difference and the GNU gettext code does not seem to
// differentiate between the two either.
// All features we care about are supported in minor revision 0,
// so we do not need to care about the minor revision.
Ok(())
}
_ => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Major revision must be 0 or 1",
)),
}
}
fn as_usize(value: u32) -> usize {
use std::mem::size_of;
const _: () = assert!(size_of::<u32>() <= size_of::<usize>());
usize::try_from(value).unwrap()
}
fn parse_strings(
file_content: &[u8],
num_strings: usize,
table_offset: usize,
read_u32: fn(&[u8]) -> u32,
) -> std::io::Result<Vec<&[u8]>> {
let file_too_short_error = || {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"MO file is too short.",
))
};
if table_offset + num_strings * 2 * U32_SIZE > file_content.len() {
return file_too_short_error();
}
let mut strings = Vec::with_capacity(num_strings);
let mut offset = table_offset;
let mut get_next_u32 = || {
let val = read_u32(&file_content[offset..]);
offset += U32_SIZE;
val
};
for _ in 0..num_strings {
// not including NUL terminator
let string_length = as_usize(get_next_u32());
let string_offset = as_usize(get_next_u32());
let string_end = string_offset.checked_add(string_length).unwrap();
if string_end > file_content.len() {
return file_too_short_error();
}
// Contexts are stored by storing the concatenation of the context, a EOT byte, and the original string, instead of the original string.
// Contexts are not supported by this implementation.
// The format allows plural forms to appear behind singular forms, separated by a NUL byte,
// where `string_length` includes the length of both.
// This is not supported here.
// Do not include the NUL terminator in the slice.
strings.push(&file_content[string_offset..string_end]);
}
Ok(strings)
}
/// Parse a MO file.
/// Format reference used: <https://www.gnu.org/software/gettext/manual/html_node/MO-Files.html>
pub fn parse_mo_file(file_content: &[u8]) -> std::io::Result<HashMap<&[u8], &[u8]>> {
if file_content.len() < 7 * U32_SIZE {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"File too short to contain header.",
));
}
// The first 4 bytes are a magic number, from which the endianness can be determined.
let read_u32 = get_u32_reader_from_magic_number(&file_content[0..U32_SIZE])?;
let mut offset = U32_SIZE;
let mut get_next_u32 = || {
let val = read_u32(&file_content[offset..]);
offset += U32_SIZE;
val
};
let file_format_revision = get_next_u32();
check_if_revision_is_supported(file_format_revision)?;
let num_strings = as_usize(get_next_u32());
let original_strings_offset = as_usize(get_next_u32());
let translation_strings_offset = as_usize(get_next_u32());
let original_strings =
parse_strings(file_content, num_strings, original_strings_offset, read_u32)?;
let translated_strings = parse_strings(
file_content,
num_strings,
translation_strings_offset,
read_u32,
)?;
let mut translation_map = HashMap::with_capacity(num_strings);
for i in 0..num_strings {
translation_map.insert(original_strings[i], translated_strings[i]);
}
Ok(translation_map)
}

View File

@@ -3,15 +3,15 @@ name = "fish-printf"
edition.workspace = true
rust-version.workspace = true
version = "0.2.1"
repository = "https://github.com/fish-shell/fish-shell"
repository.workspace = true
description = "printf implementation, based on musl"
license = "MIT"
[dependencies]
libc = "0.2.155"
widestring = { version = "1.0.2", optional = true }
unicode-segmentation = "1.12.0"
unicode-width = "0.2.0"
libc.workspace = true
widestring = { workspace = true, optional = true }
unicode-segmentation.workspace = true
unicode-width.workspace = true
[lints]
workspace = true

View File

@@ -15,7 +15,7 @@ pub enum Arg<'a> {
#[cfg(feature = "widestring")]
WString(WString),
UInt(u64),
SInt(i64, u8), // signed integers track their width as the number of bits
SInt(i64),
Float(f64),
USizeRef(&'a mut usize), // for use with %n
}
@@ -59,7 +59,7 @@ pub fn as_str<'s>(&'s self, storage: &'s mut String) -> Result<&'s str, Error>
pub fn as_uint(&self) -> Result<u64, Error> {
match *self {
Arg::UInt(u) => Ok(u),
Arg::SInt(i, _w) => i.try_into().map_err(|_| Error::Overflow),
Arg::SInt(i) => i.try_into().map_err(|_| Error::Overflow),
_ => Err(Error::BadArgType),
}
}
@@ -68,25 +68,18 @@ pub fn as_uint(&self) -> Result<u64, Error> {
pub fn as_sint(&self) -> Result<i64, Error> {
match *self {
Arg::UInt(u) => u.try_into().map_err(|_| Error::Overflow),
Arg::SInt(i, _w) => Ok(i),
Arg::SInt(i) => Ok(i),
_ => Err(Error::BadArgType),
}
}
// If this is a signed value, then return the sign (true if negative) and the magnitude,
// masked to the value's width. This allows for e.g. -1 to be returned as 0xFF, 0xFFFF, etc.
// depending on the original width.
// If this is an unsigned value, simply return (false, u64).
pub fn as_wrapping_sint(&self) -> Result<(bool, u64), Error> {
/// Unwraps [`Arg::UInt`] to [`u64`].
/// Unwraps [`Arg::SInt`] and casts the [`i64`] to [`u64`].
/// Calling this on other variants of `[Arg]` is an error.
pub fn as_wrapping_sint(&self) -> Result<u64, Error> {
match *self {
Arg::UInt(u) => Ok((false, u)),
Arg::SInt(i, w) => {
// Need to shift twice in case w is 64.
debug_assert!(w > 0);
let mask = ((1u64 << (w - 1)) << 1).wrapping_sub(1);
let ui = (i as u64) & mask;
Ok((i < 0, ui))
}
Arg::UInt(u) => Ok(u),
Arg::SInt(i) => Ok(i as u64),
_ => Err(Error::BadArgType),
}
}
@@ -97,7 +90,7 @@ pub fn as_float(&self) -> Result<f64, Error> {
match *self {
Arg::Float(f) => Ok(f),
Arg::UInt(u) => Ok(u as f64),
Arg::SInt(i, _w) => Ok(i as f64),
Arg::SInt(i) => Ok(i as f64),
_ => Err(Error::BadArgType),
}
}
@@ -181,7 +174,7 @@ macro_rules! impl_to_arg {
$(
impl<'a> ToArg<'a> for $t {
fn to_arg(self) -> Arg<'a> {
Arg::SInt(self as i64, <$t>::BITS as u8)
Arg::SInt(self as i64)
}
}
)*
@@ -211,8 +204,6 @@ mod tests {
#[test]
fn test_to_arg() {
const SIZE_WIDTH: u8 = isize::BITS as u8;
assert!(matches!("test".to_arg(), Arg::Str("test")));
assert!(matches!(String::from("test").to_arg(), Arg::Str(_)));
#[cfg(feature = "widestring")]
@@ -224,17 +215,17 @@ fn test_to_arg() {
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, 8)));
assert!(matches!(42i16.to_arg(), Arg::SInt(42, 16)));
assert!(matches!(42i32.to_arg(), Arg::SInt(42, 32)));
assert!(matches!(42i64.to_arg(), Arg::SInt(42, 64)));
assert!(matches!(42isize.to_arg(), Arg::SInt(42, SIZE_WIDTH)));
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, 8));
assert_eq!((-42i16).to_arg(), Arg::SInt(-42, 16));
assert_eq!((-42i32).to_arg(), Arg::SInt(-42, 32));
assert_eq!((-42i64).to_arg(), Arg::SInt(-42, 64));
assert_eq!((-42isize).to_arg(), Arg::SInt(-42, SIZE_WIDTH));
assert_eq!((-42i8).to_arg(), Arg::SInt(-42));
assert_eq!((-42i16).to_arg(), Arg::SInt(-42));
assert_eq!((-42i32).to_arg(), Arg::SInt(-42));
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)));

View File

@@ -279,6 +279,7 @@ fn format_a(mut y: f64, params: FormatParams<'_, impl Write>) -> Result<usize, E
// Compute the number of hex digits in the mantissa after the decimal.
// -1 for leading 1 bit (we are to the range [1, 2)), then divide by 4, rounding up.
#[allow(unknown_lints)] // for old clippy
#[allow(clippy::manual_div_ceil)]
const MANTISSA_HEX_DIGITS: usize = (MANTISSA_BITS - 1 + 3) / 4;
if had_prec && prec < MANTISSA_HEX_DIGITS {
@@ -495,7 +496,7 @@ fn format_mantissa_e(
let digit = if d < decimal.len_i32() { decimal[d] } else { 0 };
let min_width = if d > 0 { DIGIT_WIDTH } else { 1 };
buf.clear();
write!(buf, "{:0width$}", digit, width = min_width)?;
write!(buf, "{digit:0min_width$}")?;
let mut s = buf.as_str();
if d == 0 {
// First digit. Emit it, and likely also a decimal point.

View File

@@ -464,7 +464,7 @@ pub fn sprintf_locale(
let uint = arg.as_uint()?;
if uint != 0 {
prefix = "0x";
write!(buf, "{:x}", uint)?;
write!(buf, "{uint:x}")?;
}
buf
}
@@ -472,15 +472,15 @@ pub fn sprintf_locale(
// If someone passes us a negative value, format it with the width
// we were given.
let lower = conv_spec.is_lower();
let (_, uint) = arg.as_wrapping_sint()?;
let uint = arg.as_wrapping_sint()?;
if uint != 0 {
if flags.alt_form {
prefix = if lower { "0x" } else { "0X" };
}
if lower {
write!(buf, "{:x}", uint)?;
write!(buf, "{uint:x}")?;
} else {
write!(buf, "{:X}", uint)?;
write!(buf, "{uint:X}")?;
}
}
buf
@@ -488,7 +488,7 @@ pub fn sprintf_locale(
CS::o => {
let uint = arg.as_uint()?;
if uint != 0 {
write!(buf, "{:o}", uint)?;
write!(buf, "{uint:o}")?;
}
if flags.alt_form && desired_precision.unwrap_or(0) <= buf.len() + 1 {
desired_precision = Some(buf.len() + 1);
@@ -498,7 +498,7 @@ pub fn sprintf_locale(
CS::u => {
let uint = arg.as_uint()?;
if uint != 0 {
write!(buf, "{}", uint)?;
write!(buf, "{uint}")?;
}
buf
}

View File

@@ -77,7 +77,7 @@ fn write_str(&mut self, _s: &str) -> fmt::Result {
#[test]
fn smoke() {
assert_fmt!("Hello, %s!", "world" => "Hello, world!");
assert_fmt!("Hello, %ls!", "world" => "Hello, world!");
assert_fmt!("Hello, %ls!", "world" => "Hello, world!"); // length modifier
assert_fmt!("Hello, world! %d %%%%", 3 => "Hello, world! 3 %%");
assert_fmt!("" => "");
}
@@ -225,7 +225,7 @@ fn test_int() {
assert_fmt!("%d", -123 => "-123");
assert_fmt!("~%d~", 148 => "~148~");
assert_fmt!("00%dxx", -91232 => "00-91232xx");
assert_fmt!("%x", -9232 => "ffffdbf0");
assert_fmt!("%x", -9232 => "ffffffffffffdbf0");
assert_fmt!("%X", 432 => "1B0");
assert_fmt!("%09X", 432 => "0000001B0");
assert_fmt!("%9X", 432 => " 1B0");
@@ -234,6 +234,7 @@ fn test_int() {
assert_fmt!("%2o", 4 => " 4");
assert_fmt!("% 12d", -4 => " -4");
assert_fmt!("% 12d", 48 => " 48");
// with length modifier
assert_fmt!("%ld", -4_i64 => "-4");
assert_fmt!("%lld", -4_i64 => "-4");
assert_fmt!("%lX", -4_i64 => "FFFFFFFFFFFFFFFC");
@@ -248,6 +249,7 @@ fn test_int() {
assert_fmt!("%9X", 492 => " 1EC");
assert_fmt!("% 12u", 4 => " 4");
assert_fmt!("% 12u", 48 => " 48");
// with length modifier
assert_fmt!("%lu", 4_u64 => "4");
assert_fmt!("%llu", 4_u64 => "4");
assert_fmt!("%lX", 4_u64 => "4");
@@ -414,6 +416,7 @@ fn test_float() {
assert_fmt1!("%f", 0.0, "0.000000");
assert_fmt1!("%g", 0.0, "0");
assert_fmt1!("%#g", 0.0, "0.00000");
// with length modifier
assert_fmt1!("%la", 0.0, "0x0p+0");
assert_fmt1!("%le", 0.0, "0.000000e+00");
assert_fmt1!("%lf", 0.0, "0.000000");
@@ -430,7 +433,7 @@ fn test_float() {
assert_fmt1!("%.4f", 1.03125, "1.0312"); /* 0x1.08p0 */
assert_fmt1!("%.2f", 1.375, "1.38");
assert_fmt1!("%.1f", 1.375, "1.4");
assert_fmt1!("%.1lf", 1.375, "1.4");
assert_fmt1!("%.1lf", 1.375, "1.4"); // length modifier
assert_fmt1!("%.15f", 1.1, "1.100000000000000");
assert_fmt1!("%.16f", 1.1, "1.1000000000000001");
assert_fmt1!("%.17f", 1.1, "1.10000000000000009");
@@ -755,8 +758,8 @@ fn test_errors() {
sprintf_err!("%1", => BadFormatString);
sprintf_err!("%%%k", => BadFormatString);
sprintf_err!("%B", => BadFormatString);
sprintf_err!("%lC", 'q' => BadFormatString);
sprintf_err!("%lS", 'q' => BadFormatString);
sprintf_err!("%lC", 'q' => BadFormatString); // length modifier
sprintf_err!("%lS", 'q' => BadFormatString); // length modifier
sprintf_err!("%d", => MissingArg);
sprintf_err!("%d %u", 1 => MissingArg);
sprintf_err!("%*d", 5 => MissingArg);
@@ -890,7 +893,7 @@ fn test_exhaustive(rust_fmt: &str, c_fmt: *const c_char) {
// "There's only 4 billion floats so test them all."
// This tests a format string expected to be of the form "%.*g" or "%.*e".
// That is, it takes a precision and a double.
println!("Testing {}", rust_fmt);
println!("Testing {rust_fmt}");
let mut rust_str = String::with_capacity(128);
let mut c_storage = [0u8; 128];
let c_storage_ptr = c_storage.as_mut_ptr() as *mut c_char;

6
debian/control vendored
View File

@@ -23,10 +23,10 @@ Architecture: any
# for col and lock - bsdmainutils is required in Ubuntu focal
Depends: bsdextrautils | bsdmainutils,
file,
# for the gettext command
# for the msgfmt command
gettext-base,
# for nroff and preconv
groff-base,
# for man
man-db,
# for terminal definitions
ncurses-base,
# for kill

2
debian/rules vendored
View File

@@ -17,7 +17,7 @@ override_dh_auto_configure:
dh_auto_configure --buildsystem=cmake -- -DCMAKE_BUILD_TYPE=RelWithDebInfo
override_dh_clean:
dh_clean
dh_clean --exclude=Cargo.toml.orig
-unlink .cargo
-unlink vendor

View File

@@ -23,10 +23,10 @@ Steps:
## Building locally (no code signing)
To build locally without notarizing and code signing, use the `build_tools/make_pkg.sh` script:
To build locally without notarizing and code signing, use the `build_tools/make_macos_pkg.sh` script:
```
> ./build_tools/make_pkg.sh
> ./build_tools/make_macos_pkg.sh
```
Packages will be placed in `~/fish_built` by default.
@@ -45,7 +45,7 @@ You will need the following:
An example run:
```
> ./build_tools/make_pkg.sh -s \
> ./build_tools/make_macos_pkg.sh -s \
-f fish-developer-id-application.p12 \
-i fish-developer-id-installer.p12 \
-p "$NOTARIZE_PASSWORD" \

View File

@@ -15,12 +15,15 @@ Description
``_`` translates its arguments into the current language, if possible.
It is equivalent to ``gettext fish STRING``, meaning it can only be used to look up fish's own translations.
This only works with messages which are translated as part of fish's own sources, so using it as part of your own fish scripts which are not upstreamed into the fish repo will not work unless the exact same message also exists upstream.
It requires fish to be built with gettext support. If that support is disabled, or there is no translation it will echo the argument back.
It requires fish to be built with gettext support. If that support is disabled or there is no translation it will echo the argument back.
The language depends on the current locale, set with :envvar:`LANG` and :envvar:`LC_MESSAGES`.
The language depends on the current locale, set with :envvar:`LANG`, :envvar:`LC_MESSAGES`, :envvar:`LC_ALL`, and :envvar:`LANGUAGE`.
These variables do not have to be exported for fish to use them, and fish's variable scopes are supported.
If other programs launched via fish should respect these locale variables they have to be exported to make them available outside of fish.
For :envvar:`LANGUAGE` you can use a list, or use colons to separate multiple languages.
Options
-------
@@ -30,7 +33,20 @@ Options
Examples
--------
::
Use German translations::
> _ File
> set LANG de_DE.UTF-8
> _ file
Datei
Specify a precedence of languages (only works with :envvar:`LANGUAGE`)::
> set LANGUAGE pt de
> _ file # This message has a Portuguese translation.
arquivo
> _ "Invalid arguments" # This message does not have a Portuguese translation, but a German one.
Ungültige Argumente
> _ untranslatable # No translation in Portuguese, nor in German.
untranslatable
Note that the specific examples may change if translations are added/modified.

View File

@@ -14,7 +14,7 @@ Synopsis
Description
-----------
This command makes it easy for fish scripts and functions to handle arguments. You pass arguments that define the known options, followed by a literal **--**, then the arguments to be parsed (which might also include a literal **--**). ``argparse`` then sets variables to indicate the passed options with their values, and sets ``$argv`` to the remaining arguments. See the :ref:`usage <cmd-argparse-usage>` section below.
This command makes it easy for fish scripts and functions to handle arguments. You pass arguments that define the known options, followed by a literal **--**, then the arguments to be parsed (which might also include a literal **--**). ``argparse`` then sets variables to indicate the passed options with their values, sets ``$argv_opts`` to the options and their values, and sets ``$argv`` to the remaining arguments. See the :ref:`usage <cmd-argparse-usage>` section below.
Each option specification (``OPTION_SPEC``) is written in the :ref:`domain specific language <cmd-argparse-option-specification>` described below. All OPTION_SPECs must appear after any argparse flags and before the ``--`` that separates them from the arguments to be parsed.
@@ -27,8 +27,8 @@ Options
The following ``argparse`` options are available. They must appear before all *OPTION_SPEC*\ s:
**-n** or **--name**
The command name for use in error messages. By default the current function name will be used, or ``argparse`` if run outside of a function.
**-n** or **--name** *NAME*
Use *NAME* in error messages. By default the current function name will be used, or ``argparse`` if run outside of a function.
**-x** or **--exclusive** *OPTIONS*
A comma separated list of options that are mutually exclusive. You can use this more than once to define multiple sets of mutually exclusive options.
@@ -40,8 +40,44 @@ The following ``argparse`` options are available. They must appear before all *O
**-X** or **--max-args** *NUMBER*
The maximum number of acceptable non-option arguments. The default is infinity.
**-u** or **--move-unknown**
Allow unknown options, and move them from ``$argv`` to ``$argv_opts``. By default, Unknown options are treated as if they take optional arguments (i.e. have option spec ``=?``).
The above means that if a group of short options contains an unknown short option *followed* by a known short option, the known short option is
treated as an argument to the unknown one (e.g. ``--move-unknown h -- -oh`` will treat ``h`` as the argument to ``-o``, and so ``_flag_h`` will *not* be set).
In contrast, if the known option comes first (and does not take any arguments), the known option will be recognised (e.g. ``argparse --move-unknown h -- -ho`` *will* set ``$_flag_h`` to ``-h``)
**-i** or **--ignore-unknown**
Ignores unknown options, keeping them and their arguments in $argv instead.
Deprecated. This is like **--move-unknown**, except that unknown options and their arguments are kept in ``$argv`` and not moved to ``$argv_opts``. Unlike **--move-unknown**, this option makes it impossible to distinguish between an unknown option and non-option argument that starts with a ``-`` (since any ``--`` seperator in ``$argv`` will be removed).
**-S** or **--strict-longopts**
This makes the parsing of long options more strict. In particular, *without* this flag, if ``long`` is a known long option flag, ``--long`` and ``--long=<value>`` can be abbreviated as:
- ``-long`` and ``-long=<value>``, but *only* if there is no short flag ``l``.
- ``--lo`` and ``--lo=<value>``, but *only* if there is no other long flag that starts with ``lo``. Similarly with any other non-empty prefix of ``long``.
- ``-lo`` and ``-lo=<value>`` (i.e. combining the above two).
With the ``--strict-longopts`` flag, the above three are parse errors: one must use the syntax ``--long`` or ``--long=<value>`` to use a long option called ``long``.
This flag has no effect on the parsing of unknown options (which are parsed as if this flag is on).
This option may be on all the time in the future, so do not rely on the behaviour without it.
**--unknown-arguments** *KIND*
This option implies **--move-unknown**, unless **--ignore-unknown** is also given.
This will modify the parsing behaviour of unknown options depending on the value of *KIND*:
- **optional** (the default), allows each unknown option to take an optional argument (i.e. as if it had ``=?`` or ``=*`` in its option specification). For example, ``argparse --ignore-unknown --unknown-arguments=optional ab -- -u -a -ub`` will set ``_flag_a`` but *not* ``_flag_b``, as the ``b`` is treated as an argument to the second use of ``-u``.
- **required** requires each unknown option to take an argument (i.e. as if it had ``=`` or ``=+`` in its option specification). If the above example was changed to use ``--unknown-arguments=required``, *neither* ``_flag_a`` nor ``_flag_b`` would be set: the ``-a`` will be treated as an argument to the first use of ``-u``, and the ``b`` as an argument to the second.
- **none** forbids each unknown option from taking an argument (i.e. as if it had no ``=`` in its option specification). If the above example was changed to use ``--unknown-arguments=none``, *both* ``_flag_a`` and ``_flag_b`` would be set, as neither use of ``-u`` will be passed as taking an argument.
Note that the above assumes that unknown long flags use the ``--`` "GNU-style" (e.g. if *KIND* is ``none``, and there is no ``bar`` long option, ``-bar`` is interpreted as three short flags, ``b``, ``a``, and ``r``; but if ``bar`` is known, ``-bar`` is treated the same as ``--bar``).
When using ``--unknown-arguments=required``, you will get an error if the provided arguments end in an unknown option, since it has no argument. Similarly, with ``--unknown-arguments=none``, you will get an error if you use the ``--flag=value`` syntax and ``flag`` is an unknown option.
**-s** or **--stop-nonopt**
Causes scanning the arguments to stop as soon as the first non-option argument is seen. Among other things, this is useful to implement subcommands that have their own options.
@@ -91,7 +127,7 @@ But this is not::
set -l argv
argparse 'h/help' 'n/name' $argv
The first ``--`` seen is what allows the ``argparse`` command to reliably separate the option specifications and options to ``argparse`` itself (like ``--ignore-unknown``) from the command arguments, so it is required.
The first ``--`` seen is what allows the ``argparse`` command to reliably separate the option specifications and options to ``argparse`` itself (like ``--move-unknown``) from the command arguments, so it is required.
.. _cmd-argparse-option-specification:
@@ -100,9 +136,13 @@ Option Specifications
Each option specification consists of:
- An optional alphanumeric short flag character, followed by a ``/`` if the short flag can be used by someone invoking your command or, for backwards compatibility, a ``-`` if it should not be exposed as a valid short flag (in which case it will also not be exposed as a flag variable).
- An optional alphanumeric short flag character.
- An optional long flag name, which if not present the short flag can be used, and if that is also not present, an error is reported
- An optional long flag name preceded by a ``/``. If neither a short flag nor long flag are present, an error is reported.
- If there is no short flag, and the long flag name is more than one character, the ``/`` can be omitted.
- For backwards compatibility, if there is a short and a long flag, a ``-`` can be used in place of the ``/``, if the short flag is not to be usable by users (in which case it will also not be exposed as a flag variable).
- Nothing if the flag is a boolean that takes no argument or is an integer flag, or
@@ -110,9 +150,15 @@ Each option specification consists of:
- **=?** if it takes an optional value and only the last instance of the flag is saved, or
- **=+** if it requires a value and each instance of the flag is saved.
- **=+** if it requires a value and each instance of the flag is saved, or
- Optionally a ``!`` followed by fish script to validate the value. Typically this will be a function to run. If the exit status is zero the value for the flag is valid. If non-zero the value is invalid. Any error messages should be written to stdout (not stderr). See the section on :ref:`Flag Value Validation <flag-value-validation>` for more information.
- **=\*** if it takes an optional value *and* each instance of the flag is saved, storing the empty string when the flag was not given a value.
- Optionally a ``&``, indicating that the option and any attached values are not to be saved in ``$argv`` or ``$argv_opts``. This does not affect the the ``_flag_`` variables.
- Nothing if the flag is a boolean that takes no argument, or
- ``!`` followed by fish script to validate the value. Typically this will be a function to run. If the exit status is zero the value for the flag is valid. If non-zero the value is invalid. Any error messages should be written to stdout (not stderr). See the section on :ref:`Flag Value Validation <flag-value-validation>` for more information.
See the :doc:`fish_opt <fish_opt>` command for a friendlier but more verbose way to create option specifications.
@@ -132,7 +178,7 @@ This does not read numbers given as ``+NNN``, only those that look like flags -
Note: Optional arguments
------------------------
An option defined with ``=?`` can take optional arguments. Optional arguments have to be *directly attached* to the option they belong to.
An option defined with ``=?`` or ``=*`` can take optional arguments. Optional arguments have to be *directly attached* to the option they belong to.
That means the argument will only be used for the option if you use it like::
@@ -199,16 +245,22 @@ Some *OPTION_SPEC* examples:
- ``help`` means that only ``--help`` is valid. The flag is a boolean and can be used more than once. If it is used then ``_flag_help`` will be set as above. Also ``h-help`` (with an arbitrary short letter) for backwards compatibility.
- ``help&`` is similar (it will *remove* ``--help`` from ``$argv``), the difference is that ``--help``` will *not* placed in ``$argv_opts``.
- ``longonly=`` is a flag ``--longonly`` that requires an option, there is no short flag or even short flag variable.
- ``n/name=`` means that both ``-n`` and ``--name`` are valid. It requires a value and can be used at most once. If the flag is seen then ``_flag_n`` and ``_flag_name`` will be set with the single mandatory value associated with the flag.
- ``n/name=?`` means that both ``-n`` and ``--name`` are valid. It accepts an optional value and can be used at most once. If the flag is seen then ``_flag_n`` and ``_flag_name`` will be set with the value associated with the flag if one was provided else it will be set with no values.
- ``n/name=*`` is similar, but the flag can be used more than once. If the flag is seen then ``_flag_n`` and ``_flag_name`` will be set with the values associated with each occurence. Each value will be the value given to the option, or the empty string if no value was given.
- ``name=+`` means that only ``--name`` is valid. It requires a value and can be used more than once. If the flag is seen then ``_flag_name`` will be set with the values associated with each occurrence.
- ``x`` means that only ``-x`` is valid. It is a boolean that can be used more than once. If it is seen then ``_flag_x`` will be set as above.
- ``/x`` is similar, but only ``--x`` is valid (instead of ``-x``).
- ``x=``, ``x=?``, and ``x=+`` are similar to the n/name examples above but there is no long flag alternative to the short flag ``-x``.
- ``#max`` (or ``#-max``) means that flags matching the regex "^--?\\d+$" are valid. When seen they are assigned to the variable ``_flag_max``. This allows any valid positive or negative integer to be specified by prefixing it with a single "-". Many commands support this idiom. For example ``head -3 /a/file`` to emit only the first three lines of /a/file.
@@ -217,7 +269,7 @@ Some *OPTION_SPEC* examples:
- ``#longonly`` causes the last integer option to be stored in ``_flag_longonly``.
After parsing the arguments the ``argv`` variable is set with local scope to any values not already consumed during flag processing. If there are no unbound values the variable is set but ``count $argv`` will be zero.
After parsing the arguments the ``argv`` variable is set with local scope to any values not already consumed during flag processing. If there are no unbound values the variable is set but ``count $argv`` will be zero. Similarly, the ``argv_opts`` variable is set with local scope to the arguments that *were* consumed during flag processing. This allows forwarding ``$argv_opts`` to another command, together with additional arguments.
If an error occurs during argparse processing it will exit with a non-zero status and print error messages to stderr.
@@ -259,17 +311,41 @@ After this it figures out which variable it should operate on according to the `
and set $var $result
Limitations
-----------
An example of using ``$argv_opts`` to forward known options to another command, whilst adding new options::
One limitation with **--ignore-unknown** is that, if an unknown option is given in a group with known options, the entire group will be kept in $argv. ``argparse`` will not do any permutations here.
function my-head
# The following option is the only existing one to head that takes arguments
# (we will forward it verbatim).
set -l opt_spec n/lines=
# --qwords is a new option, but --bytes is an existing one which we will modify below
set -a opt_spec "qwords=&" "c/bytes=&"
argparse --strict-longopts --move-unknown --unknown-arguments=none $opt_spec -- $argv || return
if set -q _flag_qwords
# --qwords allows specifying the size in multiples of 8 bytes
set -a argv_opts --bytes=(math -- $_flag_qwords \* 8 || return)
else if set -q _flag_bytes
# Allows using a 'q' suffix, e.g. --bytes=4q to mean 4*8 bytes.
if string match -qr 'q$' -- $_flag_bytes
set -a argv_opts --bytes=(math -- (string replace -r 'q$' '*8' -- $_flag_bytes) || return)
else
# Keep the users setting
set -a argv_opts --bytes=$_flag_bytes
end
For instance::
end
argparse --ignore-unknown h -- -ho
echo $_flag_h # is -h, because -h was given
echo $argv # is still -ho
if test (count $argv) -eq 0
# Default to heading /dev/kmsg (whereas head defaults to stdin)
set -l argv /dev/kmsg
end
This limitation may be lifted in future.
# Call the real head with our modified options and arguments.
head $argv_opts -- $argv
end
Additionally, it can only parse known options up to the first unknown option in the group - the unknown option could take options, so it isn't clear what any character after an unknown option means.
The argparse call above saves all the options we do *not* want to process in ``$argv_opts``. (The ``--qwords`` and ``--bytes`` options are *not* saved there as their option spec's end in a ``~``). The code then processes the ``--qwords`` and ``--bytess`` options using the the ``$_flag_OPTION`` variables, and puts the transformed options in ``$argv_opts`` (which already contains all the original options, *other* than ``--qwords`` and ``--bytes``).
Note that because the ``argparse`` call above uses ``--move-unknown`` and ``--unknown-arguments=none``, we only need to tell it the arguments to ``head`` that take a value. This allows the wrapper script to accurately work out the *non*-option arguments (i.e. ``$argv``, the filenames that ``head`` is to operate on). Using ``--unknown-arguments=optional`` and explicitly listing all the known options to ``head`` however would have the advantage that if ``head`` were to add new options, they could still be used with the wrapper script using the "stuck" form for arguments (e.g. ``-o<arg>``, or ``--opt=<arg>``).
Note that the ``--strict-longopts`` is required to be able to correctly pass short options, e.g. without it ``my-head -q --bytes 10q``, will actually parse the ``-q`` as shorthand for ``--qwords``.

View File

@@ -42,6 +42,6 @@ The typical use is to run something, stop it with ctrl-z, and then continue it i
If only 123 and 789 exist, it will still background them and print an error about 456.
``bg 123 banana`` or ``bg banana 123`` will complain that "banana" is not a valid job specifier.
``bg 123 banana`` or ``bg banana 123`` will complain that "banana" is not a valid process ID.
``bg %2`` will background job 2.

View File

@@ -183,8 +183,11 @@ The following special input functions are available:
``clear-screen``
clears the screen and redraws the prompt.
.. _special-input-functions-scrollback-push:
``scrollback-push``
pushes earlier output to the terminal scrollback, positioning the prompt at the top.
This requires the terminal to implement the ECMA-48 :ref:`SCROLL UP <term-compat-indn>` command and :ref:`cursor position reporting <term-compat-cursor-position-report>`.
``complete``
guess the remainder of the current token

View File

@@ -19,7 +19,7 @@ Description
``break`` halts a currently running loop (*LOOP_CONSTRUCT*), such as a :doc:`for <for>` or :doc:`while <while>` loop. It is usually added inside of a conditional block such as an :doc:`if <if>` block.
There are no parameters for ``break``.
The **-h** or **--help** option displays help about using this command.
Example
-------

View File

@@ -15,6 +15,8 @@ Description
``continue`` skips the remainder of the current iteration of the current inner loop, such as a :doc:`for <for>` loop or a :doc:`while <while>` loop. It is usually added inside of a conditional block such as an :doc:`if <if>` statement or a :doc:`switch <switch>` statement.
The **-h** or **--help** option displays help about using this command.
Example
-------

View File

@@ -29,7 +29,7 @@ Currently supported are:
- ``wl-copy`` using wayland
- ``xsel`` and ``xclip`` for X11
- ``clip.exe`` on Windows.
- The OSC 52 clipboard sequence, which your terminal might support
- The :ref:`OSC 52 clipboard sequence <term-compat-osc-52>`, which your terminal might support
See also
--------

View File

@@ -8,7 +8,7 @@ Synopsis
.. synopsis::
fish_git_prompt
fish_git_prompt [FORMAT]
::
@@ -24,6 +24,8 @@ The ``fish_git_prompt`` function displays information about the current git repo
`Git <https://git-scm.com>`_ must be installed.
It is possible to modify the output format by passing an argument. The default value is ``" (%s)"``.
There are numerous customization options, which can be controlled with git options or fish variables. git options, where available, take precedence over the fish variable with the same function. git options can be set on a per-repository or global basis. git options can be set with the ``git config`` command, while fish variables can be set as usual with the :doc:`set <set>` command.
Boolean options (those which enable or disable something) understand "1", "yes" or "true" to mean true and every other value to mean false.
@@ -108,7 +110,7 @@ Variables used with ``showupstream`` (also implied by informative status):
- ``$__fish_git_prompt_char_upstream_ahead`` (>, ↑) - the character for the commits this repository is ahead of upstream
- ``$__fish_git_prompt_char_upstream_behind`` (<, ↓) - the character for the commits this repository is behind upstream
- ``$__fish_git_prompt_char_upstream_diverged`` (<>) - the symbol if this repository is both ahead and behind upstream
- ``$__fish_git_prompt_char_upstream_diverged`` (<>, ↓↑) - the symbol if this repository is both ahead and behind upstream
- ``$__fish_git_prompt_char_upstream_equal`` (=) - the symbol if this repo is equal to upstream
- ``$__fish_git_prompt_char_upstream_prefix`` ('')
- ``$__fish_git_prompt_color_upstream``

View File

@@ -43,12 +43,6 @@ The following options are available:
**--html**
Outputs HTML, which supports syntax highlighting if the appropriate CSS is defined. The CSS class names are the same as the variable names, such as ``fish_color_command``.
**-d** or **--debug=DEBUG_CATEGORIES**
Enable debug output and specify a pattern for matching debug categories. See :ref:`Debugging <debugging-fish>` in :doc:`fish <fish>` (1) for details.
**-o** or **--debug-output=DEBUG_FILE**
Specify a file path to receive the debug output, including categories and ``fish_trace``. The default is standard error.
**--dump-parse-tree**
Dumps information about the parsed statements to standard error. This is likely to be of interest only to people working on the fish source code.

View File

@@ -8,7 +8,7 @@ Synopsis
.. synopsis::
fish_opt [(-slor | --multiple-vals=) OPTNAME]
fish_opt [-s ALPHANUM] [-l LONG-NAME] [-ormd] [--long-only] [-v COMMAND OPTIONS ... ]
fish_opt --help
Description
@@ -18,14 +18,14 @@ This command provides a way to produce option specifications suitable for use wi
The following ``argparse`` options are available:
**-s** or **--short**
Takes a single letter that is used as the short flag in the option being defined. This option is mandatory.
**-s** or **--short** *ALPHANUM*
Takes a single letter or number that is used as the short flag in the option being defined. Either this option or the **--long** option must be provided.
**-l** or **--long**
**-l** or **--long** *LONG-NAME*
Takes a string that is used as the long flag in the option being defined. This option is optional and has no default. If no long flag is defined then only the short flag will be allowed when parsing arguments using the option specification.
**--long-only**
The option being defined will only allow the long flag name to be used. The short flag name must still be defined (i.e., **--short** must be specified) but it cannot be used when parsing arguments using this option specification.
Deprecated. The option being defined will only allow the long flag name to be used, even if the short flag is defined (i.e., **--short** is specified).
**-o** or **--optional-val**
The option being defined can take a value, but it is optional rather than required. If the option is seen more than once when parsing arguments, only the last value seen is saved. This means the resulting flag variable created by ``argparse`` will zero elements if no value was given with the option else it will have exactly one element.
@@ -33,8 +33,15 @@ The following ``argparse`` options are available:
**-r** or **--required-val**
The option being defined requires a value. If the option is seen more than once when parsing arguments, only the last value seen is saved. This means the resulting flag variable created by ``argparse`` will have exactly one element.
**--multiple-vals**
The option being defined requires a value each time it is seen. Each instance is stored. This means the resulting flag variable created by ``argparse`` will have one element for each instance of this option in the arguments.
**-m** or **--multiple-vals**
The value of each instance of the option is accumulated. If **--optional-val** is provided, the value is optional, and an empty string is stored if no value is provided. Otherwise, the **--requiured-val** option is implied and each instance of the option requires a value. This means the resulting flag variable created by ``argparse`` will have one element for each instance of this option in the arguments, even for instances that did not provide a value.
**-d** or **--delete**
The option and any values will be deleted from the ``$argv_opts`` variables set by ``argparse``
(as with other options, it will also be deleted from ``$argv``).
**-v** or **--validate** *COMMAND* *OPTION...*
This option must be the last one, and requires one of ``-o``, ``-r``, or ``-m``. All the remaining arguments are interpreted a fish script to run to validate the value of the argument, see ``argparse`` documentation for more details. Note that the interpretation of *COMMAND* *OPTION...* is similar to ``eval``, so you may need to quote or escape special characters *twice* if you want them to be interpreted literally when the validate script is run.
**-h** or **--help**
Displays help about using this command.
@@ -59,18 +66,25 @@ Same as above but with a second flag that requires a value:
::
set -l options (fish_opt -s h -l help)
set options $options (fish_opt -s m -l max --required-val)
set options $options (fish_opt -s m -l max -r)
argparse $options -- $argv
Same as above but the value of the second flag cannot be the empty string:
Same as above but with a third flag that can be given multiple times saving the value of each instance seen and only the long flag name (``--token``) can be used:
::
set -l options (fish_opt -s h -l help)
set options $options (fish_opt -s m -l max -rv test \$_flag_valu != "''")
argparse $options -- $argv
Same as above but with a third flag that can be given multiple times saving the value of each instance seen and only a long flag name (``--token``) is defined:
::
set -l options (fish_opt --short=h --long=help)
set options $options (fish_opt --short=m --long=max --required-val)
set options $options (fish_opt --short=t --long=token --multiple-vals --long-only)
set options $options (fish_opt --short=m --long=max --required-val --validate test \$_flag_valu != "''")
set options $options (fish_opt --long=token --multiple-vals)
argparse $options -- $argv

View File

@@ -48,7 +48,7 @@ This refuses to store any immediate "vault", "mysql" or "ls" calls. Commands sta
function fish_should_add_to_history
# I don't want `git pull`s in my history when I'm in a specific repository
if string match -qr '^git pull'
if string match -qr '^git pull' -- "$argv"
and string match -qr "^/home/me/my-secret-project/" -- (pwd -P)
return 1
end

View File

@@ -21,7 +21,7 @@ A function is a list of commands that will be executed when the name of the func
The following options are available:
**-a** *NAMES* or **--argument-names** *NAMES*
Has to be the last option. Assigns the value of successive command-line arguments to the names given in *NAMES* (separated by space). These are the same arguments given in :envvar:`argv`, and are still available there. See also :ref:`Argument Handling <variables-argv>`.
Assigns the value of successive command-line arguments to the names given in *NAMES* (separated by spaces). These are the same arguments given in :envvar:`argv`, and are still available there (unless ``--inherit-variable argv`` was used or one of the given *NAMES* is ``argv``). See also :ref:`Argument Handling <variables-argv>`.
**-d** *DESCRIPTION* or **--description** *DESCRIPTION*
A description of what the function does, suitable as a completion description.
@@ -40,7 +40,7 @@ The following options are available:
Run this function when the variable *VARIABLE_NAME* changes value. Note that :program:`fish` makes no guarantees on any particular timing or even that the function will be run for every single ``set``. Rather it will be run when the variable has been set at least once, possibly skipping some values or being run when the variable has been set to the same value (except for universal variables set in other shells - only changes in the value will be picked up for those).
**-j** *PID* or **--on-job-exit** *PID*
Run this function when the job containing a child process with the given process identifier *PID* exits. Instead of a PID, the string 'caller' can be specified. This is only allowed when in a command substitution, and will result in the handler being triggered by the exit of the job which created this command substitution.
Run this function when the job containing a child process with the given process ID *PID* exits. Instead of a PID, the string 'caller' can be specified. This is only allowed when in a command substitution, and will result in the handler being triggered by the exit of the job which created this command substitution.
This will not trigger for :doc:`disowned <disown>` jobs.
**-p** *PID* or **--on-process-exit** *PID*

View File

@@ -13,7 +13,7 @@ Synopsis
path extension GENERAL_OPTIONS [PATH ...]
path filter GENERAL_OPTIONS [-v | --invert]
[-d] [-f] [-l] [-r] [-w] [-x]
[(-t | --type) TYPE] [(-p | --perm) PERMISSION] [PATH ...]
[(-t | --type) TYPE] [(-p | --perm) PERMISSION] [--all] [PATH ...]
path is GENERAL_OPTIONS [(-v | --invert)] [(-t | --type) TYPE]
[-d] [-f] [-l] [-r] [-w] [-x]
[(-p | --perm) PERMISSION] [PATH ...]
@@ -22,7 +22,7 @@ Synopsis
path resolve GENERAL_OPTIONS [PATH ...]
path change-extension GENERAL_OPTIONS EXTENSION [PATH ...]
path sort GENERAL_OPTIONS [-r | --reverse]
[-u | --unique] [--key=basename|dirname|path] [PATH ...]
[-u | --unique] [--key=(basename | dirname | path)] [PATH ...]
GENERAL_OPTIONS
[-z | --null-in] [-Z | --null-out] [-q | --quiet]
@@ -148,7 +148,7 @@ Examples
> echo $path$extension
# reconstructs the original path again.
./foo.mp4
.. _cmd-path-filter:
"filter" subcommand
@@ -158,7 +158,7 @@ Examples
path filter [-z | --null-in] [-Z | --null-out] [-q | --quiet] \
[-d] [-f] [-l] [-r] [-w] [-x] \
[-v | --invert] [(-t | --type) TYPE] [(-p | --perm) PERMISSION] [PATH ...]
[-v | --invert] [(-t | --type) TYPE] [(-p | --perm) PERMISSION] [--all] [PATH ...]
``path filter`` returns all of the given paths that match the given checks. In all cases, the paths need to exist, nonexistent paths are always filtered.
@@ -180,6 +180,10 @@ When a path starts with ``-``, ``path filter`` will prepend ``./`` to avoid it b
It returns 0 if at least one path passed the filter.
With ``--all``, return status 0 (true) if all paths pass the filter, and status 1 (false) if any path fails. This is equivalent to ``not path filter -v``. It produces no output, only a status.
When ``--all`` combined with ``--invert``, it returns status 0 (true) if all paths fail the filter and status 1 (false) if any path passes.
``path is`` is shorthand for ``path filter -q``, i.e. just checking without producing output, see :ref:`The is subcommand <cmd-path-is>`.
Examples
@@ -211,6 +215,9 @@ Examples
>_ path filter -fx $PATH/*
# Prints all possible commands - the first entry of each name is what fish would execute!
>_ path filter --all /usr/bin /usr/argagagji
# This returns 1 (false) because not all paths pass the filter.
.. _cmd-path-is:
"is" subcommand

View File

@@ -55,3 +55,9 @@ Examples
>_ prompt_pwd --full-length-dirs=2 --dir-length=1
/t/b/s/with/mustard
>_ echo (prompt_pwd | string split /)[-1]
mustard
>_ echo (string join / (prompt_pwd | string split /)[-3..-1])
s/with/mustard

View File

@@ -83,8 +83,12 @@ The following options control how much is read and how it is stored:
**-n** or **--nchars** *NCHARS*
Makes ``read`` return after reading *NCHARS* characters or the end of the line, whichever comes first.
**-t** -or **--tokenize**
Causes read to split the input into variables by the shell's tokenization rules. This means it will honor quotes and escaping. This option is of course incompatible with other options to control splitting like **--delimiter** and does not honor :envvar:`IFS` (like fish's tokenizer). It saves the tokens in the manner they'd be passed to commands on the commandline, so e.g. ``a\ b`` is stored as ``a b``. Note that currently it leaves command substitutions intact along with the parentheses.
**-t**, **--tokenize** or **--tokenize-raw**
Causes read to split the input into variables by the shell's tokenization rules.
This means it will honor quotes and escaping.
This option is of course incompatible with other options to control splitting like **--delimiter** and does not honor :envvar:`IFS` (like fish's tokenizer).
The **-t** -or **--tokenize** variants perform quote removal, so e.g. ``a\ b`` is stored as ``a b``.
However variables and command substitutions are not expanded.
**-a** or **--list**
Stores the result as a list in a single variable. This option is also available as **--array** for backwards compatibility.

View File

@@ -30,9 +30,11 @@ Synopsis
status job-control CONTROL_TYPE
status features
status test-feature FEATURE
status buildinfo
status build-info
status get-file FILE
status list-files [PATH]
status terminal
status test-terminal-feature FEATURE
Description
-----------
@@ -99,27 +101,49 @@ The following operations (subcommands) are available:
Sets the job control type to *CONTROL_TYPE*, which can be **none**, **full**, or **interactive**.
**features**
Lists all available feature flags.
Lists all available :ref:`feature flags <featureflags>`.
**test-feature** *FEATURE*
Returns 0 when FEATURE is enabled, 1 if it is disabled, and 2 if it is not recognized.
**buildinfo**
**build-info**
This prints information on how fish was build - which architecture, which build system or profile was used, etc.
This is mainly useful for debugging.
.. _status-get-file:
**get-file** *FILE*
NOTE: this subcommand is mainly intended for fish's internal use; let us know if you want to use it elsewhere.
This prints a file embedded in the fish binary at compile time. This includes the default set of functions and completions,
as well as the man pages and themes. Which files are included depends on build settings.
Returns 0 if the file was included, 1 otherwise.
**list-files** *FILE*
NOTE: this subcommand is mainly intended for fish's internal use; let us know if you want to use it elsewhere.
This lists the files embedded in the fish binary at compile time. Only files where the path starts with the optional *FILE* argument are shown.
Returns 0 if something was printed, 1 otherwise.
.. _status-terminal:
**terminal**
Prints the name and version of the terminal fish is running inside (for example as reported via :ref:`XTVERSION <term-compat-xtversion>`).
This is not available during early startup but only starting from when the first interactive prompt is shown, possibly via builtin :doc:`read <read>`,
so before the first ``fish_prompt`` or ``fish_read`` :ref:`event <event>`.
.. _status-test-terminal-features:
**test-terminal-feature** *FEATURE*
Returns 0 when the terminal was :ref:`detected <term-compat-xtgettcap>` to support the given feature.
Like :ref:`status terminal <status-terminal>`, this only works once the first interactive prompt is shown.
Currently the only available *FEATURE* is :ref:`scroll-content-up <term-compat-indn>`.
An error will be printed when passed an unrecognized feature.
Notes
-----
For backwards compatibility most subcommands can also be specified as a long or short option. For example, rather than ``status is-login`` you can type ``status --is-login``. The flag forms are deprecated and may be removed in a future release (but not before fish 4.0).
For backwards compatibility most subcommands can also be specified as a long or short option. For example, rather than ``status is-login`` you can type ``status --is-login``. The flag forms are deprecated and may be removed in a future release.
You can only specify one subcommand per invocation even if you use the flag form of the subcommand.

View File

@@ -8,7 +8,7 @@ Synopsis
.. synopsis::
string pad [-r | --right] [(-c | --char) CHAR] [(-w | --width) INTEGER]
string pad [-r | --right] [-C | --center] [(-c | --char) CHAR] [(-w | --width) INTEGER]
[STRING ...]
.. END SYNOPSIS
@@ -22,6 +22,8 @@ Description
The escape sequences reflect what fish knows about, and how it computes its output. Your terminal might support more escapes, or not support escape sequences that fish knows about.
If **-C** or **--center** is given, add the padding to before and after the string. If it is impossible to perfectly center the result (because the required amount of padding is an odd number), extra padding will be added to the left, unless **--right** is also given.
If **-r** or **--right** is given, add the padding after a string.
If **-c** or **--char** is given, pad with *CHAR* instead of whitespace.

View File

@@ -8,9 +8,9 @@ Synopsis
.. synopsis::
string split [(-f | --fields) FIELDS] [(-m | --max) MAX] [-n | --no-empty]
string split [(-f | --fields) FIELDS [-a | --allow-empty]] [(-m | --max) MAX] [-n | --no-empty]
[-q | --quiet] [-r | --right] SEP [STRING ...]
string split0 [(-f | --fields) FIELDS] [(-m | --max) MAX] [-n | --no-empty]
string split0 [(-f | --fields) FIELDS [-a | --allow-empty]] [(-m | --max) MAX] [-n | --no-empty]
[-q | --quiet] [-r | --right] [STRING ...]
.. END SYNOPSIS
@@ -20,7 +20,7 @@ Description
.. BEGIN DESCRIPTION
``string split`` splits each *STRING* on the separator *SEP*, which can be an empty string. If **-m** or **--max** is specified, at most MAX splits are done on each *STRING*. If **-r** or **--right** is given, splitting is performed right-to-left. This is useful in combination with **-m** or **--max**. With **-n** or **--no-empty**, empty results are excluded from consideration (e.g. ``hello\n\nworld`` would expand to two strings and not three). Exit status: 0 if at least one split was performed, or 1 otherwise.
``string split`` splits each *STRING* on the separator *SEP*, which can be an empty string. If **-m** or **--max** is specified, at most MAX splits are done on each *STRING*. If **-r** or **--right** is given, splitting is performed right-to-left. This is only useful in combination with **-m** or **--max**. With **-n** or **--no-empty**, empty results are excluded from consideration (e.g. ``hello\n\nworld`` would expand to two strings and not three). Exit status: 0 if at least one split was performed, or 1 otherwise.
Use **-f** or **--fields** to print out specific fields. FIELDS is a comma-separated string of field numbers and/or spans. Each field is one-indexed, and will be printed on separate lines. If a given field does not exist, then the command exits with status 1 and does not print anything, unless **--allow-empty** is used.

View File

@@ -18,7 +18,7 @@ Synopsis
[-g | --groups-only] [-r | --regex] [-n | --index]
[-q | --quiet] [-v | --invert]
PATTERN [STRING ...]
string pad [-r | --right] [(-c | --char) CHAR] [(-w | --width) INTEGER]
string pad [-r | --right] [-C | --center] [(-c | --char) CHAR] [(-w | --width) INTEGER]
[STRING ...]
string repeat [(-n | --count) COUNT] [(-m | --max) MAX] [-N | --no-newline]
[-q | --quiet] [STRING ...]

View File

@@ -82,7 +82,7 @@ As a more comprehensive example, here's a commented excerpt of the completions f
#
# The `-n`/`--condition` option takes script as a string, which it executes.
# If it returns true, the completion is offered.
# Here the condition is the `__fish_seen_subcommands_from` helper function.
# Here the condition is the `__fish_seen_subcommand_from` helper function.
# It returns true if any of the given commands is used on the commandline,
# as determined by a simple heuristic.
# For more complex uses, you can write your own function.

View File

@@ -10,9 +10,19 @@ import glob
import os.path
import subprocess
import sys
from sphinx.highlighting import lexers
from sphinx.errors import SphinxWarning
from docutils import nodes
try:
import sphinx_markdown_builder
extensions = [
"sphinx_markdown_builder",
]
except ImportError:
pass
# -- Helper functions --------------------------------------------------------
@@ -35,11 +45,14 @@ def issue_role(name, rawtext, text, lineno, inliner, options=None, content=None)
return [link], []
def remove_fish_indent_lexer(app):
if app.builder.name in ("man", "markdown"):
del lexers["fish-docs-samples"]
# -- Load our extensions -------------------------------------------------
def setup(app):
# Our own pygments lexers
from sphinx.highlighting import lexers
this_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, this_dir)
from fish_indent_lexer import FishIndentLexer
@@ -52,6 +65,8 @@ def setup(app):
app.add_config_value("issue_url", default=None, rebuild="html")
app.add_role("issue", issue_role)
app.connect("builder-inited", remove_fish_indent_lexer)
# The default language to assume
highlight_language = "fish-docs-samples"
@@ -72,7 +87,7 @@ elif "FISH_BUILD_VERSION" in os.environ:
ret = os.environ["FISH_BUILD_VERSION"]
else:
ret = subprocess.check_output(
("fish_indent", "--version"), stderr=subprocess.STDOUT
("../build_tools/git_version_gen.sh", "--stdout"), stderr=subprocess.STDOUT
).decode("utf-8")
# The full version, including alpha/beta/rc tags

View File

@@ -332,7 +332,8 @@ Some bindings are common across Emacs and vi mode, because they aren't text edit
- :kbd:`ctrl-u` removes contents from the beginning of line to the cursor (moving it to the :ref:`killring <killring>`).
- :kbd:`ctrl-l` clears and repaints the screen.
- :kbd:`ctrl-l` pushes any text above the prompt to the terminal's scrollback,
then clears and repaints the screen.
- :kbd:`ctrl-w` removes the previous path component (everything up to the previous "/", ":" or "@") (moving it to the :ref:`killring`).
@@ -362,7 +363,7 @@ Some bindings are common across Emacs and vi mode, because they aren't text edit
- :kbd:`alt-v` Same as :kbd:`alt-e`.
- :kbd:`alt-s` Prepends ``sudo`` to the current commandline. If the commandline is empty, prepend ``sudo`` to the last commandline.
- :kbd:`alt-s` Prepends ``sudo`` to the current commandline. If the commandline is empty, prepend ``sudo`` to the last commandline. If ``sudo`` is not installed, various similar commands are tried: ``doas``, ``please``, and ``run0``.
- :kbd:`ctrl-space` Inserts a space without expanding an :ref:`abbreviation <abbreviations>`. For vi mode, this only applies to insert-mode.
@@ -507,7 +508,13 @@ Command mode is also known as normal mode.
- :kbd:`backspace` moves the cursor left.
- :kbd:`g` / :kbd:`G` moves the cursor to the beginning/end of the commandline, respectively.
- :kbd:`g,g` / :kbd:`G` moves the cursor to the beginning/end of the commandline, respectively.
- :kbd:`~` toggles the case (upper/lower) of the character and moves to the next character.
- :kbd:`g,u` lowercases to the end of the word.
- :kbd:`g,U` uppercases to the end of the word.
- :kbd:`:,q` exits fish.
@@ -551,6 +558,10 @@ Visual mode
- :kbd:`~` toggles the case (upper/lower) on the selection, and enters :ref:`command mode <vi-mode-command>`.
- :kbd:`g,u` lowercases the selection, and enters :ref:`command mode <vi-mode-command>`.
- :kbd:`g,U` uppercases the selection, and enters :ref:`command mode <vi-mode-command>`.
- :kbd:`",*,y` copies the selection to the clipboard, and enters :ref:`command mode <vi-mode-command>`.
.. _custom-binds:

View File

@@ -181,7 +181,7 @@ The destination of a stream can be changed using something called *redirection*.
- An ampersand (``&``) followed by the number of another file descriptor like ``&2`` for standard error. The output will be written to the destination descriptor.
- An ampersand followed by a minus sign (``&-``). The file descriptor will be closed. Note: This may cause the program to fail because its writes will be unsuccessful.
As a convenience, the redirection ``&>`` can be used to direct both stdout and stderr to the same destination. For example, ``echo hello &> all_output.txt`` redirects both stdout and stderr to the file ``all_output.txt``. This is equivalent to ``echo hello > all_output.txt 2>&1``.
As a convenience, the redirection ``&>`` can be used to direct both stdout and stderr to the same destination. For example, ``echo hello &> all_output.txt`` redirects both stdout and stderr to the file ``all_output.txt``. This is equivalent to ``echo hello > all_output.txt 2>&1``. You can also use ``&>>`` to append both stdout and stderr to the same destination.
Any arbitrary file descriptor can be used in a redirection by prefixing the redirection with the FD number.
@@ -1519,7 +1519,7 @@ For more information on argparse, like how to handle option arguments, see :doc:
PATH variables
^^^^^^^^^^^^^^
Path variables are a special kind of variable used to support colon-delimited path lists including :envvar:`PATH`, :envvar:`CDPATH`, :envvar:`MANPATH`, :envvar:`PYTHONPATH`, etc. All variables that end in "PATH" (case-sensitive) become PATH variables by default.
Path variables are a special kind of variable used to support colon-delimited path lists including :envvar:`PATH`, :envvar:`CDPATH`, :envvar:`MANPATH`, :envvar:`PYTHONPATH`, :envvar:`LANGUAGE` (for :doc:`localization <cmds/_>`) etc. All variables that end in "PATH" (case-sensitive) become PATH variables by default.
PATH variables act as normal lists, except they are implicitly joined and split on colons.
@@ -1572,11 +1572,13 @@ You can change the settings of fish by changing the values of certain variables.
.. envvar:: fish_term24bit
If this is set to 0, fish will not output 24-bit RGB true-color sequences but the nearest color on the 256 color palette (or the 16 color palette, if :envvar:`fish_term256` is 0).
See also :doc:`set_color <cmds/set_color>`.
The default is 1 but for historical reasons, fish defaults to behaving as if it was 0 on some terminals that are known to not support true-color sequences.
.. envvar:: fish_term256
If this is set to 0 and :envvar:`fish_term24bit` is 0, translate RGB colors down to the 16 color palette.
Also, if this is set to 0, :doc:`set_color <cmds/set_color>`/` commands such as ``set_color ff0000 red`` will prefer the named color.
Also, if this is set to 0, :doc:`set_color <cmds/set_color>` commands such as ``set_color ff0000 red`` will prefer the named color.
.. envvar:: fish_ambiguous_width
@@ -1669,6 +1671,10 @@ Fish also provides additional information through the values of certain environm
a list of arguments to the shell or function. ``argv`` is only defined when inside a function call, or if fish was invoked with a list of arguments, like ``fish myscript.fish foo bar``. This variable can be changed.
.. envvar:: argv_opts
:doc:`argparse <cmds/argparse>` sets this to the list of successfully parsed options, including option-arguments. This variable can be changed.
.. envvar:: CMD_DURATION
the runtime of the last command in milliseconds.
@@ -2026,7 +2032,9 @@ You can see the current list of features via ``status features``::
ampersand-nobg-in-token on 3.4 & only backgrounds if followed by a separating character
remove-percent-self off 4.0 %self is no longer expanded (use $fish_pid)
test-require-arg off 4.0 builtin test requires an argument
mark-prompt on 4.0 write OSC 133 prompt markers to the terminal
ignore-terminfo on 4.1 do not look up $TERM in terminfo database
query-term on 4.1 query the TTY to enable extra functionality
Here is what they mean:
@@ -2036,7 +2044,11 @@ Here is what they mean:
- ``ampersand-nobg-in-token`` was introduced in fish 3.4 (and made the default in 3.5). It makes it so a ``&`` i no longer interpreted as the backgrounding operator in the middle of a token, so dealing with URLs becomes easier. Either put spaces or a semicolon after the ``&``. This is recommended formatting anyway, and ``fish_indent`` will have done it for you already.
- ``remove-percent-self`` turns off the special ``%self`` expansion. It was introduced in 4.0. To get fish's pid, you can use the :envvar:`fish_pid` variable.
- ``test-require-arg`` removes :doc:`builtin test <cmds/test>`'s one-argument form (``test "string"``. It was introduced in 4.0. To test if a string is non-empty, use ``test -n "string"``. If disabled, any call to ``test`` that would change sends a :ref:`debug message <debugging-fish>` of category "deprecated-test", so starting fish with ``fish --debug=deprecated-test`` can be used to find offending calls.
- ``mark-prompt`` makes fish report to the terminal the beginning and and of both shell prompts and command output.
- ``ignore-terminfo`` disables lookup of $TERM in the terminfo database. Use ``no-ignore-terminfo`` to turn it back on.
- ``query-term`` allows fish to query the terminal by writing escape sequences and reading the terminal's response.
This enables features such as :ref:`scrolling <term-compat-cursor-position-report>`.
If you use an incompatible terminal, you can -- for the time being -- work around it by running (once) ``set -Ua fish_features no-query-term``.
These changes are introduced off by default. They can be enabled on a per session basis::

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -5,7 +5,7 @@ fish writes various control sequences to the terminal.
Some must be implemented to enable basic functionality,
while others enable optional features and may be ignored by the terminal.
The terminal must be able to parse Control Sequence Introducer (CSI) commands, Operating System Commands (OSC) and optionally Device Control Strings (DCS).
The terminal must be able to parse Control Sequence Introducer (CSI) commands, Operating System Commands (OSC) and :ref:`optionally <term-compat-dcs-gnu-screen>` Device Control Strings (DCS).
These are defined by ECMA-48.
If a valid CSI, OSC or DCS sequence does not represent a command implemented by the terminal, the terminal must ignore it.
@@ -68,10 +68,15 @@ Required Commands
- clear
- Clear the screen.
- VT100
* - ``\e[0c``
* - .. _term-compat-primary-da:
``\e[0c``
-
- Request primary device attribute.
- Request Primary Device Attribute.
The terminal must respond with a CSI command that starts with the ``?`` parameter byte (so a sequence starting with ``\e[?``) and has ``c`` as final byte.
Failure to implement this will cause a brief pause at startup followed by a warning.
For the time being, both can be turned off by turning off the ``query-terminal`` :ref:`feature flag <featureflags>`.
- VT100
* - n/a
- am
@@ -189,17 +194,30 @@ Optional Commands
- Su
- Reset underline color to the default (follow the foreground color).
- kitty
* - ``\e[ Ps S``
* - .. _term-compat-indn:
``\e[ Ps S``
- indn
- Scroll forward Ps lines.
-
- Scroll up the content (not the viewport) Ps lines (called ``SCROLL UP`` / ``SU`` by ECMA-48 and "scroll forward" by terminfo).
When fish detects support for this feature, :ref:`status test-terminal-features scroll-content-up <status-test-terminal-features>` will return 0,
which enables the :kbd:`ctrl-l` binding to use the :ref:`scrollback-push <special-input-functions-scrollback-push>` special input function.
- ECMA-48
* - ``\e[= Ps u``, ``\e[? Ps u``
- n/a
- Enable the kitty keyboard protocol.
- kitty
* - ``\e[6n``
* - .. _term-compat-cursor-position-report:
``\e[6n``
- n/a
- Request cursor position report.
The response must be of the form ``\e[ Ps ; Ps R``
where the first parameter is the row number
and the second parameter is the column number.
Both start at 1.
This is used by the :ref:`scrollback-push <special-input-functions-scrollback-push>` special input function,
and inside terminals that implement the OSC 133 :ref:`click_events <term-compat-osc-133>` feature.
- VT100
* - ``\e[ \x20 q``
- Se
@@ -210,9 +228,12 @@ Optional Commands
- Ss
- Set cursor style (DECSCUSR); Ps is 2, 4 or 6 for block, underscore or line shape.
- VT520
* - ``\e[ Ps q``
* - .. _term-compat-xtversion:
``\e[ Ps q``
- n/a
- Request terminal name and version (XTVERSION).
This is only used for temporary workarounds for incompatible terminals.
- XTerm
* - ``\e[?25h``
- cvvis
@@ -261,15 +282,19 @@ Optional Commands
- Create a `hyperlink (OSC 8) <https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>`_.
This is used in fish's man pages.
- GNOME Terminal
* - ``\e]52;c; Pt \x07``
* - .. _term-compat-osc-52:
``\e]52;c; Pt \x07``
-
- Copy to clipboard (OSC 52).
- Copy to clipboard (OSC 52). Used by :doc:`fish_clipboard_copy <cmds/fish_clipboard_copy>`.
- XTerm
* - .. _click-events:
* - .. _term-compat-osc-133:
``\e]133;A; click_events=1\x07``
-
- Mark prompt start (OSC 133), with kitty's ``click_events`` extension.
The ``click_events`` extension enables mouse clicks to move the cursor or select pager items,
assuming that :ref:`cursor position reporting <term-compat-cursor-position-report>` is available.
- FinalTerm, kitty
* - ``\e]133;C; cmdline_url= Pt \x07``
-
@@ -279,8 +304,41 @@ Optional Commands
-
- Mark command end (OSC 133); Ps is the exit status.
- FinalTerm
* - ``\eP+q Pt \e\\``
* - .. _term-compat-xtgettcap:
``\eP+q Pt \e\\``
-
- Request terminfo capability (XTGETTCAP). The parameter is the capability's hex-encoded terminfo code.
Specifically, fish asks for the ``indn`` string capability. At the time of writing string capabilities are supported by kitty and foot.
- XTerm, kitty, foot
- Request terminfo capability (XTGETTCAP).
The parameter is the capability's hex-encoded terminfo code.
To advertise a capability, the response must be of the form
``\eP1+q Pt \e\\`` or ``\eP1+q Pt = Pt \e\\``.
In either variant the first parameter must be the hex-encoded terminfo code.
The second variant's second parameter is ignored.
Currently, fish only queries the :ref:`indn <term-compat-indn>` string capability.
- XTerm (but without string capabilities), kitty;
also adopted by foot, wezterm, contour, ghostty
.. _term-compat-dcs-gnu-screen:
DCS commands and GNU screen
---------------------------
Fully-correct DCS parsing is optional because fish switches to the alternate screen before printing any DCS commands.
However, since GNU screen neither allows turning on the alternate screen buffer by default,
nor treats DCS commands in a compatible way,
fish's initial prompt may be garbled by a DCS payload like ``+q696e646e``.
For the time being, fish works around this by checking for presence of the :envvar:`STY` environment variable.
If that doesn't work for some reason, you can add this to your ``~/.screenrc``:
.. code-block:: none
altscreen on
Or add this to your ``config.fish``::
function GNU-screen-workaround --on-event fish_prompt
commandline -f repaint
functions --erase GNU-screen-workaround
end

View File

@@ -1,26 +1,30 @@
FROM alpine:3.19
LABEL org.opencontainers.image.source=https://github.com/fish-shell/fish-shell
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
ENV PIP_ROOT_USER_ACTION=ignore
RUN apk add --no-cache \
cmake ninja \
bash \
cargo \
cmake \
g++ \
gettext-dev \
git \
libintl \
musl-dev \
ninja \
pcre2-dev \
py3-pexpect \
py3-pip \
python3 \
rust \
rustfmt \
sudo \
tmux
RUN pip install --break-system-packages black
RUN addgroup -g 1000 fishuser
RUN adduser \
@@ -40,4 +44,6 @@ WORKDIR /home/fishuser
COPY fish_run_tests.sh /
ENV FISH_CHECK_LINT=false
CMD /fish_run_tests.sh

View File

@@ -8,17 +8,13 @@ RUN cd /etc/yum.repos.d/ && \
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
# install powertools to get ninja-build
RUN dnf -y install dnf-plugins-core \
&& dnf config-manager --set-enabled powertools \
&& yum install --assumeyes epel-release \
&& yum install --assumeyes \
cargo \
cmake \
diffutils \
gcc-c++ \
git \
ninja-build \
python3 \
python3-pexpect \
openssl \
@@ -39,4 +35,6 @@ WORKDIR /home/fishuser
COPY fish_run_tests.sh /
ENV FISH_CHECK_LINT=false
CMD /fish_run_tests.sh

Some files were not shown because too many files have changed in this diff Show More