mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-24 05:41:14 -03:00
Curly underlines in set_color and fish_color_*
set_color --underline=curly outputs \e[4:3m which breaks the following terminals: - Terminal.app interprets it as yellow background - abduco and dvtm interpret it as green foreground - JetBrains terminals interprets it as yellow background - urxvt interprets it as yellow background terminals that interpret curly as single underline: - tmux [1] - emacs ansi-term [2] - emacs vterm - GNU screen (also wrongly turns on italic mode) - terminology (also wrongly turns on italic mode) - Vim's :terminal [1]: https://github.com/orgs/tmux/discussions/4477 [2]: https://lists.gnu.org/archive/html/bug-gnu-emacs/2025-04/msg01093.html Closes #10957
This commit is contained in:
@@ -65,6 +65,7 @@ Completions
|
|||||||
|
|
||||||
Improved terminal support
|
Improved terminal support
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
- Support for curly underlines in `fish_color_*` variables and :doc:`set_color <cmds/set_color>` (:issue:`10957`).
|
||||||
- New documentation page `Terminal Compatibility <terminal-compatibility.html>`_ (also accessible via ``man fish-terminal-compatibility``) lists required and optional terminal control sequences used by fish.
|
- New documentation page `Terminal Compatibility <terminal-compatibility.html>`_ (also accessible via ``man fish-terminal-compatibility``) lists required and optional terminal control sequences used by fish.
|
||||||
|
|
||||||
Other improvements
|
Other improvements
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ The following options are available:
|
|||||||
**-r** or **--reverse**
|
**-r** or **--reverse**
|
||||||
Sets reverse mode.
|
Sets reverse mode.
|
||||||
|
|
||||||
**-u** or **--underline**
|
**-u** or **--underline**, or **-uSTYLE** or **--underline=STYLE**
|
||||||
Sets underlined mode.
|
Set the underline mode; supported styles are **single** (default) and **curly**.
|
||||||
|
|
||||||
**-h** or **--help**
|
**-h** or **--help**
|
||||||
Displays help about using this command.
|
Displays help about using this command.
|
||||||
|
|||||||
@@ -120,6 +120,10 @@ Optional Commands
|
|||||||
- smul
|
- smul
|
||||||
- Enter underline mode.
|
- Enter underline mode.
|
||||||
-
|
-
|
||||||
|
* - ``\e[4:3m``
|
||||||
|
- Su
|
||||||
|
- Enter curly underline mode.
|
||||||
|
- kitty
|
||||||
* - ``\e[7m``
|
* - ``\e[7m``
|
||||||
- rev
|
- rev
|
||||||
- Enter reverse video mode (swap foreground and background colors).
|
- Enter reverse video mode (swap foreground and background colors).
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ complete -c set -n '__fish_set_is_color true false' -a --dim -x
|
|||||||
complete -c set -n '__fish_set_is_color true false' -a --italics -x
|
complete -c set -n '__fish_set_is_color true false' -a --italics -x
|
||||||
complete -c set -n '__fish_set_is_color true true' -a --reverse -x
|
complete -c set -n '__fish_set_is_color true true' -a --reverse -x
|
||||||
complete -c set -n '__fish_set_is_color true false' -a --underline -x
|
complete -c set -n '__fish_set_is_color true false' -a --underline -x
|
||||||
|
complete -c set -n '__fish_set_is_color true false' -a --underline=curly -x
|
||||||
|
|
||||||
# Locale completions
|
# Locale completions
|
||||||
complete -c set -n '__fish_set_is_locale; and not __fish_seen_argument -s e -l erase' -x -a '(command -sq locale; and locale -a)' -d Locale
|
complete -c set -n '__fish_set_is_locale; and not __fish_seen_argument -s e -l erase' -x -a '(command -sq locale; and locale -a)' -d Locale
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ complete -c set_color -s o -l bold -d 'Make font bold'
|
|||||||
complete -c set_color -s i -l italics -d Italicise
|
complete -c set_color -s i -l italics -d Italicise
|
||||||
complete -c set_color -s d -l dim -d 'Dim text'
|
complete -c set_color -s d -l dim -d 'Dim text'
|
||||||
complete -c set_color -s r -l reverse -d 'Reverse color text'
|
complete -c set_color -s r -l reverse -d 'Reverse color text'
|
||||||
complete -c set_color -s u -l underline -d 'Underline text'
|
complete -c set_color -s u -l underline -d 'Underline style' -a 'single curly'
|
||||||
complete -c set_color -s h -l help -d 'Display help and exit'
|
complete -c set_color -s h -l help -d 'Display help and exit'
|
||||||
complete -c set_color -s c -l print-colors -d 'Print a list of all accepted color names'
|
complete -c set_color -s c -l print-colors -d 'Print a list of all accepted color names'
|
||||||
|
|||||||
@@ -240,7 +240,11 @@ def parse_color(color_str):
|
|||||||
if comp == "--bold" or comp == "-o":
|
if comp == "--bold" or comp == "-o":
|
||||||
bold = True
|
bold = True
|
||||||
elif comp == "--underline" or comp == "-u":
|
elif comp == "--underline" or comp == "-u":
|
||||||
underline = True
|
underline = "single"
|
||||||
|
elif comp.startswith("--underline="):
|
||||||
|
underline = comp.stripprefix("--underline=")
|
||||||
|
elif comp.startswith("-u"): # Multiple short options like "-rbcurly" are not yet supported.
|
||||||
|
underline = comp.stripprefix("-u")
|
||||||
elif comp == "--italics" or comp == "-i":
|
elif comp == "--italics" or comp == "-i":
|
||||||
italics = True
|
italics = True
|
||||||
elif comp == "--dim" or comp == "-d":
|
elif comp == "--dim" or comp == "-d":
|
||||||
@@ -295,8 +299,8 @@ def unparse_color(col):
|
|||||||
ret += col["color"]
|
ret += col["color"]
|
||||||
if col["bold"]:
|
if col["bold"]:
|
||||||
ret += " --bold"
|
ret += " --bold"
|
||||||
if col["underline"]:
|
if col["underline"] is not None:
|
||||||
ret += " --underline"
|
ret += " --underline=" + col["underline"]
|
||||||
if col["italics"]:
|
if col["italics"]:
|
||||||
ret += " --italics"
|
ret += " --italics"
|
||||||
if col["dim"]:
|
if col["dim"]:
|
||||||
|
|||||||
@@ -66,6 +66,14 @@ pub fn set_color(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -
|
|||||||
// and we don't error for missing colors.
|
// and we don't error for missing colors.
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
|
TextFaceArgsAndOptionsResult::InvalidUnderlineStyle(arg) => {
|
||||||
|
streams.err.append(wgettext_fmt!(
|
||||||
|
"%ls: invalid underline style: %ls\n",
|
||||||
|
argv[0],
|
||||||
|
arg
|
||||||
|
));
|
||||||
|
return Err(STATUS_INVALID_ARGS);
|
||||||
|
}
|
||||||
TextFaceArgsAndOptionsResult::UnknownOption(unknown_option_index) => {
|
TextFaceArgsAndOptionsResult::UnknownOption(unknown_option_index) => {
|
||||||
builtin_unknown_option(
|
builtin_unknown_option(
|
||||||
parser,
|
parser,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
};
|
};
|
||||||
use crate::path::{path_as_implicit_cd, path_get_cdpath, path_get_path, paths_are_same_file};
|
use crate::path::{path_as_implicit_cd, path_get_cdpath, path_get_path, paths_are_same_file};
|
||||||
use crate::terminal::Outputter;
|
use crate::terminal::Outputter;
|
||||||
use crate::text_face::{parse_text_face, TextFace};
|
use crate::text_face::{parse_text_face, TextFace, UnderlineStyle};
|
||||||
use crate::threads::assert_is_background_thread;
|
use crate::threads::assert_is_background_thread;
|
||||||
use crate::tokenizer::{variable_assignment_equals_pos, PipeOrRedir};
|
use crate::tokenizer::{variable_assignment_equals_pos, PipeOrRedir};
|
||||||
use crate::wchar::{wstr, WString, L};
|
use crate::wchar::{wstr, WString, L};
|
||||||
@@ -168,12 +168,12 @@ pub(crate) fn resolve_spec_uncached(
|
|||||||
if !valid_path_face.fg.is_normal() {
|
if !valid_path_face.fg.is_normal() {
|
||||||
face.fg = valid_path_face.fg;
|
face.fg = valid_path_face.fg;
|
||||||
}
|
}
|
||||||
face.style = face.style.union(valid_path_face.style);
|
face.style = face.style.union_prefer_right(valid_path_face.style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if highlight.force_underline {
|
if highlight.force_underline {
|
||||||
face.style.inject_underline(true);
|
face.style.inject_underline(UnderlineStyle::Single);
|
||||||
}
|
}
|
||||||
|
|
||||||
face
|
face
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use crate::future_feature_flags::{self, FeatureFlag};
|
use crate::future_feature_flags::{self, FeatureFlag};
|
||||||
use crate::highlight::HighlightColorResolver;
|
use crate::highlight::HighlightColorResolver;
|
||||||
use crate::tests::prelude::*;
|
use crate::tests::prelude::*;
|
||||||
|
use crate::text_face::UnderlineStyle;
|
||||||
use crate::wchar::prelude::*;
|
use crate::wchar::prelude::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
env::EnvStack,
|
env::EnvStack,
|
||||||
@@ -696,8 +697,9 @@ fn test_trailing_spaces_after_command() {
|
|||||||
// Check that 'echo' is underlined
|
// Check that 'echo' is underlined
|
||||||
for i in 0..4 {
|
for i in 0..4 {
|
||||||
let face = resolver.resolve_spec(&colors[i], vars);
|
let face = resolver.resolve_spec(&colors[i], vars);
|
||||||
assert!(
|
assert_eq!(
|
||||||
face.style.is_underline(),
|
face.style.underline_style(),
|
||||||
|
Some(UnderlineStyle::Single),
|
||||||
"Character at position {} of 'echo' should be underlined",
|
"Character at position {} of 'echo' should be underlined",
|
||||||
i
|
i
|
||||||
);
|
);
|
||||||
@@ -706,8 +708,9 @@ fn test_trailing_spaces_after_command() {
|
|||||||
// Check that trailing spaces are NOT underlined
|
// Check that trailing spaces are NOT underlined
|
||||||
for i in 4..text.len() {
|
for i in 4..text.len() {
|
||||||
let face = resolver.resolve_spec(&colors[i], vars);
|
let face = resolver.resolve_spec(&colors[i], vars);
|
||||||
assert!(
|
assert_eq!(
|
||||||
!face.style.is_underline(),
|
face.style.underline_style(),
|
||||||
|
None,
|
||||||
"Trailing space at position {} should NOT be underlined",
|
"Trailing space at position {} should NOT be underlined",
|
||||||
i
|
i
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
use crate::future_feature_flags::{self, FeatureFlag};
|
use crate::future_feature_flags::{self, FeatureFlag};
|
||||||
use crate::global_safety::RelaxedAtomicBool;
|
use crate::global_safety::RelaxedAtomicBool;
|
||||||
use crate::screen::{is_dumb, only_grayscale};
|
use crate::screen::{is_dumb, only_grayscale};
|
||||||
use crate::text_face::{TextFace, TextStyling};
|
use crate::text_face::{TextFace, TextStyling, UnderlineStyle};
|
||||||
use crate::threads::MainThread;
|
use crate::threads::MainThread;
|
||||||
use crate::wchar::prelude::*;
|
use crate::wchar::prelude::*;
|
||||||
use crate::FLOGF;
|
use crate::FLOGF;
|
||||||
@@ -53,6 +53,7 @@ pub(crate) enum TerminalCommand<'a> {
|
|||||||
EnterStandoutMode,
|
EnterStandoutMode,
|
||||||
ExitItalicsMode,
|
ExitItalicsMode,
|
||||||
ExitUnderlineMode,
|
ExitUnderlineMode,
|
||||||
|
EnterCurlyUnderlineMode,
|
||||||
|
|
||||||
// Screen clearing
|
// Screen clearing
|
||||||
ClearScreen,
|
ClearScreen,
|
||||||
@@ -148,6 +149,7 @@ fn write(out: &mut impl Output, sequence: &'static [u8]) -> bool {
|
|||||||
EnterStandoutMode => ti(self, b"\x1b[7m", |t| &t.enter_standout_mode),
|
EnterStandoutMode => ti(self, b"\x1b[7m", |t| &t.enter_standout_mode),
|
||||||
ExitItalicsMode => ti(self, b"\x1b[23m", |t| &t.exit_italics_mode),
|
ExitItalicsMode => ti(self, b"\x1b[23m", |t| &t.exit_italics_mode),
|
||||||
ExitUnderlineMode => ti(self, b"\x1b[24m", |t| &t.exit_underline_mode),
|
ExitUnderlineMode => ti(self, b"\x1b[24m", |t| &t.exit_underline_mode),
|
||||||
|
EnterCurlyUnderlineMode => write(self, b"\x1b[4:3m"),
|
||||||
ClearScreen => ti(self, b"\x1b[H\x1b[2J", |term| &term.clear_screen),
|
ClearScreen => ti(self, b"\x1b[H\x1b[2J", |term| &term.clear_screen),
|
||||||
ClearToEndOfLine => ti(self, b"\x1b[K", |term| &term.clr_eol),
|
ClearToEndOfLine => ti(self, b"\x1b[K", |term| &term.clr_eol),
|
||||||
ClearToEndOfScreen => ti(self, b"\x1b[J", |term| &term.clr_eos),
|
ClearToEndOfScreen => ti(self, b"\x1b[J", |term| &term.clr_eos),
|
||||||
@@ -497,18 +499,19 @@ pub(crate) fn set_text_face(&mut self, face: TextFace) {
|
|||||||
let mut last_bg_set = false;
|
let mut last_bg_set = false;
|
||||||
|
|
||||||
use TerminalCommand::{
|
use TerminalCommand::{
|
||||||
EnterBoldMode, EnterDimMode, EnterItalicsMode, EnterReverseMode, EnterStandoutMode,
|
EnterBoldMode, EnterCurlyUnderlineMode, EnterDimMode, EnterItalicsMode,
|
||||||
EnterUnderlineMode, ExitAttributeMode, ExitItalicsMode, ExitUnderlineMode,
|
EnterReverseMode, EnterStandoutMode, EnterUnderlineMode, ExitAttributeMode,
|
||||||
|
ExitItalicsMode, ExitUnderlineMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Removes all styles that are individually resettable.
|
// Removes all styles that are individually resettable.
|
||||||
let non_resettable = |mut style: TextStyling| {
|
let non_resettable = |mut style: TextStyling| {
|
||||||
style.italics = false;
|
style.italics = false;
|
||||||
style.underline = false;
|
style.underline_style = None;
|
||||||
style
|
style
|
||||||
};
|
};
|
||||||
let non_resettable_attributes_to_unset =
|
let non_resettable_attributes_to_unset =
|
||||||
non_resettable(self.last.style).difference(non_resettable(style));
|
non_resettable(self.last.style).difference_prefer_empty(non_resettable(style));
|
||||||
if !non_resettable_attributes_to_unset.is_empty() {
|
if !non_resettable_attributes_to_unset.is_empty() {
|
||||||
// Only way to exit non-resettable ones is a reset of all attributes.
|
// Only way to exit non-resettable ones is a reset of all attributes.
|
||||||
self.reset_text_face();
|
self.reset_text_face();
|
||||||
@@ -577,11 +580,22 @@ pub(crate) fn set_text_face(&mut self, face: TextFace) {
|
|||||||
self.last.style.bold = true;
|
self.last.style.bold = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let was_underline = self.last.style.is_underline();
|
if style.underline_style != self.last.style.underline_style {
|
||||||
if !style.is_underline() && was_underline && self.write_command(ExitUnderlineMode) {
|
match style.underline_style {
|
||||||
self.last.style.underline = false;
|
None => {
|
||||||
} else if style.is_underline() && !was_underline && self.write_command(EnterUnderlineMode) {
|
if self.write_command(ExitUnderlineMode) {
|
||||||
self.last.style.underline = true;
|
self.last.style.underline_style = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(underline) => {
|
||||||
|
if self.write_command(match underline {
|
||||||
|
UnderlineStyle::Single => EnterUnderlineMode,
|
||||||
|
UnderlineStyle::Curly => EnterCurlyUnderlineMode,
|
||||||
|
}) {
|
||||||
|
self.last.style.underline_style = Some(underline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let was_italics = self.last.style.is_italics();
|
let was_italics = self.last.style.is_italics();
|
||||||
|
|||||||
@@ -3,10 +3,34 @@
|
|||||||
use crate::wchar::prelude::*;
|
use crate::wchar::prelude::*;
|
||||||
use crate::wgetopt::{wopt, ArgType, WGetopter, WOption};
|
use crate::wgetopt::{wopt, ArgType, WGetopter, WOption};
|
||||||
|
|
||||||
|
trait StyleSet {
|
||||||
|
fn union_prefer_right(self, other: Self) -> Self;
|
||||||
|
fn difference_prefer_empty(self, other: Self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub(crate) enum UnderlineStyle {
|
||||||
|
Single,
|
||||||
|
Curly,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StyleSet for Option<UnderlineStyle> {
|
||||||
|
fn union_prefer_right(self, other: Self) -> Self {
|
||||||
|
other.or(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn difference_prefer_empty(self, other: Self) -> Self {
|
||||||
|
if other.is_some() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub(crate) struct TextStyling {
|
pub(crate) struct TextStyling {
|
||||||
pub(crate) bold: bool,
|
pub(crate) bold: bool,
|
||||||
pub(crate) underline: bool,
|
pub(crate) underline_style: Option<UnderlineStyle>,
|
||||||
pub(crate) italics: bool,
|
pub(crate) italics: bool,
|
||||||
pub(crate) dim: bool,
|
pub(crate) dim: bool,
|
||||||
pub(crate) reverse: bool,
|
pub(crate) reverse: bool,
|
||||||
@@ -16,7 +40,7 @@ impl TextStyling {
|
|||||||
pub(crate) const fn default() -> Self {
|
pub(crate) const fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
bold: false,
|
bold: false,
|
||||||
underline: false,
|
underline_style: None,
|
||||||
italics: false,
|
italics: false,
|
||||||
dim: false,
|
dim: false,
|
||||||
reverse: false,
|
reverse: false,
|
||||||
@@ -25,19 +49,23 @@ pub(crate) const fn default() -> Self {
|
|||||||
pub(crate) fn is_empty(&self) -> bool {
|
pub(crate) fn is_empty(&self) -> bool {
|
||||||
*self == Self::default()
|
*self == Self::default()
|
||||||
}
|
}
|
||||||
pub(crate) fn union(self, other: Self) -> Self {
|
pub(crate) fn union_prefer_right(self, other: Self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bold: self.is_bold() || other.is_bold(),
|
bold: self.is_bold() || other.is_bold(),
|
||||||
underline: self.is_underline() || other.is_underline(),
|
underline_style: self
|
||||||
|
.underline_style
|
||||||
|
.union_prefer_right(other.underline_style),
|
||||||
italics: self.is_italics() || other.is_italics(),
|
italics: self.is_italics() || other.is_italics(),
|
||||||
dim: self.is_dim() || other.is_dim(),
|
dim: self.is_dim() || other.is_dim(),
|
||||||
reverse: self.is_reverse() || other.is_reverse(),
|
reverse: self.is_reverse() || other.is_reverse(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn difference(self, other: Self) -> Self {
|
pub(crate) fn difference_prefer_empty(self, other: Self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bold: self.is_bold() && !other.is_bold(),
|
bold: self.is_bold() && !other.is_bold(),
|
||||||
underline: self.is_underline() && !other.is_underline(),
|
underline_style: self
|
||||||
|
.underline_style
|
||||||
|
.difference_prefer_empty(other.underline_style),
|
||||||
italics: self.is_italics() && !other.is_italics(),
|
italics: self.is_italics() && !other.is_italics(),
|
||||||
dim: self.is_dim() && !other.is_dim(),
|
dim: self.is_dim() && !other.is_dim(),
|
||||||
reverse: self.is_reverse() && !other.is_reverse(),
|
reverse: self.is_reverse() && !other.is_reverse(),
|
||||||
@@ -49,14 +77,14 @@ pub const fn is_bold(self) -> bool {
|
|||||||
self.bold
|
self.bold
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the text face is underlined.
|
#[cfg(test)]
|
||||||
pub const fn is_underline(self) -> bool {
|
pub const fn underline_style(self) -> Option<UnderlineStyle> {
|
||||||
self.underline
|
self.underline_style
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set whether the text face is underline.
|
/// Set the given underline style.
|
||||||
pub fn inject_underline(&mut self, underline: bool) {
|
pub fn inject_underline(&mut self, underline: UnderlineStyle) {
|
||||||
self.underline = underline;
|
self.underline_style = Some(underline);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the text face is italics.
|
/// Returns whether the text face is italics.
|
||||||
@@ -116,6 +144,7 @@ pub(crate) fn parse_text_face(arguments: &[WString]) -> SpecifiedTextFace {
|
|||||||
TextFaceArgsAndOptionsResult::Ok(parsed_text_faces) => parsed_text_faces,
|
TextFaceArgsAndOptionsResult::Ok(parsed_text_faces) => parsed_text_faces,
|
||||||
TextFaceArgsAndOptionsResult::PrintHelp
|
TextFaceArgsAndOptionsResult::PrintHelp
|
||||||
| TextFaceArgsAndOptionsResult::InvalidArgs
|
| TextFaceArgsAndOptionsResult::InvalidArgs
|
||||||
|
| TextFaceArgsAndOptionsResult::InvalidUnderlineStyle(_)
|
||||||
| TextFaceArgsAndOptionsResult::UnknownOption(_) => unreachable!(),
|
| TextFaceArgsAndOptionsResult::UnknownOption(_) => unreachable!(),
|
||||||
};
|
};
|
||||||
assert!(!print_color_mode);
|
assert!(!print_color_mode);
|
||||||
@@ -142,6 +171,7 @@ pub(crate) enum TextFaceArgsAndOptionsResult<'a> {
|
|||||||
Ok(TextFaceArgsAndOptions<'a>),
|
Ok(TextFaceArgsAndOptions<'a>),
|
||||||
PrintHelp,
|
PrintHelp,
|
||||||
InvalidArgs,
|
InvalidArgs,
|
||||||
|
InvalidUnderlineStyle(&'a wstr),
|
||||||
UnknownOption(usize),
|
UnknownOption(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,12 +180,12 @@ pub(crate) fn parse_text_face_and_options<'a>(
|
|||||||
is_builtin: bool,
|
is_builtin: bool,
|
||||||
) -> TextFaceArgsAndOptionsResult<'a> {
|
) -> TextFaceArgsAndOptionsResult<'a> {
|
||||||
let builtin_extra_args = if is_builtin { 0 } else { "hc".len() };
|
let builtin_extra_args = if is_builtin { 0 } else { "hc".len() };
|
||||||
let short_options = L!(":b:oidruch");
|
let short_options = L!(":b:oidru::ch");
|
||||||
let short_options = &short_options[..short_options.len() - builtin_extra_args];
|
let short_options = &short_options[..short_options.len() - builtin_extra_args];
|
||||||
let long_options: &[WOption] = &[
|
let long_options: &[WOption] = &[
|
||||||
wopt(L!("background"), ArgType::RequiredArgument, 'b'),
|
wopt(L!("background"), ArgType::RequiredArgument, 'b'),
|
||||||
wopt(L!("bold"), ArgType::NoArgument, 'o'),
|
wopt(L!("bold"), ArgType::NoArgument, 'o'),
|
||||||
wopt(L!("underline"), ArgType::NoArgument, 'u'),
|
wopt(L!("underline"), ArgType::OptionalArgument, 'u'),
|
||||||
wopt(L!("italics"), ArgType::NoArgument, 'i'),
|
wopt(L!("italics"), ArgType::NoArgument, 'i'),
|
||||||
wopt(L!("dim"), ArgType::NoArgument, 'd'),
|
wopt(L!("dim"), ArgType::NoArgument, 'd'),
|
||||||
wopt(L!("reverse"), ArgType::NoArgument, 'r'),
|
wopt(L!("reverse"), ArgType::NoArgument, 'r'),
|
||||||
@@ -184,7 +214,16 @@ pub(crate) fn parse_text_face_and_options<'a>(
|
|||||||
'i' => style.italics = true,
|
'i' => style.italics = true,
|
||||||
'd' => style.dim = true,
|
'd' => style.dim = true,
|
||||||
'r' => style.reverse = true,
|
'r' => style.reverse = true,
|
||||||
'u' => style.underline = true,
|
'u' => {
|
||||||
|
let arg = w.woptarg.unwrap_or(L!("single"));
|
||||||
|
if arg == "single" {
|
||||||
|
style.underline_style = Some(UnderlineStyle::Single);
|
||||||
|
} else if arg == "curly" {
|
||||||
|
style.underline_style = Some(UnderlineStyle::Curly);
|
||||||
|
} else if is_builtin {
|
||||||
|
return TextFaceArgsAndOptionsResult::InvalidUnderlineStyle(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
'c' => print_color_mode = true,
|
'c' => print_color_mode = true,
|
||||||
':' => {
|
':' => {
|
||||||
if is_builtin {
|
if is_builtin {
|
||||||
|
|||||||
@@ -17,3 +17,14 @@ string escape (set_color --bold red --background=normal)
|
|||||||
# CHECK: \e\[m
|
# CHECK: \e\[m
|
||||||
string escape (set_color --bold red --background=blue)
|
string escape (set_color --bold red --background=blue)
|
||||||
# CHECK: \e\[1m\e\[31m\e\[44m
|
# CHECK: \e\[1m\e\[31m\e\[44m
|
||||||
|
|
||||||
|
string escape (set_color --underline=curly)
|
||||||
|
# CHECK: \e\[4:3m
|
||||||
|
string escape (set_color -ucurly)
|
||||||
|
# CHECK: \e\[4:3m
|
||||||
|
string escape (set_color -u)
|
||||||
|
# CHECK: \e\[4m
|
||||||
|
set_color --underline=asdf
|
||||||
|
# CHECKERR: set_color: invalid underline style: asdf
|
||||||
|
set_color -ushort
|
||||||
|
# CHECKERR: set_color: invalid underline style: short
|
||||||
|
|||||||
Reference in New Issue
Block a user