use std::process; use bitflags::bitflags; use crate::color; use crate::config::delta_unreachable; use crate::style::{DecorationStyle, Style}; impl Style { /// Construct Style from style and decoration-style strings supplied on command line, together /// with defaults. A style string is a space-separated string containing 0, 1, or 2 colors /// (foreground and then background) and an arbitrary number of style attributes. See `delta /// --help` for more precise spec. pub fn from_str( style_string: &str, default: Option, decoration_style_string: Option<&str>, true_color: bool, is_emph: bool, ) -> Self { let (ansi_term_style, is_omitted, is_raw, is_syntax_highlighted) = parse_ansi_term_style(&style_string, default, true_color); let decoration_style = DecorationStyle::from_str(decoration_style_string.unwrap_or(""), true_color); Self { ansi_term_style, is_emph, is_omitted, is_raw, is_syntax_highlighted, decoration_style, } } pub fn from_git_str(git_style_string: &str) -> Self { Self::from_str(git_style_string, None, None, true, false) } /// Construct Style but interpreting 'ul', 'box', etc as applying to the decoration style. pub fn from_str_with_handling_of_special_decoration_attributes( style_string: &str, default: Option, decoration_style_string: Option<&str>, true_color: bool, is_emph: bool, ) -> Self { let (special_attributes_from_style_string, style_string) = extract_special_decoration_attributes_from_non_decoration_style_string(style_string); let mut style = Style::from_str( &style_string, default, decoration_style_string.as_deref(), true_color, is_emph, ); // TODO: box in this context resulted in box-with-underline for commit and file style.decoration_style = DecorationStyle::apply_special_decoration_attributes( &mut style, special_attributes_from_style_string, ); style } /// As from_str_with_handling_of_special_decoration_attributes but respecting an optional /// foreground color which has precedence when present. pub fn from_str_with_handling_of_special_decoration_attributes_and_respecting_deprecated_foreground_color_arg( style_string: &str, default: Option, decoration_style_string: Option<&str>, deprecated_foreground_color_arg: Option<&str>, true_color: bool, is_emph: bool, ) -> Self { let mut style = Self::from_str_with_handling_of_special_decoration_attributes( style_string, default, decoration_style_string, true_color, is_emph, ); if let Some(s) = deprecated_foreground_color_arg { // The deprecated --{commit,file,hunk}-color args functioned to set the decoration // foreground color. In the case of file, it set the text foreground color also. let foreground_from_deprecated_arg = parse_ansi_term_style(s, None, true_color).0.foreground; style.ansi_term_style.foreground = foreground_from_deprecated_arg; style.decoration_style = match style.decoration_style { DecorationStyle::Box(mut ansi_term_style) => { ansi_term_style.foreground = foreground_from_deprecated_arg; DecorationStyle::Box(ansi_term_style) } DecorationStyle::Underline(mut ansi_term_style) => { ansi_term_style.foreground = foreground_from_deprecated_arg; DecorationStyle::Underline(ansi_term_style) } DecorationStyle::Overline(mut ansi_term_style) => { ansi_term_style.foreground = foreground_from_deprecated_arg; DecorationStyle::Overline(ansi_term_style) } DecorationStyle::UnderOverline(mut ansi_term_style) => { ansi_term_style.foreground = foreground_from_deprecated_arg; DecorationStyle::UnderOverline(ansi_term_style) } DecorationStyle::BoxWithUnderline(mut ansi_term_style) => { ansi_term_style.foreground = foreground_from_deprecated_arg; DecorationStyle::BoxWithUnderline(ansi_term_style) } DecorationStyle::BoxWithOverline(mut ansi_term_style) => { ansi_term_style.foreground = foreground_from_deprecated_arg; DecorationStyle::BoxWithOverline(ansi_term_style) } DecorationStyle::BoxWithUnderOverline(mut ansi_term_style) => { ansi_term_style.foreground = foreground_from_deprecated_arg; DecorationStyle::BoxWithUnderOverline(ansi_term_style) } DecorationStyle::NoDecoration => style.decoration_style, }; } style } } bitflags! { struct DecorationAttributes: u8 { const EMPTY = 0b00000000; const BOX = 0b00000001; const OVERLINE = 0b00000010; const UNDERLINE = 0b00000100; } } impl DecorationStyle { pub fn from_str(style_string: &str, true_color: bool) -> Self { let (special_attributes, style_string) = extract_special_decoration_attributes(&style_string); let (style, is_omitted, is_raw, is_syntax_highlighted) = parse_ansi_term_style(&style_string, None, true_color); if is_raw { eprintln!("'raw' may not be used in a decoration style."); process::exit(1); }; if is_syntax_highlighted { eprintln!("'syntax' may not be used in a decoration style."); process::exit(1); }; #[allow(non_snake_case)] let (BOX, UL, OL, EMPTY) = ( DecorationAttributes::BOX, DecorationAttributes::UNDERLINE, DecorationAttributes::OVERLINE, DecorationAttributes::EMPTY, ); match special_attributes { bits if bits == EMPTY => DecorationStyle::NoDecoration, bits if bits == BOX => DecorationStyle::Box(style), bits if bits == UL => DecorationStyle::Underline(style), bits if bits == OL => DecorationStyle::Overline(style), bits if bits == UL | OL => DecorationStyle::UnderOverline(style), bits if bits == BOX | UL => DecorationStyle::BoxWithUnderline(style), bits if bits == BOX | OL => DecorationStyle::BoxWithOverline(style), bits if bits == BOX | UL | OL => DecorationStyle::BoxWithUnderOverline(style), _ if is_omitted => DecorationStyle::NoDecoration, _ => delta_unreachable("Unreachable code path reached in parse_decoration_style."), } } fn apply_special_decoration_attributes( style: &mut Style, special_attributes: DecorationAttributes, ) -> DecorationStyle { let ansi_term_style = match style.decoration_style { DecorationStyle::Box(ansi_term_style) => ansi_term_style, DecorationStyle::Underline(ansi_term_style) => ansi_term_style, DecorationStyle::Overline(ansi_term_style) => ansi_term_style, DecorationStyle::UnderOverline(ansi_term_style) => ansi_term_style, DecorationStyle::BoxWithUnderline(ansi_term_style) => ansi_term_style, DecorationStyle::BoxWithOverline(ansi_term_style) => ansi_term_style, DecorationStyle::BoxWithUnderOverline(ansi_term_style) => ansi_term_style, DecorationStyle::NoDecoration => ansi_term::Style::new(), }; #[allow(non_snake_case)] let (BOX, UL, OL, EMPTY) = ( DecorationAttributes::BOX, DecorationAttributes::UNDERLINE, DecorationAttributes::OVERLINE, DecorationAttributes::EMPTY, ); match special_attributes { bits if bits == EMPTY => style.decoration_style, bits if bits == BOX => DecorationStyle::Box(ansi_term_style), bits if bits == UL => DecorationStyle::Underline(ansi_term_style), bits if bits == OL => DecorationStyle::Overline(ansi_term_style), bits if bits == UL | OL => DecorationStyle::UnderOverline(ansi_term_style), bits if bits == BOX | UL => DecorationStyle::BoxWithUnderline(ansi_term_style), bits if bits == BOX | OL => DecorationStyle::BoxWithOverline(ansi_term_style), bits if bits == BOX | UL | OL => DecorationStyle::BoxWithUnderOverline(ansi_term_style), _ => DecorationStyle::NoDecoration, } } } fn parse_ansi_term_style( s: &str, default: Option