2023-02-03 16:34:29 +01:00
|
|
|
//! Flags to enable upcoming features
|
|
|
|
|
|
2026-04-04 02:37:25 +02:00
|
|
|
use fish_widestring::{L, WExt as _, wstr};
|
2026-04-04 02:40:52 +02:00
|
|
|
use std::{
|
|
|
|
|
cell::RefCell,
|
|
|
|
|
sync::atomic::{AtomicBool, Ordering},
|
|
|
|
|
};
|
2024-03-09 04:08:16 -06:00
|
|
|
|
2024-01-01 21:29:05 +01:00
|
|
|
/// The list of flags.
|
|
|
|
|
#[repr(u8)]
|
refactor: use override stack for feature tests
Several features of fish can be toggled at runtime (in practice at
startup). To keep track of the active features, `FEATURES`, an array of
`AtomicBool` is used. This can safely be shared across threads without
requiring locks.
Some of our tests override certain features to test behavior with a
specific value of the feature. Prior to this commit, they did this by
using thread-local versions of `FEATURES` instead of the process-wide
version used in non-test builds. This approach has two downsides:
- It does not allow nested overrides.
- It prevents using the code across package boundaries.
The former is a fairly minor issue, since I don't think we need nested
overrides. The latter prevents splitting up our large library crate,
since `#[cfg(test)]`-guarded code can only be used within a single
package.
To resolve these issues, a new approach to feature overrides in
tests is introduced in this commit: Instead of having a thread-local
version of `FEATURES`, all code, whether test or not, uses the
process-wide `FEATURES`. For non-test code, there is no change. For test
code, `FEATURES` is now also used. To override features in tests, a new
`with_overridden_feature` function is added, which replaces
`scoped_test` and `set`. It works by maintaining a thread-local stack of
feature overrides (`LOCAL_OVERRIDE_STACK`). The overridden `FeatureFlag`
and its new value are pushed to the stack, then the code for which the
override should be active is run, and finally the stack is popped again.
Feature tests now have to scan the stack for the first appearance of the
`FeatureFlag`, or use the value in `FEATURES` if the stack does not
contain the `FeatureFlag`. In most cases, the stack will be empty or
contain very few elements, so scanning it should not take long. For now,
it's only active in test code, so non-test code is unaffected. The plan
is to change this when the feature flag code is extracted from the main
library crate. This would slightly slow down feature tests in non-test
code, but there the stack will always be empty, since we only override
features in tests.
Part of #12494
2026-04-04 02:02:07 +02:00
|
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
2024-01-01 21:29:05 +01:00
|
|
|
pub enum FeatureFlag {
|
|
|
|
|
/// Whether ^ is supported for stderr redirection.
|
2025-12-14 18:27:29 -05:00
|
|
|
StderrNoCaret,
|
2023-02-03 16:34:29 +01:00
|
|
|
|
2024-01-01 21:29:05 +01:00
|
|
|
/// Whether ? is supported as a glob.
|
2025-12-14 18:27:29 -05:00
|
|
|
QuestionMarkNoGlob,
|
2023-02-03 16:34:29 +01:00
|
|
|
|
2024-01-01 21:29:05 +01:00
|
|
|
/// Whether string replace -r double-unescapes the replacement.
|
2025-12-14 18:27:29 -05:00
|
|
|
StringReplaceBackslash,
|
2023-02-03 16:34:29 +01:00
|
|
|
|
2024-01-01 21:29:05 +01:00
|
|
|
/// Whether "&" is not-special if followed by a word character.
|
2025-12-14 18:27:29 -05:00
|
|
|
AmpersandNoBgInToken,
|
2024-02-06 22:13:16 +01:00
|
|
|
/// Whether "%self" is expanded to fish's pid
|
2025-12-14 18:27:29 -05:00
|
|
|
RemovePercentSelf,
|
2024-04-21 14:25:54 +02:00
|
|
|
|
|
|
|
|
/// Remove `test`'s one and zero arg mode (make `test -n` return false etc)
|
2025-12-14 18:27:29 -05:00
|
|
|
TestRequireArg,
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
|
2025-08-27 09:13:27 +02:00
|
|
|
/// Whether to write OSC 133 prompt markers
|
2025-12-14 18:27:29 -05:00
|
|
|
MarkPrompt,
|
2025-08-27 09:13:27 +02:00
|
|
|
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
/// Do not look up $TERM in terminfo database.
|
2025-12-14 18:27:29 -05:00
|
|
|
IgnoreTerminfo,
|
2025-09-23 18:06:10 +02:00
|
|
|
|
|
|
|
|
/// Whether we are allowed to query the TTY for extra information.
|
2025-12-14 18:27:29 -05:00
|
|
|
QueryTerm,
|
2025-09-23 12:57:06 +02:00
|
|
|
|
|
|
|
|
/// Do not try to work around incompatible terminal.
|
2025-12-14 18:27:29 -05:00
|
|
|
OmitTermWorkarounds,
|
2023-02-03 16:34:29 +01:00
|
|
|
}
|
|
|
|
|
|
2023-07-09 22:33:02 +02:00
|
|
|
struct Features {
|
2023-02-03 16:34:29 +01:00
|
|
|
// Values for the flags.
|
|
|
|
|
// These are atomic to "fix" a race reported by tsan where tests of feature flags and other
|
|
|
|
|
// tests which use them conceptually race.
|
2023-07-09 22:33:02 +02:00
|
|
|
values: [AtomicBool; METADATA.len()],
|
2023-02-03 16:34:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Metadata about feature flags.
|
2023-07-09 22:33:02 +02:00
|
|
|
pub struct FeatureMetadata {
|
2023-02-03 16:34:29 +01:00
|
|
|
/// The flag itself.
|
2023-07-09 22:33:02 +02:00
|
|
|
pub flag: FeatureFlag,
|
2023-02-03 16:34:29 +01:00
|
|
|
|
|
|
|
|
/// User-presentable short name of the feature flag.
|
2023-07-09 22:33:02 +02:00
|
|
|
pub name: &'static wstr,
|
2023-02-03 16:34:29 +01:00
|
|
|
|
|
|
|
|
/// Comma-separated list of feature groups.
|
2023-07-09 22:33:02 +02:00
|
|
|
pub groups: &'static wstr,
|
2023-02-03 16:34:29 +01:00
|
|
|
|
|
|
|
|
/// User-presentable description of the feature flag.
|
2023-07-09 22:33:02 +02:00
|
|
|
pub description: &'static wstr,
|
2023-02-03 16:34:29 +01:00
|
|
|
|
|
|
|
|
/// Default flag value.
|
2026-04-02 18:33:15 +02:00
|
|
|
default_value: bool,
|
2023-02-03 16:34:29 +01:00
|
|
|
|
|
|
|
|
/// Whether the value can still be changed or not.
|
2026-04-02 18:33:15 +02:00
|
|
|
read_only: bool,
|
2023-02-03 16:34:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The metadata, indexed by flag.
|
2023-07-10 02:56:57 +02:00
|
|
|
pub const METADATA: &[FeatureMetadata] = &[
|
2023-02-03 16:34:29 +01:00
|
|
|
FeatureMetadata {
|
2025-12-14 18:27:29 -05:00
|
|
|
flag: FeatureFlag::StderrNoCaret,
|
2024-01-12 19:10:56 +01:00
|
|
|
name: L!("stderr-nocaret"),
|
|
|
|
|
groups: L!("3.0"),
|
|
|
|
|
description: L!("^ no longer redirects stderr (historical, can no longer be changed)"),
|
2023-02-03 16:34:29 +01:00
|
|
|
default_value: true,
|
|
|
|
|
read_only: true,
|
|
|
|
|
},
|
|
|
|
|
FeatureMetadata {
|
2025-12-14 18:27:29 -05:00
|
|
|
flag: FeatureFlag::QuestionMarkNoGlob,
|
2024-01-12 19:10:56 +01:00
|
|
|
name: L!("qmark-noglob"),
|
2024-01-25 18:26:48 +01:00
|
|
|
groups: L!("3.0"),
|
2024-01-12 19:10:56 +01:00
|
|
|
description: L!("? no longer globs"),
|
2024-01-24 21:17:36 -06:00
|
|
|
default_value: true,
|
2023-02-03 16:34:29 +01:00
|
|
|
read_only: false,
|
|
|
|
|
},
|
|
|
|
|
FeatureMetadata {
|
2025-12-14 18:27:29 -05:00
|
|
|
flag: FeatureFlag::StringReplaceBackslash,
|
2024-01-12 19:10:56 +01:00
|
|
|
name: L!("regex-easyesc"),
|
|
|
|
|
groups: L!("3.1"),
|
|
|
|
|
description: L!("string replace -r needs fewer \\'s"),
|
2023-02-03 16:34:29 +01:00
|
|
|
default_value: true,
|
|
|
|
|
read_only: false,
|
|
|
|
|
},
|
|
|
|
|
FeatureMetadata {
|
2025-12-14 18:27:29 -05:00
|
|
|
flag: FeatureFlag::AmpersandNoBgInToken,
|
2024-01-12 19:10:56 +01:00
|
|
|
name: L!("ampersand-nobg-in-token"),
|
|
|
|
|
groups: L!("3.4"),
|
|
|
|
|
description: L!("& only backgrounds if followed by a separator"),
|
2023-02-03 16:34:29 +01:00
|
|
|
default_value: true,
|
|
|
|
|
read_only: false,
|
|
|
|
|
},
|
2024-02-06 22:13:16 +01:00
|
|
|
FeatureMetadata {
|
2025-12-14 18:27:29 -05:00
|
|
|
flag: FeatureFlag::RemovePercentSelf,
|
2024-02-06 22:13:16 +01:00
|
|
|
name: L!("remove-percent-self"),
|
2025-01-15 22:11:28 +08:00
|
|
|
groups: L!("4.0"),
|
2024-02-06 22:13:16 +01:00
|
|
|
description: L!("%self is no longer expanded (use $fish_pid)"),
|
|
|
|
|
default_value: false,
|
|
|
|
|
read_only: false,
|
|
|
|
|
},
|
2024-04-21 14:25:54 +02:00
|
|
|
FeatureMetadata {
|
2025-12-14 18:27:29 -05:00
|
|
|
flag: FeatureFlag::TestRequireArg,
|
2024-04-21 14:25:54 +02:00
|
|
|
name: L!("test-require-arg"),
|
2025-01-15 22:11:28 +08:00
|
|
|
groups: L!("4.0"),
|
2024-04-21 14:25:54 +02:00
|
|
|
description: L!("builtin test requires an argument"),
|
|
|
|
|
default_value: false,
|
|
|
|
|
read_only: false,
|
|
|
|
|
},
|
2025-08-27 09:13:27 +02:00
|
|
|
FeatureMetadata {
|
2025-12-14 18:27:29 -05:00
|
|
|
flag: FeatureFlag::MarkPrompt,
|
2025-08-27 09:13:27 +02:00
|
|
|
name: L!("mark-prompt"),
|
|
|
|
|
groups: L!("4.0"),
|
|
|
|
|
description: L!("write OSC 133 prompt markers to the terminal"),
|
|
|
|
|
default_value: true,
|
|
|
|
|
read_only: false,
|
|
|
|
|
},
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
FeatureMetadata {
|
2025-12-14 18:27:29 -05:00
|
|
|
flag: FeatureFlag::IgnoreTerminfo,
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
name: L!("ignore-terminfo"),
|
|
|
|
|
groups: L!("4.1"),
|
2026-02-09 09:31:56 -08:00
|
|
|
description: L!(
|
|
|
|
|
"do not look up $TERM in terminfo database (historical, can no longer be changed)"
|
|
|
|
|
),
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
default_value: true,
|
2026-02-09 09:31:56 -08:00
|
|
|
read_only: true,
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
},
|
2025-09-23 18:06:10 +02:00
|
|
|
FeatureMetadata {
|
2025-12-14 18:27:29 -05:00
|
|
|
flag: FeatureFlag::QueryTerm,
|
2025-09-23 18:06:10 +02:00
|
|
|
name: L!("query-term"),
|
|
|
|
|
groups: L!("4.1"),
|
|
|
|
|
description: L!("query the TTY to enable extra functionality"),
|
|
|
|
|
default_value: true,
|
|
|
|
|
read_only: false,
|
|
|
|
|
},
|
2025-09-23 12:57:06 +02:00
|
|
|
FeatureMetadata {
|
2025-12-14 18:27:29 -05:00
|
|
|
flag: FeatureFlag::OmitTermWorkarounds,
|
2025-09-23 12:57:06 +02:00
|
|
|
name: L!("omit-term-workarounds"),
|
|
|
|
|
groups: L!("4.3"),
|
|
|
|
|
description: L!("skip workarounds for incompatible terminals"),
|
|
|
|
|
default_value: false,
|
|
|
|
|
read_only: false,
|
|
|
|
|
},
|
2023-02-03 16:34:29 +01:00
|
|
|
];
|
|
|
|
|
|
2023-07-10 06:50:46 +02:00
|
|
|
thread_local!(
|
refactor: use override stack for feature tests
Several features of fish can be toggled at runtime (in practice at
startup). To keep track of the active features, `FEATURES`, an array of
`AtomicBool` is used. This can safely be shared across threads without
requiring locks.
Some of our tests override certain features to test behavior with a
specific value of the feature. Prior to this commit, they did this by
using thread-local versions of `FEATURES` instead of the process-wide
version used in non-test builds. This approach has two downsides:
- It does not allow nested overrides.
- It prevents using the code across package boundaries.
The former is a fairly minor issue, since I don't think we need nested
overrides. The latter prevents splitting up our large library crate,
since `#[cfg(test)]`-guarded code can only be used within a single
package.
To resolve these issues, a new approach to feature overrides in
tests is introduced in this commit: Instead of having a thread-local
version of `FEATURES`, all code, whether test or not, uses the
process-wide `FEATURES`. For non-test code, there is no change. For test
code, `FEATURES` is now also used. To override features in tests, a new
`with_overridden_feature` function is added, which replaces
`scoped_test` and `set`. It works by maintaining a thread-local stack of
feature overrides (`LOCAL_OVERRIDE_STACK`). The overridden `FeatureFlag`
and its new value are pushed to the stack, then the code for which the
override should be active is run, and finally the stack is popped again.
Feature tests now have to scan the stack for the first appearance of the
`FeatureFlag`, or use the value in `FEATURES` if the stack does not
contain the `FeatureFlag`. In most cases, the stack will be empty or
contain very few elements, so scanning it should not take long. For now,
it's only active in test code, so non-test code is unaffected. The plan
is to change this when the feature flag code is extracted from the main
library crate. This would slightly slow down feature tests in non-test
code, but there the stack will always be empty, since we only override
features in tests.
Part of #12494
2026-04-04 02:02:07 +02:00
|
|
|
static LOCAL_OVERRIDE_STACK: RefCell<Vec<(FeatureFlag, bool)>> =
|
|
|
|
|
const { RefCell::new(Vec::new()) };
|
2023-07-10 06:50:46 +02:00
|
|
|
);
|
|
|
|
|
|
2023-02-03 16:34:29 +01:00
|
|
|
/// The singleton shared feature set.
|
2023-07-09 22:33:02 +02:00
|
|
|
static FEATURES: Features = Features::new();
|
|
|
|
|
|
|
|
|
|
/// Perform a feature test on the global set of features.
|
2026-04-04 02:33:40 +02:00
|
|
|
pub fn feature_test(flag: FeatureFlag) -> bool {
|
refactor: use override stack for feature tests
Several features of fish can be toggled at runtime (in practice at
startup). To keep track of the active features, `FEATURES`, an array of
`AtomicBool` is used. This can safely be shared across threads without
requiring locks.
Some of our tests override certain features to test behavior with a
specific value of the feature. Prior to this commit, they did this by
using thread-local versions of `FEATURES` instead of the process-wide
version used in non-test builds. This approach has two downsides:
- It does not allow nested overrides.
- It prevents using the code across package boundaries.
The former is a fairly minor issue, since I don't think we need nested
overrides. The latter prevents splitting up our large library crate,
since `#[cfg(test)]`-guarded code can only be used within a single
package.
To resolve these issues, a new approach to feature overrides in
tests is introduced in this commit: Instead of having a thread-local
version of `FEATURES`, all code, whether test or not, uses the
process-wide `FEATURES`. For non-test code, there is no change. For test
code, `FEATURES` is now also used. To override features in tests, a new
`with_overridden_feature` function is added, which replaces
`scoped_test` and `set`. It works by maintaining a thread-local stack of
feature overrides (`LOCAL_OVERRIDE_STACK`). The overridden `FeatureFlag`
and its new value are pushed to the stack, then the code for which the
override should be active is run, and finally the stack is popped again.
Feature tests now have to scan the stack for the first appearance of the
`FeatureFlag`, or use the value in `FEATURES` if the stack does not
contain the `FeatureFlag`. In most cases, the stack will be empty or
contain very few elements, so scanning it should not take long. For now,
it's only active in test code, so non-test code is unaffected. The plan
is to change this when the feature flag code is extracted from the main
library crate. This would slightly slow down feature tests in non-test
code, but there the stack will always be empty, since we only override
features in tests.
Part of #12494
2026-04-04 02:02:07 +02:00
|
|
|
if let Some(value) = LOCAL_OVERRIDE_STACK.with(|stack| {
|
|
|
|
|
for &(overridden_feature, value) in stack.borrow().iter().rev() {
|
|
|
|
|
if flag == overridden_feature {
|
|
|
|
|
return Some(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}) {
|
|
|
|
|
return value;
|
2023-07-10 06:50:46 +02:00
|
|
|
}
|
refactor: use override stack for feature tests
Several features of fish can be toggled at runtime (in practice at
startup). To keep track of the active features, `FEATURES`, an array of
`AtomicBool` is used. This can safely be shared across threads without
requiring locks.
Some of our tests override certain features to test behavior with a
specific value of the feature. Prior to this commit, they did this by
using thread-local versions of `FEATURES` instead of the process-wide
version used in non-test builds. This approach has two downsides:
- It does not allow nested overrides.
- It prevents using the code across package boundaries.
The former is a fairly minor issue, since I don't think we need nested
overrides. The latter prevents splitting up our large library crate,
since `#[cfg(test)]`-guarded code can only be used within a single
package.
To resolve these issues, a new approach to feature overrides in
tests is introduced in this commit: Instead of having a thread-local
version of `FEATURES`, all code, whether test or not, uses the
process-wide `FEATURES`. For non-test code, there is no change. For test
code, `FEATURES` is now also used. To override features in tests, a new
`with_overridden_feature` function is added, which replaces
`scoped_test` and `set`. It works by maintaining a thread-local stack of
feature overrides (`LOCAL_OVERRIDE_STACK`). The overridden `FeatureFlag`
and its new value are pushed to the stack, then the code for which the
override should be active is run, and finally the stack is popped again.
Feature tests now have to scan the stack for the first appearance of the
`FeatureFlag`, or use the value in `FEATURES` if the stack does not
contain the `FeatureFlag`. In most cases, the stack will be empty or
contain very few elements, so scanning it should not take long. For now,
it's only active in test code, so non-test code is unaffected. The plan
is to change this when the feature flag code is extracted from the main
library crate. This would slightly slow down feature tests in non-test
code, but there the stack will always be empty, since we only override
features in tests.
Part of #12494
2026-04-04 02:02:07 +02:00
|
|
|
FEATURES.test(flag)
|
2023-07-09 22:33:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Parses a comma-separated feature-flag string, updating ourselves with the values.
|
|
|
|
|
/// Feature names or group names may be prefixed with "no-" to disable them.
|
|
|
|
|
/// The special group name "all" may be used for those who like to live on the edge.
|
|
|
|
|
/// Unknown features are silently ignored.
|
|
|
|
|
pub fn set_from_string<'a>(str: impl Into<&'a wstr>) {
|
refactor: use override stack for feature tests
Several features of fish can be toggled at runtime (in practice at
startup). To keep track of the active features, `FEATURES`, an array of
`AtomicBool` is used. This can safely be shared across threads without
requiring locks.
Some of our tests override certain features to test behavior with a
specific value of the feature. Prior to this commit, they did this by
using thread-local versions of `FEATURES` instead of the process-wide
version used in non-test builds. This approach has two downsides:
- It does not allow nested overrides.
- It prevents using the code across package boundaries.
The former is a fairly minor issue, since I don't think we need nested
overrides. The latter prevents splitting up our large library crate,
since `#[cfg(test)]`-guarded code can only be used within a single
package.
To resolve these issues, a new approach to feature overrides in
tests is introduced in this commit: Instead of having a thread-local
version of `FEATURES`, all code, whether test or not, uses the
process-wide `FEATURES`. For non-test code, there is no change. For test
code, `FEATURES` is now also used. To override features in tests, a new
`with_overridden_feature` function is added, which replaces
`scoped_test` and `set`. It works by maintaining a thread-local stack of
feature overrides (`LOCAL_OVERRIDE_STACK`). The overridden `FeatureFlag`
and its new value are pushed to the stack, then the code for which the
override should be active is run, and finally the stack is popped again.
Feature tests now have to scan the stack for the first appearance of the
`FeatureFlag`, or use the value in `FEATURES` if the stack does not
contain the `FeatureFlag`. In most cases, the stack will be empty or
contain very few elements, so scanning it should not take long. For now,
it's only active in test code, so non-test code is unaffected. The plan
is to change this when the feature flag code is extracted from the main
library crate. This would slightly slow down feature tests in non-test
code, but there the stack will always be empty, since we only override
features in tests.
Part of #12494
2026-04-04 02:02:07 +02:00
|
|
|
FEATURES.set_from_string(str.into());
|
2023-07-09 22:33:02 +02:00
|
|
|
}
|
2023-02-03 16:34:29 +01:00
|
|
|
|
2023-02-07 23:18:51 +01:00
|
|
|
impl Features {
|
2023-07-03 13:39:54 -07:00
|
|
|
const fn new() -> Self {
|
2026-02-09 10:27:47 -08:00
|
|
|
// TODO: feature(const_array): use std::array::from_fn()
|
|
|
|
|
use std::mem::{MaybeUninit, transmute};
|
|
|
|
|
let values = {
|
|
|
|
|
let mut data: [MaybeUninit<AtomicBool>; METADATA.len()] =
|
|
|
|
|
[const { MaybeUninit::uninit() }; METADATA.len()];
|
|
|
|
|
|
|
|
|
|
let mut i = 0;
|
|
|
|
|
while i < METADATA.len() {
|
|
|
|
|
data[i].write(AtomicBool::new(METADATA[i].default_value));
|
|
|
|
|
i += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SAFETY: `data` is guaranteed initialized by the loop
|
|
|
|
|
unsafe {
|
|
|
|
|
transmute::<[MaybeUninit<AtomicBool>; METADATA.len()], [AtomicBool; METADATA.len()]>(
|
|
|
|
|
data,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
Features { values }
|
2023-02-03 16:34:29 +01:00
|
|
|
}
|
|
|
|
|
|
2023-07-09 22:33:02 +02:00
|
|
|
fn test(&self, flag: FeatureFlag) -> bool {
|
2024-01-01 21:29:05 +01:00
|
|
|
self.values[flag as usize].load(Ordering::SeqCst)
|
2023-02-03 16:34:29 +01:00
|
|
|
}
|
|
|
|
|
|
2023-07-09 22:33:02 +02:00
|
|
|
fn set(&self, flag: FeatureFlag, value: bool) {
|
2026-01-30 21:11:57 +00:00
|
|
|
self.values[flag as usize].store(value, Ordering::SeqCst);
|
2023-02-03 16:34:29 +01:00
|
|
|
}
|
|
|
|
|
|
2024-01-12 20:31:07 +01:00
|
|
|
fn set_from_string(&self, str: &wstr) {
|
2026-03-30 22:30:32 +02:00
|
|
|
for entry in str.split(',') {
|
|
|
|
|
let entry = entry.trim();
|
2023-02-03 16:34:29 +01:00
|
|
|
if entry.is_empty() {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A "no-" prefix inverts the sense.
|
2026-03-30 22:30:32 +02:00
|
|
|
let (name, value) = match entry.strip_prefix("no-") {
|
2023-02-03 16:34:29 +01:00
|
|
|
Some(suffix) => (suffix, false),
|
|
|
|
|
None => (entry, true),
|
|
|
|
|
};
|
|
|
|
|
// Look for a feature with this name. If we don't find it, assume it's a group name and set
|
|
|
|
|
// all features whose group contain it. Do nothing even if the string is unrecognized; this
|
|
|
|
|
// is to allow uniform invocations of fish (e.g. disable a feature that is only present in
|
|
|
|
|
// future versions).
|
|
|
|
|
// The special name 'all' may be used for those who like to live on the edge.
|
2023-07-09 22:33:02 +02:00
|
|
|
if let Some(md) = METADATA.iter().find(|md| md.name == name) {
|
2023-02-03 16:34:29 +01:00
|
|
|
// Only change it if it's not read-only.
|
|
|
|
|
// Don't complain if it is, this is typically set from a variable.
|
|
|
|
|
if !md.read_only {
|
|
|
|
|
self.set(md.flag, value);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2023-07-10 02:56:57 +02:00
|
|
|
for md in METADATA {
|
2025-10-24 13:21:40 +02:00
|
|
|
if (md.groups == name || name == L!("all")) && !md.read_only {
|
|
|
|
|
self.set(md.flag, value);
|
2023-02-03 16:34:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
refactor: use override stack for feature tests
Several features of fish can be toggled at runtime (in practice at
startup). To keep track of the active features, `FEATURES`, an array of
`AtomicBool` is used. This can safely be shared across threads without
requiring locks.
Some of our tests override certain features to test behavior with a
specific value of the feature. Prior to this commit, they did this by
using thread-local versions of `FEATURES` instead of the process-wide
version used in non-test builds. This approach has two downsides:
- It does not allow nested overrides.
- It prevents using the code across package boundaries.
The former is a fairly minor issue, since I don't think we need nested
overrides. The latter prevents splitting up our large library crate,
since `#[cfg(test)]`-guarded code can only be used within a single
package.
To resolve these issues, a new approach to feature overrides in
tests is introduced in this commit: Instead of having a thread-local
version of `FEATURES`, all code, whether test or not, uses the
process-wide `FEATURES`. For non-test code, there is no change. For test
code, `FEATURES` is now also used. To override features in tests, a new
`with_overridden_feature` function is added, which replaces
`scoped_test` and `set`. It works by maintaining a thread-local stack of
feature overrides (`LOCAL_OVERRIDE_STACK`). The overridden `FeatureFlag`
and its new value are pushed to the stack, then the code for which the
override should be active is run, and finally the stack is popped again.
Feature tests now have to scan the stack for the first appearance of the
`FeatureFlag`, or use the value in `FEATURES` if the stack does not
contain the `FeatureFlag`. In most cases, the stack will be empty or
contain very few elements, so scanning it should not take long. For now,
it's only active in test code, so non-test code is unaffected. The plan
is to change this when the feature flag code is extracted from the main
library crate. This would slightly slow down feature tests in non-test
code, but there the stack will always be empty, since we only override
features in tests.
Part of #12494
2026-04-04 02:02:07 +02:00
|
|
|
/// Run code with a feature overridden.
|
|
|
|
|
/// This should only be used in tests.
|
|
|
|
|
pub fn with_overridden_feature(flag: FeatureFlag, value: bool, test_fn: impl FnOnce()) {
|
|
|
|
|
LOCAL_OVERRIDE_STACK.with(|stack| {
|
|
|
|
|
stack.borrow_mut().push((flag, value));
|
2023-07-10 06:50:46 +02:00
|
|
|
test_fn();
|
refactor: use override stack for feature tests
Several features of fish can be toggled at runtime (in practice at
startup). To keep track of the active features, `FEATURES`, an array of
`AtomicBool` is used. This can safely be shared across threads without
requiring locks.
Some of our tests override certain features to test behavior with a
specific value of the feature. Prior to this commit, they did this by
using thread-local versions of `FEATURES` instead of the process-wide
version used in non-test builds. This approach has two downsides:
- It does not allow nested overrides.
- It prevents using the code across package boundaries.
The former is a fairly minor issue, since I don't think we need nested
overrides. The latter prevents splitting up our large library crate,
since `#[cfg(test)]`-guarded code can only be used within a single
package.
To resolve these issues, a new approach to feature overrides in
tests is introduced in this commit: Instead of having a thread-local
version of `FEATURES`, all code, whether test or not, uses the
process-wide `FEATURES`. For non-test code, there is no change. For test
code, `FEATURES` is now also used. To override features in tests, a new
`with_overridden_feature` function is added, which replaces
`scoped_test` and `set`. It works by maintaining a thread-local stack of
feature overrides (`LOCAL_OVERRIDE_STACK`). The overridden `FeatureFlag`
and its new value are pushed to the stack, then the code for which the
override should be active is run, and finally the stack is popped again.
Feature tests now have to scan the stack for the first appearance of the
`FeatureFlag`, or use the value in `FEATURES` if the stack does not
contain the `FeatureFlag`. In most cases, the stack will be empty or
contain very few elements, so scanning it should not take long. For now,
it's only active in test code, so non-test code is unaffected. The plan
is to change this when the feature flag code is extracted from the main
library crate. This would slightly slow down feature tests in non-test
code, but there the stack will always be empty, since we only override
features in tests.
Part of #12494
2026-04-04 02:02:07 +02:00
|
|
|
stack.borrow_mut().pop();
|
2023-07-10 06:50:46 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-22 13:23:13 +02:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
2026-04-04 02:33:40 +02:00
|
|
|
use super::{FeatureFlag, Features, METADATA, feature_test, with_overridden_feature};
|
2026-04-04 02:37:25 +02:00
|
|
|
use fish_widestring::L;
|
2023-02-03 16:34:29 +01:00
|
|
|
|
2025-10-22 13:23:13 +02:00
|
|
|
#[test]
|
|
|
|
|
fn test_feature_flags() {
|
|
|
|
|
let f = Features::new();
|
|
|
|
|
f.set_from_string(L!("stderr-nocaret,nonsense"));
|
2025-12-14 18:27:29 -05:00
|
|
|
assert!(f.test(FeatureFlag::StderrNoCaret));
|
2025-10-22 13:23:13 +02:00
|
|
|
f.set_from_string(L!("stderr-nocaret,no-stderr-nocaret,nonsense"));
|
2025-12-14 18:27:29 -05:00
|
|
|
assert!(f.test(FeatureFlag::StderrNoCaret));
|
2025-10-22 13:23:13 +02:00
|
|
|
|
|
|
|
|
// Ensure every metadata is represented once.
|
|
|
|
|
let mut counts: [usize; METADATA.len()] = [0; METADATA.len()];
|
|
|
|
|
for md in METADATA {
|
|
|
|
|
counts[md.flag as usize] += 1;
|
|
|
|
|
}
|
|
|
|
|
for count in counts {
|
|
|
|
|
assert_eq!(count, 1);
|
|
|
|
|
}
|
2023-07-10 06:50:46 +02:00
|
|
|
|
2025-10-22 13:23:13 +02:00
|
|
|
assert_eq!(
|
2025-12-14 18:27:29 -05:00
|
|
|
METADATA[FeatureFlag::StderrNoCaret as usize].name,
|
2025-10-22 13:23:13 +02:00
|
|
|
L!("stderr-nocaret")
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-07-10 06:50:46 +02:00
|
|
|
|
2025-10-22 13:23:13 +02:00
|
|
|
#[test]
|
refactor: use override stack for feature tests
Several features of fish can be toggled at runtime (in practice at
startup). To keep track of the active features, `FEATURES`, an array of
`AtomicBool` is used. This can safely be shared across threads without
requiring locks.
Some of our tests override certain features to test behavior with a
specific value of the feature. Prior to this commit, they did this by
using thread-local versions of `FEATURES` instead of the process-wide
version used in non-test builds. This approach has two downsides:
- It does not allow nested overrides.
- It prevents using the code across package boundaries.
The former is a fairly minor issue, since I don't think we need nested
overrides. The latter prevents splitting up our large library crate,
since `#[cfg(test)]`-guarded code can only be used within a single
package.
To resolve these issues, a new approach to feature overrides in
tests is introduced in this commit: Instead of having a thread-local
version of `FEATURES`, all code, whether test or not, uses the
process-wide `FEATURES`. For non-test code, there is no change. For test
code, `FEATURES` is now also used. To override features in tests, a new
`with_overridden_feature` function is added, which replaces
`scoped_test` and `set`. It works by maintaining a thread-local stack of
feature overrides (`LOCAL_OVERRIDE_STACK`). The overridden `FeatureFlag`
and its new value are pushed to the stack, then the code for which the
override should be active is run, and finally the stack is popped again.
Feature tests now have to scan the stack for the first appearance of the
`FeatureFlag`, or use the value in `FEATURES` if the stack does not
contain the `FeatureFlag`. In most cases, the stack will be empty or
contain very few elements, so scanning it should not take long. For now,
it's only active in test code, so non-test code is unaffected. The plan
is to change this when the feature flag code is extracted from the main
library crate. This would slightly slow down feature tests in non-test
code, but there the stack will always be empty, since we only override
features in tests.
Part of #12494
2026-04-04 02:02:07 +02:00
|
|
|
fn test_overridden_feature() {
|
|
|
|
|
with_overridden_feature(FeatureFlag::QuestionMarkNoGlob, true, || {
|
2026-04-04 02:33:40 +02:00
|
|
|
assert!(feature_test(FeatureFlag::QuestionMarkNoGlob));
|
2025-10-22 13:23:13 +02:00
|
|
|
});
|
2023-07-10 06:50:46 +02:00
|
|
|
|
refactor: use override stack for feature tests
Several features of fish can be toggled at runtime (in practice at
startup). To keep track of the active features, `FEATURES`, an array of
`AtomicBool` is used. This can safely be shared across threads without
requiring locks.
Some of our tests override certain features to test behavior with a
specific value of the feature. Prior to this commit, they did this by
using thread-local versions of `FEATURES` instead of the process-wide
version used in non-test builds. This approach has two downsides:
- It does not allow nested overrides.
- It prevents using the code across package boundaries.
The former is a fairly minor issue, since I don't think we need nested
overrides. The latter prevents splitting up our large library crate,
since `#[cfg(test)]`-guarded code can only be used within a single
package.
To resolve these issues, a new approach to feature overrides in
tests is introduced in this commit: Instead of having a thread-local
version of `FEATURES`, all code, whether test or not, uses the
process-wide `FEATURES`. For non-test code, there is no change. For test
code, `FEATURES` is now also used. To override features in tests, a new
`with_overridden_feature` function is added, which replaces
`scoped_test` and `set`. It works by maintaining a thread-local stack of
feature overrides (`LOCAL_OVERRIDE_STACK`). The overridden `FeatureFlag`
and its new value are pushed to the stack, then the code for which the
override should be active is run, and finally the stack is popped again.
Feature tests now have to scan the stack for the first appearance of the
`FeatureFlag`, or use the value in `FEATURES` if the stack does not
contain the `FeatureFlag`. In most cases, the stack will be empty or
contain very few elements, so scanning it should not take long. For now,
it's only active in test code, so non-test code is unaffected. The plan
is to change this when the feature flag code is extracted from the main
library crate. This would slightly slow down feature tests in non-test
code, but there the stack will always be empty, since we only override
features in tests.
Part of #12494
2026-04-04 02:02:07 +02:00
|
|
|
with_overridden_feature(FeatureFlag::QuestionMarkNoGlob, false, || {
|
2026-04-04 02:33:40 +02:00
|
|
|
assert!(!feature_test(FeatureFlag::QuestionMarkNoGlob));
|
2025-10-22 13:23:13 +02:00
|
|
|
});
|
2023-07-10 06:50:46 +02:00
|
|
|
|
refactor: use override stack for feature tests
Several features of fish can be toggled at runtime (in practice at
startup). To keep track of the active features, `FEATURES`, an array of
`AtomicBool` is used. This can safely be shared across threads without
requiring locks.
Some of our tests override certain features to test behavior with a
specific value of the feature. Prior to this commit, they did this by
using thread-local versions of `FEATURES` instead of the process-wide
version used in non-test builds. This approach has two downsides:
- It does not allow nested overrides.
- It prevents using the code across package boundaries.
The former is a fairly minor issue, since I don't think we need nested
overrides. The latter prevents splitting up our large library crate,
since `#[cfg(test)]`-guarded code can only be used within a single
package.
To resolve these issues, a new approach to feature overrides in
tests is introduced in this commit: Instead of having a thread-local
version of `FEATURES`, all code, whether test or not, uses the
process-wide `FEATURES`. For non-test code, there is no change. For test
code, `FEATURES` is now also used. To override features in tests, a new
`with_overridden_feature` function is added, which replaces
`scoped_test` and `set`. It works by maintaining a thread-local stack of
feature overrides (`LOCAL_OVERRIDE_STACK`). The overridden `FeatureFlag`
and its new value are pushed to the stack, then the code for which the
override should be active is run, and finally the stack is popped again.
Feature tests now have to scan the stack for the first appearance of the
`FeatureFlag`, or use the value in `FEATURES` if the stack does not
contain the `FeatureFlag`. In most cases, the stack will be empty or
contain very few elements, so scanning it should not take long. For now,
it's only active in test code, so non-test code is unaffected. The plan
is to change this when the feature flag code is extracted from the main
library crate. This would slightly slow down feature tests in non-test
code, but there the stack will always be empty, since we only override
features in tests.
Part of #12494
2026-04-04 02:02:07 +02:00
|
|
|
with_overridden_feature(FeatureFlag::QuestionMarkNoGlob, false, || {
|
|
|
|
|
with_overridden_feature(FeatureFlag::QuestionMarkNoGlob, true, || {
|
2026-04-04 02:33:40 +02:00
|
|
|
assert!(feature_test(FeatureFlag::QuestionMarkNoGlob));
|
refactor: use override stack for feature tests
Several features of fish can be toggled at runtime (in practice at
startup). To keep track of the active features, `FEATURES`, an array of
`AtomicBool` is used. This can safely be shared across threads without
requiring locks.
Some of our tests override certain features to test behavior with a
specific value of the feature. Prior to this commit, they did this by
using thread-local versions of `FEATURES` instead of the process-wide
version used in non-test builds. This approach has two downsides:
- It does not allow nested overrides.
- It prevents using the code across package boundaries.
The former is a fairly minor issue, since I don't think we need nested
overrides. The latter prevents splitting up our large library crate,
since `#[cfg(test)]`-guarded code can only be used within a single
package.
To resolve these issues, a new approach to feature overrides in
tests is introduced in this commit: Instead of having a thread-local
version of `FEATURES`, all code, whether test or not, uses the
process-wide `FEATURES`. For non-test code, there is no change. For test
code, `FEATURES` is now also used. To override features in tests, a new
`with_overridden_feature` function is added, which replaces
`scoped_test` and `set`. It works by maintaining a thread-local stack of
feature overrides (`LOCAL_OVERRIDE_STACK`). The overridden `FeatureFlag`
and its new value are pushed to the stack, then the code for which the
override should be active is run, and finally the stack is popped again.
Feature tests now have to scan the stack for the first appearance of the
`FeatureFlag`, or use the value in `FEATURES` if the stack does not
contain the `FeatureFlag`. In most cases, the stack will be empty or
contain very few elements, so scanning it should not take long. For now,
it's only active in test code, so non-test code is unaffected. The plan
is to change this when the feature flag code is extracted from the main
library crate. This would slightly slow down feature tests in non-test
code, but there the stack will always be empty, since we only override
features in tests.
Part of #12494
2026-04-04 02:02:07 +02:00
|
|
|
});
|
2025-10-22 13:23:13 +02:00
|
|
|
});
|
|
|
|
|
}
|
2023-07-10 06:50:46 +02:00
|
|
|
}
|