59 Commits

Author SHA1 Message Date
Johannes Altmanninger
30942e16dc Fix prefix/suffix icase comparisons
As reported on Gitter, running "echo İ" makes history autosuggestion
for "echo i" crash.  This is because history search correctly
returns the former, but string_prefixes_string_case_insensitive("i",
"İ") incorrectly returns false.  This is because the prefix check
is implemented by trimming the rhs to the length of the prefix and
checking if the result is equal to the prefix.  This is wrong because
the prefix computation should operate on the canonical lowercase
version, because that's what history search uses.
2025-12-27 12:19:24 +01:00
Johannes Altmanninger
a8ded9cb0d Separate wcscasecmp() concerns better 2025-12-27 09:28:01 +01:00
Daniel Rainer
107cbaddf0 strings: take IntoCharIter instead of wstr
Change the input of some functions to take `impl IntoCharIter`, allowing
them to accept more input. Implementing this efficiently means that no
owned types should be passed into these functions, because their
`IntoCharIter` implementation would require unnecessary allocations.
Instead, convert the uses which previously passed `WString` by prefixing
an `&`, so the borrowed `&WString` is passed instead.

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

Closes #12207
2025-12-25 21:52:42 +00:00
Daniel Rainer
167cfd0892 gettext-extraction: fix outdated docs
Closes #12204
2025-12-24 01:01:47 +00:00
Daniel Rainer
9b75b6ee88 l10n: move po/ to localization/po/
This is done in preparation for Fluent's FTL files, which will be placed
in `localization/fluent/`. Having a shared parent reduces top-level
clutter in the repo and makes it easier to find the localization files
for translators, including realizing that both PO and FTL files exist.

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

Closes #12193
2025-12-21 12:11:49 +01:00
Daniel Rainer
17c35217b9 lints: warn on needless_return
The needless_return lint was disabled almost two years ago, alongside
several other lints. It might have been helpful for porting from C++.
Now, I think we can enable that lint again, since omitting the returns
results in equivalent, more concise code, which should not be harder to
read.

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

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

Closes #12188
2025-12-19 19:37:11 +01:00
Johannes Altmanninger
a261ca2aff Reapply "Refactor common::is_console_session"
This reverts commit 556be5c4a8.

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

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

This reverts commit d6108e5bc0.

Fixes #12192
2025-12-19 17:36:47 +01:00
Daniel Rainer
67d78fb258 fallback: extract into crate
For faster incremental builds and to enable subsequent extraction.

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

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

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

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

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

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

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

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

Closes #12108
2025-12-18 15:04:06 +01:00
xtqqczze
27852a6734 fix: clippy::ptr_as_ptr
https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr

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

Part of #12136
2025-12-11 17:46:58 +01:00
Daniel Rainer
3e2336043a gettext-extract: fix race condition
Multiple gettext-extraction proc macro instances can run at the same
time due to Rust's compilation model. In the previous implementation,
where every instance appended to the same file, this has resulted in
corruption of the file. This was reported and discussed in
https://github.com/fish-shell/fish-shell/pull/11928#discussion_r2488047964
for the equivalent macro for Fluent message ID extraction. The
underlying problem is the same.

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

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

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

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

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

Part of #12030
2025-12-08 16:51:20 +00:00
Daniel Rainer
22a252d064 gettext: rebuild extract macro on env var change
While it's not necessary to rebuild the proc macro for extraction when
the env var controlling the output location changes, this way everything
using the macro will automatically be rebuilt when this env var changes,
making it impossible to forget adding this to other `build.rs` files.

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

Closes #12107
2025-11-30 09:20:33 +01:00
Daniel Rainer
2ee4f239d1 build_helper: add rebuild function for embedded path change
For paths embedded via `rust-embed`, we only need to rebuild on path
changes if the files are actually embedded.

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

Closes #12083
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
0c67d0565a Commit help_sections data file
The help_sections.rs file was added to the tarball only as a quick hack.
There is a cyclic dependency between docs and fish:

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

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

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

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

Prepare to remove the dependency on doc_src by committing the help
sections (we already do elsewhere).
2025-11-23 12:30:22 +01:00
Johannes Altmanninger
bed2da9b83 Check license of dependencies in CI 2025-11-23 12:30:22 +01:00
Daniel Rainer
a56b1099aa tempfile: use std::env:temp_dir
This is a more robust implementation of our custom `get_tmpdir`.

Closes #12046
2025-11-16 11:21:18 +01:00
Johannes Altmanninger
b9af3eca9f Include prebuilt man pages again
Historically, Sphinx was required when building "standalone" builds,
else they would have no man pages.

This means that commit 0709e4be8b (Use standalone code paths by
default, 2025-10-26) broke man pages for users who build from a
tarball where non-standalone builds would use prebuilt docs.

Add a hack to use prebuilt docs again.

In future, we'll remove prebuilt docs, see #12052.
2025-11-13 12:58:29 +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
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
cc95cef165 crates/build-man-pages: try to improve style 2025-11-06 12:08:10 +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
Daniel Rainer
e9936bc5ed cleanup: use let-else to reduce indentation
Closes #12019
2025-11-04 11:51:25 +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
f6d7198317 printf tests: extract function for calling libc::sprintf 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
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
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
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
6a86974b96 Fix build on Illumos
Fixes #11982
2025-10-22 10:57:36 +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
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
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
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
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
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
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
4f8aa0a78c Show warnings from sphinx-build when building with cargo 2025-09-24 15:51:32 +02:00
Johannes Altmanninger
7ef4e7dfe7 Time out terminal queries after a while
Add a timeout of 2 seconds queries; if any query takes longer, warn
about that and reduce the timeout  so we stop blocking the UI.  This 2
second delay could also happen when network latency is momentarily
really high, so we might want relax this in future.

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

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

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

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

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

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

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

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

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

Part of #11726
Closes #11583
Closes #11725
Closes #11683
2025-09-20 13:56:23 +02:00