refactor: extract fish-feature-flags crate

Another step in splitting up the main library crate.

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

Closes #12494
This commit is contained in:
Daniel Rainer
2026-04-04 02:40:52 +02:00
committed by Johannes Altmanninger
parent c44aa32a15
commit 85311546de
22 changed files with 53 additions and 36 deletions

8
Cargo.lock generated
View File

@@ -272,6 +272,7 @@ dependencies = [
"fish-color",
"fish-common",
"fish-fallback",
"fish-feature-flags",
"fish-gettext",
"fish-gettext-extraction",
"fish-gettext-mo-file-parser",
@@ -348,6 +349,13 @@ dependencies = [
"widestring",
]
[[package]]
name = "fish-feature-flags"
version = "0.0.0"
dependencies = [
"fish-widestring",
]
[[package]]
name = "fish-gettext"
version = "0.0.0"

View File

@@ -23,6 +23,7 @@ fish-build-man-pages = { path = "crates/build-man-pages" }
fish-color = { path = "crates/color" }
fish-common = { path = "crates/common" }
fish-fallback = { path = "crates/fallback" }
fish-feature-flags = { path = "crates/feature-flags" }
fish-gettext = { path = "crates/gettext" }
fish-gettext-extraction = { path = "crates/gettext-extraction" }
fish-gettext-maps = { path = "crates/gettext-maps" }
@@ -108,6 +109,7 @@ fish-build-man-pages = { workspace = true, optional = true }
fish-color.workspace = true
fish-common.workspace = true
fish-fallback.workspace = true
fish-feature-flags.workspace = true
fish-gettext = { workspace = true, optional = true }
fish-gettext-extraction = { workspace = true, optional = true }
fish-printf.workspace = true

View File

@@ -0,0 +1,13 @@
[package]
name = "fish-feature-flags"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[dependencies]
fish-widestring.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +1,10 @@
//! Flags to enable upcoming features
use fish_widestring::{L, WExt as _, wstr};
#[cfg(test)]
use std::cell::RefCell;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{
cell::RefCell,
sync::atomic::{AtomicBool, Ordering},
};
/// The list of flags.
#[repr(u8)]
@@ -154,7 +155,6 @@ pub struct FeatureMetadata {
];
thread_local!(
#[cfg(test)]
static LOCAL_OVERRIDE_STACK: RefCell<Vec<(FeatureFlag, bool)>> =
const { RefCell::new(Vec::new()) };
);
@@ -164,7 +164,6 @@ pub struct FeatureMetadata {
/// Perform a feature test on the global set of features.
pub fn feature_test(flag: FeatureFlag) -> bool {
#[cfg(test)]
if let Some(value) = LOCAL_OVERRIDE_STACK.with(|stack| {
for &(overridden_feature, value) in stack.borrow().iter().rev() {
if flag == overridden_feature {
@@ -254,7 +253,6 @@ fn set_from_string(&self, str: &wstr) {
/// Run code with a feature overridden.
/// This should only be used in tests.
#[cfg(test)]
pub fn with_overridden_feature(flag: FeatureFlag, value: bool, test_fn: impl FnOnce()) {
LOCAL_OVERRIDE_STACK.with(|stack| {
stack.borrow_mut().push((flag, value));

View File

@@ -38,7 +38,7 @@
eprintf,
event::{self, Event},
flog::{self, activate_flog_categories_by_pattern, flog, flogf, set_flog_file_fd},
fprintf, function, future_feature_flags as features,
fprintf, function,
history::{self, start_private_mode},
io::IoChain,
locale::set_libc_locales,
@@ -482,10 +482,10 @@ fn throwing_main() -> i32 {
// command line takes precedence).
if let Some(features_var) = EnvStack::globals().get(L!("fish_features")) {
for s in features_var.as_list() {
features::set_from_string(s.as_utfstr());
fish_feature_flags::set_from_string(s.as_utfstr());
}
}
features::set_from_string(opts.features.as_utfstr());
fish_feature_flags::set_from_string(opts.features.as_utfstr());
proc_init();
reader_init(true);

View File

@@ -20,7 +20,6 @@
use crate::env::env_init;
use crate::env::environment::Environment as _;
use crate::expand::INTERNAL_SEPARATOR;
use crate::future_feature_flags;
use crate::global_safety::RelaxedAtomicBool;
use crate::highlight::{HighlightRole, HighlightSpec, colorize, highlight_shell};
use crate::operation_context::OperationContext;
@@ -945,7 +944,7 @@ fn throwing_main() -> i32 {
// Only set these here so you can't set them via the builtin.
if let Some(features_var) = EnvStack::globals().get(L!("fish_features")) {
for s in features_var.as_list() {
future_feature_flags::set_from_string(s.as_utfstr());
fish_feature_flags::set_from_string(s.as_utfstr());
}
}

View File

@@ -15,7 +15,6 @@
builtins::shared::BUILTIN_ERR_UNKNOWN_OPT,
common::{PROGRAM_NAME, get_program_name, osstr2wcstring, shell_modes},
env::{EnvStack, Environment as _, env_init},
future_feature_flags,
input_common::{
CharEvent, ImplicitEvent, InputEventQueue, InputEventQueuer as _, KeyEvent,
QueryResultEvent, match_key_event_to_key,
@@ -295,7 +294,7 @@ fn throwing_main() -> i32 {
reader_init(false);
if let Some(features_var) = EnvStack::globals().get(L!("fish_features")) {
for s in features_var.as_list() {
future_feature_flags::set_from_string(s.as_utfstr());
fish_feature_flags::set_from_string(s.as_utfstr());
}
}

View File

@@ -1,7 +1,6 @@
use super::prelude::*;
use crate::common::{bytes2wcstring, get_program_name, osstr2wcstring, str2wcstring};
use crate::env::config_paths::get_fish_path;
use crate::future_feature_flags::{self as features, feature_test};
use crate::proc::{
JobControl, get_job_control_mode, get_login, is_interactive_session, set_job_control_mode,
};
@@ -9,6 +8,7 @@
use crate::tty_handoff::{TERMINAL_OS_NAME, get_scroll_content_up_capability, xtversion};
use crate::wutil::{Error, waccess, wbasename, wdirname, wrealpath};
use cfg_if::cfg_if;
use fish_feature_flags::{self as features, feature_test};
use fish_util::wcsfilecmp_glob;
use fish_wcstringutil::wcs2bytes;
use nix::unistd::AccessFlags;

View File

@@ -414,9 +414,9 @@ fn report_matches(&mut self, arg: &wstr, streams: &mut IoStreams) {
#[cfg(test)]
mod tests {
use crate::builtins::shared::{STATUS_CMD_ERROR, STATUS_CMD_OK, STATUS_INVALID_ARGS};
use crate::future_feature_flags::{FeatureFlag, with_overridden_feature};
use crate::tests::prelude::*;
use crate::validate;
use fish_feature_flags::{FeatureFlag, with_overridden_feature};
#[test]
#[serial]

View File

@@ -3,7 +3,7 @@
use pcre2::utf32::{Regex, RegexBuilder};
use super::*;
use crate::future_feature_flags::{FeatureFlag, feature_test};
use fish_feature_flags::{FeatureFlag, feature_test};
#[derive(Default)]
pub struct Replace<'args> {

View File

@@ -1,6 +1,6 @@
use super::prelude::*;
use crate::future_feature_flags::{FeatureFlag, feature_test};
use crate::should_flog;
use fish_feature_flags::{FeatureFlag, feature_test};
mod test_expressions {
use nix::unistd::{AccessFlags, Gid, Uid};

View File

@@ -4,7 +4,6 @@
BRACE_BEGIN, BRACE_END, BRACE_SEP, BRACE_SPACE, HOME_DIRECTORY, INTERNAL_SEPARATOR,
PROCESS_EXPAND_SELF, PROCESS_EXPAND_SELF_STR, VARIABLE_EXPAND, VARIABLE_EXPAND_SINGLE,
};
use crate::future_feature_flags::{FeatureFlag, feature_test};
use crate::global_safety::AtomicRef;
use crate::global_safety::RelaxedAtomicBool;
use crate::key;
@@ -15,6 +14,7 @@
use crate::wildcard::{ANY_CHAR, ANY_STRING, ANY_STRING_RECURSIVE};
use crate::wutil::fish_iswalnum;
use fish_fallback::fish_wcwidth;
use fish_feature_flags::{FeatureFlag, feature_test};
use fish_wcstringutil::wcs2bytes;
use fish_widestring::{
ENCODE_DIRECT_END, decode_byte_from_char, encode_byte_to_char, subslice_position,

View File

@@ -14,7 +14,6 @@
use crate::complete::{CompleteFlags, Completion, CompletionList, CompletionReceiver};
use crate::env::{EnvVar, Environment};
use crate::exec::exec_subshell_for_expand;
use crate::future_feature_flags::{FeatureFlag, feature_test};
use crate::history::{History, history_session_id};
use crate::operation_context::OperationContext;
use crate::parse_constants::{ParseError, ParseErrorCode, ParseErrorList, SOURCE_LOCATION_UNKNOWN};
@@ -26,6 +25,7 @@
use crate::wutil::{Options, normalize_path, wcstoi_partial};
use bitflags::bitflags;
use fish_common::{EXPAND_RESERVED_BASE, EXPAND_RESERVED_END};
use fish_feature_flags::{FeatureFlag, feature_test};
use fish_util::wcsfilecmp_glob;
use fish_wcstringutil::{join_strings, trim};
use fish_widestring::char_offset;

View File

@@ -12,7 +12,6 @@
ExpandFlags, ExpandResultCode, PROCESS_EXPAND_SELF_STR, expand_one, expand_to_command_and_args,
};
use crate::function;
use crate::future_feature_flags::{FeatureFlag, feature_test};
use crate::highlight::file_tester::FileTester;
use crate::history::all_paths_are_valid;
use crate::operation_context::OperationContext;
@@ -31,6 +30,7 @@
use crate::tokenizer::{PipeOrRedir, variable_assignment_equals_pos};
use fish_color::Color;
use fish_common::{ASCII_MAX, EXPAND_RESERVED_BASE, EXPAND_RESERVED_END};
use fish_feature_flags::{FeatureFlag, feature_test};
use fish_wcstringutil::string_prefixes_string;
use fish_widestring::{L, WExt as _, WString, wstr};
use std::collections::HashMap;
@@ -1314,12 +1314,12 @@ mod tests {
use super::{HighlightColorResolver, HighlightRole, HighlightSpec, highlight_shell};
use crate::common::ScopeGuard;
use crate::env::{EnvMode, EnvSetMode, EnvVar, EnvVarFlags, Environment as _};
use crate::future_feature_flags::{FeatureFlag, with_overridden_feature};
use crate::highlight::parse_text_face_for_highlight;
use crate::operation_context::{EXPANSION_LIMIT_BACKGROUND, OperationContext};
use crate::prelude::*;
use crate::tests::prelude::*;
use crate::text_face::{ResettableStyle, UnderlineStyle};
use fish_feature_flags::{FeatureFlag, with_overridden_feature};
use libc::PATH_MAX;
// Helper to return a string whose length greatly exceeds PATH_MAX.

View File

@@ -5,7 +5,6 @@
use crate::env::{EnvStack, Environment as _};
use crate::fd_readable_set::{FdReadableSet, Timeout};
use crate::flog::{FloggableDebug, FloggableDisplay, flog};
use crate::future_feature_flags::{FeatureFlag, feature_test};
use crate::key::{
self, Key, Modifiers, ViewportPosition, alt, canonicalize_control_char,
canonicalize_keyed_control_char, char_to_symbol, function_key, shift,
@@ -18,6 +17,7 @@
};
use crate::universal_notifier::default_notifier;
use crate::wutil::{fish_is_pua, fish_wcstol};
use fish_feature_flags::{FeatureFlag, feature_test};
use fish_widestring::encode_byte_to_char;
use nix::sys::{select::FdSet, signal::SigSet, time::TimeSpec};
use std::cell::{RefCell, RefMut};

View File

@@ -3,12 +3,12 @@
use crate::{
common::{EscapeFlags, EscapeStringStyle, escape_string},
flog::FloggableDebug,
future_feature_flags::{FeatureFlag, feature_test},
prelude::*,
reader::safe_get_terminal_mode_on_startup,
wutil::fish_wcstoul,
};
use fish_fallback::fish_wcwidth;
use fish_feature_flags::{FeatureFlag, feature_test};
use fish_widestring::decode_byte_from_char;
pub(crate) const Backspace: char = '\u{F500}'; // below ENCODE_DIRECT_BASE

View File

@@ -28,7 +28,6 @@
pub mod fork_exec;
pub mod fs;
pub mod function;
pub mod future_feature_flags;
pub mod global_safety;
pub mod highlight;
pub mod history;

View File

@@ -13,7 +13,6 @@
VARIABLE_EXPAND, VARIABLE_EXPAND_EMPTY, VARIABLE_EXPAND_SINGLE, expand_one,
expand_to_command_and_args,
};
use crate::future_feature_flags::{FeatureFlag, feature_test};
use crate::operation_context::OperationContext;
use crate::parse_constants::{
ERROR_BAD_VAR_CHAR1, ERROR_BRACKETED_VARIABLE_QUOTED1, ERROR_BRACKETED_VARIABLE1,
@@ -30,6 +29,7 @@
};
use crate::wildcard::{ANY_CHAR, ANY_STRING, ANY_STRING_RECURSIVE};
use fish_common::help_section;
use fish_feature_flags::{FeatureFlag, feature_test};
use fish_wcstringutil::{count_newlines, truncate};
use std::ops::Range;
use std::{iter, ops};

View File

@@ -46,7 +46,6 @@
use crate::fd_readable_set::poll_fd_readable;
use crate::fds::{make_fd_blocking, wopen_cloexec};
use crate::flog::{flog, flogf};
use crate::future_feature_flags::{self, FeatureFlag};
use crate::global_safety::RelaxedAtomicBool;
use crate::highlight::{
HighlightRole, HighlightSpec, autosuggest_validate_from_history, highlight_shell,
@@ -121,6 +120,7 @@
use fish_common::{UTF8_BOM_WCHAR, help_section};
use fish_fallback::fish_wcwidth;
use fish_fallback::lowercase;
use fish_feature_flags::FeatureFlag;
use fish_util::{perror, write_to_fd};
use fish_wcstringutil::{
CaseSensitivity, IsPrefix, StringFuzzyMatch, count_preceding_backslashes, is_prefix,
@@ -239,7 +239,7 @@ fn redirect_tty_after_sighup() {
}
fn querying_allowed(vars: &dyn Environment) -> bool {
future_feature_flags::feature_test(FeatureFlag::QueryTerm)
fish_feature_flags::feature_test(FeatureFlag::QueryTerm)
&& !is_dumb()
&& {
// TODO(term-workaround)

View File

@@ -1,12 +1,12 @@
// Generic output functions.
use crate::common::{self, EscapeStringStyle, escape_string};
use crate::future_feature_flags::{self, FeatureFlag};
use crate::prelude::*;
use crate::screen::{is_dumb, only_grayscale};
use crate::text_face::{ResettableStyle, TextFace, TextStyling, UnderlineStyle};
use crate::threads::MainThread;
use bitflags::bitflags;
use fish_color::{Color, Color24};
use fish_feature_flags::FeatureFlag;
use fish_wcstringutil::{wcs2bytes, wcs2bytes_appending};
use std::cell::{RefCell, RefMut};
use std::ops::{Deref, DerefMut};
@@ -181,7 +181,7 @@ fn osc_0_or_1_terminal_title(out: &mut Outputter, is_1: bool, title: &[WString])
}
fn osc_133_prompt_start(out: &mut Outputter) -> bool {
if !future_feature_flags::feature_test(FeatureFlag::MarkPrompt) {
if !fish_feature_flags::feature_test(FeatureFlag::MarkPrompt) {
return false;
}
static TEST_BALLOON: OnceLock<()> = OnceLock::new();
@@ -194,7 +194,7 @@ fn osc_133_prompt_start(out: &mut Outputter) -> bool {
}
fn osc_133_prompt_end(out: &mut Outputter) -> bool {
if !future_feature_flags::feature_test(FeatureFlag::MarkPrompt) {
if !fish_feature_flags::feature_test(FeatureFlag::MarkPrompt) {
return false;
}
write_to_output!(out, "\x1b]133;B\x07");
@@ -202,7 +202,7 @@ fn osc_133_prompt_end(out: &mut Outputter) -> bool {
}
fn osc_133_command_start(out: &mut Outputter, command: &wstr) -> bool {
if !future_feature_flags::feature_test(FeatureFlag::MarkPrompt) {
if !fish_feature_flags::feature_test(FeatureFlag::MarkPrompt) {
return false;
}
write_to_output!(
@@ -214,7 +214,7 @@ fn osc_133_command_start(out: &mut Outputter, command: &wstr) -> bool {
}
fn osc_133_command_finished(out: &mut Outputter, exit_status: libc::c_int) -> bool {
if !future_feature_flags::feature_test(FeatureFlag::MarkPrompt) {
if !fish_feature_flags::feature_test(FeatureFlag::MarkPrompt) {
return false;
}
write_to_output!(out, "\x1b]133;D;{}\x07", exit_status);

View File

@@ -3,11 +3,11 @@
use crate::ast::unescape_keyword;
use crate::common::valid_var_name_char;
use crate::future_feature_flags::{FeatureFlag, feature_test};
use crate::parse_constants::SOURCE_OFFSET_INVALID;
use crate::parser_keywords::parser_keywords_is_subcommand;
use crate::prelude::*;
use crate::redirection::RedirectionMode;
use fish_feature_flags::{FeatureFlag, feature_test};
use libc::{STDIN_FILENO, STDOUT_FILENO};
use nix::fcntl::OFlag;
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not, Range};

View File

@@ -13,12 +13,11 @@
};
use crate::complete::{CompleteFlags, Completion, CompletionReceiver, PROG_COMPLETE_SEP};
use crate::expand::ExpandFlags;
use crate::future_feature_flags::FeatureFlag;
use crate::future_feature_flags::feature_test;
use crate::prelude::*;
use crate::wutil::dir_iter::DirEntryType;
use crate::wutil::{dir_iter::DirEntry, lwstat, waccess};
use fish_fallback::wcscasecmp;
use fish_feature_flags::{FeatureFlag, feature_test};
use fish_wcstringutil::{
CaseSensitivity, string_fuzzy_match_string, string_suffixes_string_case_insensitive,
strip_executable_suffix,
@@ -1231,7 +1230,7 @@ pub fn wildcard_has(s: impl AsRef<wstr>) -> bool {
#[cfg(test)]
mod tests {
use super::*;
use crate::future_feature_flags::with_overridden_feature;
use fish_feature_flags::with_overridden_feature;
#[test]
fn test_wildcards() {