forked from extern/nushell
383e874166
* Fix a bunch of future clippy warnings * Fix a bunch of future clippy warnings
175 lines
5.8 KiB
Rust
175 lines
5.8 KiB
Rust
use super::Style;
|
|
|
|
/// When printing out one colored string followed by another, use one of
|
|
/// these rules to figure out which *extra* control codes need to be sent.
|
|
#[derive(PartialEq, Clone, Copy, Debug)]
|
|
pub enum Difference {
|
|
/// Print out the control codes specified by this style to end up looking
|
|
/// like the second string's styles.
|
|
ExtraStyles(Style),
|
|
|
|
/// Converting between these two is impossible, so just send a reset
|
|
/// command and then the second string's styles.
|
|
Reset,
|
|
|
|
/// The before style is exactly the same as the after style, so no further
|
|
/// control codes need to be printed.
|
|
Empty,
|
|
}
|
|
|
|
impl Difference {
|
|
/// Compute the 'style difference' required to turn an existing style into
|
|
/// the given, second style.
|
|
///
|
|
/// For example, to turn green text into green bold text, it's redundant
|
|
/// to write a reset command then a second green+bold command, instead of
|
|
/// just writing one bold command. This method should see that both styles
|
|
/// use the foreground color green, and reduce it to a single command.
|
|
///
|
|
/// This method returns an enum value because it's not actually always
|
|
/// possible to turn one style into another: for example, text could be
|
|
/// made bold and underlined, but you can't remove the bold property
|
|
/// without also removing the underline property. So when this has to
|
|
/// happen, this function returns None, meaning that the entire set of
|
|
/// styles should be reset and begun again.
|
|
pub fn between(first: &Style, next: &Style) -> Difference {
|
|
use self::Difference::*;
|
|
|
|
// XXX(Havvy): This algorithm is kind of hard to replicate without
|
|
// having the Plain/Foreground enum variants, so I'm just leaving
|
|
// it commented out for now, and defaulting to Reset.
|
|
|
|
if first == next {
|
|
return Empty;
|
|
}
|
|
|
|
// Cannot un-bold, so must Reset.
|
|
if first.is_bold && !next.is_bold {
|
|
return Reset;
|
|
}
|
|
|
|
if first.is_dimmed && !next.is_dimmed {
|
|
return Reset;
|
|
}
|
|
|
|
if first.is_italic && !next.is_italic {
|
|
return Reset;
|
|
}
|
|
|
|
// Cannot un-underline, so must Reset.
|
|
if first.is_underline && !next.is_underline {
|
|
return Reset;
|
|
}
|
|
|
|
if first.is_blink && !next.is_blink {
|
|
return Reset;
|
|
}
|
|
|
|
if first.is_reverse && !next.is_reverse {
|
|
return Reset;
|
|
}
|
|
|
|
if first.is_hidden && !next.is_hidden {
|
|
return Reset;
|
|
}
|
|
|
|
if first.is_strikethrough && !next.is_strikethrough {
|
|
return Reset;
|
|
}
|
|
|
|
// Cannot go from foreground to no foreground, so must Reset.
|
|
if first.foreground.is_some() && next.foreground.is_none() {
|
|
return Reset;
|
|
}
|
|
|
|
// Cannot go from background to no background, so must Reset.
|
|
if first.background.is_some() && next.background.is_none() {
|
|
return Reset;
|
|
}
|
|
|
|
let mut extra_styles = Style::default();
|
|
|
|
if first.is_bold != next.is_bold {
|
|
extra_styles.is_bold = true;
|
|
}
|
|
|
|
if first.is_dimmed != next.is_dimmed {
|
|
extra_styles.is_dimmed = true;
|
|
}
|
|
|
|
if first.is_italic != next.is_italic {
|
|
extra_styles.is_italic = true;
|
|
}
|
|
|
|
if first.is_underline != next.is_underline {
|
|
extra_styles.is_underline = true;
|
|
}
|
|
|
|
if first.is_blink != next.is_blink {
|
|
extra_styles.is_blink = true;
|
|
}
|
|
|
|
if first.is_reverse != next.is_reverse {
|
|
extra_styles.is_reverse = true;
|
|
}
|
|
|
|
if first.is_hidden != next.is_hidden {
|
|
extra_styles.is_hidden = true;
|
|
}
|
|
|
|
if first.is_strikethrough != next.is_strikethrough {
|
|
extra_styles.is_strikethrough = true;
|
|
}
|
|
|
|
if first.foreground != next.foreground {
|
|
extra_styles.foreground = next.foreground;
|
|
}
|
|
|
|
if first.background != next.background {
|
|
extra_styles.background = next.background;
|
|
}
|
|
|
|
ExtraStyles(extra_styles)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::Difference::*;
|
|
use super::*;
|
|
use crate::style::Color::*;
|
|
use crate::style::Style;
|
|
|
|
fn style() -> Style {
|
|
Style::new()
|
|
}
|
|
|
|
macro_rules! test {
|
|
($name: ident: $first: expr; $next: expr => $result: expr) => {
|
|
#[test]
|
|
fn $name() {
|
|
assert_eq!($result, Difference::between(&$first, &$next));
|
|
}
|
|
};
|
|
}
|
|
|
|
test!(nothing: Green.normal(); Green.normal() => Empty);
|
|
test!(uppercase: Green.normal(); Green.bold() => ExtraStyles(style().bold()));
|
|
test!(lowercase: Green.bold(); Green.normal() => Reset);
|
|
test!(nothing2: Green.bold(); Green.bold() => Empty);
|
|
|
|
test!(color_change: Red.normal(); Blue.normal() => ExtraStyles(Blue.normal()));
|
|
|
|
test!(addition_of_blink: style(); style().blink() => ExtraStyles(style().blink()));
|
|
test!(addition_of_dimmed: style(); style().dimmed() => ExtraStyles(style().dimmed()));
|
|
test!(addition_of_hidden: style(); style().hidden() => ExtraStyles(style().hidden()));
|
|
test!(addition_of_reverse: style(); style().reverse() => ExtraStyles(style().reverse()));
|
|
test!(addition_of_strikethrough: style(); style().strikethrough() => ExtraStyles(style().strikethrough()));
|
|
|
|
test!(removal_of_strikethrough: style().strikethrough(); style() => Reset);
|
|
test!(removal_of_reverse: style().reverse(); style() => Reset);
|
|
test!(removal_of_hidden: style().hidden(); style() => Reset);
|
|
test!(removal_of_dimmed: style().dimmed(); style() => Reset);
|
|
test!(removal_of_blink: style().blink(); style() => Reset);
|
|
}
|