1308 lines · 45.0 KB · Rust
RawDownload
// Copyright 2020 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. use std::borrow::BorrowMut;use std::collections::HashMap;use std::fmt;use std::io;use std::io::Error;use std::io::Write;use std::mem;use std::ops::Range;use std::sync::Arc; use crossterm::queue;use crossterm::style::Attribute;use crossterm::style::Color;use crossterm::style::SetAttribute;use crossterm::style::SetBackgroundColor;use crossterm::style::SetForegroundColor;use itertools::Itertools; // Lets the caller label strings and translates the labels to colorspub trait Formatter: Write {    /// Returns the backing `Write`. This is useful for writing data that is    /// already formatted, such as in the graphical log.    fn raw(&mut self) -> io::Result<Box<dyn Write + '_>>;     fn push_label(&mut self, label: &str) -> io::Result<()>;     fn pop_label(&mut self) -> io::Result<()>;} impl dyn Formatter + '_ {    pub fn labeled<S: AsRef<str>>(&mut self, label: S) -> LabeledWriter<&mut Self, S> {        LabeledWriter {            formatter: self,            label,        }    }     pub fn with_label<E: From<io::Error>>(        &mut self,        label: &str,        write_inner: impl FnOnce(&mut dyn Formatter) -> Result<(), E>,    ) -> Result<(), E> {        self.push_label(label)?;        // Call `pop_label()` whether or not `write_inner()` fails, but don't let        // its error replace the one from `write_inner()`.        write_inner(self).and(self.pop_label().map_err(Into::into))    }} /// `Formatter` wrapper to write a labeled message with `write!()` or/// `writeln!()`.pub struct LabeledWriter<T, S> {    formatter: T,    label: S,} impl<T, S> LabeledWriter<T, S> {    pub fn new(formatter: T, label: S) -> Self {        LabeledWriter { formatter, label }    }     /// Turns into writer that prints labeled message with the `heading`.    pub fn with_heading<H>(self, heading: H) -> HeadingLabeledWriter<T, S, H> {        HeadingLabeledWriter::new(self, heading)    }} impl<'a, T, S> LabeledWriter<T, S>where    T: BorrowMut<dyn Formatter + 'a>,    S: AsRef<str>,{    pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {        self.with_labeled(|formatter| formatter.write_fmt(args))    }     fn with_labeled(        &mut self,        write_inner: impl FnOnce(&mut dyn Formatter) -> io::Result<()>,    ) -> io::Result<()> {        self.formatter            .borrow_mut()            .with_label(self.label.as_ref(), write_inner)    }} /// Like `LabeledWriter`, but also prints the `heading` once.////// The `heading` will be printed within the first `write!()` or `writeln!()`/// invocation, which is handy because `io::Error` can be handled there.pub struct HeadingLabeledWriter<T, S, H> {    writer: LabeledWriter<T, S>,    heading: Option<H>,} impl<T, S, H> HeadingLabeledWriter<T, S, H> {    pub fn new(writer: LabeledWriter<T, S>, heading: H) -> Self {        HeadingLabeledWriter {            writer,            heading: Some(heading),        }    }} impl<'a, T, S, H> HeadingLabeledWriter<T, S, H>where    T: BorrowMut<dyn Formatter + 'a>,    S: AsRef<str>,    H: fmt::Display,{    pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {        self.writer.with_labeled(|formatter| {            if let Some(heading) = self.heading.take() {                write!(formatter.labeled("heading"), "{heading}")?;            }            formatter.write_fmt(args)        })    }} type Rules = Vec<(Vec<String>, Style)>; /// Creates `Formatter` instances with preconfigured parameters.#[derive(Clone, Debug)]pub struct FormatterFactory {    kind: FormatterFactoryKind,} #[derive(Clone, Debug)]enum FormatterFactoryKind {    PlainText,    Sanitized,    Color { rules: Arc<Rules>, debug: bool },} impl FormatterFactory {    pub fn plain_text() -> Self {        let kind = FormatterFactoryKind::PlainText;        FormatterFactory { kind }    }     pub fn sanitized() -> Self {        let kind = FormatterFactoryKind::Sanitized;        FormatterFactory { kind }    }     pub fn color(config: &config::Config, debug: bool) -> Result<Self, config::ConfigError> {        let rules = Arc::new(rules_from_config(config)?);        let kind = FormatterFactoryKind::Color { rules, debug };        Ok(FormatterFactory { kind })    }     pub fn new_formatter<'output, W: Write + 'output>(        &self,        output: W,    ) -> Box<dyn Formatter + 'output> {        match &self.kind {            FormatterFactoryKind::PlainText => Box::new(PlainTextFormatter::new(output)),            FormatterFactoryKind::Sanitized => Box::new(SanitizingFormatter::new(output)),            FormatterFactoryKind::Color { rules, debug } => {                Box::new(ColorFormatter::new(output, rules.clone(), *debug))            }        }    }     pub fn is_color(&self) -> bool {        matches!(self.kind, FormatterFactoryKind::Color { .. })    }} pub struct PlainTextFormatter<W> {    output: W,} impl<W> PlainTextFormatter<W> {    pub fn new(output: W) -> PlainTextFormatter<W> {        Self { output }    }} impl<W: Write> Write for PlainTextFormatter<W> {    fn write(&mut self, data: &[u8]) -> Result<usize, Error> {        self.output.write(data)    }     fn flush(&mut self) -> Result<(), Error> {        self.output.flush()    }} impl<W: Write> Formatter for PlainTextFormatter<W> {    fn raw(&mut self) -> io::Result<Box<dyn Write + '_>> {        Ok(Box::new(self.output.by_ref()))    }     fn push_label(&mut self, _label: &str) -> io::Result<()> {        Ok(())    }     fn pop_label(&mut self) -> io::Result<()> {        Ok(())    }} pub struct SanitizingFormatter<W> {    output: W,} impl<W> SanitizingFormatter<W> {    pub fn new(output: W) -> SanitizingFormatter<W> {        Self { output }    }} impl<W: Write> Write for SanitizingFormatter<W> {    fn write(&mut self, data: &[u8]) -> Result<usize, Error> {        write_sanitized(&mut self.output, data)?;        Ok(data.len())    }     fn flush(&mut self) -> Result<(), Error> {        self.output.flush()    }} impl<W: Write> Formatter for SanitizingFormatter<W> {    fn raw(&mut self) -> io::Result<Box<dyn Write + '_>> {        Ok(Box::new(self.output.by_ref()))    }     fn push_label(&mut self, _label: &str) -> io::Result<()> {        Ok(())    }     fn pop_label(&mut self) -> io::Result<()> {        Ok(())    }} #[derive(Clone, Debug, Default, PartialEq, Eq)]pub struct Style {    pub fg_color: Option<Color>,    pub bg_color: Option<Color>,    pub bold: Option<bool>,    pub underlined: Option<bool>,} impl Style {    fn merge(&mut self, other: &Style) {        self.fg_color = other.fg_color.or(self.fg_color);        self.bg_color = other.bg_color.or(self.bg_color);        self.bold = other.bold.or(self.bold);        self.underlined = other.underlined.or(self.underlined);    }} #[derive(Clone, Debug)]pub struct ColorFormatter<W: Write> {    output: W,    rules: Arc<Rules>,    /// The stack of currently applied labels. These determine the desired    /// style.    labels: Vec<String>,    cached_styles: HashMap<Vec<String>, Style>,    /// The style we last wrote to the output.    current_style: Style,    /// The debug string (space-separated labels) we last wrote to the output.    /// Initialize to None to turn debug strings off.    current_debug: Option<String>,} impl<W: Write> ColorFormatter<W> {    pub fn new(output: W, rules: Arc<Rules>, debug: bool) -> ColorFormatter<W> {        ColorFormatter {            output,            rules,            labels: vec![],            cached_styles: HashMap::new(),            current_style: Style::default(),            current_debug: debug.then(String::new),        }    }     pub fn for_config(        output: W,        config: &config::Config,        debug: bool,    ) -> Result<Self, config::ConfigError> {        let rules = rules_from_config(config)?;        Ok(Self::new(output, Arc::new(rules), debug))    }     fn requested_style(&mut self) -> Style {        if let Some(cached) = self.cached_styles.get(&self.labels) {            cached.clone()        } else {            // We use the reverse list of matched indices as a measure of how well the rule            // matches the actual labels. For example, for rule "a d" and the actual labels            // "a b c d", we'll get [3,0]. We compare them by Rust's default Vec comparison.            // That means "a d" will trump both rule "d" (priority [3]) and rule            // "a b c" (priority [2,1,0]).            let mut matched_styles = vec![];            for (labels, style) in self.rules.as_ref() {                let mut labels_iter = self.labels.iter().enumerate();                // The indexes in the current label stack that match the required label.                let mut matched_indices = vec![];                for required_label in labels {                    for (label_index, label) in &mut labels_iter {                        if label == required_label {                            matched_indices.push(label_index);                            break;                        }                    }                }                if matched_indices.len() == labels.len() {                    matched_indices.reverse();                    matched_styles.push((style, matched_indices));                }            }            matched_styles.sort_by_key(|(_, indices)| indices.clone());             let mut style = Style::default();            for (matched_style, _) in matched_styles {                style.merge(matched_style);            }            self.cached_styles                .insert(self.labels.clone(), style.clone());            style        }    }     fn write_new_style(&mut self) -> io::Result<()> {        let new_debug = match &self.current_debug {            Some(current) => {                let joined = self.labels.join(" ");                if joined == *current {                    None                } else {                    if !current.is_empty() {                        write!(self.output, ">>")?;                    }                    Some(joined)                }            }            None => None,        };        let new_style = self.requested_style();        if new_style != self.current_style {            if new_style.bold != self.current_style.bold {                if new_style.bold.unwrap_or_default() {                    queue!(self.output, SetAttribute(Attribute::Bold))?;                } else {                    // NoBold results in double underlining on some terminals, so we use reset                    // instead. However, that resets other attributes as well, so we reset                    // our record of the current style so we re-apply the other attributes                    // below.                    queue!(self.output, SetAttribute(Attribute::Reset))?;                    self.current_style = Style::default();                }            }            if new_style.underlined != self.current_style.underlined {                if new_style.underlined.unwrap_or_default() {                    queue!(self.output, SetAttribute(Attribute::Underlined))?;                } else {                    queue!(self.output, SetAttribute(Attribute::NoUnderline))?;                }            }            if new_style.fg_color != self.current_style.fg_color {                queue!(                    self.output,                    SetForegroundColor(new_style.fg_color.unwrap_or(Color::Reset))                )?;            }            if new_style.bg_color != self.current_style.bg_color {                queue!(                    self.output,                    SetBackgroundColor(new_style.bg_color.unwrap_or(Color::Reset))                )?;            }            self.current_style = new_style;        }        if let Some(d) = new_debug {            if !d.is_empty() {                write!(self.output, "<<{d}::")?;            }            self.current_debug = Some(d);        }        Ok(())    }} fn rules_from_config(config: &config::Config) -> Result<Rules, config::ConfigError> {    let mut result = vec![];    let table = config.get_table("colors")?;    for (key, value) in table {        let labels = key            .split_whitespace()            .map(ToString::to_string)            .collect_vec();        match value.kind {            config::ValueKind::String(color_name) => {                let style = Style {                    fg_color: Some(color_for_name_or_hex(&color_name)?),                    bg_color: None,                    bold: None,                    underlined: None,                };                result.push((labels, style));            }            config::ValueKind::Table(style_table) => {                let mut style = Style::default();                if let Some(value) = style_table.get("fg") {                    if let config::ValueKind::String(color_name) = &value.kind {                        style.fg_color = Some(color_for_name_or_hex(color_name)?);                    }                }                if let Some(value) = style_table.get("bg") {                    if let config::ValueKind::String(color_name) = &value.kind {                        style.bg_color = Some(color_for_name_or_hex(color_name)?);                    }                }                if let Some(value) = style_table.get("bold") {                    if let config::ValueKind::Boolean(value) = &value.kind {                        style.bold = Some(*value);                    }                }                if let Some(value) = style_table.get("underline") {                    if let config::ValueKind::Boolean(value) = &value.kind {                        style.underlined = Some(*value);                    }                }                result.push((labels, style));            }            _ => {}        }    }    Ok(result)} fn color_for_name_or_hex(name_or_hex: &str) -> Result<Color, config::ConfigError> {    match name_or_hex {        "default" => Ok(Color::Reset),        "black" => Ok(Color::Black),        "red" => Ok(Color::DarkRed),        "green" => Ok(Color::DarkGreen),        "yellow" => Ok(Color::DarkYellow),        "blue" => Ok(Color::DarkBlue),        "magenta" => Ok(Color::DarkMagenta),        "cyan" => Ok(Color::DarkCyan),        "white" => Ok(Color::Grey),        "bright black" => Ok(Color::DarkGrey),        "bright red" => Ok(Color::Red),        "bright green" => Ok(Color::Green),        "bright yellow" => Ok(Color::Yellow),        "bright blue" => Ok(Color::Blue),        "bright magenta" => Ok(Color::Magenta),        "bright cyan" => Ok(Color::Cyan),        "bright white" => Ok(Color::White),        _ => color_for_hex(name_or_hex)            .ok_or_else(|| config::ConfigError::Message(format!("invalid color: {name_or_hex}"))),    }} fn color_for_hex(color: &str) -> Option<Color> {    if color.len() == 7        && color.starts_with('#')        && color[1..].chars().all(|c| c.is_ascii_hexdigit())    {        let r = u8::from_str_radix(&color[1..3], 16);        let g = u8::from_str_radix(&color[3..5], 16);        let b = u8::from_str_radix(&color[5..7], 16);        match (r, g, b) {            (Ok(r), Ok(g), Ok(b)) => Some(Color::Rgb { r, g, b }),            _ => None,        }    } else {        None    }} impl<W: Write> Write for ColorFormatter<W> {    fn write(&mut self, data: &[u8]) -> Result<usize, Error> {        /*        We clear the current style at the end of each line, and then we re-apply the style        after the newline. There are several reasons for this: 

Lines 1–500 of 1308.