diff --git a/src/builtins/set_color.rs b/src/builtins/set_color.rs index 06ac23ab6..5d232c87f 100644 --- a/src/builtins/set_color.rs +++ b/src/builtins/set_color.rs @@ -4,7 +4,7 @@ use crate::color::RgbColor; use crate::common::str2wcstring; use crate::output::{self, Outputter}; -use crate::terminal::{self, Term}; +use crate::terminal::{self, term, Term}; #[allow(clippy::too_many_arguments)] fn print_modifiers( @@ -79,9 +79,16 @@ fn print_colors( let term = terminal::term(); for color_name in args { if streams.out_is_terminal() { - if let Some(term) = term.as_ref() { - print_modifiers(outp, term, bold, underline, italics, dim, reverse, bg); - } + print_modifiers( + outp, + term.as_ref(), + bold, + underline, + italics, + dim, + reverse, + bg, + ); let color = RgbColor::from_wstr(color_name).unwrap_or(RgbColor::NONE); outp.set_color(color, RgbColor::NONE); if !bg.is_none() { @@ -92,9 +99,7 @@ fn print_colors( if !bg.is_none() { // If we have a background, stop it after the color // or it goes to the end of the line and looks ugly. - if let Some(term) = term.as_ref() { - outp.tputs_if_some(&term.exit_attribute_mode); - } + outp.tputs_if_some(&term.exit_attribute_mode); } outp.writech('\n'); } // conveniently, 'normal' is always the last color so we don't need to reset here @@ -217,9 +222,7 @@ pub fn set_color(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) - // Test if we have at least basic support for setting fonts, colors and related bits - otherwise // just give up... - let Some(term) = terminal::term() else { - return Err(STATUS_CMD_ERROR); - }; + let term = term(); let Some(exit_attribute_mode) = &term.exit_attribute_mode else { return Err(STATUS_CMD_ERROR); }; diff --git a/src/env_dispatch.rs b/src/env_dispatch.rs index ba9cf8756..5c984739c 100644 --- a/src/env_dispatch.rs +++ b/src/env_dispatch.rs @@ -382,7 +382,7 @@ fn update_fish_color_support(vars: &EnvStack) { .get(L!("TERM")) .map(|v| v.as_string()) .unwrap_or_else(WString::new); - let max_colors = terminal::term().and_then(|term| term.max_colors); + let max_colors = terminal::term().max_colors; let mut supports_256color = false; let mut supports_24bit = false; @@ -578,9 +578,7 @@ fn init_terminal(vars: &EnvStack) { apply_non_term_hacks(vars); // Store some global variables that reflect the term's capabilities - if let Some(term) = terminal::term() { - TERM_HAS_XN.store(term.eat_newline_glitch, Ordering::Relaxed); - } + TERM_HAS_XN.store(terminal::term().eat_newline_glitch, Ordering::Relaxed); update_fish_color_support(vars); // Invalidate the cached escape sequences since they may no longer be valid. diff --git a/src/output.rs b/src/output.rs index df01d0ea0..995af0866 100644 --- a/src/output.rs +++ b/src/output.rs @@ -2,7 +2,7 @@ use crate::color::{self, RgbColor}; use crate::common::{self, wcs2string_appending}; use crate::env::EnvVar; -use crate::terminal::{self, tparm1, Term}; +use crate::terminal::{tparm1, Term}; use crate::threads::MainThread; use crate::wchar::prelude::*; use bitflags::bitflags; @@ -173,18 +173,15 @@ fn maybe_flush(&mut self) { /// Unconditionally write the color string to the output. /// Exported for builtin_set_color's usage only. pub fn write_color(&mut self, color: RgbColor, is_fg: bool) -> bool { - let Some(term) = terminal::term() else { - return false; - }; - let term: &Term = &term; + let term = crate::terminal::term(); let supports_term24bit = get_color_support().contains(ColorSupport::TERM_24BIT); if !supports_term24bit || !color.is_rgb() { // Indexed or non-24 bit color. let idx = index_for_color(color); if is_fg { - return write_foreground_color(self, idx, term); + return write_foreground_color(self, idx, &term); } else { - return write_background_color(self, idx, term); + return write_background_color(self, idx, &term); }; } @@ -223,10 +220,7 @@ pub fn write_color(&mut self, color: RgbColor, is_fg: bool) -> bool { pub fn set_color(&mut self, mut fg: RgbColor, mut bg: RgbColor) { // Test if we have at least basic support for setting fonts, colors and related bits - otherwise // just give up... - let Some(term) = terminal::term() else { - return; - }; - let term: &Term = &term; + let term = crate::terminal::term(); let Term { enter_bold_mode, enter_underline_mode, @@ -238,7 +232,7 @@ pub fn set_color(&mut self, mut fg: RgbColor, mut bg: RgbColor) { enter_standout_mode, exit_attribute_mode, .. - } = term; + } = &*term; let Some(exit_attribute_mode) = exit_attribute_mode else { return; }; @@ -261,7 +255,7 @@ pub fn set_color(&mut self, mut fg: RgbColor, mut bg: RgbColor) { self.reset_modes(); // If we exit attribute mode, we must first set a color, or previously colored text might // lose its color. Terminals are weird... - write_foreground_color(self, 0, term); + write_foreground_color(self, 0, &term); self.tputs(exit_attribute_mode); return; } @@ -304,7 +298,7 @@ pub fn set_color(&mut self, mut fg: RgbColor, mut bg: RgbColor) { self.tputs(exit_attribute_mode); self.reset_modes(); // We don't know if exit_attribute_mode resets colors, so we set it to something known. - if write_foreground_color(self, 0, term) { + if write_foreground_color(self, 0, &term) { self.last_color = RgbColor::BLACK; } } @@ -312,7 +306,7 @@ pub fn set_color(&mut self, mut fg: RgbColor, mut bg: RgbColor) { if self.last_color != fg { if fg.is_normal() { - write_foreground_color(self, 0, term); + write_foreground_color(self, 0, &term); self.tputs(exit_attribute_mode); self.last_color2 = RgbColor::NORMAL; @@ -325,7 +319,7 @@ pub fn set_color(&mut self, mut fg: RgbColor, mut bg: RgbColor) { if self.last_color2 != bg { if bg.is_normal() { - write_background_color(self, 0, term); + write_background_color(self, 0, &term); self.tputs(exit_attribute_mode); if !self.last_color.is_normal() { diff --git a/src/proc.rs b/src/proc.rs index 878b1d009..98ba4e3eb 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -18,7 +18,6 @@ use crate::reader::{fish_is_unwinding_for_exit, reader_schedule_prompt_repaint}; use crate::redirection::RedirectionSpecList; use crate::signal::{signal_set_handlers_once, Signal}; -use crate::terminal::term; use crate::threads::MainThread; use crate::topic_monitor::{topic_monitor_principal, GenerationsList, Topic}; use crate::wait_handle::{InternalJobId, WaitHandle, WaitHandleRef, WaitHandleStore}; @@ -1214,7 +1213,7 @@ pub fn set_job_control_mode(mode: JobControl) { /// Notify the user about stopped or terminated jobs, and delete completed jobs from the job list. /// If `interactive` is set, allow removing interactive jobs; otherwise skip them. /// Return whether text was printed to stdout. -pub fn job_reap(parser: &Parser, allow_interactive: bool) -> bool { +pub fn job_reap(parser: &Parser, interactive: bool) -> bool { parser.assert_can_execute(); // Early out for the common case that there are no jobs. @@ -1223,7 +1222,7 @@ pub fn job_reap(parser: &Parser, allow_interactive: bool) -> bool { } process_mark_finished_children(parser, false /* not block_ok */); - process_clean_after_marking(parser, allow_interactive) + process_clean_after_marking(parser, interactive) } /// Return the list of background jobs which we should warn the user about, if the user attempts to @@ -1774,7 +1773,7 @@ fn save_wait_handle_for_completed_job(job: &Job, store: &mut WaitHandleStore) { /// Remove completed jobs from the job list, printing status messages as appropriate. /// Return whether something was printed. -fn process_clean_after_marking(parser: &Parser, allow_interactive: bool) -> bool { +fn process_clean_after_marking(parser: &Parser, interactive: bool) -> bool { parser.assert_can_execute(); // This function may fire an event handler, we do not want to call ourselves recursively (to @@ -1785,10 +1784,6 @@ fn process_clean_after_marking(parser: &Parser, allow_interactive: bool) -> bool let _cleaning = parser.push_scope(|s| s.is_cleaning_procs = true); - // This may be invoked in an exit handler, after the TERM has been torn down - // Don't try to print in that case (#3222) - let interactive = allow_interactive && term().is_some(); - // Remove all disowned jobs. remove_disowned_jobs(&mut parser.jobs_mut()); diff --git a/src/screen.rs b/src/screen.rs index 9f7c58be9..e03d7589f 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -667,7 +667,6 @@ pub fn reset_abandoning_line(&mut self, screen_width: usize) { // omitted_newline_char in common.rs. let non_space_width = get_omitted_newline_width(); let term = term(); - let term = term.as_ref(); // We do `>` rather than `>=` because the code below might require one extra space. if screen_width > non_space_width { let mut justgrey = true; @@ -678,18 +677,15 @@ pub fn reset_abandoning_line(&mut self, screen_width: usize) { abandon_line_string.push_utfstr(&str2wcstring(s.as_bytes())); true }; - if let Some(enter_dim_mode) = term.and_then(|term| term.enter_dim_mode.as_ref()) { + if let Some(enter_dim_mode) = term.enter_dim_mode.as_ref() { if add(&mut abandon_line_string, Some(enter_dim_mode.clone())) { // Use dim if they have it, so the color will be based on their actual normal // color and the background of the terminal. justgrey = false; } } - if let (true, Some(set_a_foreground)) = ( - justgrey, - term.and_then(|term| term.set_a_foreground.as_ref()), - ) { - let max_colors = term.unwrap().max_colors.unwrap_or_default(); + if let (true, Some(set_a_foreground)) = (justgrey, term.set_a_foreground.as_ref()) { + let max_colors = term.max_colors.unwrap_or_default(); if max_colors >= 238 { // draw the string in a particular grey add(&mut abandon_line_string, tparm1(set_a_foreground, 237)); @@ -697,7 +693,7 @@ pub fn reset_abandoning_line(&mut self, screen_width: usize) { // bright black (the ninth color, looks grey) add(&mut abandon_line_string, tparm1(set_a_foreground, 8)); } else if max_colors >= 2 { - if let Some(enter_bold_mode) = term.unwrap().enter_bold_mode.as_ref() { + if let Some(enter_bold_mode) = term.enter_bold_mode.as_ref() { // we might still get that color by setting black and going bold for bright add(&mut abandon_line_string, Some(enter_bold_mode.clone())); add(&mut abandon_line_string, tparm1(set_a_foreground, 0)); @@ -707,9 +703,7 @@ pub fn reset_abandoning_line(&mut self, screen_width: usize) { abandon_line_string.push_utfstr(&get_omitted_newline_str()); - if let Some(exit_attribute_mode) = - term.and_then(|term| term.exit_attribute_mode.as_ref()) - { + if let Some(exit_attribute_mode) = term.exit_attribute_mode.as_ref() { // normal text ANSI escape sequence add(&mut abandon_line_string, Some(exit_attribute_mode.clone())); } @@ -739,7 +733,7 @@ pub fn reset_abandoning_line(&mut self, screen_width: usize) { // pasting your terminal log becomes a pain. This commit clears that line, making it an // actual empty line. if !is_dumb() { - if let Some(clr_eol) = term.unwrap().clr_eol.as_ref() { + if let Some(clr_eol) = term.clr_eol.as_ref() { abandon_line_string.push_utfstr(&str2wcstring(clr_eol.as_bytes())); } } @@ -909,10 +903,7 @@ fn do_move(&mut self, new_x: usize, new_y: usize) { let y_steps = isize::try_from(new_y).unwrap() - isize::try_from(self.actual.cursor.y).unwrap(); - let Some(term) = term() else { - return; - }; - let term = term.as_ref(); + let term = term(); let s = if y_steps < 0 { term.cursor_up.as_ref() @@ -1101,12 +1092,11 @@ fn update( } let term = term(); - let term = term.as_ref(); // Output the left prompt if it has changed. if self.scrolled() && !is_final_rendering { self.r#move(0, 0); - self.write_mbs_if_some(&term.and_then(|term| term.clr_eol.as_ref())); + self.write_mbs_if_some(&term.clr_eol.as_ref()); self.actual_left_prompt = None; self.actual.cursor.x = 0; } else if self @@ -1130,7 +1120,7 @@ fn update( || (self.scrolled() && is_final_rendering) { for (i, &line_break) in left_prompt_layout.line_breaks.iter().enumerate() { - self.write_mbs_if_some(&term.and_then(|term| term.clr_eol.as_ref())); + self.write_mbs_if_some(&term.clr_eol); if i == 0 { osc_133_prompt_start(self); } @@ -1167,7 +1157,7 @@ fn s_line(zelf: &Screen, i: usize) -> &Line { // Don't issue clr_eos if we think the cursor will end up in the last column - see #6951. let should_clear_screen_this_line = need_clear_screen && i + 1 == self.desired.line_count() - && term.is_some_and(|term| term.clr_eos.is_some()) + && term.clr_eos.is_some() && !(self.desired.cursor.x == 0 && self.desired.cursor.y == self.desired.line_count()); @@ -1184,11 +1174,11 @@ fn s_line(zelf: &Screen, i: usize) -> &Line { if shared_prefix < o_line(self, i).indentation { if o_line(self, i).indentation > s_line(self, i).indentation && !has_cleared_screen - && term.is_some_and(|term| term.clr_eol.is_some() && term.clr_eos.is_some()) + && term.clr_eol.is_some() + && term.clr_eos.is_some() { set_color(self, HighlightSpec::new()); self.r#move(0, i); - let term = term.unwrap(); self.write_mbs_if_some(if should_clear_screen_this_line { &term.clr_eos } else { @@ -1248,7 +1238,7 @@ fn s_line(zelf: &Screen, i: usize) -> &Line { { set_color(self, HighlightSpec::new()); self.r#move(current_width, i); - self.write_mbs_if_some(&term.and_then(|term| term.clr_eos.as_ref())); + self.write_mbs_if_some(&term.clr_eos.as_ref()); has_cleared_screen = true; } if done { @@ -1291,9 +1281,7 @@ fn s_line(zelf: &Screen, i: usize) -> &Line { // This means that we switch background correctly on the next, // including our weird implicit bolding. set_color(self, HighlightSpec::new()); - if let (true, Some(clr_eol)) = - (clear_remainder, term.and_then(|term| term.clr_eol.as_ref())) - { + if let (true, Some(clr_eol)) = (clear_remainder, term.clr_eol.as_ref()) { self.r#move(current_width, i); self.write_mbs(clr_eol); } @@ -1332,11 +1320,9 @@ fn s_line(zelf: &Screen, i: usize) -> &Line { } // Clear remaining lines (if any) if we haven't cleared the screen. - if let (false, true, Some(clr_eol)) = ( - has_cleared_screen, - need_clear_screen, - term.and_then(|term| term.clr_eol.as_ref()), - ) { + if let (false, true, Some(clr_eol)) = + (has_cleared_screen, need_clear_screen, term.clr_eol.as_ref()) + { set_color(self, HighlightSpec::new()); for i in self.desired.line_count()..lines_with_stuff { self.r#move(0, i); @@ -1366,7 +1352,7 @@ pub fn mtime_stdout_stderr() -> (Option, Option) { pub fn screen_force_clear_to_end() { Outputter::stdoutput() .borrow_mut() - .tputs_if_some(&term().unwrap().clr_eos); + .tputs_if_some(&term().clr_eos); } /// Information about the layout of a prompt. @@ -1587,7 +1573,6 @@ pub fn escape_code_length(code: &wstr) -> Option { pub fn screen_clear() -> WString { term() - .unwrap() .clear_screen .as_ref() .map(|clear_screen| str2wcstring(clear_screen.as_bytes())) @@ -1621,7 +1606,7 @@ fn try_sequence(seq: &[u8], s: &wstr) -> usize { /// Returns the number of columns left until the next tab stop, given the current cursor position. fn next_tab_stop(current_line_width: usize) -> usize { // Assume tab stops every 8 characters if undefined. - let tab_width = term().unwrap().init_tabs.unwrap_or(8); + let tab_width = term().init_tabs.unwrap_or(8); ((current_line_width / tab_width) + 1) * tab_width } @@ -1629,7 +1614,7 @@ fn next_tab_stop(current_line_width: usize) -> usize { /// physical line on a wrapped logical line; instead we just output it. fn allow_soft_wrap() -> bool { // Should we be looking at eat_newline_glitch as well? - term().unwrap().auto_right_margin + term().auto_right_margin } /// Does this look like the escape sequence for setting a screen name? @@ -1747,7 +1732,7 @@ fn is_csi_style_escape_seq(code: &wstr) -> Option { /// Detect whether the escape sequence sets one of the terminal attributes that affects how text is /// displayed other than the color. fn is_visual_escape_seq(code: &wstr) -> Option { - let term = term()?; + let term = term(); let esc2 = [ &term.enter_bold_mode, &term.exit_attribute_mode, @@ -1930,12 +1915,11 @@ fn line_shared_prefix(a: &Line, b: &Line) -> usize { /// Returns true if we are using a dumb terminal. pub(crate) fn is_dumb() -> bool { - term().is_none_or(|term| { - term.cursor_up.is_none() - || term.cursor_down.is_none() - || term.cursor_left.is_none() - || term.cursor_right.is_none() - }) + let term = term(); + term.cursor_up.is_none() + || term.cursor_down.is_none() + || term.cursor_left.is_none() + || term.cursor_right.is_none() } // Exposed for testing. diff --git a/src/terminal.rs b/src/terminal.rs index 4942ba056..cc97d0cf6 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -13,10 +13,6 @@ /// The [`Term`] singleton. Initialized via a call to [`setup()`] and surfaced to the outside world via [`term()`]. /// -/// It isn't guaranteed that fish will ever be able to successfully call `setup()`, so this must -/// remain an `Option` instead of returning `Term` by default and just panicking if [`term()`] was -/// called before `setup()`. -/// /// We can't just use an [`AtomicPtr>`](std::sync::atomic::AtomicPtr) here because there's a race condition when the old Arc /// gets dropped - we would obtain the current (non-null) value of `TERM` in [`term()`] but there's /// no guarantee that a simultaneous call to [`setup()`] won't result in this refcount being @@ -26,11 +22,8 @@ /// Returns a reference to the global [`Term`] singleton or `None` if not preceded by a successful /// call to [`terminal::setup()`](setup). -pub fn term() -> Option> { - TERM.lock() - .expect("Mutex poisoned!") - .as_ref() - .map(Arc::clone) +pub fn term() -> Arc { + Arc::clone(TERM.lock().expect("Mutex poisoned!").as_ref().unwrap()) } /// The safe wrapper around terminfo functionality, initialized by a successful call to [`setup()`]