]> git.proxmox.com Git - rustc.git/blobdiff - vendor/annotate-snippets-0.6.1/src/formatter/mod.rs
Update upstream source from tag 'upstream/1.52.1+dfsg1'
[rustc.git] / vendor / annotate-snippets-0.6.1 / src / formatter / mod.rs
diff --git a/vendor/annotate-snippets-0.6.1/src/formatter/mod.rs b/vendor/annotate-snippets-0.6.1/src/formatter/mod.rs
new file mode 100644 (file)
index 0000000..654b518
--- /dev/null
@@ -0,0 +1,367 @@
+//! DisplayListFormatter is a module handling the formatting of a
+//! `DisplayList` into a formatted string.
+//!
+//! Besides formatting into a string it also uses a `style::Stylesheet` to
+//! provide additional styling like colors and emphasis to the text.
+
+pub mod style;
+
+use self::style::{Style, StyleClass, Stylesheet};
+use crate::display_list::*;
+use std::cmp;
+
+use crate::stylesheets::no_color::NoColorStylesheet;
+#[cfg(feature = "ansi_term")]
+use crate::stylesheets::color::AnsiTermStylesheet;
+
+fn repeat_char(c: char, n: usize) -> String {
+    let mut s = String::with_capacity(c.len_utf8());
+    s.push(c);
+    s.repeat(n)
+}
+
+/// DisplayListFormatter' constructor accepts two arguments:
+///
+/// * `color` allows the formatter to optionally apply colors and emphasis
+/// using the `ansi_term` crate.
+/// * `anonymized_line_numbers` will replace line numbers in the left column with the text `LL`.
+///
+/// Example:
+///
+/// ```
+/// use annotate_snippets::formatter::DisplayListFormatter;
+/// use annotate_snippets::display_list::{DisplayList, DisplayLine, DisplaySourceLine};
+///
+/// let dlf = DisplayListFormatter::new(false, false); // Don't use colors, Don't anonymize line numbers
+///
+/// let dl = DisplayList {
+///     body: vec![
+///         DisplayLine::Source {
+///             lineno: Some(192),
+///             inline_marks: vec![],
+///             line: DisplaySourceLine::Content {
+///                 text: "Example line of text".into(),
+///                 range: (0, 21)
+///             }
+///         }
+///     ]
+/// };
+/// assert_eq!(dlf.format(&dl), "192 | Example line of text");
+/// ```
+pub struct DisplayListFormatter {
+    stylesheet: Box<dyn Stylesheet>,
+    anonymized_line_numbers: bool,
+}
+
+impl DisplayListFormatter {
+    const ANONYMIZED_LINE_NUM: &'static str = "LL";
+
+    /// Constructor for the struct.
+    ///
+    /// The argument `color` selects the stylesheet depending on the user preferences and
+    /// `ansi_term` crate availability.
+    ///
+    /// The argument `anonymized_line_numbers` will replace line numbers in the left column with
+    /// the text `LL`. This can be useful to enable when running UI tests, such as in the Rust
+    /// test suite.
+    pub fn new(color: bool, anonymized_line_numbers: bool) -> Self {
+        if color {
+            Self {
+                #[cfg(feature = "ansi_term")]
+                stylesheet: Box::new(AnsiTermStylesheet {}),
+                #[cfg(not(feature = "ansi_term"))]
+                stylesheet: Box::new(NoColorStylesheet {}),
+                anonymized_line_numbers,
+            }
+        } else {
+            Self {
+                stylesheet: Box::new(NoColorStylesheet {}),
+                anonymized_line_numbers,
+            }
+        }
+    }
+
+    /// Formats a `DisplayList` into a String.
+    pub fn format(&self, dl: &DisplayList) -> String {
+        let lineno_width = dl.body.iter().fold(0, |max, line| match line {
+            DisplayLine::Source {
+                lineno: Some(lineno),
+                ..
+            } => {
+                if self.anonymized_line_numbers {
+                    Self::ANONYMIZED_LINE_NUM.len()
+                } else {
+                    cmp::max(lineno.to_string().len(), max)
+                }
+            },
+            _ => max,
+        });
+        let inline_marks_width = dl.body.iter().fold(0, |max, line| match line {
+            DisplayLine::Source { inline_marks, .. } => cmp::max(inline_marks.len(), max),
+            _ => max,
+        });
+
+        dl.body
+            .iter()
+            .map(|line| self.format_line(line, lineno_width, inline_marks_width))
+            .collect::<Vec<String>>()
+            .join("\n")
+    }
+
+    fn format_annotation_type(&self, annotation_type: &DisplayAnnotationType) -> &'static str {
+        match annotation_type {
+            DisplayAnnotationType::Error => "error",
+            DisplayAnnotationType::Warning => "warning",
+            DisplayAnnotationType::Info => "info",
+            DisplayAnnotationType::Note => "note",
+            DisplayAnnotationType::Help => "help",
+            DisplayAnnotationType::None => "",
+        }
+    }
+
+    fn get_annotation_style(&self, annotation_type: &DisplayAnnotationType) -> Box<dyn Style> {
+        self.stylesheet.get_style(match annotation_type {
+            DisplayAnnotationType::Error => StyleClass::Error,
+            DisplayAnnotationType::Warning => StyleClass::Warning,
+            DisplayAnnotationType::Info => StyleClass::Info,
+            DisplayAnnotationType::Note => StyleClass::Note,
+            DisplayAnnotationType::Help => StyleClass::Help,
+            DisplayAnnotationType::None => StyleClass::None,
+        })
+    }
+
+    fn format_label(&self, label: &[DisplayTextFragment]) -> String {
+        let emphasis_style = self.stylesheet.get_style(StyleClass::Emphasis);
+        label
+            .iter()
+            .map(|fragment| match fragment.style {
+                DisplayTextStyle::Regular => fragment.content.clone(),
+                DisplayTextStyle::Emphasis => emphasis_style.paint(&fragment.content),
+            })
+            .collect::<Vec<String>>()
+            .join("")
+    }
+
+    fn format_annotation(
+        &self,
+        annotation: &Annotation,
+        continuation: bool,
+        in_source: bool,
+    ) -> String {
+        let color = self.get_annotation_style(&annotation.annotation_type);
+        let formatted_type = if let Some(ref id) = annotation.id {
+            format!(
+                "{}[{}]",
+                self.format_annotation_type(&annotation.annotation_type),
+                id
+            )
+        } else {
+            self.format_annotation_type(&annotation.annotation_type)
+                .to_string()
+        };
+        let label = self.format_label(&annotation.label);
+
+        let label_part = if label.is_empty() {
+            "".to_string()
+        } else if in_source {
+            color.paint(&format!(": {}", self.format_label(&annotation.label)))
+        } else {
+            format!(": {}", self.format_label(&annotation.label))
+        };
+        if continuation {
+            let indent = formatted_type.len() + 2;
+            return format!("{}{}", repeat_char(' ', indent), label);
+        }
+        if !formatted_type.is_empty() {
+            format!("{}{}", color.paint(&formatted_type), label_part)
+        } else {
+            label
+        }
+    }
+
+    fn format_source_line(&self, line: &DisplaySourceLine) -> Option<String> {
+        match line {
+            DisplaySourceLine::Empty => None,
+            DisplaySourceLine::Content { text, .. } => Some(format!(" {}", text)),
+            DisplaySourceLine::Annotation {
+                range,
+                annotation,
+                annotation_type,
+                annotation_part,
+            } => {
+                let indent_char = match annotation_part {
+                    DisplayAnnotationPart::Standalone => ' ',
+                    DisplayAnnotationPart::LabelContinuation => ' ',
+                    DisplayAnnotationPart::Consequitive => ' ',
+                    DisplayAnnotationPart::MultilineStart => '_',
+                    DisplayAnnotationPart::MultilineEnd => '_',
+                };
+                let mark = match annotation_type {
+                    DisplayAnnotationType::Error => '^',
+                    DisplayAnnotationType::Warning => '-',
+                    DisplayAnnotationType::Info => '-',
+                    DisplayAnnotationType::Note => '-',
+                    DisplayAnnotationType::Help => '-',
+                    DisplayAnnotationType::None => ' ',
+                };
+                let color = self.get_annotation_style(annotation_type);
+                let indent_length = match annotation_part {
+                    DisplayAnnotationPart::LabelContinuation => range.1,
+                    DisplayAnnotationPart::Consequitive => range.1,
+                    _ => range.0,
+                };
+                let indent = color.paint(&repeat_char(indent_char, indent_length + 1));
+                let marks = color.paint(&repeat_char(mark, range.1 - indent_length));
+                let annotation = self.format_annotation(
+                    annotation,
+                    annotation_part == &DisplayAnnotationPart::LabelContinuation,
+                    true,
+                );
+                if annotation.is_empty() {
+                    return Some(format!("{}{}", indent, marks));
+                }
+                Some(format!("{}{} {}", indent, marks, color.paint(&annotation)))
+            }
+        }
+    }
+
+    fn format_lineno(&self, lineno: Option<usize>, lineno_width: usize) -> String {
+        match lineno {
+            Some(n) => format!("{:>width$}", n, width = lineno_width),
+            None => repeat_char(' ', lineno_width),
+        }
+    }
+
+    fn format_raw_line(&self, line: &DisplayRawLine, lineno_width: usize) -> String {
+        match line {
+            DisplayRawLine::Origin {
+                path,
+                pos,
+                header_type,
+            } => {
+                let header_sigil = match header_type {
+                    DisplayHeaderType::Initial => "-->",
+                    DisplayHeaderType::Continuation => ":::",
+                };
+                let lineno_color = self.stylesheet.get_style(StyleClass::LineNo);
+
+                if let Some((col, row)) = pos {
+                    format!(
+                        "{}{} {}:{}:{}",
+                        repeat_char(' ', lineno_width),
+                        lineno_color.paint(header_sigil),
+                        path,
+                        col,
+                        row
+                    )
+                } else {
+                    format!(
+                        "{}{} {}",
+                        repeat_char(' ', lineno_width),
+                        lineno_color.paint(header_sigil),
+                        path
+                    )
+                }
+            }
+            DisplayRawLine::Annotation {
+                annotation,
+                source_aligned,
+                continuation,
+            } => {
+                if *source_aligned {
+                    if *continuation {
+                        format!(
+                            "{}{}",
+                            repeat_char(' ', lineno_width + 3),
+                            self.format_annotation(annotation, *continuation, false)
+                        )
+                    } else {
+                        let lineno_color = self.stylesheet.get_style(StyleClass::LineNo);
+                        format!(
+                            "{} {} {}",
+                            repeat_char(' ', lineno_width),
+                            lineno_color.paint("="),
+                            self.format_annotation(annotation, *continuation, false)
+                        )
+                    }
+                } else {
+                    self.format_annotation(annotation, *continuation, false)
+                }
+            }
+        }
+    }
+
+    fn format_line(
+        &self,
+        dl: &DisplayLine,
+        lineno_width: usize,
+        inline_marks_width: usize,
+    ) -> String {
+        match dl {
+            DisplayLine::Source {
+                lineno,
+                inline_marks,
+                line,
+            } => {
+                let lineno = if self.anonymized_line_numbers  && lineno.is_some() {
+                    Self::ANONYMIZED_LINE_NUM.to_string()
+                } else {
+                    self.format_lineno(*lineno, lineno_width)
+                };
+                let marks = self.format_inline_marks(inline_marks, inline_marks_width);
+                let lf = self.format_source_line(line);
+                let lineno_color = self.stylesheet.get_style(StyleClass::LineNo);
+
+                let mut prefix = lineno_color.paint(&format!("{} |", lineno));
+
+                match lf {
+                    Some(lf) => {
+                        if !marks.is_empty() {
+                            prefix.push_str(&format!(" {}", marks));
+                        }
+                        format!("{}{}", prefix, lf)
+                    }
+                    None => {
+                        if !marks.trim().is_empty() {
+                            prefix.push_str(&format!(" {}", marks));
+                        }
+                        prefix
+                    }
+                }
+            }
+            DisplayLine::Fold { inline_marks } => {
+                let marks = self.format_inline_marks(inline_marks, inline_marks_width);
+                let indent = lineno_width;
+                if marks.trim().is_empty() {
+                    String::from("...")
+                } else {
+                    format!("...{}{}", repeat_char(' ', indent), marks)
+                }
+            }
+            DisplayLine::Raw(line) => self.format_raw_line(line, lineno_width),
+        }
+    }
+
+    fn format_inline_marks(
+        &self,
+        inline_marks: &[DisplayMark],
+        inline_marks_width: usize,
+    ) -> String {
+        format!(
+            "{}{}",
+            " ".repeat(inline_marks_width - inline_marks.len()),
+            inline_marks
+                .iter()
+                .map(|mark| {
+                    let sigil = match mark.mark_type {
+                        DisplayMarkType::AnnotationThrough => "|",
+                        DisplayMarkType::AnnotationStart => "/",
+                    };
+                    let color = self.get_annotation_style(&mark.annotation_type);
+                    color.paint(sigil)
+                })
+                .collect::<Vec<String>>()
+                .join(""),
+        )
+    }
+}