diff --git a/src/bin/fish_key_reader.rs b/src/bin/fish_key_reader.rs index 6d83da1a4..65cca790b 100644 --- a/src/bin/fish_key_reader.rs +++ b/src/bin/fish_key_reader.rs @@ -19,7 +19,10 @@ env::env_init, eprintf, fprintf, input::input_terminfo_get_name, - input_common::{terminal_protocols_enable_ifn, CharEvent, InputEventQueue, InputEventQueuer}, + input_common::{ + terminal_protocol_hacks, terminal_protocols_enable_ifn, CharEvent, InputEventQueue, + InputEventQueuer, + }, key::{self, char_to_symbol, Key}, panic::panic_handler, print_help::print_help, @@ -136,6 +139,8 @@ fn setup_and_process_keys(continuous_mode: bool, verbose: bool) -> i32 { // in fish-proper this is done once a command is run. unsafe { libc::tcsetattr(STDIN_FILENO, TCSANOW, &*shell_modes()) }; + terminal_protocol_hacks(); + if continuous_mode { eprintf!("\n"); eprintf!("To terminate this program type \"exit\" or \"quit\" in this window,\n"); diff --git a/src/input_common.rs b/src/input_common.rs index 3904ffd2f..d566575a4 100644 --- a/src/input_common.rs +++ b/src/input_common.rs @@ -1,7 +1,8 @@ use libc::STDOUT_FILENO; use crate::common::{ - fish_reserved_codepoint, is_windows_subsystem_for_linux, read_blocked, shell_modes, WSL, + fish_reserved_codepoint, is_windows_subsystem_for_linux, read_blocked, shell_modes, + str2wcstring, WSL, }; use crate::env::{EnvStack, Environment}; use crate::fd_readable_set::FdReadableSet; @@ -20,6 +21,7 @@ use std::collections::VecDeque; use std::ops::ControlFlow; use std::os::fd::RawFd; +use std::os::unix::ffi::OsStrExt; use std::ptr; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; @@ -427,10 +429,49 @@ pub fn update_wait_on_sequence_key_ms(vars: &EnvStack) { static TERMINAL_PROTOCOLS: AtomicBool = AtomicBool::new(false); -pub(crate) static IS_TMUX: RelaxedAtomicBool = RelaxedAtomicBool::new(false); -pub(crate) static IN_MIDNIGHT_COMMANDER: RelaxedAtomicBool = RelaxedAtomicBool::new(false); -pub(crate) static IN_ITERM_PRE_CSI_U: RelaxedAtomicBool = RelaxedAtomicBool::new(false); -pub(crate) static IN_WEZTERM: RelaxedAtomicBool = RelaxedAtomicBool::new(false); +static IS_TMUX: RelaxedAtomicBool = RelaxedAtomicBool::new(false); +static IN_MIDNIGHT_COMMANDER: RelaxedAtomicBool = RelaxedAtomicBool::new(false); +static IN_ITERM_PRE_CSI_U: RelaxedAtomicBool = RelaxedAtomicBool::new(false); +static IN_WEZTERM: RelaxedAtomicBool = RelaxedAtomicBool::new(false); + +pub fn terminal_protocol_hacks() { + use std::env::var_os; + IS_TMUX.store(var_os("TMUX").is_some()); + IN_MIDNIGHT_COMMANDER.store(var_os("MC_TMPDIR").is_some()); + IN_WEZTERM.store( + var_os("TERM_PROGRAM") + .is_some_and(|term_program| term_program.as_os_str().as_bytes() == b"WezTerm"), + ); + IN_ITERM_PRE_CSI_U.store( + var_os("LC_TERMINAL").is_some_and(|term| term.as_os_str().as_bytes() == b"iTerm2") + && var_os("LC_TERMINAL_VERSION").is_some_and(|version| { + let Some(version) = parse_version(&str2wcstring(version.as_os_str().as_bytes())) + else { + return false; + }; + version < (3, 5, 6) + }), + ); +} + +fn parse_version(version: &wstr) -> Option<(i64, i64, i64)> { + let mut numbers = version.split('.'); + let major = fish_wcstol(numbers.next()?).ok()?; + let minor = fish_wcstol(numbers.next()?).ok()?; + let patch = numbers.next()?; + let patch = &patch[..patch + .chars() + .position(|c| !c.is_ascii_digit()) + .unwrap_or(patch.len())]; + let patch = fish_wcstol(patch).ok()?; + Some((major, minor, patch)) +} + +#[test] +fn test_parse_version() { + assert_eq!(parse_version(L!("3.5.2")), Some((3, 5, 2))); + assert_eq!(parse_version(L!("3.5.3beta")), Some((3, 5, 3))); +} pub fn terminal_protocols_enable_ifn() { if IN_MIDNIGHT_COMMANDER.load() { diff --git a/src/reader.rs b/src/reader.rs index 4c7b5556e..4aae0ae1e 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -74,11 +74,9 @@ SearchType, }; use crate::input::init_input; -use crate::input_common::IN_ITERM_PRE_CSI_U; -use crate::input_common::IN_MIDNIGHT_COMMANDER; -use crate::input_common::IN_WEZTERM; use crate::input_common::{ - terminal_protocols_enable_ifn, CharEvent, CharInputStyle, InputData, ReadlineCmd, IS_TMUX, + terminal_protocol_hacks, terminal_protocols_enable_ifn, CharEvent, CharInputStyle, InputData, + ReadlineCmd, }; use crate::io::IoChain; use crate::kill::{kill_add, kill_replace, kill_yank, kill_yank_rotate}; @@ -126,7 +124,6 @@ string_prefixes_string_case_insensitive, StringFuzzyMatch, }; use crate::wildcard::wildcard_has; -use crate::wutil::fish_wcstol; use crate::wutil::{fstat, perror, write_to_fd}; use crate::{abbrs, event, function, history}; @@ -3958,55 +3955,7 @@ fn reader_interactive_init(parser: &Parser) { .vars() .set_one(L!("_"), EnvMode::GLOBAL, L!("fish").to_owned()); - interactive_hacks(parser); -} - -fn interactive_hacks(parser: &Parser) { - IS_TMUX.store(parser.vars().get_unless_empty(L!("TMUX")).is_some()); - IN_MIDNIGHT_COMMANDER.store(parser.vars().get_unless_empty(L!("MC_TMPDIR")).is_some()); - IN_WEZTERM.store( - parser - .vars() - .get_unless_empty(L!("TERM_PROGRAM")) - .is_some_and(|term_program| term_program.as_list() == [L!("WezTerm")]), - ); - IN_ITERM_PRE_CSI_U.store( - parser - .vars() - .get(L!("LC_TERMINAL")) - .is_some_and(|term| term.as_list() == [L!("iTerm2")]) - && parser - .vars() - .get(L!("LC_TERMINAL_VERSION")) - .is_some_and(|version| { - if version.as_list().is_empty() { - return false; - } - let Some(version) = parse_version(&version.as_list()[0]) else { - return false; - }; - version < (3, 5, 6) - }), - ); -} - -fn parse_version(version: &wstr) -> Option<(i64, i64, i64)> { - let mut numbers = version.split('.'); - let major = fish_wcstol(numbers.next()?).ok()?; - let minor = fish_wcstol(numbers.next()?).ok()?; - let patch = numbers.next()?; - let patch = &patch[..patch - .chars() - .position(|c| !c.is_ascii_digit()) - .unwrap_or(patch.len())]; - let patch = fish_wcstol(patch).ok()?; - Some((major, minor, patch)) -} - -#[test] -fn test_parse_version() { - assert_eq!(parse_version(L!("3.5.2")), Some((3, 5, 2))); - assert_eq!(parse_version(L!("3.5.3beta")), Some((3, 5, 3))); + terminal_protocol_hacks(); } /// Destroy data for interactive use.