diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d2933f951..bcf90436d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,7 @@ fish ?.?.? (released ???) Notable improvements and fixes ------------------------------ - New Spanish translations (:issue:`12489`). +- ``set_color`` is able to turn off italics, reverse mode and strikethrough individually (e.g. ``--italics=off``). For distributors and developers ------------------------------- diff --git a/doc_src/cmds/set_color.rst b/doc_src/cmds/set_color.rst index d868e203e..fd23d1d54 100644 --- a/doc_src/cmds/set_color.rst +++ b/doc_src/cmds/set_color.rst @@ -48,14 +48,14 @@ The following options are available: **-d** or **--dim** Sets dim mode. -**-i** or **--italics** - Sets italics mode. +**-i** or **--italics**, or **-iSTATE** or **--italics=STATE** + Sets italics mode. The state can be **on** / **true** (default), or **off** / **false** -**-r** or **--reverse** - Sets reverse mode. +**-r** or **--reverse**, or **-iSTATE** or **--reverse=STATE** + Sets reverse mode. The state can be **on** / **true** (default), or **off** / **false** -**-s** or **--strikethrough** - Sets strikethrough mode. +**-s** or **--strikethrough**, or **-sSTATE** or **--strikethrough=STATE** + Sets strikethrough mode. The state can be **on** / **true** (default), or **off** / **false** **-u** or **--underline**, or **-uSTYLE** or **--underline=STYLE** Set the underline mode; supported styles are **single** (default), **double**, **curly**, **dotted** and **dashed**. diff --git a/localization/po/de.po b/localization/po/de.po index 4114da99e..76f294937 100644 --- a/localization/po/de.po +++ b/localization/po/de.po @@ -199,6 +199,10 @@ msgstr "" msgid "%s: %s: invalid mode name. See `help %s`" msgstr "" +#, c-format +msgid "%s: %s: invalid option argument: %s" +msgstr "" + #, c-format msgid "%s: %s: invalid scale" msgstr "" diff --git a/localization/po/en.po b/localization/po/en.po index ac34eb09b..c34b9c017 100644 --- a/localization/po/en.po +++ b/localization/po/en.po @@ -199,6 +199,10 @@ msgstr "" msgid "%s: %s: invalid mode name. See `help %s`" msgstr "" +#, c-format +msgid "%s: %s: invalid option argument: %s" +msgstr "" + #, c-format msgid "%s: %s: invalid scale" msgstr "" diff --git a/localization/po/es.po b/localization/po/es.po index 909078588..5adb0fb6e 100644 --- a/localization/po/es.po +++ b/localization/po/es.po @@ -199,6 +199,10 @@ msgstr "%s: %s: modo no válido" msgid "%s: %s: invalid mode name. See `help %s`" msgstr "%s: %s: nombre de modo no válido. Consulte `help %s`" +#, c-format +msgid "%s: %s: invalid option argument: %s" +msgstr "" + #, c-format msgid "%s: %s: invalid scale" msgstr "%s: %s: escala no válida" diff --git a/localization/po/fr.po b/localization/po/fr.po index 4f0e4bb25..5fe84b534 100644 --- a/localization/po/fr.po +++ b/localization/po/fr.po @@ -328,6 +328,10 @@ msgstr "%s : %s : mode invalide" msgid "%s: %s: invalid mode name. See `help %s`" msgstr "%s : %s : nom de mode invalide. Voir « help %s »" +#, c-format +msgid "%s: %s: invalid option argument: %s" +msgstr "" + #, c-format msgid "%s: %s: invalid scale" msgstr "%s : %s : échelle invalide" diff --git a/localization/po/pl.po b/localization/po/pl.po index 9dcd32a34..40fc9371f 100644 --- a/localization/po/pl.po +++ b/localization/po/pl.po @@ -195,6 +195,10 @@ msgstr "" msgid "%s: %s: invalid mode name. See `help %s`" msgstr "" +#, c-format +msgid "%s: %s: invalid option argument: %s" +msgstr "" + #, c-format msgid "%s: %s: invalid scale" msgstr "" diff --git a/localization/po/pt_BR.po b/localization/po/pt_BR.po index b519cbf0c..30c71cf1f 100644 --- a/localization/po/pt_BR.po +++ b/localization/po/pt_BR.po @@ -200,6 +200,10 @@ msgstr "" msgid "%s: %s: invalid mode name. See `help %s`" msgstr "" +#, c-format +msgid "%s: %s: invalid option argument: %s" +msgstr "" + #, c-format msgid "%s: %s: invalid scale" msgstr "" diff --git a/localization/po/sv.po b/localization/po/sv.po index 765d6be62..191198780 100644 --- a/localization/po/sv.po +++ b/localization/po/sv.po @@ -196,6 +196,10 @@ msgstr "" msgid "%s: %s: invalid mode name. See `help %s`" msgstr "" +#, c-format +msgid "%s: %s: invalid option argument: %s" +msgstr "" + #, c-format msgid "%s: %s: invalid scale" msgstr "" diff --git a/localization/po/zh_CN.po b/localization/po/zh_CN.po index c7b9e50b7..2f6ee2d6d 100644 --- a/localization/po/zh_CN.po +++ b/localization/po/zh_CN.po @@ -220,6 +220,10 @@ msgstr "%s: %s: 无效舍入模式" msgid "%s: %s: invalid mode name. See `help %s`" msgstr "%s: %s: 无效模式名。参见 `help %s`" +#, c-format +msgid "%s: %s: invalid option argument: %s" +msgstr "" + #, c-format msgid "%s: %s: invalid scale" msgstr "%s: %s: 无效位数" diff --git a/localization/po/zh_TW.po b/localization/po/zh_TW.po index 12a3f295d..3ccaf9965 100644 --- a/localization/po/zh_TW.po +++ b/localization/po/zh_TW.po @@ -193,6 +193,10 @@ msgstr "%s:%s:無效的模式" msgid "%s: %s: invalid mode name. See `help %s`" msgstr "%s:%s:無效的模式名稱。參見「help %s」" +#, c-format +msgid "%s: %s: invalid option argument: %s" +msgstr "" + #, c-format msgid "%s: %s: invalid scale" msgstr "%s:%s:無效的小數位數" diff --git a/src/builtins/set_color.rs b/src/builtins/set_color.rs index 8d674c31c..3ecdd83a0 100644 --- a/src/builtins/set_color.rs +++ b/src/builtins/set_color.rs @@ -96,6 +96,15 @@ pub fn set_color(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) - ); return Err(STATUS_INVALID_ARGS); } + Err(InvalidOptArg(name, value)) => { + streams.err.appendln(&wgettext_fmt!( + "%s: %s: invalid option argument: %s", + argv[0], + name, + value + )); + return Err(STATUS_INVALID_ARGS); + } Err(UnknownColor(arg)) => { streams .err diff --git a/src/highlight/highlight.rs b/src/highlight/highlight.rs index 05e5bd14d..1e4360ba3 100644 --- a/src/highlight/highlight.rs +++ b/src/highlight/highlight.rs @@ -24,7 +24,9 @@ }; use crate::path::{path_as_implicit_cd, path_get_cdpath, path_get_path, paths_are_same_file}; use crate::terminal::Outputter; -use crate::text_face::{SpecifiedTextFace, TextFace, TextStyling, UnderlineStyle, parse_text_face}; +use crate::text_face::{ + ResettableStyle, SpecifiedTextFace, TextFace, UnderlineStyle, parse_text_face, +}; use crate::threads::assert_is_background_thread; use crate::tokenizer::{PipeOrRedir, variable_assignment_equals_pos}; use fish_color::Color; @@ -179,7 +181,9 @@ fn resolve_spec_uncached(highlight: &HighlightSpec, vars: &dyn Environment) -> T face.bg = bg_face.bg; // In case the background role is different from the foreground one, we ignore its style // except for reverse mode. - face.style.reverse |= bg_face.style.is_reverse(); + if face.style.reverse != ResettableStyle::On { + face.style.reverse = bg_face.style.reverse; + } } // Handle modifiers. @@ -213,11 +217,7 @@ pub(crate) fn parse_text_face_for_highlight(var: &EnvVar) -> Option { let fg = face.fg.unwrap_or(default.fg); let bg = face.bg.unwrap_or(default.bg); let underline_color = face.underline_color.unwrap_or(default.underline_color); - let style = if face.style != TextStyling::unknown() { - face.style - } else { - TextStyling::terminal_default() - }; + let style = default.style.union_prefer_right(face.style); TextFace { fg, bg, @@ -1312,7 +1312,7 @@ pub struct HighlightSpec { mod tests { use super::{HighlightColorResolver, HighlightRole, HighlightSpec, highlight_shell}; use crate::common::ScopeGuard; - use crate::env::{EnvMode, EnvSetMode, Environment as _}; + use crate::env::{EnvMode, EnvSetMode, EnvVar, EnvVarFlags, Environment as _}; use crate::future_feature_flags::{self, FeatureFlag}; use crate::highlight::parse_text_face_for_highlight; use crate::operation_context::{EXPANSION_LIMIT_BACKGROUND, OperationContext}; @@ -1896,4 +1896,31 @@ fn test_resolve_role() { parse_text_face_for_highlight(&vars.get(L!("fish_color_command")).unwrap()).unwrap(); assert_eq!(face, command_face); } + + #[test] + fn test_parse_text_face_for_highlight_fully_specified() { + let assert_all_set = |values: Vec| { + let var = EnvVar::new_vec(values.clone(), EnvVarFlags::empty()); + let face = parse_text_face_for_highlight(&var); + assert!( + face.is_some_and(|face| face.all_set()), + "Underspecified result for {:?}\n => {:?}", + values, + face + ); + }; + + assert_all_set(vec![L!("normal").into()]); + assert_all_set(vec![L!("green").into()]); + assert_all_set(vec![L!("--background=normal").into()]); + assert_all_set(vec![L!("--background=green").into()]); + assert_all_set(vec![L!("--underline-color=normal").into()]); + assert_all_set(vec![L!("--underline-color=green").into()]); + assert_all_set(vec![L!("--italics").into()]); + assert_all_set(vec![L!("--italics=off").into()]); + assert_all_set(vec![L!("--reverse").into()]); + assert_all_set(vec![L!("--reverse=off").into()]); + assert_all_set(vec![L!("--strikethrough").into()]); + assert_all_set(vec![L!("--strikethrough=off").into()]); + } } diff --git a/src/terminal.rs b/src/terminal.rs index 68b6e5eb5..491fe6826 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -3,7 +3,7 @@ use crate::future_feature_flags::{self, FeatureFlag}; use crate::prelude::*; use crate::screen::{is_dumb, only_grayscale}; -use crate::text_face::{TextFace, TextStyling, UnderlineStyle}; +use crate::text_face::{ResettableStyle, TextFace, TextStyling, UnderlineStyle}; use crate::threads::MainThread; use bitflags::bitflags; use fish_color::{Color, Color24}; @@ -54,6 +54,7 @@ pub(crate) enum SgrTerminalCommand { EnterStrikethroughMode, ExitItalicsMode, ExitUnderlineMode, + ExitReverseMode, ExitStrikethroughMode, // Colors @@ -377,7 +378,7 @@ fn set_text_face_internal( use SgrTerminalCommand::{ DefaultBackgroundColor, DefaultUnderlineColor, EnterBoldMode, EnterDimMode, EnterItalicsMode, EnterReverseMode, EnterStrikethroughMode, EnterUnderlineMode, - ExitItalicsMode, ExitStrikethroughMode, ExitUnderlineMode, + ExitItalicsMode, ExitReverseMode, ExitStrikethroughMode, ExitUnderlineMode, }; let mut style_writer = self.style_writer(); @@ -388,8 +389,10 @@ fn set_text_face_internal( // Removes all styles that are individually resettable. let non_resettable = |mut style: TextStyling| { - style.italics = false; + style.italics = ResettableStyle::Unchanged; style.underline_style = None; + style.reverse = ResettableStyle::Unchanged; + style.strikethrough = ResettableStyle::Unchanged; style }; let non_resettable_attributes_to_unset = non_resettable(style_writer.last().style) @@ -458,14 +461,6 @@ fn set_text_face_internal( } } - let was_italics = style_writer.last().style.is_italics(); - if !style.is_italics() && was_italics && style_writer.write_command(ExitItalicsMode) { - style_writer.last().style.italics = false; - } else if style.is_italics() && !was_italics && style_writer.write_command(EnterItalicsMode) - { - style_writer.last().style.italics = true; - } - if style.is_dim() && !style_writer.last().style.is_dim() && style_writer.write_command(EnterDimMode) @@ -473,25 +468,46 @@ fn set_text_face_internal( style_writer.last().style.dim = true; } - let was_strikethrough = style_writer.last().style.is_strikethrough(); - if !style.is_strikethrough() - && was_strikethrough - && style_writer.write_command(ExitStrikethroughMode) - { - style_writer.last().style.strikethrough = false; - } else if style.is_strikethrough() - && !was_strikethrough - && style_writer.write_command(EnterStrikethroughMode) - { - style_writer.last().style.strikethrough = true; - } + let mut current_style = style_writer.last().style; + let mut apply_resettable_style = + |new_style: ResettableStyle, + current_style: &mut ResettableStyle, + enter_cmd: SgrTerminalCommand, + exit_cmd: SgrTerminalCommand| { + if new_style == *current_style { + return; + } + let cmd = match new_style { + ResettableStyle::Unchanged => { + return; + } + ResettableStyle::On => enter_cmd, + ResettableStyle::Off => exit_cmd, + }; + if style_writer.write_command(cmd) { + *current_style = new_style; + } + }; - if style.is_reverse() - && !style_writer.last().style.is_reverse() - && style_writer.write_command(EnterReverseMode) - { - style_writer.last().style.reverse = true; - } + apply_resettable_style( + style.italics, + &mut current_style.italics, + EnterItalicsMode, + ExitItalicsMode, + ); + apply_resettable_style( + style.reverse, + &mut current_style.reverse, + EnterReverseMode, + ExitReverseMode, + ); + apply_resettable_style( + style.strikethrough, + &mut current_style.strikethrough, + EnterStrikethroughMode, + ExitStrikethroughMode, + ); + style_writer.last().style = current_style; } /// Write a wide character to the receiver. @@ -655,6 +671,7 @@ pub(crate) fn write_command(&mut self, cmd: SgrTerminalCommand) -> bool { EnterItalicsMode => self.write_param_str(1, b"3"), EnterUnderlineMode(style) => self.write_underline_mode(style), EnterReverseMode => self.write_param_str(1, b"7"), + ExitReverseMode => self.write_param_str(1, b"27"), EnterStrikethroughMode => self.write_param_str(1, b"9"), ExitStrikethroughMode => self.write_param_str(1, b"29"), ExitItalicsMode => self.write_param_str(1, b"23"), @@ -771,7 +788,9 @@ fn drop(&mut self) { #[cfg(test)] mod tests { - use fish_color::Color24; + use fish_color::{Color, Color24}; + + use crate::text_face::{ResettableStyle, TextFace, TextStyling}; use super::{ Outputter, @@ -867,4 +886,49 @@ fn sgr_max_length() { ) ); } + + #[test] + fn resettable_style_attribute() { + use ResettableStyle::{Off, On, Unchanged}; + + let mut outp = Outputter::new_buffering_no_assume_normal(); + + let mut set_attr = + |italics: ResettableStyle, reverse: ResettableStyle, strikethrough: ResettableStyle| { + let mut style = TextStyling::unknown_style(); + style.italics = italics; + style.reverse = reverse; + style.strikethrough = strikethrough; + + let face = TextFace::new(Color::None, Color::None, Color::None, style); + outp.set_text_face(face); + }; + + // `#[cfg_attr(...)]` because `#[rustfmt::skip]` triggers `error[E0658]: attributes on expressions are experimental` + #[cfg_attr(any(), rustfmt::skip)] + { + set_attr(On, Unchanged, Off); + set_attr(On, On, Unchanged); + set_attr(Unchanged, On, Unchanged); + set_attr(Unchanged, Unchanged, On); + set_attr(Off, Unchanged, On); + set_attr(Off, Off, Unchanged); + set_attr(Unchanged, Off, Unchanged); + set_attr(Unchanged, Unchanged, Off); + } + + assert_eq!( + String::from_utf8_lossy(outp.contents()), + concat!( + "\u{1b}[3;29m", + "\u{1b}[7m", + "", + "\u{1b}[9m", + "\u{1b}[23m", + "\u{1b}[27m", + "", + "\u{1b}[29m", + ) + ); + } } diff --git a/src/text_face.rs b/src/text_face.rs index dbf6c9554..afef6df47 100644 --- a/src/text_face.rs +++ b/src/text_face.rs @@ -3,11 +3,37 @@ use fish_color::Color; use fish_wgetopt::{ArgType, WGetopter, WOption, wopt}; -trait StyleSet { +pub(crate) trait StyleSet { fn union_prefer_right(self, other: Self) -> Self; fn difference_prefer_empty(self, other: Self) -> Self; } +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] +pub(crate) enum ResettableStyle { + #[default] + Unchanged, + Off, + On, +} + +impl StyleSet for ResettableStyle { + fn union_prefer_right(self, other: Self) -> Self { + if other == Self::Unchanged { + self + } else { + other + } + } + + fn difference_prefer_empty(self, other: Self) -> Self { + if other != Self::Unchanged { + Self::Unchanged + } else { + self + } + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(crate) enum UnderlineStyle { Single, @@ -34,10 +60,10 @@ fn difference_prefer_empty(self, other: Self) -> Self { pub(crate) struct TextStyling { pub(crate) bold: bool, pub(crate) underline_style: Option, - pub(crate) italics: bool, + pub(crate) italics: ResettableStyle, pub(crate) dim: bool, - pub(crate) reverse: bool, - pub(crate) strikethrough: bool, + pub(crate) reverse: ResettableStyle, + pub(crate) strikethrough: ResettableStyle, } impl TextStyling { @@ -45,36 +71,44 @@ pub(crate) const fn terminal_default() -> Self { Self { bold: false, underline_style: None, - italics: false, + italics: ResettableStyle::Off, dim: false, - reverse: false, - strikethrough: false, + reverse: ResettableStyle::Off, + strikethrough: ResettableStyle::Off, } } pub(crate) const fn unknown() -> Self { Self { bold: false, underline_style: None, - italics: false, + italics: ResettableStyle::Unchanged, dim: false, - reverse: false, - strikethrough: false, + reverse: ResettableStyle::Unchanged, + strikethrough: ResettableStyle::Unchanged, } } pub(crate) fn is_empty(&self) -> bool { *self == Self::unknown() } + + #[cfg(test)] + pub(crate) fn all_set(&self) -> bool { + (self.italics != ResettableStyle::Unchanged) + && (self.reverse != ResettableStyle::Unchanged) + && (self.strikethrough != ResettableStyle::Unchanged) + } + pub(crate) fn union_prefer_right(self, other: Self) -> Self { Self { bold: self.is_bold() || other.is_bold(), underline_style: self .underline_style .union_prefer_right(other.underline_style), - italics: self.is_italics() || other.is_italics(), + italics: self.italics.union_prefer_right(other.italics), dim: self.is_dim() || other.is_dim(), - reverse: self.is_reverse() || other.is_reverse(), - strikethrough: self.is_strikethrough() || other.is_strikethrough(), + reverse: self.reverse.union_prefer_right(other.reverse), + strikethrough: self.strikethrough.union_prefer_right(other.strikethrough), } } pub(crate) fn difference_prefer_empty(self, other: Self) -> Self { @@ -83,10 +117,12 @@ pub(crate) fn difference_prefer_empty(self, other: Self) -> Self { underline_style: self .underline_style .difference_prefer_empty(other.underline_style), - italics: self.is_italics() && !other.is_italics(), + italics: self.italics.difference_prefer_empty(other.italics), dim: self.is_dim() && !other.is_dim(), - reverse: self.is_reverse() && !other.is_reverse(), - strikethrough: self.is_strikethrough() && !other.is_strikethrough(), + reverse: self.reverse.difference_prefer_empty(other.reverse), + strikethrough: self + .strikethrough + .difference_prefer_empty(other.strikethrough), } } @@ -105,25 +141,10 @@ pub fn inject_underline(&mut self, underline: UnderlineStyle) { self.underline_style = Some(underline); } - /// Returns whether the text face is italics. - pub const fn is_italics(self) -> bool { - self.italics - } - /// Returns whether the text face is dim. pub const fn is_dim(self) -> bool { self.dim } - - /// Returns whether the text face has reverse foreground/background colors. - pub const fn is_reverse(self) -> bool { - self.reverse - } - - /// Returns whether the text face is strikethrough. - pub const fn is_strikethrough(self) -> bool { - self.strikethrough - } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -160,6 +181,14 @@ pub fn new(fg: Color, bg: Color, underline_color: Color, style: TextStyling) -> style, } } + + #[cfg(test)] + pub(crate) fn all_set(&self) -> bool { + !self.fg.is_none() + && !self.bg.is_none() + && !self.underline_color.is_none() + && self.style.all_set() + } } #[derive(Debug, Eq, PartialEq)] @@ -211,27 +240,41 @@ pub(crate) enum ParsedArgs<'argarray, 'args> { pub(crate) enum ParseError<'args> { MissingOptArg, UnexpectedOptArg(usize), + InvalidOptArg(&'static wstr, &'args wstr), UnknownColor(&'args wstr), UnknownUnderlineStyle(&'args wstr), UnknownOption(usize), } +fn parse_resettable_style<'a>(w: &WGetopter<'_, 'a, '_>) -> Result { + let Some(arg) = w.woptarg else { + return Ok(ResettableStyle::On); + }; + if (arg == "off") || (arg == "false") { + Ok(ResettableStyle::Off) + } else if (arg == "on") || (arg == "true") { + Ok(ResettableStyle::On) + } else { + Err(arg) + } +} + pub(crate) fn parse_text_face_and_options<'argarray, 'args>( argv: &'argarray mut [&'args wstr], is_builtin: bool, ) -> Result, ParseError<'args>> { let builtin_extra_args = if is_builtin { 0 } else { "hc".len() }; - let short_options = L!("b:oidru::ch"); + let short_options = L!("b:oi::dr::s::u::ch"); let short_options = &short_options[..short_options.len() - builtin_extra_args]; let long_options: &[WOption] = &[ wopt(L!("background"), ArgType::RequiredArgument, 'b'), wopt(L!("underline-color"), ArgType::RequiredArgument, '\x02'), wopt(L!("bold"), ArgType::NoArgument, 'o'), wopt(L!("underline"), ArgType::OptionalArgument, 'u'), - wopt(L!("italics"), ArgType::NoArgument, 'i'), + wopt(L!("italics"), ArgType::OptionalArgument, 'i'), wopt(L!("dim"), ArgType::NoArgument, 'd'), - wopt(L!("strikethrough"), ArgType::NoArgument, 's'), - wopt(L!("reverse"), ArgType::NoArgument, 'r'), + wopt(L!("reverse"), ArgType::OptionalArgument, 'r'), + wopt(L!("strikethrough"), ArgType::OptionalArgument, 's'), wopt(L!("theme"), ArgType::RequiredArgument, '\x01'), wopt(L!("help"), ArgType::NoArgument, 'h'), wopt(L!("print-colors"), ArgType::NoArgument, 'c'), @@ -276,10 +319,19 @@ pub(crate) fn parse_text_face_and_options<'argarray, 'args>( return Ok(PrintHelp); } 'o' => style.bold = true, - 'i' => style.italics = true, + 'i' => { + style.italics = + parse_resettable_style(&w).map_err(|v| InvalidOptArg(L!("--italics"), v))?; + } 'd' => style.dim = true, - 'r' => style.reverse = true, - 's' => style.strikethrough = true, + 'r' => { + style.reverse = + parse_resettable_style(&w).map_err(|v| InvalidOptArg(L!("--reverse"), v))?; + } + 's' => { + style.strikethrough = parse_resettable_style(&w) + .map_err(|v| InvalidOptArg(L!("--strikethrough"), v))?; + } 'u' => { let arg = w.woptarg.unwrap_or(L!("single")); style.underline_style = Some(if arg == "single" { diff --git a/tests/checks/set_color.fish b/tests/checks/set_color.fish index 5e9148f14..403b105df 100644 --- a/tests/checks/set_color.fish +++ b/tests/checks/set_color.fish @@ -39,6 +39,81 @@ string escape (set_color --background=green) fish_term256=0 string escape (set_color --background=f00 --background=green --background=00f) # CHECK: \e\[42m +string escape (set_color --italics) +# CHECK: \e\[3m +string escape (set_color --italics=on) +# CHECK: \e\[3m +string escape (set_color --italics=true) +# CHECK: \e\[3m +string escape (set_color --italics=off) +# CHECK: \e\[23m +string escape (set_color --italics=false) +# CHECK: \e\[23m +string escape (set_color --italics=foo) +# CHECKERR: set_color: --italics: invalid option argument: foo +string escape (set_color -i) +# CHECK: \e\[3m +string escape (set_color -ion) +# CHECK: \e\[3m +string escape (set_color -itrue) +# CHECK: \e\[3m +string escape (set_color -ioff) +# CHECK: \e\[23m +string escape (set_color -ifalse) +# CHECK: \e\[23m +string escape (set_color -ifoo) +# CHECKERR: set_color: --italics: invalid option argument: foo + +string escape (set_color --reverse) +# CHECK: \e\[7m +string escape (set_color --reverse=on) +# CHECK: \e\[7m +string escape (set_color --reverse=true) +# CHECK: \e\[7m +string escape (set_color --reverse=off) +# CHECK: \e\[27m +string escape (set_color --reverse=false) +# CHECK: \e\[27m +string escape (set_color --reverse=foo) +# CHECKERR: set_color: --reverse: invalid option argument: foo +string escape (set_color -r) +# CHECK: \e\[7m +string escape (set_color -ron) +# CHECK: \e\[7m +string escape (set_color -rtrue) +# CHECK: \e\[7m +string escape (set_color -roff) +# CHECK: \e\[27m +string escape (set_color -rfalse) +# CHECK: \e\[27m +string escape (set_color -rfoo) +# CHECKERR: set_color: --reverse: invalid option argument: foo + +string escape (set_color --strikethrough) +# CHECK: \e\[9m +string escape (set_color --strikethrough=on) +# CHECK: \e\[9m +string escape (set_color --strikethrough=true) +# CHECK: \e\[9m +string escape (set_color --strikethrough=off) +# CHECK: \e\[29m +string escape (set_color --strikethrough=false) +# CHECK: \e\[29m +string escape (set_color --strikethrough=foo) +# CHECKERR: set_color: --strikethrough: invalid option argument: foo +string escape (set_color -s) +# CHECK: \e\[9m +string escape (set_color -son) +# CHECK: \e\[9m +string escape (set_color -strue) +# CHECK: \e\[9m +string escape (set_color -soff) +# CHECK: \e\[29m +string escape (set_color -sfalse) +# CHECK: \e\[29m +string escape (set_color -sfoo) +# CHECKERR: set_color: --strikethrough: invalid option argument: foo + string escape (set_color --underline=curly) # CHECK: \e\[4:3m string escape (set_color -ucurly) @@ -63,4 +138,4 @@ string escape (set_color --underline=dashed) # CHECK: \e\[4:5m string escape (set_color f00 --background=00f --underline-color=0f0 --bold --dim --italics --reverse --strikethrough --underline=curly) -# CHECK: \e\[38\;2\;255\;0\;0\;48\;2\;0\;0\;255\;58:2::0:255:0\;1\;4:3\;3\;2\;9m\e\[7m +# CHECK: \e\[38\;2\;255\;0\;0\;48\;2\;0\;0\;255\;58:2::0:255:0\;1\;4:3\;2\;3\;7m\e\[9m