diff options
Diffstat (limited to 'vendor/nu-ansi-term/src/difference.rs')
-rw-r--r-- | vendor/nu-ansi-term/src/difference.rs | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/vendor/nu-ansi-term/src/difference.rs b/vendor/nu-ansi-term/src/difference.rs new file mode 100644 index 000000000..beee8ea25 --- /dev/null +++ b/vendor/nu-ansi-term/src/difference.rs @@ -0,0 +1,174 @@ +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); +} |