Compare commits

..

474 Commits
4.1.0 ... 4.2.0

Author SHA1 Message Date
Johannes Altmanninger
896f0606cd Release 4.2.0
Created by ./build_tools/release.sh 4.2.0
2025-11-10 08:19:40 +01:00
Johannes Altmanninger
2649f8d591 tests/sphinx-markdown-changelog: also disable when pushing a tag
Also, fix the command that should only filter out the current tag.
2025-11-10 08:18:53 +01:00
Johannes Altmanninger
e77102d73e Install Sphinx in macOS CI/CD
Looks like macOS packages didn't have docs??
I noticed via our Cargo warning about sphinx-build.

macOS has a variety of pythons installed:

	bash-3.2$ type -a python python3
	python is /Library/Frameworks/Python.framework/Versions/Current/bin/python
	python3 is /opt/homebrew/bin/python3
	python3 is /usr/local/bin/python3
	python3 is /Library/Frameworks/Python.framework/Versions/Current/bin/python3
	python3 is /usr/bin/python3

by default, "uv --no-managed-python" uses python3 (homebrew), but
"python" (and "pip") use the other one.  Make uv use the same as
our scripts.  Not all uv commands support "--python" today, so set
UV_PYTHON.
2025-11-10 08:18:53 +01:00
Johannes Altmanninger
ac94bc774b Update changelog 2025-11-10 07:28:35 +01:00
Johannes Altmanninger
83eca32111 release.sh: build local tarball with pinned Sphinx versions
Use the pinned versions of Sphinx + dependencies, for reproducibility
and correctness. (Specifically, this gives us proper OSC 8
hyperlinks when /bin/sphinx-build is affected by a packaging bug,
see https://github.com/orgs/sphinx-doc/discussions/14048)
2025-11-10 07:28:35 +01:00
Johannes Altmanninger
f5662d578e release-notes.sh: extract authors from trailers
Ideally we want to credit reported-by/helped-by etc. but we'd need
to ask the GitHub API for this information.

Also we should use %(trailers) if possible.
2025-11-10 07:02:08 +01:00
Daniel Rainer
80363314aa cleanup: remove obsolete comments
These comments were made obsolete in c323a2d5fe
("Continued refactoring of history", 2025-11-09)

Closes #12041
2025-11-10 07:02:08 +01:00
Daniel Rainer
6ce3bb858d cleanup: fix swapped comments
Part of #12041
2025-11-10 07:02:08 +01:00
Daniel Rainer
1c34fb064a cleanup: remove obsolete comment
This comment should have been removed in 3736636d99
("msrv: update to Rust 1.85", 2025-10-19)

Part of #12041
2025-11-10 07:02:08 +01:00
Johannes Altmanninger
366b85beb2 completions/help: fix bad description
Fixes 2cd60077e6 (help: get section titles from Sphinx, 2025-11-05).
2025-11-09 22:20:52 +01:00
Peter Ammon
82038e236d Revert "Add history file format internal docs"
This reverts commit 458fe1be2f.

This was a proposal which ought not to have been pushed.
2025-11-09 10:47:31 -08:00
Peter Ammon
f72fc7f09b Remove HistoryIdentifier
HistoryIdentifier was an over-engineered way of annotating history items
with the paths that were found to be valid, for autosuggestion hinting.
It wasn't persisted to the file. Let's just get rid of this.
2025-11-09 10:37:05 -08:00
Peter Ammon
c323a2d5fe Continued refactoring of history
Use fstat() instead of lseek() to determine history file size.
Pass around the file_id instead of recomputing it.

This saves a few syscalls; no behavior change is expected.
2025-11-09 10:03:45 -08:00
Peter Ammon
662b55ee6b Continue refactoring history
Simplify some logic and group related items together.
2025-11-09 10:03:45 -08:00
Peter Ammon
2e92255032 Factor history item offsets into an iterator
More idiomatic.
2025-11-09 10:03:45 -08:00
Peter Ammon
5182901bcc Factor history files better
Preparation for eventually cleaning this up. No user-visible changes
expected.
2025-11-09 10:03:45 -08:00
Peter Ammon
248eb2eb4a Move history.rs into its own module 2025-11-09 10:03:45 -08:00
Peter Ammon
458fe1be2f Add history file format internal docs 2025-11-09 10:03:45 -08:00
Johannes Altmanninger
8589231742 functions/__fish_print_help: work around for FreeBSD's man missing -l
This fixes "man abbr" on macOS and FreeBSD.

Upstream feature request:
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=290911

Note that this workarond requires another one, copied from ec939fb22f
(Work around BSD man calling pager when stdout is not a TTY,
2024-10-30).

Closes #12037
2025-11-09 13:10:46 +01:00
Johannes Altmanninger
59658b44a9 functions/man: reuse __fish_print_help for viewing built-in man-pages
Historically,
- our "man" wrapper prepends /usr/share/fish/man to $MANPATH
- "__fish_print_help" only operates on /usr/share/fish/man, to it
  accesses that directly

However standalone builds -- where /usr/share/fish/man may not exist
-- have complicated things; we temporarily materialize a fake man
directory.

Reuse __fish_print_help instead of duplicating this.

Part of #12037
2025-11-09 13:08:47 +01:00
Johannes Altmanninger
3f2e4b71bc functions/__fish_print_help: bravely remove fallback to mandoc/groff
I don't know if anyone has installed mandoc/groff but not man.
Since 4.1 we officially require "man" as dependency (hope that works
out; we're missing some standardization, see #12037).  Remove the
fallback with the old mandoc/nroff logic.  This makes the next commit
slightly simpler -- which is not enough reason by itself, but we want
to do this anyway at some point.
2025-11-09 13:08:47 +01:00
Johannes Altmanninger
9b42051ba7 functions/man: don't export MANPATH unnecessarily
We always try to prepend our man path to $MANPATH before calling man.
But this is not necessary when we're gonna display the output of
"status get-file man/man1/abbr.1".

Overriding man pages can cause issues like
https://github.com/fish-shell/fish-shell/issues/11472
https://gitlab.com/man-db/man-db/-/merge_requests/15

which by themselves are maybe not enough reason to avoid exporting
MANPATH, but given that embedded man pages might become the only
choice in future (see #12037), we should reduce the scope of our
custom MANPATH.
2025-11-09 13:08:47 +01:00
Johannes Altmanninger
674e93d127 functions/{__fish_print_help,man}: extract function for resolving builtin docs
These are code clones, apart from the fact that __fish_print_help
doesn't have the '{' case, because alt-h/f1 will never find that.
But adding it doesn't really hurt because it's unlikely that the user
will have a manpage for '{'. Extract a function.
2025-11-09 13:08:47 +01:00
Johannes Altmanninger
b1472827e7 Update BSD sourcehut images
Part of #12037
2025-11-09 13:08:47 +01:00
Daniel Rainer
856e649487 zh_TW.po: remove empty comments
These were added automatically when we used the `--strict` flag with
gettext commands. We stopped using this flag, but existing empty
comments are not automatically deleted, so it is done here manually.

Closes #12038
2025-11-09 13:08:47 +01:00
Johannes Altmanninger
bc71a1662d Update changelog 2025-11-09 13:08:47 +01:00
Johannes Altmanninger
33bf808084 fish_tab_title: fix tab title accidentally including window title
Also use the correct OSC number.

Note that this only works on few terminals (such as iTerm2).  Not sure
if it's worth for us to have this feature but I guess multiple users
have requested it.
2025-11-09 13:08:47 +01:00
seg6
78f71971cb fix: Python 3.5+ compatibility in webconfig.py type hints
Signed-off-by: seg6 <hi@seg6.space>

Closes #12039
Closes #12040
2025-11-09 13:04:05 +01:00
ridiculousfish
b4fc5160ba Set FISH_TEST_NO_CURSOR_POSITION_QUERY in pexpect tests
This was causing query timeouts with high parallelism in the tests.
2025-11-08 18:28:41 -08:00
SandWood Jones
9d3acbdd82 fix(abbr) --command conflicts
Fixes #11184

Closes #12021
2025-11-08 21:24:28 +01:00
Johannes Altmanninger
873ede7a77 CONTRIBUTING: modernize
The section on build_tools/style.fish was stale.  That tool does not
automagically format unstaged changes or the last commit's changes
anymore.  Relying on such tools is bad practice anyway, people should
configure their editor to fix the style violations at the source.
Replace "before committing" with neutral phrasing.

Remove the redundant mention of "cargo fmt" and "clippy".  Use of
clippy goes without saying for Rust projects.  Instead, add another
mention of "build_tools/checks" which also does clippy as well as
translation update checks (which are commonly needed).

Drop the "testing with CMake" part since it's a legacy thing and
basically never needed, especially by new faces.

Use semantic line breaks in the paragraphs starting at "When in doubt".
2025-11-08 21:24:28 +01:00
Johannes Altmanninger
d7581dbaa4 CONTRIBUTING: remove Git pre-push hook
- this is not specific to fish and only relevant to people who push
  directly to master
- we don't push directly to master as often anymore
- I don't think we used it much in practice (when we should have).
- a good portion of contributions is pushed with jj which probably
  does this differently

Let's remove it even though this is a nice piece of documentation.
I guess we could add it back to doc_internal/ if we have the capacity
to maintain it.
2025-11-08 21:21:13 +01:00
Daniel Rainer
71e4d7ba87 tempfile: utilize new tempfile crate
Closes #12029
2025-11-08 21:18:25 +01:00
Daniel Rainer
46f7f47bda util: add crate for creating tempfiles
ja: the motivation for our own crate is
1. the tempfile crate is probably overkill for such a small
   piece of functionality (given that we already assume Unix)
2. we want to have full control over the few temp files we
   do create

Closes #12028
2025-11-08 21:18:25 +01:00
Johannes Altmanninger
4046df7412 Disable parallelism in macOS CI
Since the parent commit, these tests fail repeatedly in macOS GitHub
Actions CI: complete-group-order.py, nullterm.py, scrollback.py and
set_color.py. They run fine in isolation.

We'll fix the flaky system tests soon (#11815) but until then, remove
parallelism from macOS CI.
2025-11-08 21:18:25 +01:00
Johannes Altmanninger
a772470b76 Multi-line autosuggestions
Unlike other shells, fish tries to make it easy to work with multiline
commands. Arguably, it's often better to use a full text editor but
the shell can feel more convenient.

Spreading long commands into multiple lines can improve readability,
especially when there is some semantic grouping (loops, pipelines,
command substitutions, quoted parts). Note that in Unix shell, every
quoted string can span multiple lines, like Python's triple quotes,
so the barrier to writing a multiline command is quite low.

However these commands are not autosuggested. From
1c4e5cadf2 (commitcomment-150853293)

> the reason we don't offer multi-line autosuggestion is that they
> can cause the command line to "jump" to make room for the second
> and third lines, if you're at the bottom of your terminal.

This jumping (as done by nushell for example) might be surprising,
especially since there is no limit on the height of a command.

Let's maybe avoid this jumping by rendering only however many lines
from the autosuggestion can fit on the screen without scrolling.

The truncation is hinted at by a single ellipsis ("…") after the
last suggested character, just like when a single-line autosuggestion
is truncated. (We might want to use something else in future.)

To implement this, query for the cursor position after every command,
so we know the y-position of the shell prompt within the terminal
window (whose height we already know).

Also, after we register a terminal window resize, query for the cursor
position before doing anything else (until we od #12004, only height
changes are relevant), to prevent this scenario:

	1. move prompt to bottom of terminal
	2. reduce terminal height
	3. increase terminal height
	4. type a command that triggers a multi-line autosuggestion
	5. observe that it would fail to truncate properly

As a refresher: when we fail to receive a query response, we always
wait for 2 seconds, except if the initial query had also failed,
see b907bc775a (Use a low TTY query timeout only if first query
failed, 2025-09-25).

If the terminal does not support cursor position report (which is
unlikely), show at most 1 line worth of autosuggestion.  Note that
either way, we don't skip multiline commands anymore.  This might make
the behavior worse on such terminals, which are probably not important
enough.  Alternatively, we could use no limit for such terminals,
that's probably the better fallback behavior. The only reason I didn't
do that yet is to stay a little bit closer to historical behavior.

Storing the prompt's position simplifies scrollback-push and the mouse
click handler, which no longer need to query.  Move some associated
code to the screen module.

Technically we don't need to query for cursor position if the previous
command was empty. But for now we do, trading a potential optimization
for andother simplification.

Disable this feature in pexpect tests for now, since those are still
missing some terminal emulation features.
2025-11-08 21:18:25 +01:00
Johannes Altmanninger
e1ce53fe15 screen: fix inconsistent initialization order 2025-11-08 21:18:25 +01:00
Johannes Altmanninger
77bdd4fbde reader: use unqualified enum variants in match statement
This reduces noise although the indentation is not great.
2025-11-08 21:18:25 +01:00
Johannes Altmanninger
3fdaa543ed reader: remove unused parameter 2025-11-08 21:18:25 +01:00
Johannes Altmanninger
336be1e36d termsize: better types
The u16 is implied by libc::winsize.
2025-11-08 21:18:25 +01:00
Johannes Altmanninger
382027663f termsize: try to simplify a little bit
Also add "safe_" prefix to functions we require to be
async-signal-safe.
2025-11-08 21:18:25 +01:00
Johannes Altmanninger
1d58b84637 mouse handling: fish's screen always starts at x=0
We already assume elsewhere that e.g. \r will return us to x=0
(fish coordinates).
2025-11-08 21:18:25 +01:00
Johannes Altmanninger
88ead18709 Move query-interrupt event-variant into query-result enum
I think the interruption event is grouped next to the check-exit one
because it used to be implemented as check exit but with a global flag
(I didn't check whether that applied to master).

Move it to the logical place.
2025-11-08 21:18:25 +01:00
Johannes Altmanninger
70058bdee2 reader: remove unused check
The readline state is never finished before the very first loop
iteration.  Move the check for rls.finished next to the one that
implements "read -n1" -- in future we might use the return value
for both.
2025-11-08 21:18:25 +01:00
Johannes Altmanninger
d1c85966d9 __fish_echo: fully overwrite lines, take 2
For the same reason as before (upcoming multi-line autosuggestions),
reapply 1fdf37cc4a (__fish_echo: fully overwrite lines, 2025-09-17)
after it was reverted in b7fabb11ac (Make command run by __fish_echo
output to TTY for color detection, 2025-10-05)
 (Make command run by __fish_echo output
to TTY for color detection, 2025-10-05).  Use clear-to-end-of-screen
to allow making "ls" output directly to the terminal.

Alternatively, we could create a pseudo TTY (e.g. call something like
"unbuffer ls --color=auto").
2025-11-08 21:18:25 +01:00
Daniel Rainer
0679d98950 printf: do not check for I flag
This flag is not supported by our sprintf, so remove it here, to avoid
suggesting that it might be supported.

Closes #11874
2025-11-08 21:18:25 +01:00
Johannes Altmanninger
a8b7d89ba5 doc terminal-compatibility: document Unicode characters used in output
Not sure if this will be useful but the fact that we use very
few Unicode characters, suggests that we are insecure about
this. Having some kind of central and explicit listing might help
future decision-making.  Obviously, completions and translations use
more characters, but those are not as central.
2025-11-08 21:18:25 +01:00
Fabian Boehm
828773b391 __fish_print_help: Also add "-l"
mandoc refuses to open files without.

See #12037
2025-11-08 15:30:47 +01:00
John Paul Adrian Glaubitz
35f4eb8e9a Fix build on Linux/SPARC by disabling SIGSTKFLT 2025-11-08 14:56:25 +01:00
Fabian Boehm
8c69e62a78 terminal-compatibility: Remove "Origin" column
This isn't very useful, makes the table very wide and invites discussion on who exactly invented
what.

Let's leave that to the historians.

Fixes #12031
2025-11-08 14:54:57 +01:00
Fabian Boehm
8e4fa9aafb functions/man: Pass "-l" to get man to open a file
Supported by mandoc, man-db and NetBSD man, and mandoc now requires
this.

Fixes #12037
2025-11-08 14:50:10 +01:00
Johannes Altmanninger
d6ed5f843e fish_tab_title to set terminal tab title independent of window title
Some modern terminals allow creating tabs in a single window;
this functionality has a lot of overlap with what a window manager
already provides, so I'm not sure if it's a good idea.  Regardless,
a lot of people still use terminal tabs (or even multiple levels of
tabs via tmux!), so let's add a fish-native way to set the tab title
independent of the window title.

Closes #2692
2025-11-06 13:02:23 +01:00
Daniel Rainer
ba7bc2be13 cleanup: remove unused variable
Closes #12023
2025-11-06 13:02:23 +01:00
ken
199475b6ca Stop disabling mouse tracking
Commit eecc223 (Recognize and disable mouse-tracking CSI events,
2021-02-06) made fish disable mouse reporting whenever we receive a
mouse event.  This was because at the time we didn't have a parser
for mouse inputs.  We do now, so let's allow users to toggle mouse
support with

	printf '\e[?1000h'
	printf '\e[?1000l'

Currently the only mouse even we support is left click (to move cursor
in commandline, select pager items).

Part of #4918
See #12026

[ja: tweak patch and commit message]
2025-11-06 13:02:23 +01:00
Johannes Altmanninger
60b50afefd Fix missing ICANON regression after read in noninteractive shell
Since commit 7e3fac561d (Query terminal only just before reading
from it, 2025-09-25),

	fish -c 'read; cat'

fails to turn ICANON back on for cat.

Even before that commit, I don't know how this ever worked.  Before or
after, we'd call tcsetattr() only to set our shell modes, not the
ones for external commands (term_donate)

I also don't think there's a good reason why we set modes twice
(between term_donate () and old_modes), but I'll make a minimal (=
safer) change for now until I understand this.

The only change from 7e3fac561d was that instead of doing things in
this order:

	terminal_init()
		set_shell_modes
	read
		reader_push()
		reader_readline()
			save & restore old_modes
	cat

we now do

	read
		reader_push()
			terminal_init()
				set_shell_modes()
		reader_readline()
			save & restore old_modes
	cat

So we call set_shell_modes() just before saving modes,
which obviously makes us save the wrong thing.
Again, not sure how that wasn't a problem before.

Fix it by saving modes before calling terminal_init().

Fixes #12024
2025-11-06 13:02:23 +01:00
Johannes Altmanninger
7aa64dc423 help: use "introduction" instead of "index" in user-visible section title
The new completions are like

	help index#default-shell

Add a hack to use the "introduction" here.

Not sure if this is worth it but I guess we should be okay because
we at least have test for completions.

In future, we should find a better way to declare that "introduction"
is our landing page.
2025-11-06 13:01:08 +01:00
Johannes Altmanninger
7a59540517 docs: use :doc: role when referencing entire pages
No need to define "cmd-foo" anchors; use :doc:`foo <cmds/foo>`
instead. If we want "cmd-foo" but it should be tested.

See also 38b24c2325 (docs: Use :doc: role when linking to commands,
2022-09-23).
2025-11-06 12:58:59 +01:00
Johannes Altmanninger
2cd60077e6 help: get section titles from Sphinx
functions/help and completions/help duplicate a lot of information
from doc_src. Get this information from Sphinx.

Drop short section titles such as "help globbing" in favor of the
full HTML anchor:

	help language#wildcards-globbing 

I think the verbosity is no big deal because we have tab completion,
we're trading in conciseness for consistency and better searchability.

In future, we can add back shorter invocations like "help globbing"
(especially given that completion descriptions often already repeated
the anchor path), but it should be checked by CI.

Also
- Remove some unused Sphinx anchors
- Remove an obsoleted script.
- Test that completions are in sync with Sphinx sources.
  (note that an alternative would be to check
  in the generated help_sections.rs file, see
  https://internals.rust-lang.org/t/how-fail-on-cargo-warning-warnings-from-build-rs/23590/5)

Here's a list of deleted msgids. Some of them were unused, for others
there was a better message (+ translation).

	$variable $variable 变量
	(command) command substitution (命令) 命令替换
	< and > redirections < 和 > 重定向
	Autoloading functions 自动加载函数
	Background jobs 后台作业
	Builtin commands 内建命令
	Combining different expansions 合并不同的展开
	Command substitution (SUBCOMMAND) 命令替换 (子命令)
	Defining aliases 定义别名
	Escaping characters 转义字符
	Help on how to reuse previously entered commands 关于如何重复使用先前输入的命令的帮助
	How lists combine 列表如何组合
	Job control 作业控制
	Local, global and universal scope 局域、全局和通用作用域
	Other features 其他功能
	Programmable prompt 可编程提示符
	Shell variable and function names Shell 变量和函数名
	Some common words 一些常用词
	The status variable 状况变量
	Variable scope for functions 函数的变量作用域
	Vi mode commands Vi 模式命令
	What set -x does `set -x` 做什么
	Writing your own completions 自己写补全
	ifs and elses if 和 else
	var[x..y] slices var[x..y] 切片
	{a,b} brace expansion {a,b} 大括号展开
	~ expansion ~ 展开


Closes #11796
2025-11-06 12:58:59 +01:00
Johannes Altmanninger
2c68a6704f Don't escape '#' in some cases where it's not necessary
This is useful for the next commit where tab completion produces
tokens like "foo#bar". Some cases are missing though, because we
still need to tell this function what's the previous character.
I guess next commit could also use a different sigil.
2025-11-06 12:58:59 +01:00
Johannes Altmanninger
cc95cef165 crates/build-man-pages: try to improve style 2025-11-06 12:08:10 +01:00
Johannes Altmanninger
cd76c2cb26 help/man: support \{ 2025-11-06 12:06:05 +01:00
Johannes Altmanninger
c586210306 help: more style changes
Seems better to propagate return code?
Otherwise only style changes.
2025-11-06 12:06:05 +01:00
Johannes Altmanninger
ec3cd4f4cb help: style changes and fixes 2025-11-06 12:06:05 +01:00
Johannes Altmanninger
eef5a7ff2b Use path to sphinx-build from CMake
Commit 0709e4be8b (Use standalone code paths by default, 2025-10-26)
made CMake builds enable the embed-data feature.  This means that
crates/build-man-pages/build.rs will run "sphinx-build" to create
man pages for embedding, now also for CMake builds.

Let's use the sphinx-build found by CMake at configuration-time.

This makes VARS_FOR_CARGO depend on cmake/Docs.cmake, so adjust the
order accordingly.
2025-11-05 17:34:21 +01:00
Johannes Altmanninger
988985727f Disable failing fish_config test for now
This has probably been failing intermittently in CI ever since it
was introduced. We have a reproducible test now, so let's disable
this test in CI for now to reduce noise.

See #12018
2025-11-04 14:48:40 +01:00
Shigure Kurosaki
dad58ac20a fix(edit_command_buffer): ignore cat alias
Signed-off-by: Shigure Kurosaki <shigure@hqsy.net>

Closes #12007
2025-11-04 14:13:22 +01:00
Johannes Altmanninger
3f9d8db5b7 builtin ulimit: extract function 2025-11-04 14:13:22 +01:00
Johannes Altmanninger
0b6afbd17b Prefer commandline case over same-length autosuggestion
If I run

	history append 'echo -X'

and type "echo -x", it renders as "echo -X".

This is not wrong but can be pretty confusing, (since the
autosuggestion is the same length as the command line).

Let's favor the case typed by the user in this specific case I guess.
Should add a full solution later.

Part of #11452
2025-11-04 14:13:22 +01:00
Daniel Rainer
e9936bc5ed cleanup: use let-else to reduce indentation
Closes #12019
2025-11-04 11:51:25 +01:00
Daniel Rainer
068a1ab95c refactor: make wwrite_to_fd more idiomatic
There is no need to use `'\0'`, we can use `0u8` instead.

`maxaccum` does not clearly indicate what it represents; use
`accum_capacity` instead.

To get the size of `accum`, we can use `accum.len()`, which is easier to
understand.

Use `copy_from_slice` instead of `std::ptr::copy`. This avoids the
unsafe block, at the cost of a runtime length check. If we don't want
this change for performance reasons, we might consider using
`std::ptr::copy_nonoverlapping` here (which is done by `copy_from_slice`
internally).

Part of #12008
2025-11-04 11:51:09 +01:00
Daniel Rainer
74d9a3537c gettext: fall back to all language variants
If a language is specified using only the language code, without a
region identifier, assume that the user prefers translations from any
variant of the language over the next fallback option. For example, when
a user sets `LANGUAGE=zh:pt`, assume that the user prefers both `zh_CN` and
`zh_TW` over the next fallback option. The precedence of the different
variants of a language will be arbitrary. In this example, with the
current set of relevant available catalogs (`pt_BR`, `zh_CN`, `zh_TW`),
the effective precedence will be either `zh_CN:zh_TW:pt_BR` or
`zh_TW:zh_CN:pt_BR`.

Users who want more control over the order can
specify variants to get the results they want.
For example:
- `LANGUAGE=zh_TW:zh:pt` will result in `zh_TW:zh_CN:pt_BR`.
- `LANGUAGE=zh_CN:pt:zh` will result  in `zh_CN:pt_BR:zh_TW`.
- `LANGUAGE=zh_CN:pt` will result  in `zh_CN:pt_BR`.
English is always used as the last fallback.

This approach (like the previous approach) differs from GNU gettext
semantics, which map region-less language codes to on specific "default"
variant of the language, without specifying how this default is chosen.
We want to avoid making such choices and believe it is better to utilize
translations from all language variants we have available when users do
not explicitly specify their preferred variant. This way, users have an
easier time discovering localization availability, and can be more
explicit in their preferences if they don't like the defaults.
If there are conflicts with gettext semantics, users can also set locale
variables without exporting them, so fish uses different values than its
child processes.

Closes #12011
2025-11-03 17:13:42 +00:00
Johannes Altmanninger
143a4aca0f Readme: fix RST syntax 2025-11-03 09:17:00 +01:00
Johannes Altmanninger
20e66ad990 Add workaround for terminals confused by MSYS2 paths
Some terminals handle invalid OSC 7 working directories by falling
back to the home directory, which is worse than if they didn't support
OSC 7.  Try to work arond the common case (when $MSYSTEM is set).

	local wezterm = require 'wezterm'
	local config = wezterm.config_builder()
	config.default_prog = {
	     'C:\\msys64\\msys2_shell.cmd',
	    '-ucrt64',
	    '-defterm',
	    '-here',
	    '-no-start',
	    '-shell', 'fish'
	}
	config.default_cwd = 'C:\\msys64\\home\\xxx'
	return config

Upstream issues:
- https://github.com/wezterm/wezterm/discussions/7330
- https://invent.kde.org/utilities/konsole/-/merge_requests/1136

Closes #11981
2025-11-03 09:17:00 +01:00
Johannes Altmanninger
ceec382161 Disable repaint on WINCH again for VTE/Konsole/WezTerm
This was accidentally turned on because c31e769f7d (Use XTVERSION for
terminal-specific workarounds, 2025-09-21) used the wrong argument
order.  Fix that.
2025-11-03 09:17:00 +01:00
Johannes Altmanninger
782916930a Work around failing asan tests
I could reproduce both
tests/checks/tmux-empty-prompt.fish
tests/pexpects/autosuggest.py

failing in ASan CI. I didn't bisect it, since I don't think there is
a problematic code change. Bump the timeout a bit.

Closes #12016
2025-11-03 09:17:00 +01:00
Johannes Altmanninger
a767739c06 doc terminal-compatibility: fix inconsistent parameter wildcard notation 2025-11-03 09:17:00 +01:00
Peter Ammon
cf3c9d75d7 Don't run the man check test if FISH_BUILD_DOCS is 0 2025-11-01 13:12:58 -07:00
Peter Ammon
1846d7fd7e Merge branch 'cleanup-thread-pool'
This merges changes that make thread pools instanced. We no longer have
a single global thread pool. This results in significant simplifications
especially in the reader (no more "canary").
2025-11-01 11:46:25 -07:00
Johannes Altmanninger
b8c7ee3a85 Fix Dockerfile syntax
Enigmatic error:

	   1 | >>> FROM alpine:3.22 # updatecli.d/docker.yml
	ERROR: failed to build: failed to solve: dockerfile parse error on line 1: FROM requires either one or three arguments

Fixes daadd81ab6 (More automation for updating dependencies, 2025-10-31).
2025-11-01 13:11:33 +01:00
Johannes Altmanninger
0709e4be8b Use standalone code paths by default
When users update fish by replacing files, existing shells might
throw weird errors because internal functions in share/functions/
might have changed.

This is easy to remedy by restarting the shell,
but I guess it would be nice if old shells kept using old data.
This is somewhat at odds with lazy-loading.

But we can use the standalone mode.  Turn that on by default.

Additionally, this could simplify packaging.  Some packages incorrectly
put third party files into /usr/share/fish/completion/ when they ought
to use /usr/share/fish/vendor_completions.d/ for that.  That packaging
mistake can make file conflicts randomly appearing when either fish
or foo ships a completion for foo.  Once we actually stop installing
/usr/share/fish/completions, there will no longer be a conflict (for
better or worse, things will silently not work, unless packagers
notice that the directory doesn't actually exist anymore).

The only advantage of having /usr/share/fish/ on the file system is
discoverability. But getting the full source code is better anyway.

Note that we still install (redundant) $__fish_data_dir when using
CMake.  This is to not unnecessarily break both
1. already running (old) shells as users upgrade to 4.2
2. plugins (as mentioned in an earlier commit),

We can stop installing $__fish_data_dir once we expect 99% of users
to upgrade from at least 4.2.

If we end up reverting this, we should try to get rid of the embed-data
knob in another way, but I'm not sure how.

Closes #11921

To-do:
- maybe make it a feature flag so users can turn it off without
  recompiling with "set -Ua no-embed-data".
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
328f9a9d16 Readme: reword the "Building fish with Cargo" section
The next commit will make embed-data the default also for CMake builds.

Even if we revert that, from a user perspective we better call it
"Building fish with Cargo" rather than "Building fish with embedded
data".

While at it, let's list the user-facing differences when building
with Cargo as opposed to CMake.

I suppose it's not needed to mention :

> An empty ``/etc/fish/config.fish`` as well as empty directories
> ``/etc/fish/{functions,completions,conf.d}``

because that's obvious.

Since installation via Cargo is already aimed at developers, maybe
add "uv run" here to reliably install a recent version of Sphinx.
A small extra step up-front seems better than not having docs.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
1d9233abc7 Fix CMake build with embed-data 2025-11-01 12:58:13 +01:00
Johannes Altmanninger
c9cc2a4069 config_paths: inline function again
Now that the dust has settled, there is only one caller of the path
detection logic, so we don't need the enum.  Remove it.  No functional
change.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
e0a2fa35cc Define __fish_{data,help}_dir again on standalone builds
As mentioned in a previous commit ("Don't special-case __fish_data_dir
in standalone builds"), we want to enable embed-data by default.
To reduce the amount of breakage, we'll keep installing files,
and keep defining those variables at least for some time.

We have no use for $__fish_data_dir apart from helping plugins and
improving upgrade experience, but we still use $__fish_help_dir;
so this will allow installed "standalone" builds to use local docs
via our "help" function.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
da5a57890a env: extract function for setting our config path variables
While at it, reorder the assignments to match the order in ConfigPaths.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
c7772db4fb build.rs: remove unused LOCALEDIR override
Implied by bee1e122f9 (Remove unused locale path code, 2025-09-16).
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
f00b775172 config_paths: remove unused clone() 2025-11-01 12:58:13 +01:00
Johannes Altmanninger
a5cbbd7f10 Revert "config_paths: separate ifdef'd path logic into functions"
To prepare for defining __fish_data_dir/__fish_help_dir again on
embed-data builds, reverts commit 04a2398c90. No functional change.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
c409e816df Don't branch on __fish_data_dir[1] in standalone builds
We use absence of "$__fish_data_dir[1]" as criteria to use the
"standalone" code paths that use "status list-files/get-file" instead
of $__fish_data_dir.

However, third party software seems slow to react to such breaking
changes, see https://github.com/ajeetdsouza/zoxide/issues/1045

So keep $__fish_data_dir for now to give plugins more time.
This commit makes us ignore $__fish_data_dir on standalone builds
even if defined; a  following commit will actually define it again.

I guess the approach in b815fff292 (Set $__fish_data_dir to empty
for embed-data builds, 2025-04-01) made sense back when we didn't
anticipate switching to standalone builds by default yet.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
8d02987c64 config_paths: reduce differences between standalone/installed builds
Some packagers such as Homebrew use the "relocatable-tree" logic,
i.e. "fish -d config" shows paths like:

	/usr/local/etc/fish
	/usr/local/share/fish

Other packagers use -DSYSCONFDIR=/etc, meaning we get

	/etc/fish
	/usr/share/fish

which we don't treat as relocatable tree,
because "/usr/etc/fish" does **not** exists.

To get embed-data in shape for being used as a default for installed
builds, we want it to support both cases.

Today, embed-data builds only handle the "in-build-dir" logic.
Teach them also about the relocatable-tree logic.  This will make
embed-data builds installed via Homebrew (i.e. CMake) use the correct
sysconfdir ("/usr/local/etc").

This means that if standalone fish is moved to ~/.local/bin/fish
and both ~/.local/share/fish as well as ~/.local/etc/fish exist,
fish will use the relocatable tree paths.

But only embedded files will be used, although a following commit will
make standalone builds define $__fish_data_dir and $__fish_help_dir
again; the latter will be used to look up help.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
e091bc3ba2 config_paths: rename "base_path" to "prefix"
This matches the convention used by Make/CMake and others.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
4f9c9b5be4 build.rs: sysconf dir defaults to $prefix/etc, not $prefix/share/etc
(This doesn't matter in practice because we always override SYSCONFDIR
in CMake builds.)

According to https://cmake.org/cmake/help/latest/command/install.html
default paths look like

	Type    variable                    default
	INCLUDE ${CMAKE_INSTALL_INCLUDEDIR} include
	SYSCONF ${CMAKE_INSTALL_SYSCONFDIR} etc
	DATA    ${CMAKE_INSTALL_DATADIR}    <DATAROOT dir>
	MAN     ${CMAKE_INSTALL_MANDIR}     <DATAROOT dir>/man

so "etc" is supposed to be a sibling of "include", not a child of DATA
(aka "share/").

This allows us to remove a hack where we needed to know the data dir
in non-standalone builds.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
10e0515d50 Also embed __fish_build_paths.fish
Today, this file is only supported in CMake builds.  This is the only
place where CMake builds currently need $__fish_data_dir as opposed
to using "status get-file".

Let's embed __fish_build_paths so we can treat it like other assets.

This enables users of "embed-data" to do "rm -rf $__fish_data_dir"
(though that might break plugins).
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
bae735740c Rename FISH_BUILD_DIR variable, to assert that CMake is used
It's always the CMake output directory, so call it
FISH_CMAKE_BINARY_DIR. It's possible to set it via some other build
system but if such builds exist, they are likely subtly broken, or
at least with the following commit which adds the assumption that
"share/__fish_build_paths.fish.in" exists in this directory.

We could even call it CMAKE_BINARY_DIR but let's namespace it to make
our use more obvious. Also, stop using the $CMAKE environment variable,
it's not in our namespace.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
3b0fa95870 embed-data: remove code clone
Seems to save only 1KiB in binary size (might be noise).
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
d4390c2fad Use cfg_if for embed-data checks 2025-11-01 12:58:13 +01:00
Johannes Altmanninger
9845074a53 create_manpage_completions: run for standalone builds too
On first startup, we run this script, but not for standalone
(embed-data) builds.  Rectify that.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
e7dc1c4635 fish_update_completions: use status get-file instead of temp files
This helps the next commit.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
07e26518fc create_manpage_completions: include deroff.py directly
Enables the next commit.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
034b3b758d create_manpage_completions: ignore gcloud-* man pages
Google cloud CLI ships >10k man pages for subcommands, but the
completions are not useful because they don't know to replace
underscores by spaces, e.g. in:

	complete -c gcloud_access-approval_requests_approve

We also ship gcloud completions, so the generated ones should not
be used.
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
ea5d77ad6f tests/man: try to fix intermittent failure in CI
This failed once in Ubuntu CI because there was one extra space in
the first column (after "ABBR(1)").
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
cb320e17ed Sign releases again
The new automation workflow doesn't sign the Git tag or the tarball as
we used to.  Since the tag is still created locally, sign it directly.

For signing the tarball; build it locally and compare the extracted
tarball with the one produced by GitHub.

Closes #11996
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
ca8b18cad5 build_tools/make_tarball.sh: make it reproducible when building a tag
When building from a clean tag, set the date at the bottom of the
manpages to the tag creation date.  This allows to "diff -r" the
extracted tarball to check that CI produces the same as any other
system.

Part of #11996
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
aec459c795 Upgrade and pin Sphinx version
In future, we should ask "renovatebot" to update these version. I
don't have an opinion on whether to use "uv" or something else, but
I think we do want lockfiles, and I don't know of a natural way to
install Sphinx via Cargo.

No particular reason for this Python version.

Part of #11996
2025-11-01 12:58:13 +01:00
Johannes Altmanninger
13d62d5851 tests/sphinx-markdown-changelog: work around shallow clones in CI
GitHub CI uses shallow clones where no Git tags available, which
breaks tests/checks/sphinx-markdown-changelog.fish.

Somehow tests/checks/sphinx-markdown-changelog.fish doesn't seem to
have run CI before the next commit (or perhaps the python3 change
from commit "tests/sphinx-markdown-changelog: workaround for mawk",
2025-10-26?).

Anway, to prevent failure, disable that part of this test in CI
for now; the point of the test is mostly to check the RST->Markdown
conversion and syntax.
2025-11-01 12:55:01 +01:00
Johannes Altmanninger
d8516139c8 release-notes.sh: compute stats only when needed 2025-11-01 12:55:01 +01:00
Johannes Altmanninger
7f8263b625 release-notes.sh: simplify previous version computation 2025-11-01 12:55:01 +01:00
Johannes Altmanninger
daadd81ab6 More automation for updating dependencies
- Convert update checks in check.sh to mechanical updates.
  - Use https://www.updatecli.io/ for now, which is not as
    full-featured as renovatebot or dependabot, but I found it easier
    to plug arbitrary shell scripts into.
- Add updaters for
  - ubuntu-latest-lts (which is similar to GitHub Action's "ubuntu-latest").
  - FreeBSD image used in Cirrus (requires "gcloud auth login" for now,
    see https://github.com/cirruslabs/cirrus-ci-docs/issues/1315)
  - littlecheck and widecharwidth
- Update all dependencies except Cargo ones.
- As a reminder, our version policies are arbitrary and can be changed
  as needed.
- To-do:
  - Add updaters for GitHub Actions (such as "actions/checkout").
    Renovatebot could do that.
2025-11-01 12:55:01 +01:00
Johannes Altmanninger
c0b7167082 Remove unused docker images for frozen OS releases
Most of our docker images are for an OS release which is past EOL. Most
are not checked in CI, which leads to more staleness. It's not
obvious which docker are expected to work and which are best-effort.
I've updated all of them in the past, which would be slightly easier
if we got rid of the redundancy.

Remove most unused ones for now, to reduce confusion and maintenance
effort. Some Ubuntu images are replaced by
docker/ubuntu-latest-lts.Dockerfile
docker/ubuntu-oldest-supported.Dockerfile

Leave around the fedora:latest and opensuse/tumbleweed:latest images
for now, though I don't think there's a reason to publish them in
build_docker_images until we add CI jobs.

We can add some images back (even past-EOL versions) but preferrably
with a documentted update policy (see next commit) and CI tests
(could be a nightly/weekly/pre-release check).
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
ff50e761dd Upgrade sphinx-markdown-builder 2025-11-01 12:45:17 +01:00
Johannes Altmanninger
7c61fc5151 tests/sphinx-markdown-changelog: workaround for mawk
awk is mawk on Ubuntu.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
ab39fab68c Fix a wrong path to fish_indent on OpenBSD
If fish is invoked as

	execve("/bin/fish", "fish")

on OpenBSD, "status fish-path" will output "fish".  As a result,
our wrapper for fish_indent tries to execute "./fish" if it exists.

We actually no longer need to call any executable, since "fish_indent"
is available as builtin now.

Stop using the wrapper, fixing the OpenBSD issue.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
9953edb9ab config_paths: don't try to use "$PWD/fish" as exec path
As mentioned in earlier commits, "status fish-path" is either an
absolute path or "fish".  At startup, we try to canonicalize this
path. This is wrong for the "fish" case -- we'll do subtly wrong
things if a file with that name happens to exist in the current
working directory.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
8d2dabbded Extract function for program name 2025-11-01 12:45:17 +01:00
Johannes Altmanninger
708703b9ec Reimplement should_suppress_stderr_for_tests() for some tests
"cargo test" captures stdout by default but not stderr.

So it's probably still useful to suppress test output like

	in function 'recursive1'
	in function 'recursive2'
	[repeats many times]

This was done by should_suppress_stderr_for_tests() which has been
broken. Fix that, but only for the relevant cases instead of setting
a global.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
def9230ad6 fish-path: remove needless UTF-8 requirement and code clone
At least on our MSRV, strip_suffix() also exists for byte slices,
not just str.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
4ef0e37011 status fish-path: remove dead code
This value is either "fish" or an absolute path (as promised by the
docs of std::env::current_exe()); it can't be empty.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
ce82577d2f Actually fix stripping of " (deleted)" suffix
Commit 7b59ae0d82 (Unbreak hack to strip " (deleted)" suffix from
executable path, 2025-10-02) accidentally droped the "!".
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
f6584225c2 Pin down "status fish-path" at startup
On some platforms, Rust's std::env::current_exe() may use relative
paths from at least argv[0].  That function also canonicalizes the
path, so we could only detect this case by duplicating logic from
std::env::current_exe() (not sure if that's worth it).

Relative path canonicalization seems potentially surprising, especially
after fish has used "cd". Let's try to reduce surprise by saving the
value we compute at startup (before any "cd"), and use only that.

The remaining problem is that

	creat("/some/directory/with/FISH");
	chdir("/some/directory/with/");
	execve("/bin/fish", "FISH", "-c", "status fish-path")

surprisingly prints "/some/directory/with/FISH" on some platforms.

But that requires the user actively trying to break things (passing
a relative argv[0] that doesn't match the cwd).
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
a0a8a0b817 Move get_fish_path to a meaningful module 2025-11-01 12:45:17 +01:00
Johannes Altmanninger
f88b1fd393 Don't use argv[0] directly when computing executable path
When "status fish-path" fails, we fall back to argv[0],
hoping that it contains an absolute path to fish, or at
least is equal to "fish" (which can happen on OpenBSD, see
https://github.com/rust-lang/rust/issues/60560#issuecomment-489425888).

I don't think it makes sense to duplicate logic that's probably
already in std::env::current_exe().
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
bd720ec9f6 Use early return in get_executable_path 2025-11-01 12:45:17 +01:00
Johannes Altmanninger
56555f6319 Remove obsolete Unicode char reinitialization code path
Since c8001b5023 (encoding: use UTF-8 everywhere, 2025-10-18), a
change in locale variables can no longer cause us to toggle between
"…" or "...".  Have the control flow reflect this.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
71a962653d Be explicit about setlocale() scope
We use the following locale-variables via locale-aware C functions
(to look them up, grep for "libc::$function_name"):

- LC_CTYPE:    wcwidth
- LC_MESSAGES: strerror, strerror 
- LC_NUMERIC:  localeconv/localeconv_l, snprintf (only in tests)
- LC_TIME:     strftime

Additionally, we interpret LC_MESSAGES in our own code.

As of today, the PCRE2 library does not seem to use LC_MESSAGES
(their error messages are English-only); and I don't think it uses
LC_CTYPE unless we use "pcre2_maketables()".

Let's make it more obvious which locale categories are actually used
by setting those instead of LC_ALL.

This means that instead of logging the return value of «
setlocale(LC_ALL, "") » (which is an opaque binary string on Linux),
we can log the actual per-category locales that are in effect.
This means that there's no longer really a reason to hardcode a log
for the LC_MESSAGES locale. We're not logging at early startup but
we will log if any locale var had been set at startup.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
732942ec62 Force libc wcwidth to use UTF-8 locale again
Commit 046db09f90 (Try to set LC_CTYPE to something UTF-8 capable
(#8031), 2021-06-06) forced UTF-8 encoding if available.

Since c8001b5023 (encoding: use UTF-8 everywhere, 2025-10-18) we no
longer override LC_CTYPE, since we no longer use it for anything like
mbrtowc or iswalpha.

However there is one remaining use: our fallbacks to system wcwidth().
If we are started as

	LC_ALL=C fish

then wcwidth('😃') will fail to return the correct value, even if
the UTF-8 locale would have been available.

Restore the previous behavior, so locale variables don't affect
emoji width.

This is consistent with the direction in c8001b5023 which stops us
from falling back to ASCII characters if our desired multibyte locale
is missing (that was not the ideal criteria).

In future we should maybe stop using wcwidth().
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
5eebeff5a9 Retire our "use-LC_NUMERIC-internally" workaround
Commit 8dc3982408 (Always use LC_NUMERIC=C internally (#8204),
2021-10-13) made us use LC_NUMERIC=C internally, to make C library
functions behave in a predictable way.

We no longer use library functions affected by LC_NUMERIC[*]..

Since the effective value of LC_NUMERIC no longer matters, let's
simplify things by using the user locale again, like we do for the
other locale variables.

The printf crate still uses libc::snprintf() which respects LC_NUMERIC,
but it looks like "cargo test" creates a separate process per crate,
and the printf crate does not call setlocale().
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
f6d7198317 printf tests: extract function for calling libc::sprintf 2025-11-01 12:45:17 +01:00
Johannes Altmanninger
dc553ac628 Minor style change in read_locale() 2025-11-01 12:45:17 +01:00
Johannes Altmanninger
6643b8c3e1 Document LANGUAGE as :envvar: too 2025-11-01 12:45:17 +01:00
Johannes Altmanninger
2954ff3991 Update docs on locale variables
* since c8001b5023 (encoding: use UTF-8 everywhere, 2025-10-18)
  we always use UTF-8, which simplifies docs.
* emphasize that we (as of an earlier commit) document only the locale
  variables actually used by fish. We could change this in future,
  as long as the docs make it obvious whether it's about fish or
  external programs.
* make things a bit more concise
* delete a stale comment - missing encoding support is no longer a problem
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
ab69ef4b83 Remove unused LC_COLLATE and LC_MONETARY
We may have used LC_COLLATE in the past via libc functions but I
don't think we do today.  In future, we could document the variables
not used by fish, but we should make it obvious what we're documenting.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
e8c0b3df24 Extract some setlocale() calls; use C-string literals 2025-11-01 12:45:17 +01:00
Johannes Altmanninger
5a12247572 Extract function for listing all locale variables
These include variables that are ignored by fish, which seems
questionable for __fish_set_locale?  The the effect seems benign
because __fish_set_locale will do nothing if any of those variables
is defined.  Maybe we can get rid of this function eventually.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
58eec96a5b Document fish-specific bits about locale vars
Link to history, printf and "builtin _" which are the only(?) users
of LC_TIME, LC_NUMERIC and LC_MESSAGES respectively (besides the core
equivalent of "builtin _").
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
2be3f34f2c Remove more obsolete uses of LC_CTYPE
Our test driver unsets all LC_* variables as well as LANGUAGE, and
it sets LANG=C, to make errors show up as

	set_color: Unknown color 'reset'

rather than

	set_color: Unknown color “reset”

It also sets LC_CTYPE which is hardly necessary since c8001b5023
(encoding: use UTF-8 everywhere, 2025-10-18).

The only place where we need it seems to be the use of GNU sed as
called in tests/checks/sphinx-markdown-changelog.fish.

In future we might want to avoid these issues by setting LANG=C.UTF-8
in the test_driver again.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
7bd6e577d9 Remove obsolete uses of LC_CTYPE
These are obsolete as of c8001b5023 (encoding: use UTF-8 everywhere,
2025-10-18). The only place where we still read the user's LC_CTYPE
is in libc::wcwidth(), but that's kind of a regression -- we should
always be using a UTF-8 LC_CTYPE if possible -- which will be fixed
by a following commit.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
e09583e99e Double-down on using non-ASCII characters even if MB_CUR_MAX==1
Commit c8001b5023 (encoding: use UTF-8 everywhere, 2025-10-18)
removed some places where we fallback to ASCII if no UTF-8 encoding
is available.  That fallback may have been a reasonable approximator
for glyph availability in some cases but it's probably also wrong in
many cases (SSH, containers..).

There are some cases where we still have this sort of fallback.
Remove them for consistency.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
d1983b29c1 Reword warnings about missing sphinx-build/msgfmt
Also merge them into one warning because some of these lines wrap
on a small (80 column) terminal, which looks weird.  The reason they
wrap might be the long prefix ("warning: fish-build-man-pages@0.0.0:").
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
c0a988da21 Re-enable tests/checks/locale in CI
We don't currently run tsan tests anywhere.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
f5544fe2ae Fix libc import convention
Functions are always qualified with "libc::".  This is important for
finding who still uses "setlocale()".
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
75be4e3f6a tests/config-paths-standalone: skip on installed builds
This fails:

	tests/test_driver.py ~/.local/opt/fish/bin tests/checks/config-paths-standalone.fish

because  we don't expect to run from a relocatable tree.
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
6c267e88a1 __fish_macos_set_env: match macOS "path_helper" if MANPATH is overridden
Try to match
b33f386ac4/path_helper/path_helper.c
1. don't add empty segments
2. For MANPATH specifically, always add an empty segment at the end

See #10684
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
27f49b9523 __fish_macos_set_env: extract function to make it testable 2025-11-01 12:45:17 +01:00
Johannes Altmanninger
b8f12ed857 More build fixes for Illumos' msgfmt
Their msgfmt doesn't support --output-file=- yet, so use a temporary
file if "msgfmt" doesn't support "--check-format".  Else we should
have GNU gettext which supports both.

See #11982
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
5cad71c081 Fix weird indentation in macro_rules 2025-11-01 12:45:17 +01:00
Johannes Altmanninger
f331f6a8a9 Relax test_close_during_select_ebadf
It failed for me on Linux.  Same as abf3f50bb9 (Relax test
`test_dup2_during_select_ebadf` (#11976), 2025-10-19).
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
461670c36a alt-p binding: don't add extra space character before pipe 2025-11-01 12:45:17 +01:00
Kaya Arro
0f32866980 docs: corrected some section title conflicts
Part of #11796
2025-11-01 12:45:17 +01:00
Kaya Arro
c123126991 help: fix anchor link to intropages
Part of #11796
2025-11-01 12:45:17 +01:00
Johannes Altmanninger
1302ac16f0 printf: remove unused re-export of en_US locale
As suggested in
https://github.com/fish-shell/fish-shell/pull/11992#discussion_r2464108030,
I don't know if anyone wants to use the re-exports rather than using
the `locale` crate.
2025-11-01 12:45:17 +01:00
Daniel Rainer
cc51d91e77 cleanup: avoid allocation in autoloader
There is no need to allocate a new box in these cases.
Flagged by nightly clippy.

There's also no need for the box at all; the comment is no longer
valid, especially because Rust const-propagates by default.

Closes #12014
2025-11-01 12:45:17 +01:00
Daniel Rainer
2d1c34c36a ci: update stable Rust version to 1.91
Closes #12013
2025-11-01 12:44:48 +01:00
Daniel Rainer
6422139fe0 cleanup: derive Default where possible
Not doing so triggers a clippy lint on Rust >= 1.91.

Part of #12013
2025-11-01 12:44:48 +01:00
Daniel Rainer
f511ef69c3 gettext: don't cache messages outside of gettext
Using gettext by calling it once on initialization and then reusing the
result prevents changes to the messages as when locale variables change.
A call to the gettext implementation should be made every time the
message is used to handle language changes.

Closes #12012
2025-11-01 12:42:55 +01:00
Johannes Altmanninger
525c9bbdcb Move Rust tests to implementation files
For historical reasons[^1], most of our Rust tests are in src/tests,
which
1. is unconventional (Rust unit tests are supposed to be either in the
   same module as the implementation, or in a child module).
   This makes them slightly harder to discover, navigate etc.
2. can't test private APIs (motivating some of the "exposed for
   testing" comments).

Fix this by moving tests to the corresponding implementation file.
Reviewed with

        git show $commit \
            --color-moved=dimmed-zebra \
            --color-moved-ws=allow-indentation-change

- Shared test-only code lives in
  src/tests/prelude.rs,
  src/builtins/string/test_helpers.rs
  src/universal_notifier/test_helpers.rs
  We might want to slim down the prelude in future.
- I put our two benchmarks below tests ("mod tests" followed by "mod bench").

Verified that "cargo +nightly bench --features=benchmark" still
compiles and runs.

[^1]: Separate files are idiomatic in most other languages; also
separate files makes it easy to ignore when navigating the call graph.
("rg --vimgrep | rg -v tests/").  Fortunately, rust-analyzer provides
a setting called references.excludeTests for textDocument/references,
the textDocument/prepareCallHierarchy family, and potentially
textDocument/documentHighlight (which can be used to find all
references in the current file).

Closes #11992
2025-11-01 12:42:55 +01:00
Johannes Altmanninger
7bc560190f Fix warning about unused test_notifiers on windows 2025-11-01 12:42:55 +01:00
Johannes Altmanninger
54b39be7e7 build_tools/style.fish: check that {rustfmt,Cargo}.toml edition specs are in sync
Commit 1fe6b28877 (rustfmt.toml: specify edition to allow 2024 syntax,
2025-10-19) mentions that "cargo fmt" has different behavior than
"rustfmt" before that commit.  Probably because when .rustfmt.toml
exists, rustfmt implicitly uses a different edition (2018?)  that
doesn't support c"" yet.  That commit bumped the edition to 2024,
which caused yet another deviation from "cargo fmt":

    Error writing files: failed to resolve mod `tests`: cannot parse /home/johannes/git/fish-shell/src/wutil/tests.rs
    error: expected identifier, found reserved keyword `gen`
      --> /home/johannes/git/fish-shell/src/tests/topic_monitor.rs:48:9
       |
    48 |     for gen in &mut gens_list {
       |         ^^^ expected identifier, found reserved keyword

This has since been fixed by
00784248db (Update to rust 2024 edition, 2025-10-22).

Let's add a test so that such changes won't randomly break "rustfmt"
again.

Fix that by using 2021 edition, like we do in Cargo.toml.

In future, rustfmt should probably default to a current edition (or
maybe read the edition from Cargo.toml?)  Not yet sure which one is the
upstream issue, maybe https://github.com/rust-lang/rustfmt/issues/5650
2025-11-01 12:42:55 +01:00
kerty
578f46c008 build_tools/style.fish: improve style and consistency 2025-11-01 12:42:55 +01:00
Johannes Altmanninger
b1cbbf7ce5 build_tools/style.fish: extract function 2025-11-01 12:42:55 +01:00
Johannes Altmanninger
af9b03625b build_tools/style.fish: reuse variable 2025-11-01 12:42:55 +01:00
Johannes Altmanninger
71f0e75651 Remove dead misc_init() 2025-11-01 12:42:55 +01:00
Johannes Altmanninger
3c4243fdd2 clang-format fish_test_helper 2025-11-01 12:42:55 +01:00
Peter Ammon
1a1da0649a Clean up Debounces and remove the global thread pool
Prior to this commit, there was a singleton set of "debouncers" used to run
code in the background because it might perform blocking I/O - for example,
for syntax highlighting or computing autosuggestions. The complexity arose
because we might push or pop a reader. For example, a long-blocking, stale
autosuggestion thread might suddenly complete, but the reader it was for
(e.g. for `builtin_read`) is now popped. This was the basis for complex
logic like the "canary" to detect a dead Reader.

Fix this by making the Debouncers per-reader. This removes some globals and
complicated logic; it also makes this case trivial: a long-running thread
that finishes after the Reader is popped, will just produce a Result and
then go away, with nothing reading out that Result.

This also simplifies tests because there's no longer a global thread pool
to worry about. Furthermore we can remove other gross hacks like ForceSend.
2025-10-31 19:40:13 -07:00
Peter Ammon
69ccc9be18 Factor Debounce out of threads.rs
Preparation to clean this up.
2025-10-31 19:40:12 -07:00
Peter Ammon
61d6a83661 Migrate threads into its own submodule
Preparation for improving its factoring.
2025-10-31 19:40:11 -07:00
Peter Ammon
2f5260aabd Make a reader-specific thread pool
This concerns threads spawned by the reader for tasks like syntax
highlighting that may need to perform I/O.

These are different from other threads typically because they need to
"report back" and have their results handled.

Create a dedicated module where this logic can live.
This also eliminates the "global" thread pool.
2025-10-31 19:40:10 -07:00
Peter Ammon
eca19006ad Use a separate thread pool for history file detection 2025-10-31 19:40:08 -07:00
Peter Ammon
d22b5910c2 Remove the "cant_wait" variants of thread pool execution
This is now handled via separate thread pools.
2025-10-31 19:40:07 -07:00
Peter Ammon
a868be6ba4 Factor fill_history_pager_complete into its own function 2025-10-31 19:40:06 -07:00
Peter Ammon
8822ba3035 Migrate reader into a submodule.
Support future breaking up of this big module. No functional change.
2025-10-31 19:40:05 -07:00
Peter Ammon
e20b06df1a Background threads to use a separate pool from reader
Sometimes we need to spawn threads to service internal processes. Make
this use a separate thread pool from the pool used for interactive tasks
(like detecting which arguments are files for syntax highlighting).
2025-10-31 19:40:03 -07:00
Peter Ammon
e299b71560 Stop using io_thread pool in test_universal
Just use ordinary threads.

Also allow universal variable tests to run in parallel, by using real
temporary paths.
2025-10-31 19:40:00 -07:00
Peter Ammon
d6f0e1fdf2 Make Debounce hold a thread pool
Allow these to be instanced.
2025-10-31 17:40:21 -07:00
Peter Ammon
4a5bce3fb8 Use OnceLock instead of once_cell::race in debouncers
No reason to be fancy here; and OnceLock is lock-free in the fast path
2025-10-31 17:40:21 -07:00
Peter Ammon
7f1dc80b9c Get rid of WorkerThread
Now that ThreadPool itself is behind Arc we can do without this type.
2025-10-31 17:40:21 -07:00
Peter Ammon
43731c88bd Migrate ThreadPool's Arc to the outside
This will simplify debouncing and allowing for multiple pools.
2025-10-31 17:40:21 -07:00
Peter Ammon
332712866c Move some iothread functions into ThreadPool
Continue to get away from singletons.
2025-10-31 17:40:20 -07:00
Peter Ammon
b0d643c4ce Move MAIN_THREAD_QUEUE into ThreadPool
Continue to remove globals and improve ThreadPool testability.
2025-10-31 17:40:20 -07:00
Peter Ammon
2b9967bf01 Migrate NOTIFY_SIGNALLER into ThreadPool
Help remove some globals; prepare ThreadPool for certain tests.
2025-10-31 17:40:20 -07:00
Peter Ammon
9d53d61141 Remove a Mutex around ThreadPool
This is not in fact needed.
2025-10-31 17:40:20 -07:00
Peter Ammon
c66fa682e9 Fix some clipplies 2025-10-31 17:38:37 -07:00
David Adam
819759840e Debian packaging: use MSRV compiler if available
Allows the Ubuntu builds to work when using the cargo-VERSION packages.
2025-10-28 05:55:10 +08:00
David Adam
ccde87c4e3 Debian packaging: force name of build directory
make uses the GNUMakefile in the source directory otherwise
2025-10-28 05:51:01 +08:00
David Adam
3a9c5c7dc0 Debian packaging: pass buildsystem argument to all dh invocations 2025-10-28 05:48:55 +08:00
David Adam
d27537c4fc CMake: use configured Rust_CARGO for tests, not the cargo on $PATH 2025-10-27 06:07:41 +08:00
Peter Ammon
973f0f6134 Fix tmux-commandline.fish test harder
Make this pass on both macOS and Linux.

This was an obnoxious and uninteresting test to debug and so I used
claude code. It insists this is due to differences in pty handling between
macOS and Linux. Specifically it writes:

    The test was failing inconsistently because macOS and Linux have different
    PTY scrollback behavior after rendering prompts with right-prompts.

    Root cause: After fish writes a right-prompt to the rightmost column,
    different PTY drivers position the cursor differently:
    - macOS PTY: Cursor wraps to next line, creating a blank line
    - Linux PTY: Cursor stays on same line, no blank line

    This is OS kernel-level PTY driver behavior, not terminal emulator behavior.

    Fix: Instead of hardcoding platform-specific offsets, detect the actual
    terminal behavior by probing the output:
    1. Capture with -S -12 and check if the first line is blank
    2. If blank (macOS behavior), use -S -13 to go back one more line
    3. If not blank (Linux behavior), use -S -12

    Also split the C-l (clear-screen) command into its own send-keys call
    with tmux-sleep after it, ensuring the screen clears before new output
    appears. This improves test stability on both platforms.

    The solution is platform-independent and adapts to actual terminal
    behavior rather than making assumptions based on OS.
2025-10-26 13:34:35 -07:00
ridiculousfish
1973f3110e Make tmux-multiline-prompt.fish pass reliably on macOS 2025-10-26 12:06:35 -07:00
Peter Ammon
95e7977e70 Rename some constants to UPPER_CASE 2025-10-26 11:28:04 -07:00
Peter Ammon
15d06f964d Make tmux-commandline.fish pass reliably on macOS 2025-10-25 19:36:34 -07:00
Peter Ammon
5f96a4e665 Make test_fish_update_completions.fish pass on macOS 2025-10-25 11:56:43 -07:00
Henrik Hørlück Berg
075e4040be Remove resolver="2" to opt into resolver="3" default
This makes dependecy-resolving rust-version aware.

See: https://doc.rust-lang.org/edition-guide/rust-2024/cargo-resolver.html#cargo-rust-version-aware-resolver

This changes to lockfile to V4, which was released with rust 1.78
https://doc.rust-lang.org/nightly/cargo/CHANGELOG.html#cargo-178-2024-05-02,
and became the default with rust 1.83 https://doc.rust-lang.org/nightly/cargo/CHANGELOG.html#cargo-183-2024-11-28

Closes #11990
2025-10-24 13:48:19 +02:00
Henrik Hørlück Berg
7d41158b1d Bravely revert if let => match edition changes
See: https://doc.rust-lang.org/edition-guide/rust-2024/temporary-if-let-scope.html

I dot believe drop order matters in any of these cases.

Part of #11990
2025-10-24 13:48:19 +02:00
Henrik Hørlück Berg
00784248db Update to rust 2024 edition
This bravely avoids changing expr to expr_2021 in macros, which seems
extremely unlikely to be silently breaking anything.

See: https://doc.rust-lang.org/edition-guide/rust-2024/macro-fragment-specifiers.html

I do not believe we have any macros where this is relevant

Part of #11990
2025-10-24 13:48:19 +02:00
Henrik Hørlück Berg
b47b61ea08 Ignore noisy rule
This lint will be triggered by a forthcoming change, see
https://github.com/fish-shell/fish-shell/pull/11990#discussion_r2455299865

It bans the usage of

```rust
fn hello() -> impl Debug {
    let useful_name = xxx;
    useful_name
}
```

in favour of

```rust
fn hello() -> impl Debug {
    xxx
}
```
Which is less humanly-understandable.

Part of #11990
2025-10-24 13:48:19 +02:00
Johannes Altmanninger
787c6a443d Remove redundant per-module lints; fix some
As mentioned in 6896898769 (Add [lints] table to suppress lints
across all our crates, 2024-01-12), we can use workspace lints in
Cargo.toml now that we have MSRV >=1.74 and since we probably don't
support building without cargo.

This implies moving some lints from src/lib.rs to "workspace.lints".
While at it, address some of them insrtead.
2025-10-24 13:48:19 +02:00
kerty
f075c58eb5 Try to make tmux-transient-prompt.fish more consistent 2025-10-23 11:48:24 +02:00
kerty
07f0b1816e Wait until any output when initalasing tmux tests
Previously, if test setup didn't output word 'prompt' it would wait 5 second
timeout. This affected tmux-wrapping.fish and tmux-read.fish tests.

Change it so initialization function wait until any output and error out
on timeout.
2025-10-23 11:48:24 +02:00
kerty
73696da21c Allow for attaching to test tmux session without resizing 2025-10-23 11:48:24 +02:00
kerty
e22d90cbad Instead of variable pass args to tmux fish directly 2025-10-23 11:48:24 +02:00
kerty
c7fdb8dc6b Simplify behavior of ScreenData.cursor
Let `y=0` be the first line of the rendered commandline. Then, in final
rendering, the final value of the desired cursor was measured from
`y=scroll_amount`. This wasn't a problem because
```
if !MIDNIGHT_COMMANDER_HACK.load() {
    self.r#move(0, 0);
}
```
implicitly changed the actual cursor to be measured from `y=0` to `y=scroll_amount`.
This happened because the cursor moved to `y=scroll_amount` but had its
`y` value set to 0. Since the actual and desired cursors were both measured
from the same line, the code was correct, just unintuitive.

Change this to always measure cursor position from the start of the
rendered commandline.
2025-10-23 11:48:24 +02:00
kerty
f7359751c9 Small refactoring of Screen::write 2025-10-23 11:48:24 +02:00
kerty
0b7ab5a1b5 Fix not preserving empty last line in multiline prompts
Reproduction:
fish -C '
    function fish_prompt
        echo left-prompt\n
    end
    function fish_right_prompt
        echo right-prompt
    end
'
and pressing Enter would not preserve the line with right prompt.

This occurred because Screen::cursor_is_wrapped_to_own_line assumed
the last prompt line always had index 0. However, commit 606802daa (Make
ScreenData track multiline prompt, 2025-10-15) changed this so the last
prompt line's index is now `Screen.actual.visible_prompt_lines - 1`.

Screen::cursor_is_wrapped_to_own_line also didn't account for situations
where commandline indentation was 0.

Fix Screen::cursor_is_wrapped_to_own_line and add tests for prompts with
empty last lines.
2025-10-23 11:48:24 +02:00
kerty
e7ad7e5cf6 Fix missing right prompt when left prompt ends with empty line
Reproduction:
fish -C '
    function fish_prompt
        echo left-prompt\n
    end
    function fish_right_prompt
        echo right-prompt
    end
'

This was caused by an off-by-one error in initial Screen.desired resizing
from commit 606802daa (Make ScreenData track multiline prompt, 2025-10-15).
2025-10-23 11:48:24 +02:00
Étienne Deparis
9ccd6fb2d2 Add me as french maintainer
Part of #11842
2025-10-22 10:57:36 +02:00
Étienne Deparis
e7e97833a3 Improve some french translations
Closes #11842
2025-10-22 10:57:36 +02:00
Étienne Deparis
2dd6a7ba22 Sort recommended translations in french po file
Part of #11842
2025-10-22 10:57:36 +02:00
Johannes Altmanninger
034cd7ea35 Always handle mktemp failure in user-facing code
mktemp might be missing, see #11972
2025-10-22 10:57:36 +02:00
Johannes Altmanninger
037a399746 Add test for brace in command position with trailling text
See #11962
2025-10-22 10:57:36 +02:00
Johannes Altmanninger
6a86974b96 Fix build on Illumos
Fixes #11982
2025-10-22 10:57:36 +02:00
Johannes Altmanninger
3c468054bd completions/iwctl: fix spurious error output
Regressed in 4.1 in c94eddaf4b (Replaced all uses of argparse -i
with argparse -u, 2025-08-30).

Closes #11983
2025-10-22 10:57:36 +02:00
David Adam
6fd1304e52 Debian packaging: broaden packages for rustc to support Ubuntu 2025-10-21 11:33:54 +08:00
Nahor
a51b2f4023 Enable CI testing for MSYS 2025-10-21 04:25:30 +02:00
Nahor
abf3f50bb9 Relax test test_dup2_during_select_ebadf (#11976)
On MSYS/Cygwin, when select/poll wait on a descriptor and `dup2` is used
to copy into that descriptor, they return the descriptor as "ready",
unlike Linux (timeout) and MacOS (EBADF error).

The test specifically checked for Linux and MaxOS behaviors, failing on
Cygwin/MSYS. So relax it to also allow that behavior.
2025-10-21 04:25:30 +02:00
Nahor
20a6ac947b Disable invalid Windows filename character tests
Colons and backslashes are valid on Posix but not on Windows. So
remove those checks on Cygwin/MSYS
2025-10-21 04:25:30 +02:00
Nahor
b158ba1523 Disable permission tests on MSYS/Cygwin
POSIX permissions on Windows is problematic. MSYS and Cygwin have
a "acl/noacl" when mounting filesystems to specify how hard to try.

MSYS by default uses "noacl", which prevents some permissions to be set.
So disable the failing checks.

Note: Cygwin by default does use "acl", which may allow the check to
pass (unverified). But to be consereative, and because MSYS is the only
one providing a Fish-4.x package, we'll skip.
2025-10-21 04:25:30 +02:00
Nahor
372a65aa15 Temporary workaround for #11933 on Cygwin
Without the sleep, `flock` sometimes returns an error 14 "bad addr".
This puts the application into a deadlock.
2025-10-21 04:25:30 +02:00
Nahor
804bda5ba6 Disable test_history_races on Cygwin
The test always fail on Cygwin (with rare exceptions) so disable it
for now.
A likely cause is issue #11933, so this change should be revisited
once that issue is fixed.
2025-10-21 04:25:30 +02:00
Nahor
2f07df717d Disable symlink tests on Cygwin
Symbolic links on Windows/Cygwin are complicated and dependent on
multiple factors (env variable, file system, ...). Limitations in the
implementation causes the tests failures and there is little that Fish
can do about it. So disable those tests on Cygwin.
2025-10-21 04:25:30 +02:00
David Adam
1d0de5ce71 debian packaging: find alternative up-to-date Rust
Also drop cmake-mozilla, which disappeared a long time ago.
2025-10-20 19:00:26 +08:00
Daniel Rainer
c8001b5023 encoding: use UTF-8 everywhere
Assume that UTF-8 is used everywhere. This allows for significant
simplification of encoding-related functionality. We no longer need
branching on single-byte vs. multi-byte locales, and we can get rid of
all the libc calls for encoding and decoding, replacing them with Rust's
built-in functionality, or removing them without replacement in cases
where their functionality is no longer needed.

Several tests are removed from `tests/checks/locale.fish`, since setting
the locale no longer impacts encoding behavior. We might want more
rigorous testing of UTF-8 handling instead.

Closes #11975
2025-10-20 03:42:38 +02:00
kerty
755d5ae222 Try to always maintain prompt marker at (0, 0)
First, print prompt marker on repaint even if prompt is not visible.
Second, if we issue a clear at (0, 0) we need to restore marker.

This is necessary for features like Kitty's prompt navigation (`ctrl-shift-{jk}`),
which requires the prompt marker at the top of the scrolled command line
to actually jump back to the original state.

Closes #11911
2025-10-19 14:09:27 +02:00
Aditya Bhargava
5cfcfc64d8 Add tests for final prompt having fewer lines than initial (transient)
Part of #11911
2025-10-19 14:09:27 +02:00
kerty
606802daaf Make ScreenData track multiline prompt
Instead of pretending that prompt is always 1 line, track multiline
prompt in ScreenData.visible_prompt_lines and ScreenData.line_datas as
empty lines.

This enables:
- Trimming part of the prompt that leaves the viewport.
- Removing of the old hack needed for locating first prompt line.
- Fixing #11875.

Part of #11911
2025-10-19 14:09:27 +02:00
kerty
71b619bab0 Refactor prompt printing and var names in Screen::update
Part of #11911
2025-10-19 14:09:27 +02:00
kerty
a033a5d0e7 Instead of prompt line_breaks track line_starts
It makes it easier to do manipulations with prompt lines.

Part of #11911
2025-10-19 14:09:27 +02:00
Kabakov Grigoriy
3f2c58a269 Remove unused PromptLayout.last_line_width
Part of #11911
2025-10-19 14:09:27 +02:00
kerty
f946e5ec73 Remove redundant getter Screen::scrolled
Part of #11911
2025-10-19 14:09:27 +02:00
kerty
9d06302778 Add ScreenData::clear_lines() instead of ScreenDate::resize(0)
Part of #11911
2025-10-19 14:09:27 +02:00
kerty
bb7180a2d8 Fix off-by-one pager height error on empty lines
Commit 7db9553 (Fix regression causing off-by-one pager height on
truncated autosuggestion) adds line to pager if cursor located at
the start of line and previous line is softwrapped, even if line was
softwrapped normally and not by autosuggestion, giving pager too much space.

Fix that.

Part of #11911
2025-10-19 14:09:27 +02:00
kerty
6f5644a77c Disable fish_transient_prompt in tmux test setup
Part of #11911
2025-10-19 14:09:27 +02:00
Daniel Rainer
d79273089f lint: update clippy annotations
Some annotations are no longer needed with more recent clippy (1.85+),
so let's remove them.

Closes #11964
2025-10-19 14:09:27 +02:00
Daniel Rainer
68d02916e2 stdlib: use map_break to simplify code
Part of #11964
2025-10-19 14:09:27 +02:00
Daniel Rainer
d414967b79 stdlib: use fchown from stdlib instead of libc
Part of #11964
2025-10-19 14:09:27 +02:00
Daniel Rainer
448471bd50 stdlib: remove is_none_or backport
The backport is no longer necessary with our new MSRV.

Part of #11964
2025-10-19 14:09:27 +02:00
Daniel Rainer
15fd99072b stdlib: remove is_sorted_by backport
The backport is no longer necessary with our new MSRV.

Part of #11964
2025-10-19 14:09:27 +02:00
Daniel Rainer
61ee695e56 refactor: use bytes instead of string
Some string handling functions deal with `Vec<u8>` or `&[u8]`, which
have been referred to as `string` or `str` in the function names. This
is confusing, since they don't deal with Rust's `String` type. Use
`bytes` in the function names instead to reduce confusion.

Closes #11969
2025-10-19 14:09:27 +02:00
Johannes Altmanninger
1fe6b28877 rustfmt.toml: specify edition to allow 2024 syntax
cargo fmt works but "rustfmt src/env_dispatch.rs" fails,

	error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `"C"`
	   --> src/env_dispatch.rs:572:63
	    |
	572 |     let loc_ptr = unsafe { libc::setlocale(libc::LC_NUMERIC, c"C".as_ptr().cast()) };
	    |                                                               -^^

Fix that as suggested
2025-10-19 14:09:27 +02:00
Johannes Altmanninger
4f48797a09 build_tools/check.sh: allow to check for dependency updates
Opt-in because we don't want failures not related to local changes,
see d93fc5eded (Revert "build_tools/check.sh: check that stable rust
is up-to-date", 2025-08-10).

For the same reason, it's not currently checked by CI, though we should
definitely have "renovatebot" do this in future.

Also, document the minimum supported Rust version (MSRV) policy.  There's no
particular reason for this specific MSRV policy, but it's better than nothing,
since this will allow us to always update without thinking about it.

Closes #11960
2025-10-19 14:08:11 +02:00
Daniel Rainer
3736636d99 msrv: update to Rust 1.85
Update our MSRV to Rust 1.85.

Includes fixes for lints which were previously suppressed due to them
relying on features added after Rust 1.70.

Rust 1.85 prints a warning when using `#[cfg(target_os = "cygwin")]`, so
we work around the one instance where this is a problem for now. This
workaround can be reverted when we update to Rust 1.86 or newer.

Certain old versions of macOS are no longer supported by Rust starting
with Rust 1.74, so this commit raises our macOS version requirement to
10.12.
https://blog.rust-lang.org/2023/09/25/Increasing-Apple-Version-Requirements/
https://github.com/fish-shell/fish-shell/pull/11961#discussion_r2442415411

Closes #11961
2025-10-19 14:08:10 +02:00
Johannes Altmanninger
16f2135976 cirrus: update alpine, make jammy use rustup to get Rust>=1.85
See https://github.com/fish-shell/fish-shell/pull/11961#discussion_r2439033959

I didn't find a good way to add ~/.cargo/bin to $PATH only for
fishuser, so hardcode it for all users for now.  The default rustup
behavior (source it in ~/.bashrc) only works for interactive shells,
so it doesn't work in CI.

I updated the images manually for now, because
I'm not sure I want to test on master (see
https://github.com/fish-shell/fish-shell/pull/11884 and
https://github.com/fish-shell/fish-shell/pull/11961#discussion_r2439033959)

	sed -i '/if: github.repository_owner/d' .github/workflows/build_docker_images.yml
	git commit
	# Upload new docker images.
	repo_owner=krobelus
	git push $repo_owner HEAD:tmp
	gh --repo=$repo_owner/fish-shell \
	    workflow run --ref=tmp \
	    .github/workflows/build_docker_images.yml
	# https://github.com/$repo_owner/fish-shell/actions/workflows/build_docker_images.yml
	sleep 3m
	# Enable Cirrus CI on fork.
	sed -i "s/OWNER == 'fish-shell'/OWNER == '$repo_owner'/g" .cirrus.yml
	git commit
	git push $repo_owner HEAD:tmp

Part of #11961
2025-10-19 13:27:16 +02:00
Lumynous
73fe50f6b1 l10n: Add zh-TW translation
Adds human-translated tier-1 strings.

Closes #11967
2025-10-18 13:52:09 +02:00
Johannes Altmanninger
0b5f82a5f0 Enable history-races test in CI
Also remove the check for WSL again, since that failure may be
covered by us checking for flock() success (else there's a bug in our
implementation or in WSL); we'll see if anyone runs into this again..

Closes #11963
2025-10-18 13:45:32 +02:00
Johannes Altmanninger
02725b66b3 README: move file dependency to optional deps 2025-10-18 13:42:33 +02:00
Daniel Rainer
52ea511768 cleanup: remove useless INTERNAL_SEPARATOR handling
There does not seem to be a good reason for treating
`INTERNAL_SEPARATOR` (`\u{fdd8}`) specially in this function.

Related discussion:
https://github.com/fish-shell/fish-shell/discussions/11949#discussioncomment-14712851

Closes #11971
2025-10-18 12:59:52 +02:00
Daniel Rainer
c65748c098 refactor: replace getcwd with current_dir
The `std::env::current_dir` function calls `getcwd` internally and saves
us from having to deal with the `libc` call directly.

Closes #11970
2025-10-18 12:58:39 +02:00
Daniel Rainer
8aeafa13c9 format strings: remove length modifier
This length modifier was reintroduced in
b7fe3190bb, which reverts
993b977c9b, a commit predating the removal
of redundant length modifiers in format strings.

Closes #11968
2025-10-18 12:55:36 +02:00
Johannes Altmanninger
07514c5df0 build-man-pages: use multiline string literal 2025-10-18 09:29:50 +02:00
Daniel Rainer
43f8d7478e style: change rustfmt edition to 2024
This commit adds `style_edition = "2024"` as a rustfmt config setting.
All other changes are automatically generated by `cargo fmt`.

The 2024 style edition fixes several bugs and changes some defaults.
https://doc.rust-lang.org/edition-guide/rust-2024/rustfmt-style-edition.html

Most of the changes made to our code result from a different sorting
method for `use` statements, improved ability to split long lines, and
contraction of short trailing expressions into single-line expressions.

While our MSRV is still 1.70, we use more recent toolchains for
development, so we can already benefit from the improvements of the new
style edition. Formatting is not require for building fish, so builds
with Rust 1.70 are not affected by this change.

More context can be found at
https://github.com/fish-shell/fish-shell/issues/11630#issuecomment-3406937077

Closes #11959
2025-10-18 09:29:50 +02:00
Daniel Rainer
1c3a6a463d style: move postfix comments to line above
These comments should be placed on top of the line they're commenting on.
Multi-line postfix comments will no longer be aligned using rustfmt's
style edition 2024, so fix this now to prevent formatting issues later.
For consistency, single-line postfix comments are also moved.

Part of #11959
2025-10-18 09:29:50 +02:00
Johannes Altmanninger
41636c8e35 Fix build on Linux/MIPS
Fixes #11965
2025-10-18 09:29:50 +02:00
Daniel Rainer
78e8f87e54 tests: remove duplicates
These were made duplicates by removing length modifiers.

Closes #11951
2025-10-16 16:43:03 +02:00
Daniel Rainer
8cb5ad9693 lints: avoid unwrapping after .is_ok() succeeded
This was flagged by nightly Rust.

Closes #11950
2025-10-16 16:43:03 +02:00
Daniel Rainer
83ece2161d lints: remove unused imports reported by nightly Rust
Part of #11950
2025-10-16 16:43:03 +02:00
Luca Weiss
63cda65815 completions/git: fix remotes missing in e.g. git fetch
Update the regex to allow remote names which are allowed in git, like
`foo-bar`, `foo.bar` or `😜`. Do not try to artificially limit the
allowed remote names through the regex.

Closes #11941
2025-10-16 16:43:03 +02:00
traal
165e0d0ed5 man.fish: fix for commands ! . : [
[ja: add test]

Closes #11956
2025-10-16 16:43:03 +02:00
王宇逸
cab6b97539 fs: open file with O_RDWR before fsync
On Cygwin, fsync() on the history and uvar file fails with "permission
denied". Work around this by opening the file in read-write mode
instead of read-only mode.

Closes #11948
2025-10-16 14:14:22 +02:00
Johannes Altmanninger
1c853a4d24 tests fish_{config,delta}: fix for BSD sed and BusyBox cat 2025-10-15 12:51:20 +02:00
Johannes Altmanninger
73fbd5d994 tests/fish_config prompt choose: no "embedded:functions/foo.fish" via source
Today fish script can't produce this type of behavior:

	$ type fish_prompt
	# Defined in embedded:functions/fish_prompt.fish @ line 2

The above is only created for autoloaded functions.

On standalone builds, "fish_config prompt choose" uses the "source"
builtin, making this line say is "Defined via `source`".

In future, we might want to make things consistent (one way or
the other).
2025-10-15 12:00:24 +02:00
Johannes Altmanninger
9a43acd77b fish_config theme choose: better variable name 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
28e8f45828 fish_config theme choose: correct error message for standalone builds 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
ac8ef4da9e fish_config: don't redo __fish_bin_dir computation
__fish_bin_dir is already computed exactly like this.
2025-10-15 12:00:24 +02:00
Johannes Altmanninger
52241712b4 fish_vi_key_bindings: hack to prevent error when overriding fish_vi_cursor
As discovered in https://matrix.to/#/!YLTeaulxSDauOOxBoR:matrix.org/$BFAjMPXBA9UczLT8h-7XpiyGUDv0Cz5g6ijbgdEVl_w
2025-10-15 12:00:24 +02:00
Johannes Altmanninger
584a21b34b tests/fish_config theme {choose,save} 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
b82920dc26 fish_delta: share logic between standalone and installed builds 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
ea69133e48 tests/fish_delta 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
f72ebca1e4 fish_delta: fix spurious output in standalone builds 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
40d772fde3 share: share logic between standalone and installed builds 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
d4837f9ef1 fish_config prompt choose: reset mode prompt and load it like right prompt
Fixes #11937
2025-10-15 12:00:24 +02:00
Alan Wu
b44209e14e Nim sample prompt: Don't print replace_one mode twice
Previously, it showed `[R]` twice when  `fish_bind_mode` is `replace_one`:

```console
┬─[user@hostname:~]─[00:00:01 PM][R]
─[R]
╰─>$ 
```

Closes #11942
2025-10-15 12:00:24 +02:00
Johannes Altmanninger
531269bb84 fish_config theme {choose,save}: share logic between standalone and installed builds 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
80faf5e805 fish_config theme show: load fish_config's internal functions too
To be used in the next commit.
The comment seems incorrect.
2025-10-15 12:00:24 +02:00
Johannes Altmanninger
0d5fd181be tests/fish_config: share logic between standalone and installed builds 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
8c279b854d fish_config theme {choose,save}: explode color list
Easier to cross-reference and update this way.
2025-10-15 12:00:24 +02:00
Johannes Altmanninger
f1f95c6867 fish_config theme {choose,save}: use early return 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
ecedd8caed fish_config theme: share logic between standalone and installed mode 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
ce5b5ec053 fish_config prompt {choose,save}: delete code clone
The "choose" one did not define fish_prompt unconditionally but that
looks like a bug.
2025-10-15 12:00:24 +02:00
Johannes Altmanninger
37e0a5ae8d fish_config prompt save: don't define right prompt unnecessarily
If no "fish_right_prompt" is defined, then we also don't need to
define and save an empty one.  We can do nothing, which seems cleaner.
It's also what "fish_config prompt choose" does.  Git blame shows no
explicit reason for for this inconsistency.

Probably the implicit reason is that when both styles where introduced
(both in 69074c1591 (fish_config: Remove right prompt in `choose` and
`save`, 2021-09-26)), that was before the "fish_config prompt choose:
make right prompt hack less weird" commit, so the "prompt choose"
code path didn't know whether the prompt existed until after checking,
hence "--erase" was more suitable.. but that inconsistency is gone.
2025-10-15 12:00:24 +02:00
Johannes Altmanninger
5792df9738 fish_config prompt: share logic between standalone and installed mode 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
73f0e14d90 tests/fish_config {prompt,theme} list 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
a216cfdd2f fish_{config,update_completions}: extract function for standalone builds 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
fad0e39cfa tests/fish_update_completions: simple system test 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
4532fc0c1b builtin status: sort list-files, like globs
This is important for deterministic behavior across both standalone
and installed builds in "fish_config prompt list".

I later realized that we could use "path sort" I suppose, but why
not fix the problem for everyone.
2025-10-15 12:00:24 +02:00
Johannes Altmanninger
03d21edd63 builtin status list-files: extract loop 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
f72b833e38 fish_config theme save: reset prompts in standalone builds too 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
45a3f4f1d6 fish_config prompt {choose,save}: remove dead code
Introduced by dd0d45f88f (fish_config prompt: remove dead code,
2025-09-28).
2025-10-15 12:00:24 +02:00
Johannes Altmanninger
3aab119e5b fish_config prompt save: extract loop 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
0a1ca206fa fish_config prompt save: use early return 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
028e05fff1 fish_config prompt choose: improve right prompt hack
Rather than first sourcing the prompt, and then erasing the right
prompt unless the prompt file defined the right prompt, start by
erasing the right prompt.  This is simpler (needs no conditional).
2025-10-15 12:00:24 +02:00
Johannes Altmanninger
dabd956216 fish_config prompt show: make indentation more helpful 2025-10-15 12:00:24 +02:00
Johannes Altmanninger
441f90fb55 __fish_data_list_files: fix for installed builds
This is only used in test code where bad output is ignored.
To be tested by a following commit.
2025-10-15 12:00:24 +02:00
Johannes Altmanninger
3b52d3db42 tests/fish_config prompt {choose,save} 2025-10-14 20:38:17 +02:00
Johannes Altmanninger
39943a8406 tests/fish_config: sort 2025-10-14 12:37:26 +02:00
Johannes Altmanninger
48bdf24964 __fish_data_with_file: better variable name 2025-10-14 12:37:26 +02:00
Johannes Altmanninger
fed2714e30 Rename abstraction for standalone/installed data files 2025-10-14 12:02:08 +02:00
Peter Ammon
c8262929e1 Restore statfs instead of statvfs behavior
MNT_LOCAL is NOT a flag for statvfs on macOS or possibly other BSDs
(except NetBSD which does not have statfs). Revert to statfs on other
platforms.
2025-10-13 14:15:05 -07:00
Peter Ammon
0ded3ed6f9 Rework ulimit constants
Use a macro to reduce the size of these modules.
2025-10-13 13:05:36 -07:00
Peter Ammon
2b428fae38 Fix remaining BSD build issues
Now building on FreeBSD / OpenBSD / NetBSD / DragonflyBSD
2025-10-13 11:16:48 -07:00
Johannes Altmanninger
b07adb532a chsh docs: look up fish in $PATH instead of /usr/local/bin
Now that the « chsh -s $(command -v) » approach should work both
in and outside fish, it seems like we should use that.

Non-macOS users probably shouldn't do this, but there's already a
big warning above this section.

Fixes #11931
2025-10-13 14:03:36 +02:00
Johannes Altmanninger
af72d4aebc Upgrade libc crate for _CS_PATH 2025-10-13 14:03:36 +02:00
Johannes Altmanninger
bfab595379 tests/checks/fish_config: try to work around intermittent CI failure
This is mysteriously failing intermittently on GitHub Actions CI
and OBS.  We get

  The CHECK on line 31 wants:
    {{\\x1b\\[m}}{{\\x1b\\[4m}}fish default{{\\x1b\\[m}}
  which failed to match line stdout:21:
    \x1b[38;2;7;114;161m/bright/vixens\x1b[m \x1b[38;2;34;94;121mjump\x1b[m \x1b[38;2;99;175;208m|\x1b[m \x1b[m\x1b[4mfish default\x1b[m

and it doesn't look like it's produced by the "grep -A1" below,
because the later output looks correct.

See https://github.com/fish-shell/fish-shell/pull/11923#discussion_r2417592216
2025-10-13 14:03:35 +02:00
Peter Ammon
d72adc0124 Add some vagrantfiles for building on BSDs
This only builds, doesn't run tests. Use:

    ./build_fish_on_bsd.sh dragonflybsd_6_4 freebsd_14_0 netbsd_9_3 openbsd_7_4

To build on all of them.

Note they don't all build yet.
2025-10-12 19:00:59 -07: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
704 changed files with 140680 additions and 62153 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,10 +5,10 @@ packages:
- gettext
- gmake
- llvm
- terminfo-db
- ninja
- pcre2
- py311-pexpect
- py311-sphinx
- python
- rust
- tmux
@@ -27,4 +27,4 @@ tasks:
ninja
- test: |
cd fish-shell/build
ninja test
ninja fish_run_tests

30
.builds/openbsd.yml Normal file
View File

@@ -0,0 +1,30 @@
image: openbsd/latest
packages:
- cmake
- gcc
- gettext
- gmake
- llvm
- ninja
- pcre2
- py3-pexpect
- py3-sphinx
- 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

@@ -8,18 +8,10 @@ linux_task:
container: &step
image: ghcr.io/krobelus/fish-ci/alpine:latest
memory: 4GB
- name: jammy
- name: ubuntu-oldest-supported
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/krobelus/fish-ci/ubuntu-oldest-supported:latest
tests_script:
# cirrus at times gives us 32 procs and 2 GB of RAM
# Unrestriced parallelism results in OOM
@@ -31,32 +23,11 @@ linux_task:
- ninja fish_run_tests
only_if: $CIRRUS_REPO_OWNER == 'fish-shell'
linux_arm_task:
matrix:
- 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
- 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'
freebsd_task:
matrix:
- name: FreeBSD 14
- name: FreeBSD Stable
freebsd_instance:
image: freebsd-14-3-release-amd64-ufs
image: freebsd-14-3-release-amd64-ufs # updatecli.d/cirrus-freebsd.yml
tests_script:
- 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

View File

@@ -0,0 +1,41 @@
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_pcre: ${{ inputs.include_pcre }}
run: |
set -x
: "optional dependencies"
sudo apt install \
gettext \
$(if $include_pcre; then echo libpcre2-dev; 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
if: ${{ inputs.include_sphinx == 'true' }}

View File

@@ -1,14 +0,0 @@
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,22 @@
name: Install sphinx
permissions:
contents: read
runs:
using: "composite"
steps:
- shell: bash
run: |
set -x
pip=$(command -v pip pip3 | head -1)
sudo "$pip" install uv --break-system-packages
# Check that pyproject.toml and the lock file are in sync.
# TODO Use "uv" to install Python as well.
: 'Note that --no-managed-python below would be implied but be explicit'
uv='env UV_PYTHON=python uv --no-managed-python'
$uv lock --check
# Install globally.
sudo $uv pip install --group=dev --system --break-system-packages
# Smoke test.
python -c 'import sphinx; 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.91 ;; # updatecli.d/rust.yml
(msrv) echo 1.85 ;; # updatecli.d/rust.yml
(*)
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

@@ -14,7 +14,8 @@ permissions:
runs:
using: "composite"
steps:
- uses: dtolnay/rust-toolchain@1.70
- uses: ./.github/actions/rust-toolchain
with:
toolchain_channel: "msrv"
targets: ${{ inputs.targets }}
components: ${{ inputs.components}}
components: ${{ inputs.components }}

View File

@@ -14,7 +14,8 @@ permissions:
runs:
using: "composite"
steps:
- uses: dtolnay/rust-toolchain@1.90
- uses: ./.github/actions/rust-toolchain
with:
toolchain_channel: "stable"
targets: ${{ inputs.targets }}
components: ${{ inputs.components }}

View File

@@ -0,0 +1,64 @@
name: Build Docker test images
on:
push:
branches:
- master
paths:
- 'docker/**'
workflow_dispatch:
concurrency:
group: docker-builds
env:
REGISTRY: ghcr.io
NAMESPACE: fish-ci
jobs:
docker-build:
if: github.repository_owner == 'fish-shell'
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: ubuntu-oldest-supported
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 }}

View File

@@ -1,4 +1,4 @@
name: Rust checks
name: Lint
on: [push, pull_request]
@@ -6,25 +6,39 @@ permissions:
contents: read
jobs:
rustfmt:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/rust-toolchain@stable
with:
components: rustfmt
- name: cargo fmt
run: cargo fmt --check
- 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
- name: check rustfmt
run: find build.rs crates src -type f -name '*.rs' | xargs rustfmt --check
clippy-stable:
clippy:
runs-on: ubuntu-latest
strategy:
matrix:
features: ["", "--no-default-features"]
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@stable
- uses: ./.github/actions/rust-toolchain
with:
toolchain_channel: ${{ matrix.rust_version }}
components: clippy
- name: Install deps
run: |
@@ -32,19 +46,6 @@ jobs:
- name: cargo clippy
run: cargo clippy --workspace --all-targets ${{ matrix.features }} -- --deny=warnings
clippy-msrv:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/rust-toolchain@oldest-supported
with:
components: clippy
- name: Install deps
run: |
sudo apt install gettext
- name: cargo clippy
run: cargo clippy --workspace --all-targets -- --deny=warnings
rustdoc:
runs-on: ubuntu-latest
steps:

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

@@ -41,8 +41,8 @@ jobs:
# 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
run: sudo apt install cmake gettext ninja-build python3-pip
- uses: ./.github/actions/install-sphinx
- name: Create tarball
run: |
set -x
@@ -52,7 +52,10 @@ jobs:
# 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"
gpg_public_key_url=https://github.com/${{ github.actor }}.gpg
curl -sS "$gpg_public_key_url" | grep 'PGP PUBLIC KEY BLOCK' -A5
FISH_GPG_PUBLIC_KEY_URL=$gpg_public_key_url \
sh -x ./build_tools/release-notes.sh >"$relnotes"
# Delete title
sed -n 1p "$relnotes" | grep -q "^## fish .*"
sed -n 2p "$relnotes" | grep -q '^$'
@@ -68,7 +71,7 @@ jobs:
packages-for-linux:
needs: [is-release-tag]
name: Build single-file fish for Linux (experimental)
name: Build single-file fish for Linux
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -80,7 +83,8 @@ jobs:
with:
targets: x86_64-unknown-linux-musl,aarch64-unknown-linux-musl
- name: Install dependencies
run: sudo apt install crossbuild-essential-arm64 musl-tools python3-sphinx
run: sudo apt install crossbuild-essential-arm64 gettext musl-tools
- uses: ./.github/actions/install-sphinx
- name: Build statically-linked executables
run: |
set -x
@@ -143,13 +147,12 @@ jobs:
# 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
targets: aarch64-apple-darwin,x86_64-apple-darwin
- name: Install dependencies
run: brew install gettext
- uses: ./.github/actions/install-sphinx
- name: Build and codesign
run: |
die() { echo >&2 "$*"; exit 1; }

View File

@@ -1,4 +1,4 @@
name: make fish_run_tests
name: Test
on: [push, pull_request]
@@ -11,18 +11,17 @@ permissions:
jobs:
ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- 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
- uses: ./.github/actions/install-sphinx-markdown-builder
- name: cmake
run: |
mkdir build && cd build
@@ -43,18 +42,20 @@ jobs:
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: ./.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"
@@ -69,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:
@@ -79,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
@@ -89,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:
@@ -119,38 +121,8 @@ 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: ./.github/actions/rust-toolchain@oldest-supported
# - 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.
@@ -163,14 +135,45 @@ jobs:
# --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
- uses: ./.github/actions/install-sphinx
- name: cmake
run: |
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Debug ..
FISH_TEST_MAX_CONCURRENCY=1 \
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
- name: Setup tmate session
if: ${{ failure() }}
uses: mxschmitt/action-tmate@v3
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 run `build_tools/check.sh` 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 ]
cargo test

4
.gitignore vendored
View File

@@ -7,7 +7,6 @@
*.DS_Store
*.a
*.app
*.d
*.dll
*.dylib
*.exe
@@ -105,3 +104,6 @@ target/
# JetBrains editors.
.idea/
# AI slop
.claude/

1
.rustfmt.toml Normal file
View File

@@ -0,0 +1 @@
edition = "2024"

View File

@@ -1,3 +1,102 @@
fish 4.2.0 (released November 10, 2025)
=======================================
Notable improvements and fixes
------------------------------
- History-based autosuggestions now include multi-line commands.
- A :ref:`transient prompt <transient-prompt>` containing more lines than the final prompt will now be cleared properly (:issue:`11875`).
- Taiwanese Chinese translations have been added.
- French translations have been supplemented (:issue:`11842`).
Deprecations and removed features
---------------------------------
- fish now assumes UTF-8 for character encoding even if the system does not have a UTF-8 locale.
Input bytes which are not valid UTF-8 are still round-tripped correctly.
For example, file paths using legacy encodings can still be used,
but may be rendered differently on the command line.
- On systems where no multi-byte locale is available,
fish will no longer fall back to using ASCII replacements for :ref:`Unicode characters <term-compat-unicode-codepoints>` such as "…".
Interactive improvements
------------------------
- The title of the terminal tab can now be set separately from the window title by defining the :doc:`fish_tab_title <cmds/fish_tab_title>` function (:issue:`2692`).
- fish now hides the portion of a multiline prompt that is scrolled out of view due to a huge command line. This prevents duplicate lines after repainting with partially visible prompt (:issue:`11911`).
- :doc:`fish_config prompt <cmds/fish_config>`'s ``choose`` and ``save`` subcommands have been taught to reset :doc:`fish_mode_prompt <cmds/fish_mode_prompt>` in addition to the other prompt functions (:issue:`11937`).
- fish no longer force-disables mouse capture (DECSET/DECRST 1000),
so you can use those commands
to let mouse clicks move the cursor or select completions items (:issue:`4918`).
- The :kbd:`alt-p` binding no longer adds a redundant space to the command line.
- When run as a login shell on macOS, fish now sets :envvar:`MANPATH` correctly when that variable was already present in the environment (:issue:`10684`).
- A Windows-specific case of the :doc:`web-based config <cmds/fish_config>` failing to launch has been fixed (:issue:`11805`).
- A MSYS2-specific workaround for Konsole and WezTerm has been added,
to prevent them from using the wrong working directory when opening new tabs (:issue:`11981`).
For distributors and developers
-------------------------------
- Release tags and source code tarballs are GPG-signed again (:issue:`11996`).
- Documentation in release tarballs is now built with the latest version of Sphinx,
which means that pre-built man pages include :ref:`OSC 8 hyperlinks <term-compat-osc-8>`.
- The Sphinx dependency is now specified in ``pyproject.toml``,
which allows you to use `uv <https://github.com/astral-sh/uv>`__ to provide Sphinx for building documentation (e.g. ``uv run cargo install --path .``).
- The minimum supported Rust version (MSRV) has been updated to 1.85.
- The standalone build mode has been made the default.
This means that the files in ``$CMAKE_INSTALL_PREFIX/share/fish`` will not be used anymore, except for HTML docs.
As a result, future upgrades will no longer break running shells
if one of fish's internal helper functions has been changed in the updated version.
For now, the data files are still installed redundantly,
to prevent upgrades from breaking already-running shells (:issue:`11921`).
To reverse this change (which should not be necessary),
patch out the ``embed-data`` feature from ``cmake/Rust.cmake``.
This option will be removed in future.
- OpenBSD 7.8 revealed an issue with fish's approach for displaying builtin man pages, which has been fixed.
Regression fixes:
-----------------
- (from 4.1.0) Fix the :doc:`web-based config <cmds/fish_config>` for Python 3.9 and older (:issue:`12039`).
- (from 4.1.0) Correct wrong terminal modes set by ``fish -c 'read; cat`` (:issue:`12024`).
- (from 4.1.0) On VTE-based terminals, stop redrawing the prompt on resize again, to avoid glitches.
- (from 4.1.0) On MSYS2, fix saving/loading of universal variables (:issue:`11948`).
- (from 4.1.0) Fix error using ``man`` for the commands ``!`` ``.`` ``:`` ``[`` ``{`` (:issue:`11955`).
- (from 4.1.0) Fix build issues on illumos systems (:issue:`11982`).
- (from 4.1.0) Fix crash on invalid :doc:`function <cmds/function>` command (:issue:`11912`).
- (from 4.0.0) Fix build on SPARC and MIPS Linux by disabling ``SIGSTKFLT``.
- (from 4.0.0) Fix crash when passing negative PIDs to builtin :doc:`wait <cmds/wait>` (:issue:`11929`).
- (from 4.0.0) On Linux, fix :doc:`status fish-path <cmds/status>` output when fish has been reinstalled since it was started.
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)
========================================
@@ -30,7 +129,7 @@ Deprecations and removed features
- 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>`.
- 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.
@@ -83,9 +182,6 @@ Improved terminal support
- 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 :doc:`Terminal Compatibility <terminal-compatibility>` (also accessible via ``man fish-terminal-compatibility``) lists the terminal control sequences used by fish.
- 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``.
Other improvements
------------------
@@ -827,7 +923,7 @@ Notable improvements and fixes
which expands ``!!`` to the last history item, anywhere on the command line, mimicking other shells' history expansion.
See :ref:`the documentation <cmd-abbr>` for more.
See :doc:`the documentation <cmds/abbr>` for more.
- ``path`` gained a new ``mtime`` subcommand to print the modification time stamp for files. For example, this can be used to handle cache file ages (:issue:`9057`)::
> touch foo
@@ -1252,7 +1348,7 @@ Scripting improvements
two
'blue '
- ``$fish_user_paths`` is now automatically deduplicated to fix a common user error of appending to it in config.fish when it is universal (:issue:`8117`). :ref:`fish_add_path <cmd-fish_add_path>` remains the recommended way to add to $PATH.
- ``$fish_user_paths`` is now automatically deduplicated to fix a common user error of appending to it in config.fish when it is universal (:issue:`8117`). :doc:`fish_add_path <cmds/fish_add_path>` remains the recommended way to add to $PATH.
- ``return`` can now be used outside functions. In scripts, it does the same thing as ``exit``. In interactive mode,it sets ``$status`` without exiting (:issue:`8148`).
- An oversight prevented all syntax checks from running on commands given to ``fish -c`` (:issue:`8171`). This includes checks such as ``exec`` not being allowed in a pipeline, and ``$$`` not being a valid variable. Generally, another error was generated anyway.
- ``fish_indent`` now correctly reformats tokens that end with a backslash followed by a newline (:issue:`8197`).

View File

@@ -28,6 +28,24 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
# This defines the FBVF variable.
include(Version)
# Set up the docs.
include(cmake/Docs.cmake)
# Tell Cargo where our build directory is so it can find Cargo.toml.
set(VARS_FOR_CARGO
"FISH_CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR}"
"PREFIX=${CMAKE_INSTALL_PREFIX}"
"DOCDIR=${CMAKE_INSTALL_FULL_DOCDIR}"
"DATADIR=${CMAKE_INSTALL_FULL_DATADIR}"
"SYSCONFDIR=${CMAKE_INSTALL_FULL_SYSCONFDIR}"
"BINDIR=${CMAKE_INSTALL_FULL_BINDIR}"
"CARGO_TARGET_DIR=${FISH_RUST_BUILD_DIR}"
"CARGO_BUILD_RUSTC=${Rust_COMPILER}"
"${FISH_PCRE2_BUILDFLAG}"
"RUSTFLAGS=$ENV{RUSTFLAGS} ${rust_debugflags}"
"FISH_SPHINX=${SPHINX_EXECUTABLE}"
)
# Let fish pick up when we're running out of the build directory without installing
get_filename_component(REAL_CMAKE_BINARY_DIR "${CMAKE_BINARY_DIR}" REALPATH)
get_filename_component(REAL_CMAKE_SOURCE_DIR "${CMAKE_SOURCE_DIR}" REALPATH)
@@ -71,9 +89,6 @@ create_target(fish_indent)
# Define fish_key_reader.
create_target(fish_key_reader)
# Set up the docs.
include(cmake/Docs.cmake)
# Set up tests.
include(cmake/Tests.cmake)

View File

@@ -71,7 +71,7 @@ Completion scripts should
1. Use as few dependencies as possible - try to use fish's builtins like ``string`` instead of ``grep`` and ``awk``,
use ``python`` to read json instead of ``jq`` (because it's already a soft dependency for fish's tools)
2. If it uses a common unix tool, use posix-compatible invocations - ideally it would work on GNU/Linux, macOS, the BSDs and other systems
2. If it uses a common unix tool, use POSIX-compatible invocations - ideally it would work on GNU/Linux, macOS, the BSDs and other systems
3. Option and argument descriptions should be kept short.
The shorter the description, the more likely it is that fish can use more columns.
4. Function names should start with ``__fish``, and functions should be kept in the completion file unless they're used elsewhere.
@@ -100,33 +100,18 @@ The builtins and various functions shipped with fish are documented in doc_src/c
Code Style
==========
To ensure your changes conform to the style rules run
::
build_tools/style.fish
before committing your change. That will run our autoformatters:
For formatting, we use:
- ``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
youve merged another persons change and want to check that its style
is acceptable. However, in that case it will run ``clang-format`` to
ensure the entire file, not just the lines modified by the commit,
conform to the style.
If you want to check the style of the entire code base run
To reformat files, there is a script
::
build_tools/style.fish --all
That command will refuse to restyle any files if you have uncommitted
changes.
build_tools/style.fish somefile.rs some.fish
Fish Script Style Guide
-----------------------
@@ -178,10 +163,10 @@ made to run fish_indent via e.g.
(add-hook 'fish-mode-hook (lambda ()
(add-hook 'before-save-hook 'fish_indent-before-save)))
Rust Style Guide
----------------
Minimum Supported Rust Version (MSRV) Policy
--------------------------------------------
Use ``cargo fmt`` and ``cargo clippy``. Clippy warnings can be turned off if there's a good reason to.
We support at least the version of ``rustc`` available in Debian Stable.
Testing
=======
@@ -196,78 +181,47 @@ You are strongly encouraged to add tests when changing the functionality
of fish, especially if you are fixing a bug to help ensure there are no
regressions in the future (i.e., we dont reintroduce the bug).
The tests can be found in three places:
Unit tests live next to the implementation in Rust source files, in inline submodules (``mod tests {}``).
- src/tests for unit tests.
- tests/checks for script tests, run by `littlecheck <https://github.com/ridiculousfish/littlecheck>`__
- tests/pexpects for interactive tests using `pexpect <https://pexpect.readthedocs.io/en/stable/>`__
System tests live in ``tests/``:
When in doubt, the bulk of the tests should be added as a littlecheck test in tests/checks, as they are the easiest to modify and run, and much faster and more dependable than pexpect tests. The syntax is fairly self-explanatory. It's a fish script with the expected output in ``# CHECK:`` or ``# CHECKERR:`` (for stderr) comments.
If your littlecheck test has a specific dependency, use ``# REQUIRE: ...`` with a posix sh script.
- ``tests/checks`` are run by `littlecheck <https://github.com/ridiculousfish/littlecheck>`__
and test noninteractive (script) behavior,
except for ``tests/checks/tmux-*`` which test interactive scenarios.
- ``tests/pexpects`` tests interactive scenarios using `pexpect <https://pexpect.readthedocs.io/en/stable/>`__
The pexpects are written in python and can simulate input and output to/from a terminal, so they are needed for anything that needs actual interactivity. The runner is in tests/pexpect_helper.py, in case you need to modify something there.
When in doubt, the bulk of the tests should be added as a littlecheck test in tests/checks, as they are the easiest to modify and run, and much faster and more dependable than pexpect tests.
The syntax is fairly self-explanatory.
It's a fish script with the expected output in ``# CHECK:`` or ``# CHECKERR:`` (for stderr) comments.
If your littlecheck test has a specific dependency, use ``# REQUIRE: ...`` with a POSIX sh script.
These tests can be run via the tests/test_driver.py python script, which will set up the environment.
The pexpect tests are written in Python and can simulate input and output to/from a terminal, so they are needed for anything that needs actual interactivity.
The runner is in tests/pexpect_helper.py, in case you need to modify something there.
These tests can be run via the tests/test_driver.py Python script, which will set up the environment.
It sets up a temporary $HOME and also uses it as the current directory, so you do not need to create a temporary directory in them.
If you need a command to do something weird to test something, maybe add it to the ``fish_test_helper`` binary (in tests/fish_test_helper.c), or see if it can already do it.
If you need a command to do something weird to test something, maybe add it to the ``fish_test_helper`` binary (in ``tests/fish_test_helper.c``).
Local testing
-------------
The tests can be run on your local computer on all operating systems.
The tests can be run on your local system::
::
cmake path/to/fish-shell
make fish_run_tests
Or you can run them on a fish, without involving cmake::
cargo build
cargo test # for the unit tests
tests/test_driver.py target/debug # for the script and interactive tests
cargo build
# Run unit tests
cargo test
# Run system tests
tests/test_driver.py target/debug
# Run a specific system test.
tests/test_driver.py target/debug tests/checks/abbr.fish
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.
In this example we're in the root of the workspace and have run ``cargo build`` without ``--release``, so it's a debug build.
Git hooks
---------
To run all tests and linters, use::
Since developers sometimes forget to run the tests, it can be helpful to
use git hooks (see githooks(5)) to automate it.
One possibility is a pre-push hook script like this one:
.. code:: sh
#!/bin/sh
#### A pre-push hook for the fish-shell project
# This will run the tests when a push to master is detected, and will stop that if the tests fail
# Save this as .git/hooks/pre-push and make it executable
protected_branch='master'
# 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 [ "$to" = "refs/heads/$protected_branch" ]; then
isprotected=true
fi
done
if "$isprotected"; then
echo "Running checks before push to master"
build_tools/check.sh
fi
This will check if the push is to the master branch and, if it is, only
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.
build_tools/check.sh
Contributing Translations
=========================
@@ -343,7 +297,7 @@ command-line and graphical user interface programs. For simple use, you can use
Open up the PO file, for example ``po/sv.po``, and you'll see something like::
msgid "%ls: No suitable job\n"
msgid "%s: No suitable job\n"
msgstr ""
The ``msgid`` here is the "name" of the string to translate, typically the English string to translate.
@@ -351,10 +305,10 @@ 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.
@@ -381,7 +335,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.
@@ -404,6 +358,12 @@ You can use either single or double quotes to enclose the
message to be translated. You can also optionally include spaces after
the opening parentheses or before the closing parentheses.
Updating Dependencies
=====================
To update dependencies, run ``build_tools/update-dependencies.sh``.
This currently requires `updatecli <https://github.com/updatecli/updatecli>`__ and a few other tools.
Versioning
==========

480
Cargo.lock generated
View File

@@ -1,6 +1,15 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
@@ -10,15 +19,15 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "autocfg"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bitflags"
version = "2.6.0"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "block-buffer"
@@ -30,11 +39,22 @@ dependencies = [
]
[[package]]
name = "cc"
version = "1.2.7"
name = "bstr"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "cc"
version = "1.2.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
@@ -42,9 +62,9 @@ dependencies = [
[[package]]
name = "cfg-if"
version = "1.0.0"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
@@ -54,9 +74,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "cpufeatures"
version = "0.2.16"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
@@ -82,16 +102,37 @@ dependencies = [
]
[[package]]
name = "equivalent"
version = "1.0.1"
name = "dirs"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.11"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys",
@@ -103,12 +144,19 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "find-msvc-tools"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
[[package]]
name = "fish"
version = "4.1.0"
version = "4.2.0"
dependencies = [
"bitflags",
"cc",
"cfg-if",
"errno",
"fish-build-helper",
"fish-build-man-pages",
@@ -116,8 +164,10 @@ dependencies = [
"fish-gettext-maps",
"fish-gettext-mo-file-parser",
"fish-printf",
"fish-tempfile",
"libc",
"lru",
"macro_rules_attribute",
"nix",
"num-traits",
"once_cell",
@@ -181,6 +231,13 @@ dependencies = [
"widestring",
]
[[package]]
name = "fish-tempfile"
version = "0.0.0"
dependencies = [
"nix",
]
[[package]]
name = "fnv"
version = "1.0.7"
@@ -189,25 +246,61 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "generic-array"
version = "0.14.7"
version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "hashbrown"
version = "0.15.2"
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
]
[[package]]
name = "globset"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"allocator-api2",
"equivalent",
@@ -216,34 +309,44 @@ dependencies = [
[[package]]
name = "jobserver"
version = "0.1.32"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
"getrandom 0.3.4",
"libc",
]
[[package]]
name = "libc"
version = "0.2.172"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "libredox"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
dependencies = [
"bitflags",
"libc",
]
[[package]]
name = "lock_api"
version = "0.4.12"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.22"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "lru"
@@ -255,10 +358,26 @@ dependencies = [
]
[[package]]
name = "memchr"
version = "2.7.4"
name = "macro_rules_attribute"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
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.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "minimal-lexical"
@@ -299,15 +418,21 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.20.2"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "parking_lot"
version = "0.12.3"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
@@ -315,17 +440,23 @@ dependencies = [
[[package]]
name = "parking_lot_core"
version = "0.9.10"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
"windows-link",
]
[[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"
@@ -424,34 +555,40 @@ dependencies = [
[[package]]
name = "pkg-config"
version = "0.3.31"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "portable-atomic"
version = "1.10.0"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
name = "proc-macro2"
version = "1.0.95"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.8.5"
@@ -469,13 +606,41 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "redox_syscall"
version = "0.5.8"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
"getrandom 0.2.16",
"libredox",
"thiserror",
]
[[package]]
name = "regex-automata"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "rsconf"
version = "0.2.2"
@@ -487,9 +652,9 @@ dependencies = [
[[package]]
name = "rust-embed"
version = "8.7.2"
version = "8.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a"
checksum = "fb44e1917075637ee8c7bcb865cf8830e3a92b5b1189e44e3a0ab5a0d5be314b"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
@@ -498,23 +663,25 @@ dependencies = [
[[package]]
name = "rust-embed-impl"
version = "8.7.2"
version = "8.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c"
checksum = "382499b49db77a7c19abd2a574f85ada7e9dbe125d5d1160fa5cad7c4cf71fc9"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"shellexpand",
"syn",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "8.7.2"
version = "8.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594"
checksum = "21fcbee55c2458836bcdbfffb6ec9ba74bbc23ca7aa6816015a3dd2c4d8fc185"
dependencies = [
"globset",
"sha2",
"walkdir",
]
@@ -530,9 +697,9 @@ dependencies = [
[[package]]
name = "scc"
version = "2.3.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28e1c91382686d21b5ac7959341fcb9780fa7c03773646995a87c950fa7be640"
checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc"
dependencies = [
"sdd",
]
@@ -545,9 +712,38 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sdd"
version = "3.0.5"
version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478f121bb72bbf63c52c93011ea1791dca40140dfe13f8336c4c5ac952c33aa9"
checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serial_test"
@@ -574,15 +770,24 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.10.8"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shellexpand"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb"
dependencies = [
"dirs",
]
[[package]]
name = "shlex"
version = "1.3.0"
@@ -597,15 +802,15 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]]
name = "smallvec"
version = "1.13.2"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "syn"
version = "2.0.95"
version = "2.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a"
checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b"
dependencies = [
"proc-macro2",
"quote",
@@ -625,16 +830,36 @@ dependencies = [
]
[[package]]
name = "typenum"
version = "1.17.0"
name = "thiserror"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "unicode-ident"
version = "1.0.14"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06"
[[package]]
name = "unicode-segmentation"
@@ -644,9 +869,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-width"
version = "0.2.0"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
[[package]]
name = "unix_path"
@@ -680,89 +905,52 @@ dependencies = [
]
[[package]]
name = "widestring"
version = "1.2.0"
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "widestring"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
[[package]]
name = "winapi-util"
version = "0.1.9"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.59.0"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-targets",
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"

View File

@@ -1,16 +1,16 @@
[workspace]
resolver = "2"
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"
rust-version = "1.85"
edition = "2024"
repository = "https://github.com/fish-shell/fish-shell"
[workspace.dependencies]
bitflags = "2.5.0"
cc = "1.0.94"
cfg-if = "1.0.3"
errno = "0.3.0"
fish-build-helper = { path = "crates/build-helper" }
fish-build-man-pages = { path = "crates/build-man-pages" }
@@ -18,7 +18,8 @@ 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"
fish-tempfile = { path = "crates/tempfile" }
libc = "0.2.177"
# lru pulls in hashbrown by default, which uses a faster (though less DoS resistant) hashing algo.
# disabling default features uses the stdlib instead, but it doubles the time to rewrite the history
# files as of 22 April 2024.
@@ -45,7 +46,12 @@ proc-macro2 = "1.0"
# minimum supported version to 10.12.
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
rsconf = "0.2.2"
rust-embed = { version = "8.7.2", features = ["deterministic-timestamps"] }
rust-embed = { version = "8.7.2", features = [
"deterministic-timestamps",
"include-exclude",
"interpolate-folder-path",
] }
serial_test = { version = "3", default-features = false }
# We need 0.9.0 specifically for some crash fixes.
terminfo = "0.9.0"
@@ -64,7 +70,7 @@ debug = true
[package]
name = "fish"
version = "4.1.0"
version = "4.2.0"
edition.workspace = true
rust-version.workspace = true
default-run = "fish"
@@ -76,14 +82,17 @@ 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
fish-tempfile.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
@@ -97,9 +106,18 @@ widestring.workspace = true
portable-atomic.workspace = true
[target.'cfg(windows)'.dependencies]
rust-embed = { workspace = true, optional = true, features = ["deterministic-timestamps", "debug-embed"] }
rust-embed = { workspace = true, optional = true, features = [
"deterministic-timestamps",
"debug-embed",
"include-exclude",
"interpolate-folder-path",
] }
[target.'cfg(not(windows))'.dependencies]
rust-embed = { workspace = true, optional = true, features = ["deterministic-timestamps"] }
rust-embed = { workspace = true, optional = true, features = [
"deterministic-timestamps",
"include-exclude",
"interpolate-folder-path",
] }
[dev-dependencies]
serial_test.workspace = true
@@ -152,9 +170,14 @@ rust.non_camel_case_types = "allow"
rust.non_upper_case_globals = "allow"
rust.unknown_lints = "allow"
rust.unstable_name_collisions = "allow"
rustdoc.private_intra_doc_links = "allow"
clippy.len_without_is_empty = "allow" # we're not a library crate
clippy.let_and_return = "allow"
clippy.manual_range_contains = "allow"
clippy.needless_return = "allow"
clippy.needless_lifetimes = "allow"
clippy.needless_return = "allow"
clippy.new_without_default = "allow"
clippy.option_map_unit_fn = "allow"
# We do not want to use the e?print(ln)?! macros.
# These lints flag their use.

View File

@@ -90,7 +90,7 @@ 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``, ``sh``, ``sort``, ``tee``, ``tr``,
``ls``, ``mkdir``, ``mkfifo``, ``rm``, ``sh``, ``sort``, ``tee``, ``tr``,
``uname`` and ``sed`` at least, but the full coreutils plus ``find`` and
``awk`` is preferred)
@@ -100,6 +100,7 @@ The following optional features also have specific requirements:
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
- the :ref:`alt-o <shared-binds-alt-o>` binding requires the ``file`` program.
- system clipboard integration (with the default Ctrl-V and Ctrl-X
bindings) require either the ``xsel``, ``xclip``,
``wl-copy``/``wl-paste`` or ``pbcopy``/``pbpaste`` utilities
@@ -111,14 +112,12 @@ The following optional features also have specific requirements:
Building
--------
.. _dependencies-1:
Dependencies
~~~~~~~~~~~~
Compiling fish requires:
- Rust (version 1.70 or later)
- Rust (version 1.85 or later)
- 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
@@ -128,7 +127,7 @@ Compiling fish requires:
Sphinx is also optionally required to build the documentation from a
cloned git repository.
Additionally, running the full test suite requires Python 3.5+, 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -140,7 +139,7 @@ links above, and up-to-date `development builds of fish are available for many p
To install into ``/usr/local``, run:
.. code:: bash
.. code:: shell
mkdir build; cd build
cmake ..
@@ -165,34 +164,41 @@ In addition to the normal CMake build options (like ``CMAKE_INSTALL_PREFIX``), f
- 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)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Building fish with Cargo
~~~~~~~~~~~~~~~~~~~~~~~~
You can also build fish with the data files embedded.
You can also build fish with Cargo.
This example uses `uv <https://github.com/astral-sh/uv>`__ to install Sphinx (which is used for man-pages and ``--help`` options).
You can also install Sphinx another way and drop the ``uv run --no-managed-python`` prefix.
This will include all the datafiles like the included functions or web configuration tool in the main ``fish`` binary.
.. code:: shell
Fish will then read these right from its own binary, and print them out when needed. Some files, like the webconfig tool and the manpage completion generator, will be extracted to a temporary directory on-demand. You can list the files with ``status list-files`` and print one with ``status get-file path/to/file`` (e.g. ``status get-file functions/fish_prompt.fish`` to get the default prompt).
git clone https://github.com/fish-shell/fish-shell
cd fish-shell
To install fish with embedded files, just use ``cargo``, like::
# Optional: check out a specific version rather than building the latest
# development version.
git checkout "$(git for-each-ref refs/tags/ | awk '$2 == "tag" { print $3 }' | tail -1)"
cargo install --path /path/to/fish # if you have a git clone
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
uv run --no-managed-python \
cargo install --path .
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).
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.
This will place standalone binaries in ``~/.cargo/bin/``, but you can move them wherever you want.
To disable translations, disable the ``localize-messages`` feature by passing ``--no-default-features --features=embed-data`` to cargo.
You can also link this build statically (but not against glibc) and move it to other computers.
Here are the remaining advantages of a full installation, as currently done by CMake:
- Man pages like ``fish(1)`` installed in standard locations, easily accessible from outside fish.
- A local copy of the HTML documentation, typically accessed via the ``help`` fish function.
In Cargo builds, ``help`` will redirect to `<https://fishshell.com/docs/current/>`__
- Ability to use our CMake options extra_functionsdir, extra_completionsdir and extra_confdir,
(also recorded in ``$PREFIX/share/pkgconfig/fish.pc``)
which are used by some package managers to house third-party completions.
Regardless of build system, fish uses ``$XDG_DATA_DIRS/{vendor_completion.d,vendor_conf.d,vendor_functions.d}``.
Contributing Changes to the Code
--------------------------------

182
build.rs
View File

@@ -1,9 +1,6 @@
#![allow(clippy::uninlined_format_args)]
use fish_build_helper::{fish_build_dir, workspace_root};
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, PathBuf};
fn canonicalize<P: AsRef<Path>>(path: P) -> PathBuf {
@@ -17,7 +14,7 @@ fn main() {
// language server.
rsconf::set_env_value(
"FISH_BUILD_DIR",
"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(),
@@ -31,16 +28,17 @@ fn main() {
);
// 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,
// the source directory is the current working directory of the build script
rsconf::set_env_value("FISH_BUILD_VERSION", version);
std::env::set_var("FISH_BUILD_VERSION", version);
// safety: single-threaded code.
unsafe { std::env::set_var("FISH_BUILD_VERSION", version) };
// These are necessary if built with embedded functions,
// but only in release builds (because rust-embed in debug builds reads from the filesystem).
@@ -51,9 +49,6 @@ fn main() {
#[cfg(feature = "gettext-extract")]
rsconf::rebuild_if_env_changed("FISH_GETTEXT_EXTRACTION_FILE");
rsconf::rebuild_if_path_changed("src/libc.c");
cc::Build::new().file("src/libc.c").compile("flibc.a");
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
@@ -61,7 +56,9 @@ fn main() {
detect_cfgs(&mut target);
#[cfg(all(target_env = "gnu", target_feature = "crt-static"))]
compile_error!("Statically linking against glibc has unavoidable crashes and is unsupported. Use dynamic linking or link statically against musl.");
compile_error!(
"Statically linking against glibc has unavoidable crashes and is unsupported. Use dynamic linking or link statically against musl."
);
}
/// Check target system support for certain functionality dynamically when the build is invoked,
@@ -81,53 +78,45 @@ 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),
("using_cmake", &|_| option_env!("FISH_CMAKE_BINARY_DIR").is_some()),
("cygwin", &detect_cygwin),
("small_main_stack", &has_small_stack),
// See if libc supports the thread-safe localeconv_l(3) alternative to localeconv(3).
("localeconv_l", &|target| {
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
@@ -136,10 +125,10 @@ 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();
}
@@ -151,7 +140,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)
is_bsd
}
/// Rust sets the stack size of newly created threads to a sane value, but is at at the mercy of the
@@ -160,31 +149,28 @@ fn detect_bsd(_: &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"))]
{
use core::ffi;
extern "C" {
fn pthread_get_stacksize_np(thread: *const ffi::c_void) -> usize;
fn pthread_self() -> *const ffi::c_void;
unsafe extern "C" {
unsafe fn pthread_get_stacksize_np(thread: *const ffi::c_void) -> usize;
unsafe fn pthread_self() -> *const ffi::c_void;
}
// build.rs is executed on the main thread, so we are getting the main thread's stack size.
// 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
}
}
@@ -192,59 +178,77 @@ fn setup_paths() {
#[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>) -> Option<PathBuf>,
) -> Option<PathBuf> {
rsconf::rebuild_if_env_changed(env_var_name);
let maybe_path = f(env_var(env_var_name));
if let Some(path) = maybe_path.as_ref() {
rsconf::set_env_value(env_var_name, path.to_str().unwrap());
}
var
maybe_path
}
let prefix = PathBuf::from(env::var("PREFIX").unwrap_or("/usr/local".to_string()));
rsconf::rebuild_if_env_changed("PREFIX");
rsconf::set_env_value("PREFIX", prefix.to_str().unwrap());
let datadir = get_path("DATADIR", "share/", &prefix);
let sysconfdir = get_path(
"SYSCONFDIR",
// Embedded builds use "/etc," not "./share/etc".
if cfg!(feature = "embed-data") {
"/etc/"
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 {
"etc/"
},
&datadir,
);
rsconf::set_env_value("SYSCONFDIR", sysconfdir.to_str().unwrap());
rsconf::rebuild_if_env_changed("SYSCONFDIR");
#[cfg(not(feature = "embed-data"))]
{
rsconf::set_env_value("DATADIR", datadir.to_str().unwrap());
rsconf::rebuild_if_env_changed("DATADIR");
let bindir = get_path("BINDIR", "bin/", &prefix);
rsconf::set_env_value("BINDIR", bindir.to_str().unwrap());
rsconf::rebuild_if_env_changed("BINDIR");
let localedir = get_path("LOCALEDIR", "locale/", &datadir);
let localedir = localedir.to_str().unwrap();
assert!(!localedir.is_empty(), "empty LOCALEDIR is not supported");
rsconf::set_env_value("LOCALEDIR", localedir);
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");
path
}
}
let prefix = overridable_path("PREFIX", |env_prefix| {
Some(PathBuf::from(
env_prefix.unwrap_or("/usr/local".to_string()),
))
})
.unwrap();
overridable_path("SYSCONFDIR", |env_sysconfdir| {
Some(join_if_relative(
&prefix,
env_sysconfdir.unwrap_or(
// Embedded builds use "/etc," not "$PREFIX/etc".
if cfg!(feature = "embed-data") {
"/etc/"
} else {
"etc/"
}
.to_string(),
),
))
});
let default_ok = !cfg!(feature = "embed-data");
let datadir = overridable_path("DATADIR", |env_datadir| {
let default = default_ok.then_some("share/".to_string());
env_datadir
.or(default)
.map(|p| join_if_relative(&prefix, p))
});
overridable_path("BINDIR", |env_bindir| {
let default = default_ok.then_some("bin/".to_string());
env_bindir.or(default).map(|p| join_if_relative(&prefix, p))
});
overridable_path("DOCDIR", |env_docdir| {
let default = default_ok.then_some("doc/fish".to_string());
env_docdir.or(default).map(|p| {
join_if_relative(
&datadir
.expect("Setting DOCDIR without setting DATADIR is not currently supported"),
p,
)
})
});
}
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;
}

View File

@@ -8,6 +8,22 @@ if [ "$FISH_CHECK_LINT" = false ]; then
lint=false
fi
check_dependency_versions=false
if [ "${FISH_CHECK_DEPENDENCY_VERSIONS:-false}" != false ]; then
check_dependency_versions=true
fi
if $check_dependency_versions; then
command -v curl
command -v jq
command -v rustup
command -v uv
sort --version-sort </dev/null
# To match existing behavior, only check Rust/dockerfiles for now.
# TODO: remove this from this script.
updatecli diff --config=updatecli.d/docker.yml --config=updatecli.d/rust.yml
fi
cargo_args=$FISH_CHECK_CARGO_ARGS
target_triple=$FISH_CHECK_TARGET_TRIPLE
if [ -n "$target_triple" ]; then
@@ -43,8 +59,16 @@ fi
# 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)
FISH_GETTEXT_EXTRACTION_FILE=$template_file cargo build --workspace --all-targets --features=gettext-extract
(
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

View File

@@ -1,23 +0,0 @@
#!/usr/bin/env fish
# Build a list of all sections in the html sphinx docs, separately by page,
# so it can be added to share/functions/help.fish
# Use like
# fish extract_help_sections.fish user_doc/html/{fish_for_bash_users.html,faq.html,interactive.html,language.html,tutorial.html}
# TODO: Currently `help` uses variable names we can't generate, so it needs to be touched up manually.
# Also this could easily be broken by changes in sphinx, ideally we'd have a way to let it print the section titles.
#
for file in $argv
set -l varname (string replace -r '.*/(.*).html' '$1' -- $file | string escape --style=var)pages
# Technically we can use any id in the document as an anchor, but listing them all is probably too much.
# Sphinx stores section titles (in a slug-ized form) in the id,
# and stores explicit section links in a `span` tag like
# `<span id="identifiers"></span>`
# We extract both separately.
set -l sections (string replace -rf '.*class="headerlink" href="#([^"]*)".*' '$1' <$file)
# Sections titled "id5" and such are internal cruft and shouldn't be offered.
set -a sections (string replace -rf '.*span id="([^"]*)".*' '$1' <$file | string match -rv 'id\d+')
set sections (printf '%s\n' $sections | sort -u)
echo set -l $varname $sections
end

View File

@@ -19,7 +19,7 @@ begin
echo ""
end
set -g workspace_root (status dirname)/..
set -g workspace_root (path resolve (status dirname)/..)
set -l rust_extraction_file
if set -l --query _flag_use_existing_template
@@ -27,20 +27,30 @@ begin
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 --features=gettext-extract
FISH_GETTEXT_EXTRACTION_FILE=$rust_extraction_file cargo check --no-default-features --features=gettext-extract
or exit 1
end
function mark_section
set -l section_name $argv[1]
echo 'msgid "fish-section-'$section_name'"'
echo 'msgstr ""'
echo ''
end
mark_section tier1-from-rust
# Get rid of duplicates and sort.
msguniq --no-wrap --strict --sort-output $rust_extraction_file
msguniq --no-wrap --sort-output $rust_extraction_file
or exit 1
if not set -l --query _flag_use_existing_template
rm $rust_extraction_file
end
function extract_fish_script_messages --argument-names regex
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).
@@ -59,24 +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_dir/config.fish $share_dir/completions/*.fish $share_dir/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
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).*'
mark_section "$tier-from-script-implicitly-added"
extract_fish_script_messages_impl $implicit_regex $argv
end
set -g share_dir $workspace_root/share
# 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
set -l tier1 $share_dir/config.fish
set -l tier2
set -l tier3
# 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
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,

View File

@@ -24,13 +24,7 @@ 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
X86_64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=10.12'
while getopts "sf:i:p:e:nj:" opt; do
case $opt in
@@ -65,34 +59,29 @@ OUTPUT_PATH=${FISH_ARTEFACT_PATH:-~/fish_built}
mkdir -p "$PKGDIR/build_x86_64" "$PKGDIR/build_arm64" "$PKGDIR/root" "$PKGDIR/intermediates" "$PKGDIR/dst"
do_cmake() {
cmake \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \
-DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \
-DFISH_USE_SYSTEM_PCRE2=OFF \
"$@" \
"$SRC_DIR"
}
# 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" \
&& do_cmake -DRust_CARGO_TARGET=aarch64-apple-darwin \
&& 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" \
&& do_cmake -DRust_CARGO_TARGET=x86_64-apple-darwin \
&& env $X86_64_DEPLOY_TARGET make VERBOSE=1 -j 12; }
# Fatten them up.

View File

@@ -41,6 +41,12 @@ wd="$PWD"
# Get the version
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
tag_creation_date=$(
# If not dirty (i.e. we're building an immutable tag), pin the build date.
if [ "$VERSION" = "$(git describe)" ]; then
git log --format=%ad '--date=format:%b %d, %Y' -1
fi
)
# The name of the prefix, which is the directory that you get when you untar
prefix="fish-$VERSION"
@@ -60,7 +66,8 @@ PREFIX_TMPDIR=$(mktemp -d)
cd "$PREFIX_TMPDIR"
echo "$VERSION" > version
cmake -G "$BUILD_GENERATOR" -DCMAKE_BUILD_TYPE=Debug "$wd"
$BUILD_TOOL doc
FISH_SPHINX_BUILD_DATE=$tag_creation_date \
$BUILD_TOOL doc
TAR_APPEND="$TAR --append --file=$path --mtime=now --owner=0 --group=0 \
--mode=g+w,a+rX --transform s/^/$prefix\//"

View File

@@ -11,37 +11,38 @@ mkdir -p "$relnotes_tmp/fake-workspace" "$relnotes_tmp/out"
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%.*}
add_stats=false
# Skip on shallow clone (CI) for now.
if test -z "$CI" || [ "$(git -C "$workspace_root" tag | wc -l)" -gt 1 ]; then {
previous_version=$(
cd "$workspace_root"
git for-each-ref --format='%(objecttype) %(refname:strip=2)' refs/tags |
awk '/tag/ {print $2}' | sort --version-sort |
grep -vxF "$(git describe)" | tail -1
)
minor_version=${version%.*}
previous_minor_version=${previous_version%.*}
if [ "$minor_version" != "$previous_minor_version" ]; then
add_stats=true
fi
} fi
{
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
if $add_stats; then {
ExtractCommitters() {
git log "$1" --format="%aN"
trailers='Co-authored-by|Signed-off-by'
git log "$1" --format="%b" | sed -En "/^($trailers):\s*/{s///;s/\s*<.*//;p}"
}
ListCommitters() {
comm "$@" "$relnotes_tmp/committers-then" "$relnotes_tmp/committers-now"
}
(
cd "$workspace_root"
ExtractCommitters "$previous_version" | sort -u >"$relnotes_tmp/committers-then"
ExtractCommitters "$previous_version".. | sort -u >"$relnotes_tmp/committers-now"
ListCommitters -13 >"$relnotes_tmp/committers-new"
ListCommitters -12 >"$relnotes_tmp/committers-returning"
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")
@@ -51,18 +52,18 @@ previous_minor_version=${previous_version%.*}
echo
echo
)
fi
} fi
printf %s "$(awk <"$workspace_root/CHANGELOG.rst" '
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
if $add_stats; then {
JoinEscaped() {
sed 's/\S/\\&/g' |
LC_CTYPE=C.UTF-8 sed 's/\S/\\&/g' |
awk '
NR != 1 { printf ",\n" }
{ printf "%s", $0 }
@@ -79,13 +80,18 @@ previous_minor_version=${previous_version%.*}
echo
printf "Welcome back our returning committers: "
JoinEscaped <"$relnotes_tmp/committers-returning"
fi
} 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 'Download links:'
echo 'To download the source code for fish, we suggest the file named ``fish-'"$version"'.tar.xz``.'
echo 'The file downloaded from ``Source code (tar.gz)`` will not build correctly.'
echo 'A GPG signature using the key published at '"${FISH_GPG_PUBLIC_KEY_URL:-???}"' is available as ``fish-'"$version"'.tar.xz.asc``.'
echo
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.*"
echo 'The files called ``fish-'"$version"'-linux-*.tar.xz`` contain'
echo '`standalone fish binaries <https://github.com/fish-shell/fish-shell/?tab=readme-ov-file#building-fish-with-cargo>`__'
echo 'for any Linux with the given CPU architecture.'
} >"$relnotes_tmp/fake-workspace"/CHANGELOG.rst
sphinx-build >&2 -j auto \
@@ -93,7 +99,7 @@ sphinx-build >&2 -j auto \
-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 \
-D markdown_flavor=github \
"$@"
# Skip changelog header

View File

@@ -19,10 +19,14 @@ fi
for tool in \
bundle \
diff \
gh \
gpg \
jq \
ruby \
tar \
timeout \
uv \
; do
if ! command -v "$tool" >/dev/null; then
echo >&2 "$0: missing command: $1"
@@ -30,8 +34,14 @@ for tool in \
fi
done
committer=$(git var GIT_AUTHOR_IDENT)
committer=${committer% *} # strip timezone
committer=${committer% *} # strip timestamp
gpg --local-user="$committer" --sign </dev/null >/dev/null
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
@@ -42,6 +52,13 @@ do
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
@@ -74,8 +91,8 @@ 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 -c "user.signingKey=$committer" \
tag --sign --message="Release $version" $version
git push $remote $version
@@ -100,13 +117,21 @@ done
# Update fishshell.com
tag_oid=$(git rev-parse "$version")
tmpdir=$(mktemp -d)
fish_tar_xz=fish-$version.tar.xz
(
local_tarball=$tmpdir/local-tarball
mkdir "$local_tarball"
FISH_ARTEFACT_PATH=$local_tarball uv run ./build_tools/make_tarball.sh
cd "$local_tarball"
tar xf "$fish_tar_xz"
)
# 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"
--pattern="$fish_tar_xz"
do
TIMEOUT=30 gh run watch "$run_id" ||:
sleep 5
@@ -114,7 +139,16 @@ 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 )
(
cd "$tmpdir"
tar xf "$fish_tar_xz"
diff -ur "fish-$version" "local-tarball/fish-$version"
gpg --local-user="$committer" --sign --detach --armor \
"$fish_tar_xz"
gh release upload "$version" "$fish_tar_xz.asc"
)
CopyDocs() {
rm -rf "$fish_site/site/docs/$1"
cp -r "$tmpdir/fish-$version/user_doc/html" "$fish_site/site/docs/$1"
@@ -136,7 +170,10 @@ rm -rf "$tmpdir"
cd "$fish_site"
make
git add -u
! git ls-files --others --exclude-standard | grep .
git add docs
if git ls-files --others --exclude-standard | grep .; then
exit 1
fi
git commit --message="$(printf %s "\
| Release $version (docs)
|
@@ -144,15 +181,21 @@ rm -rf "$tmpdir"
" | sed 's,^\s*| \?,,')"
)
# Approve macos-codesign
# TODO what if current user can't approve?
gh_pending_deployments() {
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/actions/runs/$run_id/pending_deployments" \
"/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" ]
@@ -167,7 +210,7 @@ echo '
"comment": "Approved via ./build_tools/release.sh"
}
' |
gh_pending_deployments -XPOST --input=-
gh_pending_deployments --method POST --input=-
# Await completion.
gh run watch "$run_id"
@@ -184,7 +227,10 @@ done
cd "$fish_site"
make new-release
git add -u
! git ls-files --others --exclude-standard | grep .
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)
|
@@ -192,12 +238,12 @@ done
" | 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 git@github.com:$repository_owner/fish-site HEAD:master
git push "$fish_site_repo" HEAD:master
)
if [ -n "$integration_branch" ]; then
if [ -n "$integration_branch" ]; then {
git push $remote "$version^{commit}":refs/heads/$integration_branch
else
} else {
changelog=$(cat - CHANGELOG.rst <<EOF
fish ?.?.? (released ???)
=========================
@@ -207,23 +253,27 @@ EOF
printf %s\\n "$changelog" >CHANGELOG.rst
CommitVersion ${version}-snapshot "start new cycle"
git push $remote HEAD:master
fi
# TODO This can currently require a TTY for editing and password
# prompts.
if [ "$repository_owner" = fish-shell ]; then {
mail=$(mktemp)
cat >$mail <<EOF
From: $(git var GIT_AUTHOR_IDENT | sed 's/ [0-9]* +[0-9]*$//')
Subject: fish $version released
See https://github.com/fish-shell/fish-shell/releases/tag/$version
EOF
git send-email --suppress-cc=all --confirm=always $mail \
--to="fish-users Mailing List <fish-users@lists.sourceforge.net>"
rm $mail
} 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

@@ -44,7 +44,7 @@ if test $all = yes
end
end
set fish_files $workspace_root/{benchmarks,build_tools,etc,share}/**.fish
set python_files {doc_src,share,tests}/**.py
set python_files $workspace_root
else
# Format the files specified as arguments.
set -l files $argv
@@ -58,6 +58,11 @@ set -l green (set_color green)
set -l yellow (set_color yellow)
set -l normal (set_color normal)
function die -V red -V normal
echo $red$argv[1]$normal
exit 1
end
if set -q fish_files[1]
if not type -q fish_indent
echo
@@ -66,58 +71,52 @@ if set -q fish_files[1]
end
echo === Running "$green"fish_indent"$normal"
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
fish_indent --check -- $fish_files
or die "Fish files are not formatted correctly."
else
fish_indent -w -- $fish_files
end
end
if set -q python_files[1]
if not type -q black
if not type -q ruff
echo
echo $yellow'Please install `black` to style python'$normal
echo $yellow'Please install `ruff` to style python'$normal
exit 127
end
echo === Running "$green"black"$normal"
echo === Running "$green"ruff format"$normal"
if set -l -q _flag_check
if not black --check $python_files
echo $red"Python files are not formatted correctly."$normal
exit 1
end
ruff format --check $python_files
or die "Python files are not formatted correctly."
else
black $python_files
ruff format $python_files
end
end
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 --check
echo $red"Rust files are not formatted correctly."$normal
exit 1
end
else
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
if test $all = yes; or set -q rust_files[1]
if not cargo fmt --version >/dev/null
echo
echo $yellow'Please install "rustfmt" to style Rust, e.g. via:'
echo "rustup component add rustfmt"$normal
exit 127
end
else
if set -l -q _flag_all
cargo fmt
set -l edition_spec string match -r '^edition\s*=.*'
test "$($edition_spec <Cargo.toml)" = "$($edition_spec <.rustfmt.toml)"
or die "Cargo.toml and .rustfmt.toml use different editions"
echo === Running "$green"rustfmt"$normal"
if set -l -q _flag_check
if test $all = yes
cargo fmt --all --check
else
rustfmt --check --files-with-diff $rust_files
end
or die "Rust files are not formatted correctly."
else
if set -q rust_files[1]
if test $all = yes
cargo fmt --all
else
rustfmt $rust_files
end
end

View File

@@ -0,0 +1,33 @@
#!/bin/sh
set -ex
command -v curl
command -v gcloud
command -v jq
command -v rustup
command -v updatecli
command -v uv
sort --version-sort </dev/null
uv lock --check
updatecli "${@:-apply}"
uv lock # Python version constraints may have changed.
uv lock --upgrade
from_gh() {
repo=$1
path=$2
out_dir=$3
contents=$(curl -fsS https://raw.githubusercontent.com/"${repo}"/refs/heads/master/"${path}")
printf '%s\n' >"$out_dir/$(basename "$path")" "$contents"
}
from_gh ridiculousfish/widecharwidth widechar_width.rs src/widecharwidth/
from_gh ridiculousfish/littlecheck littlecheck/littlecheck.py tests/
# Update Cargo.lock
cargo update
# Update Cargo.toml and Cargo.lock
cargo +nightly -Zunstable-options update --breaking

View File

@@ -31,11 +31,9 @@
set -gx LC_ALL C.UTF-8
set -l build_tools (status dirname)
set -g tmpdir
set -l po_dir $build_tools/../po
set -l extract
set -l po
argparse dry-run use-existing-template= -- $argv
or exit $status
@@ -46,8 +44,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 ""
@@ -99,18 +97,49 @@ if set -l --query _flag_dry_run
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 --no-wrap --update --no-fuzzy-matching --backup=none --quiet $po_file $template_file
and msgattrib --no-wrap --no-obsolete -o $po_file $po_file
or cleanup_exit
else
cp $template_file $po_file
end
if test -e $po_file
merge_po_files $template_file $po_file
else
begin
print_header
cat $template_file
end >$po_file
end
end

View File

@@ -0,0 +1,16 @@
#!/bin/bash
set -euo pipefail
channel=$1 # e.g. stable, testing
package=$2 # e.g. rustc, sphinx
codename=$(
curl -fsS https://ftp.debian.org/debian/dists/"${channel}"/Release |
grep '^Codename:' | cut -d' ' -f2)
curl -fsS https://sources.debian.org/api/src/"${package}"/ |
jq -r --arg codename "${codename}" '
.versions[] | select(.suites[] == $codename) | .version' |
sed 's/^\([0-9]\+\.[0-9]\+\).*/\1/' |
sort --version-sort |
tail -1

View File

@@ -136,7 +136,7 @@ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/
PATTERN "*.1"
PATTERN ${CONDEMNED_PAGE} EXCLUDE)
install(PROGRAMS share/tools/create_manpage_completions.py share/tools/deroff.py
install(PROGRAMS share/tools/create_manpage_completions.py
DESTINATION ${rel_datadir}/fish/tools/)
install(DIRECTORY share/tools/web_config

View File

@@ -3,6 +3,8 @@ find_package(Rust REQUIRED)
set(FISH_RUST_BUILD_DIR "${CMAKE_BINARY_DIR}/cargo/build")
list(APPEND FISH_CARGO_FEATURES_LIST "embed-data")
if(DEFINED ASAN)
list(APPEND CARGO_FLAGS "-Z" "build-std")
list(APPEND FISH_CARGO_FEATURES_LIST "asan")
@@ -28,19 +30,3 @@ if(NOT DEFINED WITH_GETTEXT OR "${WITH_GETTEXT}")
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}"
# 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}"
"CARGO_TARGET_DIR=${FISH_RUST_BUILD_DIR}"
"CARGO_BUILD_RUSTC=${Rust_COMPILER}"
"${FISH_PCRE2_BUILDFLAG}"
"RUSTFLAGS=$ENV{RUSTFLAGS} ${rust_debugflags}"
)

View File

@@ -57,7 +57,7 @@ endif()
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}
COMMAND env ${VARS_FOR_CARGO} ${Rust_CARGO} test --no-default-features ${CARGO_FLAGS} --workspace --target-dir ${rust_target_dir} ${cargo_test_flags}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
DEPENDS fish fish_indent fish_key_reader fish_test_helper
USES_TERMINAL

View File

@@ -1,4 +1,21 @@
use std::{borrow::Cow, env, path::Path};
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"));
@@ -12,8 +29,7 @@ fn cargo_target_dir() -> Cow<'static, Path> {
}
pub fn fish_build_dir() -> Cow<'static, Path> {
// FISH_BUILD_DIR is set by CMake, if we are using it.
option_env!("FISH_BUILD_DIR")
option_env!("FISH_CMAKE_BINARY_DIR")
.map(|d| Cow::Borrowed(Path::new(d)))
.unwrap_or(cargo_target_dir())
}

View File

@@ -1,53 +1,75 @@
#[cfg(not(clippy))]
use fish_build_helper::env_var;
use std::path::Path;
fn main() {
let mandir = fish_build_helper::fish_build_dir().join("fish-man");
let sec1dir = mandir.join("man1");
let man_dir = fish_build_helper::fish_build_dir().join("fish-man");
let sec1_dir = man_dir.join("man1");
// Running `cargo clippy` on a clean build directory panics, because when rust-embed tries to
// embed a directory which does not exist it will panic.
let _ = std::fs::create_dir_all(sec1dir.to_str().unwrap());
let _ = std::fs::create_dir_all(&sec1_dir);
let help_sections_path = Path::new(&env_var("OUT_DIR").unwrap()).join("help_sections.rs");
std::fs::write(
help_sections_path.clone(),
r#"pub static HELP_SECTIONS: &str = "";"#,
)
.unwrap();
#[cfg(not(clippy))]
build_man(&mandir);
build_man(&man_dir, &sec1_dir, &help_sections_path);
}
#[cfg(not(clippy))]
fn build_man(man_dir: &Path) {
fn build_man(man_dir: &Path, sec1_dir: &Path, help_sections_path: &Path) {
use std::{
env,
ffi::OsStr,
process::{Command, Stdio},
};
use fish_build_helper::workspace_root;
let workspace_root = workspace_root();
let doc_src_dir = workspace_root.join("doc_src");
let man_str = man_dir.to_str().unwrap();
fish_build_helper::rebuild_if_paths_changed([
&workspace_root.join("CHANGELOG.rst"),
&workspace_root.join("CONTRIBUTING.rst"),
&doc_src_dir,
]);
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);
let help_sections_arg = format!("fish_help_sections_output={}", help_sections_path.display());
let args: &[&OsStr] = {
fn as_os_str<S: AsRef<OsStr> + ?Sized>(s: &S) -> &OsStr {
s.as_ref()
}
macro_rules! as_os_strs {
( [ $( $x:expr, )* ] ) => {
&[
$( as_os_str($x), )*
]
}
}
as_os_strs!([
"-j",
"auto",
"-q",
"-b",
"man",
"-c",
&doc_src_dir,
// doctree path - put this *above* the man1 dir to exclude it.
// this is ~6M
"-d",
&man_dir,
&doc_src_dir,
&sec1_dir,
"-D",
&help_sections_arg,
])
};
rsconf::rebuild_if_env_changed("FISH_BUILD_DOCS");
if env::var("FISH_BUILD_DOCS") == Ok("0".to_string()) {
if env_var("FISH_BUILD_DOCS") == Some("0".to_string()) {
rsconf::warn!("Skipping man pages because $FISH_BUILD_DOCS is set to 0");
return;
}
@@ -57,18 +79,24 @@ fn build_man(man_dir: &Path) {
// - 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")
let sphinx_build = match Command::new(option_env!("FISH_SPHINX").unwrap_or("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") == 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.");
if env_var("FISH_BUILD_DOCS") == Some("1".to_string()) {
panic!(
"Could not find sphinx-build required to build man pages.\n\
Install Sphinx or disable building the docs by setting $FISH_BUILD_DOCS=0."
);
}
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.");
rsconf::warn!(
"Could not find sphinx-build required to build man pages. \
If you install Sphinx now, you need to trigger a rebuild to include man pages. \
For example by running `touch doc_src` followed by the build command."
);
return;
}
Err(e) => {

View File

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

View File

@@ -50,7 +50,9 @@ fn append_po_entry_to_file(message: &TokenStream, file_name: &OsString) {
.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}'")
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('%') {
@@ -84,23 +86,26 @@ pub fn gettext_extract(message: TokenStream) -> TokenStream {
.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.")
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 {
let proc_macro2::TokenTree::Group(group) = first_token else {
panic!("Expected group in gettext_extract, but got: {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:?}");
}
}
message

View File

@@ -1,10 +1,11 @@
use std::{
env,
ffi::OsStr,
path::{Path, PathBuf},
process::Command,
};
use fish_build_helper::env_var;
fn main() {
let cache_dir =
PathBuf::from(fish_build_helper::fish_build_dir()).join("fish-localization-map-cache");
@@ -27,29 +28,28 @@ fn embed_localizations(cache_dir: &Path) {
std::fs::create_dir_all(cache_dir).unwrap();
let localization_map_path =
Path::new(&env::var("OUT_DIR").unwrap()).join("localization_maps.rs");
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").status() {
match Command::new("msgfmt").arg("-h").output() {
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."
"Could not find msgfmt required to build message catalogs. \
Localization will not work. \
If you install gettext now, you need to trigger a rebuild to include localization support. \
For example by running `touch po` followed by the build command."
);
}
Err(e) => {
panic!("Error when trying to run `msgfmt -h`: {e:?}");
}
Ok(_) => {
Ok(output) => {
let has_check_format =
String::from_utf8_lossy(&output.stdout).contains("--check-format");
for dir_entry_result in po_dir.read_dir().unwrap() {
let dir_entry = dir_entry_result.unwrap();
let po_file_path = dir_entry.path();
@@ -91,13 +91,32 @@ fn embed_localizations(cache_dir: &Path) {
// 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=-")
let mut tmp_mo_file = None;
let output = {
let mut cmd = &mut Command::new("msgfmt");
if has_check_format {
cmd = cmd.arg("--check-format");
} else {
tmp_mo_file = Some(cache_dir.join("messages.mo"));
};
cmd.arg(format!(
"--output-file={}",
tmp_mo_file
.as_ref()
.map_or("-", |path| path.to_str().unwrap())
))
.arg(&po_file_path)
.output()
.unwrap();
let mo_data = output.stdout;
.unwrap()
};
if !output.status.success() {
panic!(
"msgfmt failed:\n{}",
String::from_utf8(output.stderr).unwrap()
);
}
let mo_data =
tmp_mo_file.map_or(output.stdout, |path| std::fs::read(path).unwrap());
// Extract map from MO data.
let language_localizations = parse_mo_file(&mo_data).unwrap();

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

@@ -3,8 +3,8 @@
mod tests;
use super::locale::Locale;
use super::printf_impl::{pad, ConversionSpec, Error, ModifierFlags};
use decimal::{Decimal, DigitLimit, DIGIT_WIDTH};
use super::printf_impl::{ConversionSpec, Error, ModifierFlags, pad};
use decimal::{DIGIT_WIDTH, Decimal, DigitLimit};
use std::cmp::min;
use std::fmt::Write;
@@ -279,7 +279,6 @@ 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 {

View File

@@ -4,9 +4,8 @@
mod fmt_fp;
mod printf_impl;
pub use printf_impl::{sprintf_locale, Error, FormatString};
pub use printf_impl::{Error, FormatString, sprintf_locale};
pub mod locale;
pub use locale::{Locale, C_LOCALE, EN_US_LOCALE};
#[cfg(test)]
mod tests;

View File

@@ -66,11 +66,7 @@ fn next_group_size(&self, digits_left: usize) -> usize {
// Divide remaining digits by repeat_group.
// Apply any remainder to the first group.
let res = (digits_left - accum) % (repeat_group as usize);
if res > 0 {
res
} else {
repeat_group as usize
}
if res > 0 { res } else { repeat_group as usize }
}
}
@@ -122,86 +118,91 @@ pub fn separator_count(&self, digits_count: usize) -> usize {
group_repeat: true,
};
#[test]
fn test_apply_grouping() {
let input = "123456789";
let mut result: String;
#[cfg(test)]
mod tests {
use super::{C_LOCALE, EN_US_LOCALE, Locale};
// en_US has commas.
assert_eq!(EN_US_LOCALE.thousands_sep, Some(','));
result = EN_US_LOCALE.apply_grouping(input);
assert_eq!(result, "123,456,789");
#[test]
fn test_apply_grouping() {
let input = "123456789";
let mut result: String;
// Test weird locales.
let input: &str = "1234567890123456";
let mut locale: Locale = C_LOCALE;
locale.thousands_sep = Some('!');
// en_US has commas.
assert_eq!(EN_US_LOCALE.thousands_sep, Some(','));
result = EN_US_LOCALE.apply_grouping(input);
assert_eq!(result, "123,456,789");
locale.grouping = [5, 3, 1, 0];
locale.group_repeat = false;
result = locale.apply_grouping(input);
assert_eq!(result, "1234567!8!901!23456");
// Test weird locales.
let input: &str = "1234567890123456";
let mut locale: Locale = C_LOCALE;
locale.thousands_sep = Some('!');
// group_repeat doesn't matter because trailing group is 0
locale.grouping = [5, 3, 1, 0];
locale.group_repeat = true;
result = locale.apply_grouping(input);
assert_eq!(result, "1234567!8!901!23456");
locale.grouping = [5, 3, 1, 0];
locale.group_repeat = false;
result = locale.apply_grouping(input);
assert_eq!(result, "1234567!8!901!23456");
locale.grouping = [5, 3, 1, 2];
locale.group_repeat = false;
result = locale.apply_grouping(input);
assert_eq!(result, "12345!67!8!901!23456");
// group_repeat doesn't matter because trailing group is 0
locale.grouping = [5, 3, 1, 0];
locale.group_repeat = true;
result = locale.apply_grouping(input);
assert_eq!(result, "1234567!8!901!23456");
locale.grouping = [5, 3, 1, 2];
locale.group_repeat = true;
result = locale.apply_grouping(input);
assert_eq!(result, "1!23!45!67!8!901!23456");
}
locale.grouping = [5, 3, 1, 2];
locale.group_repeat = false;
result = locale.apply_grouping(input);
assert_eq!(result, "12345!67!8!901!23456");
#[test]
#[should_panic]
fn test_thousands_grouping_length_panics_if_no_sep() {
// We should panic if we try to group with no thousands separator.
assert_eq!(C_LOCALE.thousands_sep, None);
C_LOCALE.apply_grouping("123");
}
#[test]
fn test_thousands_grouping_length() {
fn validate_grouping_length_hint(locale: Locale, mut input: &str) {
loop {
let expected = locale.separator_count(input.len()) + input.len();
let actual = locale.apply_grouping(input).len();
assert_eq!(expected, actual);
if input.is_empty() {
break;
}
input = &input[1..];
}
locale.grouping = [5, 3, 1, 2];
locale.group_repeat = true;
result = locale.apply_grouping(input);
assert_eq!(result, "1!23!45!67!8!901!23456");
}
validate_grouping_length_hint(EN_US_LOCALE, "123456789");
#[test]
#[should_panic]
fn test_thousands_grouping_length_panics_if_no_sep() {
// We should panic if we try to group with no thousands separator.
assert_eq!(C_LOCALE.thousands_sep, None);
C_LOCALE.apply_grouping("123");
}
// Test weird locales.
let input = "1234567890123456";
let mut locale: Locale = C_LOCALE;
locale.thousands_sep = Some('!');
#[test]
fn test_thousands_grouping_length() {
fn validate_grouping_length_hint(locale: Locale, mut input: &str) {
loop {
let expected = locale.separator_count(input.len()) + input.len();
let actual = locale.apply_grouping(input).len();
assert_eq!(expected, actual);
if input.is_empty() {
break;
}
input = &input[1..];
}
}
locale.grouping = [5, 3, 1, 0];
locale.group_repeat = false;
validate_grouping_length_hint(locale, input);
validate_grouping_length_hint(EN_US_LOCALE, "123456789");
// group_repeat doesn't matter because trailing group is 0
locale.grouping = [5, 3, 1, 0];
locale.group_repeat = true;
validate_grouping_length_hint(locale, input);
// Test weird locales.
let input = "1234567890123456";
let mut locale: Locale = C_LOCALE;
locale.thousands_sep = Some('!');
locale.grouping = [5, 3, 1, 2];
locale.group_repeat = false;
validate_grouping_length_hint(locale, input);
locale.grouping = [5, 3, 1, 0];
locale.group_repeat = false;
validate_grouping_length_hint(locale, input);
locale.grouping = [5, 3, 1, 2];
locale.group_repeat = true;
validate_grouping_length_hint(locale, input);
// group_repeat doesn't matter because trailing group is 0
locale.grouping = [5, 3, 1, 0];
locale.group_repeat = true;
validate_grouping_length_hint(locale, input);
locale.grouping = [5, 3, 1, 2];
locale.group_repeat = false;
validate_grouping_length_hint(locale, input);
locale.grouping = [5, 3, 1, 2];
locale.group_repeat = true;
validate_grouping_length_hint(locale, input);
}
}

View File

@@ -472,7 +472,7 @@ 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" };

View File

@@ -1,8 +1,9 @@
use crate::arg::ToArg;
use crate::locale::{Locale, C_LOCALE, EN_US_LOCALE};
use crate::{sprintf_locale, Error, FormatString};
use crate::locale::{C_LOCALE, EN_US_LOCALE, Locale};
use crate::{Error, FormatString, sprintf_locale};
use libc::c_char;
use std::f64::consts::{E, PI, TAU};
use std::ffi::CStr;
use std::fmt;
// sprintf, checking length
@@ -77,7 +78,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 +226,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 +235,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 +250,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 +417,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 +434,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 +759,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);
@@ -852,25 +856,20 @@ fn test_float_hex_prec() {
// Note that our hex float formatting rounds according to the rounding mode,
// while libc may not; as a result we may differ in the last digit. So this
// requires manual comparison.
let mut c_storage = [0u8; 256];
let c_storage_ptr = c_storage.as_mut_ptr() as *mut c_char;
let mut rust_str = String::with_capacity(256);
let c_fmt = b"%.*a\0".as_ptr() as *const c_char;
let mut c_storage = [0u8; 256];
let mut libc_sprintf = libc_sprintf_one_float_with_precision(&mut c_storage, c"%.*a");
let mut failed = false;
for sign in [1.0, -1.0].into_iter() {
for mut v in [0.0, 0.5, 1.0, 1.5, PI, TAU, E].into_iter() {
v *= sign;
for preci in 1..=200_i32 {
for preci in 1..=200_usize {
rust_str.clear();
crate::sprintf!(=> &mut rust_str, "%.*a", preci, v);
let printf_str = unsafe {
let len = libc::snprintf(c_storage_ptr, c_storage.len(), c_fmt, preci, v);
assert!(len >= 0);
let sl = std::slice::from_raw_parts(c_storage_ptr as *const u8, len as usize);
std::str::from_utf8(sl).unwrap()
};
let printf_str = libc_sprintf(preci, v);
if rust_str != printf_str {
println!(
"Our printf and libc disagree on hex formatting of float: {v}
@@ -886,14 +885,27 @@ fn test_float_hex_prec() {
assert!(!failed);
}
fn test_exhaustive(rust_fmt: &str, c_fmt: *const c_char) {
fn libc_sprintf_one_float_with_precision<'a>(
storage: &'a mut [u8],
fmt: &'a CStr,
) -> impl FnMut(usize, f64) -> &'a str {
|preci, float_val| unsafe {
let storage_ptr = storage.as_mut_ptr() as *mut c_char;
let len = libc::snprintf(storage_ptr, storage.len(), fmt.as_ptr(), preci, float_val);
assert!(len >= 0);
let sl = std::slice::from_raw_parts(storage_ptr as *const u8, len as usize);
std::str::from_utf8(sl).unwrap()
}
}
fn test_exhaustive(rust_fmt: &str, c_fmt: &CStr) {
// "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}");
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;
let mut libc_sprintf = libc_sprintf_one_float_with_precision(&mut c_storage, c_fmt);
for i in 0..=u32::MAX {
if i % 1000000 == 0 {
@@ -905,12 +917,7 @@ fn test_exhaustive(rust_fmt: &str, c_fmt: *const c_char) {
rust_str.clear();
crate::sprintf!(=> &mut rust_str, rust_fmt, preci, ff);
let printf_str = unsafe {
let len = libc::snprintf(c_storage_ptr, c_storage.len(), c_fmt, preci, ff);
assert!(len >= 0);
let sl = std::slice::from_raw_parts(c_storage_ptr as *const u8, len as usize);
std::str::from_utf8(sl).unwrap()
};
let printf_str = libc_sprintf(preci, ff);
if rust_str != printf_str {
println!(
"Rust and libc disagree on formatting float {i:x}: {ff}\n
@@ -929,19 +936,19 @@ fn test_exhaustive(rust_fmt: &str, c_fmt: *const c_char) {
#[ignore]
fn test_float_g_exhaustive() {
// To run: cargo test test_float_g_exhaustive --release -- --ignored --nocapture
test_exhaustive("%.*g", b"%.*g\0".as_ptr() as *const c_char);
test_exhaustive("%.*g", c"%.*g");
}
#[test]
#[ignore]
fn test_float_e_exhaustive() {
// To run: cargo test test_float_e_exhaustive --release -- --ignored --nocapture
test_exhaustive("%.*e", b"%.*e\0".as_ptr() as *const c_char);
test_exhaustive("%.*e", c"%.*e");
}
#[test]
#[ignore]
fn test_float_f_exhaustive() {
// To run: cargo test test_float_f_exhaustive --release -- --ignored --nocapture
test_exhaustive("%.*f", b"%.*f\0".as_ptr() as *const c_char);
test_exhaustive("%.*f", c"%.*f");
}

View File

@@ -0,0 +1,12 @@
[package]
name = "fish-tempfile"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
[dependencies]
nix = { workspace = true, features = ["fs", "feature"] }
[lints]
workspace = true

127
crates/tempfile/src/lib.rs Normal file
View File

@@ -0,0 +1,127 @@
use std::{fs::File, path::PathBuf};
pub struct TempFile {
file: File,
path: PathBuf,
}
impl TempFile {
pub fn get(&self) -> &File {
&self.file
}
pub fn get_mut(&mut self) -> &mut File {
&mut self.file
}
pub fn path(&self) -> &PathBuf {
&self.path
}
}
impl Drop for TempFile {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.path);
}
}
pub struct TempDir {
path: PathBuf,
}
impl TempDir {
pub fn path(&self) -> &PathBuf {
&self.path
}
}
impl Drop for TempDir {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.path);
}
}
fn get_tmpdir() -> PathBuf {
PathBuf::from(std::env::var_os("TMPDIR").unwrap_or("/tmp".into()))
}
fn get_template() -> PathBuf {
get_tmpdir().join("fish_tmp_XXXXXX")
}
/// Tries to create a new temporary file using `mkstemp`.
/// On success, a [`TempFile`] is returned.
/// When this struct is dropped, the backing file will be deleted.
pub fn new_file() -> std::io::Result<TempFile> {
let (fd, path) = nix::unistd::mkstemp(&get_template())?;
let file = File::from(fd);
Ok(TempFile { file, path })
}
/// Tries to create a new temporary directory using `mkdtemp`.
/// On success, a [`TempDir`] is returned.
/// When this struct is dropped, the backing directory, including all its contents, will be deleted.
pub fn new_dir() -> std::io::Result<TempDir> {
let path = nix::unistd::mkdtemp(&get_template())?;
Ok(TempDir { path })
}
#[cfg(test)]
mod tests {
use std::{
fs::File,
io::{Read, Seek, Write},
};
#[test]
fn create_tempfile() {
super::new_file().unwrap();
}
#[test]
#[should_panic(expected = "file should no longer exist")]
fn use_tempfile() {
let mut tempfile = super::new_file().unwrap();
let expected_content = "test";
{
let file = tempfile.get_mut();
file.write_all(expected_content.as_bytes()).unwrap();
file.seek(std::io::SeekFrom::Start(0)).unwrap();
}
let mut actual_content = String::new();
{
let mut file = tempfile.get();
file.read_to_string(&mut actual_content).unwrap();
}
let path = tempfile.path().to_owned();
drop(tempfile);
assert_eq!(expected_content, actual_content);
File::open(&path).expect("file should no longer exist");
}
#[test]
fn create_tempdir() {
super::new_dir().unwrap();
}
#[test]
#[should_panic(expected = "file should no longer exist")]
fn use_tempdir() {
let tempdir = super::new_dir().unwrap();
let file_path = tempdir.path().join("foo");
let expected_content = "test";
{
let mut file = File::create(&file_path).unwrap();
file.write_all(expected_content.as_bytes()).unwrap();
}
{
let mut file = File::open(&file_path).unwrap();
let mut actual_content = String::new();
file.read_to_string(&mut actual_content).unwrap();
assert_eq!(expected_content, actual_content);
}
drop(tempdir);
File::open(&file_path).expect("file should no longer exist");
}
}

7
debian/control vendored
View File

@@ -4,11 +4,12 @@ Priority: optional
Maintainer: ridiculous_fish <corydoras@ridiculousfish.com>
Uploaders: David Adam <zanchey@ucc.gu.uwa.edu.au>
Build-Depends: debhelper (>= 12),
cargo (>= 0.66) | cargo-mozilla (>= 0.66),
cmake (>= 3.15.0) | cmake-mozilla (>= 3.15.0),
# -web is for Debian's updated version, -X.Y is for Ubuntu's backported versions
cargo (>= 1.85) | cargo-web (>= 1.85) | cargo-1.85,
cmake (>= 3.15.0),
gettext,
libpcre2-dev,
rustc (>= 1.70),
rustc (>= 1.85) | rustc-web (>= 1.85) | rustc-1.85,
# Test dependencies
locales-all,
ncurses-base,

8
debian/rules vendored
View File

@@ -8,13 +8,15 @@ export DH_VERBOSE=1
export DEB_BUILD_MAINT_OPTIONS=optimize=-lto
%:
dh $@
dh $@ --buildsystem=cmake --builddirectory=build
# Setting the build system is still required, because otherwise the GNUmakefile gets picked up
override_dh_auto_configure:
ln -s cargo-vendor/vendor vendor
ln -s cargo-vendor/.cargo .cargo
dh_auto_configure --buildsystem=cmake -- -DCMAKE_BUILD_TYPE=RelWithDebInfo
dh_auto_configure -- -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DRust_CARGO=$$(command -v cargo-1.85 || command -v cargo) \
-DRust_COMPILER=$$(command -v rustc-1.85 || command -v rustc)
override_dh_clean:
dh_clean --exclude=Cargo.toml.orig
@@ -22,4 +24,4 @@ override_dh_clean:
-unlink vendor
override_dh_auto_test:
make fish_run_tests
cd build && make fish_run_tests

View File

@@ -18,7 +18,7 @@ We use forks of the last two - see the [FFI section](#ffi) below. No special act
### Build Dependencies
fish-shell currently depends on Rust 1.70 or later. To install Rust, follow https://rustup.rs.
fish-shell currently depends on Rust 1.85 or later. To install Rust, follow https://rustup.rs.
### Build via CMake

View File

@@ -1,5 +1,3 @@
.. _cmd-_:
_ - call fish's translations
============================

View File

@@ -1,5 +1,3 @@
.. _cmd-abbr:
abbr - manage fish abbreviations
================================
@@ -10,8 +8,8 @@ Synopsis
abbr --add NAME [--position command | anywhere] [-r | --regex PATTERN] [-c | --command COMMAND]
[--set-cursor[=MARKER]] ([-f | --function FUNCTION] | EXPANSION)
abbr --erase NAME ...
abbr --rename OLD_WORD NEW_WORD
abbr --erase [ [-c | --command COMMAND]... ] NAME ...
abbr --rename [ [-c | --command COMMAND]... ] OLD_WORD NEW_WORD
abbr --show
abbr --list
abbr --query NAME ...
@@ -136,9 +134,10 @@ Other subcommands
::
abbr --rename OLD_NAME NEW_NAME
abbr --rename [ [-c | --command COMMAND]... ] OLD_NAME NEW_NAME
Renames an abbreviation, from *OLD_NAME* to *NEW_NAME*
Renames an abbreviation, from *OLD_NAME* to *NEW_NAME*.
For command-specific abbreviations, the ``--command`` options must be provided to disambiguate which abbreviation to rename.
::
@@ -154,9 +153,10 @@ Prints the names of all abbreviation
::
abbr [-e | --erase] NAME
abbr [-e | --erase] [ [-c | --command COMMAND]... ] NAME ...
Erases the abbreviation with the given name
Erases the abbreviation with the given name.
For command-specific abbreviations, the ``--command`` options must be provided to disambiguate which abbreviation to rename.
::

View File

@@ -1,5 +1,3 @@
.. _cmd-alias:
alias - create a function
=========================

View File

@@ -1,5 +1,3 @@
.. _cmd-and:
and - conditionally execute a command
=====================================

View File

@@ -1,5 +1,3 @@
.. _cmd-argparse:
argparse - parse options passed to a fish script or function
============================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-begin:
begin - start a new block of code
=================================

View File

@@ -1,5 +1,3 @@
.. _cmd-bg:
bg - send jobs to background
============================
@@ -42,6 +40,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

@@ -1,5 +1,3 @@
.. _cmd-bind:
bind - handle fish key bindings
===============================
Synopsis

View File

@@ -1,5 +1,3 @@
.. _cmd-block:
block - temporarily block delivery of events
============================================

View File

@@ -1,5 +1,3 @@
.. _cmd-break:
break - stop the current inner loop
===================================

View File

@@ -1,5 +1,3 @@
.. _cmd-breakpoint:
breakpoint - launch debug mode
==============================

View File

@@ -1,5 +1,3 @@
.. _cmd-builtin:
builtin - run a builtin command
===============================

View File

@@ -1,5 +1,3 @@
.. _cmd-case:
case - conditionally execute a block of commands
================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-cd:
cd - change directory
=====================

View File

@@ -1,5 +1,3 @@
.. _cmd-cdh:
cdh - change to a recently visited directory
============================================

View File

@@ -1,5 +1,3 @@
.. _cmd-command:
command - run a program
=======================

View File

@@ -1,5 +1,3 @@
.. _cmd-commandline:
commandline - set or get the current command line buffer
========================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-complete:
complete - edit command-specific tab-completions
================================================
@@ -16,7 +14,7 @@ Description
``complete`` defines, removes or lists completions for a command.
For an introduction to writing your own completions, see :ref:`Writing your own completions <completion-own>` in
For an introduction to writing your own completions, see :doc:`Writing your own completions <../completions>` in
the fish manual.
The following options are available:

View File

@@ -1,5 +1,3 @@
.. _cmd-contains:
contains - test if a word is present in a list
==============================================

View File

@@ -1,5 +1,3 @@
.. _cmd-continue:
continue - skip the remainder of the current iteration of the current inner loop
================================================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-count:
count - count the number of elements of a list
================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-dirh:
dirh - print directory history
==============================

View File

@@ -1,5 +1,3 @@
.. _cmd-dirs:
dirs - print directory stack
============================

View File

@@ -1,5 +1,3 @@
.. _cmd-disown:
disown - remove a process from the list of jobs
===============================================

View File

@@ -1,5 +1,3 @@
.. _cmd-echo:
echo - display a line of text
=============================

View File

@@ -1,5 +1,3 @@
.. _cmd-else:
else - execute command if a condition is not met
================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-emit:
emit - emit a generic event
===========================

View File

@@ -1,5 +1,3 @@
.. _cmd-end:
end - end a block of commands
=============================

View File

@@ -1,5 +1,3 @@
.. _cmd-eval:
eval - evaluate the specified commands
======================================

View File

@@ -1,5 +1,3 @@
.. _cmd-exec:
exec - execute command in current process
=========================================

View File

@@ -1,6 +1,3 @@
.. _cmd-exit:
.. program::exit
exit - exit the shell
=====================

View File

@@ -1,5 +1,3 @@
.. _cmd-export:
export - compatibility function for exporting variables
=======================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-false:
false - return an unsuccessful result
=====================================

View File

@@ -1,5 +1,3 @@
.. _cmd-fg:
fg - bring job to foreground
============================

View File

@@ -1,6 +1,3 @@
.. _cmd-fish:
.. program::fish
fish - the friendly interactive shell
=====================================
@@ -18,7 +15,7 @@ Description
:command:`fish` is a command-line shell written mainly with interactive use in mind.
This page briefly describes the options for invoking :command:`fish`.
The :ref:`full manual <intro>` is available in HTML by using the :command:`help` command from inside fish, and in the `fish-doc(1)` man page.
The :ref:`tutorial <tutorial>` is available as HTML via ``help tutorial`` or in `man fish-tutorial`.
The :doc:`tutorial <../tutorial>` is available as HTML via ``help tutorial`` or in `man fish-tutorial`.
The following options are available:

View File

@@ -1,6 +1,3 @@
.. _cmd-fish_add_path:
.. program::fish_add_path
fish_add_path - add to the path
==============================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-fish_breakpoint_prompt:
fish_breakpoint_prompt - define the prompt when stopped at a breakpoint
=======================================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-fish_clipboard_copy:
fish_clipboard_copy - copy text to the system's clipboard
==============================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-fish_clipboard_paste:
fish_clipboard_paste - get text from the system's clipboard
==============================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-fish_command_not_found:
fish_command_not_found - what to do when a command wasn't found
===============================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-fish_config:
fish_config - start the web-based configuration interface
=========================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-fish_default_key_bindings:
fish_default_key_bindings - set emacs key bindings for fish
===============================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-fish_git_prompt:
fish_git_prompt - output git information for use in a prompt
============================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-fish_greeting:
fish_greeting - display a welcome message in interactive shells
===============================================================

View File

@@ -1,5 +1,3 @@
.. _cmd-fish_hg_prompt:
fish_hg_prompt - output Mercurial information for use in a prompt
=================================================================

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