fallback: extract into crate

For faster incremental builds and to enable subsequent extraction.

Closes #12183
This commit is contained in:
Daniel Rainer
2025-12-18 01:19:16 +01:00
committed by Johannes Altmanninger
parent caef2c309d
commit 67d78fb258
19 changed files with 105 additions and 47 deletions

20
Cargo.lock generated
View File

@@ -161,6 +161,7 @@ dependencies = [
"fish-build-helper", "fish-build-helper",
"fish-build-man-pages", "fish-build-man-pages",
"fish-common", "fish-common",
"fish-fallback",
"fish-gettext", "fish-gettext",
"fish-gettext-extraction", "fish-gettext-extraction",
"fish-gettext-mo-file-parser", "fish-gettext-mo-file-parser",
@@ -205,6 +206,25 @@ dependencies = [
[[package]] [[package]]
name = "fish-common" name = "fish-common"
version = "0.0.0" version = "0.0.0"
dependencies = [
"libc",
"nix",
"once_cell",
]
[[package]]
name = "fish-fallback"
version = "0.0.0"
dependencies = [
"fish-build-helper",
"fish-common",
"fish-wchar",
"fish-widecharwidth",
"libc",
"once_cell",
"rsconf",
"widestring",
]
[[package]] [[package]]
name = "fish-gettext" name = "fish-gettext"

View File

@@ -18,6 +18,7 @@ errno = "0.3.0"
fish-build-helper = { path = "crates/build-helper" } fish-build-helper = { path = "crates/build-helper" }
fish-build-man-pages = { path = "crates/build-man-pages" } fish-build-man-pages = { path = "crates/build-man-pages" }
fish-common = { path = "crates/common" } fish-common = { path = "crates/common" }
fish-fallback = { path = "crates/fallback" }
fish-gettext = { path = "crates/gettext" } fish-gettext = { path = "crates/gettext" }
fish-gettext-extraction = { path = "crates/gettext-extraction" } fish-gettext-extraction = { path = "crates/gettext-extraction" }
fish-gettext-maps = { path = "crates/gettext-maps" } fish-gettext-maps = { path = "crates/gettext-maps" }
@@ -93,6 +94,7 @@ errno.workspace = true
fish-build-helper.workspace = true fish-build-helper.workspace = true
fish-build-man-pages = { workspace = true, optional = true } fish-build-man-pages = { workspace = true, optional = true }
fish-common.workspace = true fish-common.workspace = true
fish-fallback.workspace = true
fish-gettext = { workspace = true, optional = true } fish-gettext = { workspace = true, optional = true }
fish-gettext-extraction = { workspace = true, optional = true } fish-gettext-extraction = { workspace = true, optional = true }
fish-printf.workspace = true fish-printf.workspace = true

View File

@@ -6,5 +6,10 @@ version = "0.0.0"
repository.workspace = true repository.workspace = true
license.workspace = true license.workspace = true
[dependencies]
libc.workspace = true
nix.workspace = true
once_cell.workspace = true
[lints] [lints]
workspace = true workspace = true

View File

@@ -1,3 +1,8 @@
use libc::STDIN_FILENO;
use once_cell::sync::OnceCell;
use std::env;
use std::os::unix::ffi::OsStrExt;
// These are in the Unicode private-use range. We really shouldn't use this // These are in the Unicode private-use range. We really shouldn't use this
// range but have little choice in the matter given how our lexer/parser works. // range but have little choice in the matter given how our lexer/parser works.
// We can't use non-characters for these two ranges because there are only 66 of // We can't use non-characters for these two ranges because there are only 66 of
@@ -26,3 +31,30 @@ pub fn subslice_position<T: Eq>(a: &[T], b: &[T]) -> Option<usize> {
} }
a.windows(b.len()).position(|aw| aw == b) a.windows(b.len()).position(|aw| aw == b)
} }
/// This function attempts to distinguish between a console session (at the actual login vty) and a
/// session within a terminal emulator inside a desktop environment or over SSH. Unfortunately
/// there are few values of $TERM that we can interpret as being exclusively console sessions, and
/// most common operating systems do not use them. The value is cached for the duration of the fish
/// session. We err on the side of assuming it's not a console session. This approach isn't
/// bullet-proof and that's OK.
pub fn is_console_session() -> bool {
static IS_CONSOLE_SESSION: OnceCell<bool> = OnceCell::new();
// TODO(terminal-workaround)
*IS_CONSOLE_SESSION.get_or_init(|| {
nix::unistd::ttyname(unsafe { std::os::fd::BorrowedFd::borrow_raw(STDIN_FILENO) })
.is_ok_and(|buf| {
// Check if the tty matches /dev/(console|dcons|tty[uv\d])
let is_console_tty = match buf.as_os_str().as_bytes() {
b"/dev/console" => true,
b"/dev/dcons" => true,
bytes => bytes.strip_prefix(b"/dev/tty").is_some_and(|rest| {
matches!(rest.first(), Some(b'u' | b'v' | b'0'..=b'9'))
}),
};
// and that $TERM is simple, e.g. `xterm` or `vt100`, not `xterm-something` or `sun-color`.
is_console_tty && env::var_os("TERM").is_none_or(|t| !t.as_bytes().contains(&b'-'))
})
})
}

View File

@@ -0,0 +1,22 @@
[package]
name = "fish-fallback"
edition.workspace = true
rust-version.workspace = true
version = "0.0.0"
repository.workspace = true
license.workspace = true
[dependencies]
fish-common.workspace = true
fish-wchar.workspace = true
fish-widecharwidth.workspace = true
libc.workspace = true
once_cell.workspace = true
widestring.workspace = true
[build-dependencies]
fish-build-helper.workspace = true
rsconf.workspace = true
[lints]
workspace = true

5
crates/fallback/build.rs Normal file
View File

@@ -0,0 +1,5 @@
use fish_build_helper::target_os_is_cygwin;
fn main() {
rsconf::declare_cfg("cygwin", target_os_is_cygwin())
}

View File

@@ -3,7 +3,7 @@
//! //!
//! Many of these functions are more or less broken and incomplete. //! Many of these functions are more or less broken and incomplete.
use crate::wchar::prelude::*; use fish_wchar::prelude::*;
use fish_widecharwidth::{WcLookupTable, WcWidth}; use fish_widecharwidth::{WcLookupTable, WcWidth};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::cmp; use std::cmp;
@@ -46,7 +46,7 @@ pub fn fish_wcwidth(c: char) -> isize {
// in the console session, but knows nothing about the capabilities of other terminal emulators // in the console session, but knows nothing about the capabilities of other terminal emulators
// or ttys. Use it from the start only if we are logged in to the physical console. // or ttys. Use it from the start only if we are logged in to the physical console.
#[cfg(not(cygwin))] #[cfg(not(cygwin))]
if crate::common::is_console_session() { if fish_common::is_console_session() {
return wcwidth(c); return wcwidth(c);
} }
@@ -165,7 +165,7 @@ pub fn new(mut chars: std::iter::Map<CharsUtf32<'a>, Canonicalize>) -> Self {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::wcscasecmp; use super::wcscasecmp;
use crate::wchar::prelude::*; use fish_wchar::prelude::*;
use std::cmp::Ordering; use std::cmp::Ordering;
#[test] #[test]

View File

@@ -1,5 +1,5 @@
use super::*; use super::*;
use crate::fallback::fish_wcwidth; use fish_fallback::fish_wcwidth;
pub struct Pad { pub struct Pad {
char_to_pad: char, char_to_pad: char,

View File

@@ -5,8 +5,8 @@
use nix::sys::resource::Resource as ResourceEnum; use nix::sys::resource::Resource as ResourceEnum;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use crate::fallback::{fish_wcswidth, wcscasecmp};
use crate::wutil::perror; use crate::wutil::perror;
use fish_fallback::{fish_wcswidth, wcscasecmp};
use super::prelude::*; use super::prelude::*;

View File

@@ -4,7 +4,6 @@
BRACE_BEGIN, BRACE_END, BRACE_SEP, BRACE_SPACE, HOME_DIRECTORY, INTERNAL_SEPARATOR, BRACE_BEGIN, BRACE_END, BRACE_SEP, BRACE_SPACE, HOME_DIRECTORY, INTERNAL_SEPARATOR,
PROCESS_EXPAND_SELF, PROCESS_EXPAND_SELF_STR, VARIABLE_EXPAND, VARIABLE_EXPAND_SINGLE, PROCESS_EXPAND_SELF, PROCESS_EXPAND_SELF_STR, VARIABLE_EXPAND, VARIABLE_EXPAND_SINGLE,
}; };
use crate::fallback::fish_wcwidth;
use crate::future_feature_flags::{FeatureFlag, feature_test}; use crate::future_feature_flags::{FeatureFlag, feature_test};
use crate::global_safety::AtomicRef; use crate::global_safety::AtomicRef;
use crate::global_safety::RelaxedAtomicBool; use crate::global_safety::RelaxedAtomicBool;
@@ -17,7 +16,8 @@
use crate::wildcard::{ANY_CHAR, ANY_STRING, ANY_STRING_RECURSIVE}; use crate::wildcard::{ANY_CHAR, ANY_STRING, ANY_STRING_RECURSIVE};
use crate::wutil::fish_iswalnum; use crate::wutil::fish_iswalnum;
use bitflags::bitflags; use bitflags::bitflags;
use fish_common::{ENCODE_DIRECT_END, char_offset, subslice_position}; use fish_common::{ENCODE_DIRECT_END, char_offset, is_console_session, subslice_position};
use fish_fallback::fish_wcwidth;
use fish_wchar::{decode_byte_from_char, encode_byte_to_char}; use fish_wchar::{decode_byte_from_char, encode_byte_to_char};
use libc::{SIG_IGN, SIGTTOU, STDIN_FILENO}; use libc::{SIG_IGN, SIGTTOU, STDIN_FILENO};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
@@ -1796,33 +1796,6 @@ impl<T, F: FnOnce(T)> ScopeGuarding for ScopeGuard<T, F> {}
pub const fn assert_send<T: Send>() {} pub const fn assert_send<T: Send>() {}
pub const fn assert_sync<T: Sync>() {} pub const fn assert_sync<T: Sync>() {}
/// This function attempts to distinguish between a console session (at the actual login vty) and a
/// session within a terminal emulator inside a desktop environment or over SSH. Unfortunately
/// there are few values of $TERM that we can interpret as being exclusively console sessions, and
/// most common operating systems do not use them. The value is cached for the duration of the fish
/// session. We err on the side of assuming it's not a console session. This approach isn't
/// bullet-proof and that's OK.
pub fn is_console_session() -> bool {
static IS_CONSOLE_SESSION: OnceCell<bool> = OnceCell::new();
// TODO(terminal-workaround)
*IS_CONSOLE_SESSION.get_or_init(|| {
nix::unistd::ttyname(unsafe { std::os::fd::BorrowedFd::borrow_raw(STDIN_FILENO) })
.is_ok_and(|buf| {
// Check if the tty matches /dev/(console|dcons|tty[uv\d])
let is_console_tty = match buf.as_os_str().as_bytes() {
b"/dev/console" => true,
b"/dev/dcons" => true,
bytes => bytes.strip_prefix(b"/dev/tty").is_some_and(|rest| {
matches!(rest.first(), Some(b'u' | b'v' | b'0'..=b'9'))
}),
};
// and that $TERM is simple, e.g. `xterm` or `vt100`, not `xterm-something` or `sun-color`.
is_console_tty && env::var_os("TERM").is_none_or(|t| !t.as_bytes().contains(&b'-'))
})
})
}
/// Asserts that a slice is alphabetically sorted by a <code>&[wstr]</code> `name` field. /// Asserts that a slice is alphabetically sorted by a <code>&[wstr]</code> `name` field.
/// ///
/// Mainly useful for static asserts/const eval. /// Mainly useful for static asserts/const eval.

View File

@@ -155,9 +155,9 @@ fn handle_timezone(var_name: &wstr, vars: &EnvStack) {
} }
} }
/// Update the value of [`FISH_EMOJI_WIDTH`](crate::fallback::FISH_EMOJI_WIDTH). /// Update the value of [`FISH_EMOJI_WIDTH`](fish_fallback::FISH_EMOJI_WIDTH).
pub fn guess_emoji_width(vars: &EnvStack) { pub fn guess_emoji_width(vars: &EnvStack) {
use crate::fallback::FISH_EMOJI_WIDTH; use fish_fallback::FISH_EMOJI_WIDTH;
if let Some(width_str) = vars.get(L!("fish_emoji_width")) { if let Some(width_str) = vars.get(L!("fish_emoji_width")) {
// The only valid values are 1 or 2; we default to 1 if it was an invalid int. // The only valid values are 1 or 2; we default to 1 if it was an invalid int.
@@ -199,7 +199,7 @@ pub fn guess_emoji_width(vars: &EnvStack) {
// Default to whatever the system's wcwidth gives for U+1F603, but only if it's at least // Default to whatever the system's wcwidth gives for U+1F603, but only if it's at least
// 1 and at most 2. // 1 and at most 2.
#[cfg(not(cygwin))] #[cfg(not(cygwin))]
let width = crate::fallback::wcwidth('😃').clamp(1, 2); let width = fish_fallback::wcwidth('😃').clamp(1, 2);
#[cfg(cygwin)] #[cfg(cygwin)]
let width = 2_isize; let width = 2_isize;
FISH_EMOJI_WIDTH.store(width, Ordering::Relaxed); FISH_EMOJI_WIDTH.store(width, Ordering::Relaxed);
@@ -238,7 +238,7 @@ fn handle_change_ambiguous_width(vars: &EnvStack) {
.unwrap_or(1) .unwrap_or(1)
// Clamp in case of negative values. // Clamp in case of negative values.
.max(0) as isize; .max(0) as isize;
crate::fallback::FISH_AMBIGUOUS_WIDTH.store(new_width, Ordering::Relaxed); fish_fallback::FISH_AMBIGUOUS_WIDTH.store(new_width, Ordering::Relaxed);
} }
fn handle_term_size_change(vars: &EnvStack) { fn handle_term_size_change(vars: &EnvStack) {

View File

@@ -2,13 +2,13 @@
use crate::{ use crate::{
common::{EscapeFlags, EscapeStringStyle, escape_string}, common::{EscapeFlags, EscapeStringStyle, escape_string},
fallback::fish_wcwidth,
flog::FloggableDebug, flog::FloggableDebug,
future_feature_flags::{FeatureFlag, test as feature_test}, future_feature_flags::{FeatureFlag, test as feature_test},
reader::safe_get_terminal_mode_on_startup, reader::safe_get_terminal_mode_on_startup,
wchar::prelude::*, wchar::prelude::*,
wutil::fish_wcstoul, wutil::fish_wcstoul,
}; };
use fish_fallback::fish_wcwidth;
use fish_wchar::decode_byte_from_char; use fish_wchar::decode_byte_from_char;
pub(crate) const Backspace: char = '\u{F500}'; // below ENCODE_DIRECT_BASE pub(crate) const Backspace: char = '\u{F500}'; // below ENCODE_DIRECT_BASE

View File

@@ -22,7 +22,6 @@
pub mod event; pub mod event;
pub mod exec; pub mod exec;
pub mod expand; pub mod expand;
pub mod fallback;
pub mod fd_monitor; pub mod fd_monitor;
pub mod fd_readable_set; pub mod fd_readable_set;
pub mod fds; pub mod fds;

View File

@@ -1,8 +1,8 @@
//! Constants used in the programmatic representation of fish code. //! Constants used in the programmatic representation of fish code.
use crate::fallback::{fish_wcswidth, fish_wcwidth};
use crate::wchar::prelude::*; use crate::wchar::prelude::*;
use bitflags::bitflags; use bitflags::bitflags;
use fish_fallback::{fish_wcswidth, fish_wcwidth};
pub type SourceOffset = u32; pub type SourceOffset = u32;

View File

@@ -71,7 +71,6 @@
use crate::exec::exec_subshell; use crate::exec::exec_subshell;
use crate::expand::expand_one; use crate::expand::expand_one;
use crate::expand::{ExpandFlags, ExpandResultCode, expand_string, expand_tilde}; use crate::expand::{ExpandFlags, ExpandResultCode, expand_string, expand_tilde};
use crate::fallback::fish_wcwidth;
use crate::fd_readable_set::poll_fd_readable; use crate::fd_readable_set::poll_fd_readable;
use crate::fds::{AutoCloseFd, make_fd_blocking, wopen_cloexec}; use crate::fds::{AutoCloseFd, make_fd_blocking, wopen_cloexec};
use crate::flog::{flog, flogf}; use crate::flog::{flog, flogf};
@@ -158,6 +157,7 @@
use crate::wildcard::wildcard_has; use crate::wildcard::wildcard_has;
use crate::wutil::{fstat, perror, write_to_fd, wstat}; use crate::wutil::{fstat, perror, write_to_fd, wstat};
use crate::{abbrs, event, function}; use crate::{abbrs, event, function};
use fish_fallback::fish_wcwidth;
/// A description of where fish is in the process of exiting. /// A description of where fish is in the process of exiting.
#[repr(u8)] #[repr(u8)]

View File

@@ -26,7 +26,6 @@
has_working_tty_timestamps, shell_modes, wcs2bytes, write_loop, has_working_tty_timestamps, shell_modes, wcs2bytes, write_loop,
}; };
use crate::env::Environment; use crate::env::Environment;
use crate::fallback::fish_wcwidth;
use crate::flog::{flog, flogf}; use crate::flog::{flog, flogf};
use crate::global_safety::RelaxedAtomicBool; use crate::global_safety::RelaxedAtomicBool;
use crate::highlight::{HighlightColorResolver, HighlightRole, HighlightSpec}; use crate::highlight::{HighlightColorResolver, HighlightRole, HighlightSpec};
@@ -39,6 +38,7 @@
use crate::wchar::prelude::*; use crate::wchar::prelude::*;
use crate::wcstringutil::{fish_wcwidth_visible, string_prefixes_string}; use crate::wcstringutil::{fish_wcwidth_visible, string_prefixes_string};
use crate::wutil::fstat; use crate::wutil::fstat;
use fish_fallback::fish_wcwidth;
#[derive(Copy, Clone, Default)] #[derive(Copy, Clone, Default)]
pub enum CharOffset { pub enum CharOffset {

View File

@@ -1,8 +1,8 @@
//! Helper functions for working with wcstring. //! Helper functions for working with wcstring.
use crate::common::{get_ellipsis_char, get_ellipsis_str}; use crate::common::{get_ellipsis_char, get_ellipsis_str};
use crate::fallback::{fish_wcwidth, wcscasecmp, wcscasecmp_fuzzy};
use crate::wchar::prelude::*; use crate::wchar::prelude::*;
use fish_fallback::{fish_wcwidth, wcscasecmp, wcscasecmp_fuzzy};
use fish_wchar::decode_byte_from_char; use fish_wchar::decode_byte_from_char;
/// Return the number of newlines in a string. /// Return the number of newlines in a string.

View File

@@ -13,7 +13,6 @@
}; };
use crate::complete::{CompleteFlags, Completion, CompletionReceiver, PROG_COMPLETE_SEP}; use crate::complete::{CompleteFlags, Completion, CompletionReceiver, PROG_COMPLETE_SEP};
use crate::expand::ExpandFlags; use crate::expand::ExpandFlags;
use crate::fallback::wcscasecmp;
use crate::future_feature_flags::FeatureFlag; use crate::future_feature_flags::FeatureFlag;
use crate::future_feature_flags::feature_test; use crate::future_feature_flags::feature_test;
use crate::wchar::prelude::*; use crate::wchar::prelude::*;
@@ -23,6 +22,7 @@
}; };
use crate::wutil::dir_iter::DirEntryType; use crate::wutil::dir_iter::DirEntryType;
use crate::wutil::{dir_iter::DirEntry, lwstat, waccess}; use crate::wutil::{dir_iter::DirEntry, lwstat, waccess};
use fish_fallback::wcscasecmp;
localizable_consts!( localizable_consts!(
COMPLETE_EXEC_DESC "command" COMPLETE_EXEC_DESC "command"

View File

@@ -11,8 +11,8 @@
use crate::common::{ use crate::common::{
bytes2wcstring, fish_reserved_codepoint, wcs2bytes, wcs2osstring, wcs2zstring, bytes2wcstring, fish_reserved_codepoint, wcs2bytes, wcs2osstring, wcs2zstring,
}; };
use crate::flog;
use crate::wcstringutil::{join_strings, wcs2bytes_callback}; use crate::wcstringutil::{join_strings, wcs2bytes_callback};
use crate::{fallback, flog};
use errno::errno; use errno::errno;
use fish_wchar::{L, WExt, WString, wstr}; use fish_wchar::{L, WExt, WString, wstr};
pub use gettext::{ pub use gettext::{
@@ -448,7 +448,7 @@ pub fn fish_iswalnum(c: char) -> bool {
} }
pub fn fish_wcswidth(s: &wstr) -> isize { pub fn fish_wcswidth(s: &wstr) -> isize {
fallback::fish_wcswidth(s) fish_fallback::fish_wcswidth(s)
} }
/// Given that `cursor` is a pointer into `base`, return the offset in characters. /// Given that `cursor` is a pointer into `base`, return the offset in characters.