|  | // Code for annotating snippets. | 
|  |  | 
|  | use rustc_macros::{Decodable, Encodable}; | 
|  |  | 
|  | use crate::{Level, Loc}; | 
|  |  | 
|  | #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] | 
|  | pub(crate) struct Line { | 
|  | pub line_index: usize, | 
|  | pub annotations: Vec<Annotation>, | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Default)] | 
|  | pub(crate) struct AnnotationColumn { | 
|  | /// the (0-indexed) column for *display* purposes, counted in characters, not utf-8 bytes | 
|  | pub display: usize, | 
|  | /// the (0-indexed) column in the file, counted in characters, not utf-8 bytes. | 
|  | /// | 
|  | /// this may be different from `self.display`, | 
|  | /// e.g. if the file contains hard tabs, because we convert tabs to spaces for error messages. | 
|  | /// | 
|  | /// for example: | 
|  | /// ```text | 
|  | /// (hard tab)hello | 
|  | ///           ^ this is display column 4, but file column 1 | 
|  | /// ``` | 
|  | /// | 
|  | /// we want to keep around the correct file offset so that column numbers in error messages | 
|  | /// are correct. (motivated by <https://github.com/rust-lang/rust/issues/109537>) | 
|  | pub file: usize, | 
|  | } | 
|  |  | 
|  | impl AnnotationColumn { | 
|  | pub(crate) fn from_loc(loc: &Loc) -> AnnotationColumn { | 
|  | AnnotationColumn { display: loc.col_display, file: loc.col.0 } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] | 
|  | pub(crate) struct MultilineAnnotation { | 
|  | pub depth: usize, | 
|  | pub line_start: usize, | 
|  | pub line_end: usize, | 
|  | pub start_col: AnnotationColumn, | 
|  | pub end_col: AnnotationColumn, | 
|  | pub is_primary: bool, | 
|  | pub label: Option<String>, | 
|  | pub overlaps_exactly: bool, | 
|  | } | 
|  |  | 
|  | impl MultilineAnnotation { | 
|  | pub(crate) fn increase_depth(&mut self) { | 
|  | self.depth += 1; | 
|  | } | 
|  |  | 
|  | /// Compare two `MultilineAnnotation`s considering only the `Span` they cover. | 
|  | pub(crate) fn same_span(&self, other: &MultilineAnnotation) -> bool { | 
|  | self.line_start == other.line_start | 
|  | && self.line_end == other.line_end | 
|  | && self.start_col == other.start_col | 
|  | && self.end_col == other.end_col | 
|  | } | 
|  |  | 
|  | pub(crate) fn as_start(&self) -> Annotation { | 
|  | Annotation { | 
|  | start_col: self.start_col, | 
|  | end_col: AnnotationColumn { | 
|  | // these might not correspond to the same place anymore, | 
|  | // but that's okay for our purposes | 
|  | display: self.start_col.display + 1, | 
|  | file: self.start_col.file + 1, | 
|  | }, | 
|  | is_primary: self.is_primary, | 
|  | label: None, | 
|  | annotation_type: AnnotationType::MultilineStart(self.depth), | 
|  | } | 
|  | } | 
|  |  | 
|  | pub(crate) fn as_end(&self) -> Annotation { | 
|  | Annotation { | 
|  | start_col: AnnotationColumn { | 
|  | // these might not correspond to the same place anymore, | 
|  | // but that's okay for our purposes | 
|  | display: self.end_col.display.saturating_sub(1), | 
|  | file: self.end_col.file.saturating_sub(1), | 
|  | }, | 
|  | end_col: self.end_col, | 
|  | is_primary: self.is_primary, | 
|  | label: self.label.clone(), | 
|  | annotation_type: AnnotationType::MultilineEnd(self.depth), | 
|  | } | 
|  | } | 
|  |  | 
|  | pub(crate) fn as_line(&self) -> Annotation { | 
|  | Annotation { | 
|  | start_col: Default::default(), | 
|  | end_col: Default::default(), | 
|  | is_primary: self.is_primary, | 
|  | label: None, | 
|  | annotation_type: AnnotationType::MultilineLine(self.depth), | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] | 
|  | pub(crate) enum AnnotationType { | 
|  | /// Annotation under a single line of code | 
|  | Singleline, | 
|  |  | 
|  | // The Multiline type above is replaced with the following three in order | 
|  | // to reuse the current label drawing code. | 
|  | // | 
|  | // Each of these corresponds to one part of the following diagram: | 
|  | // | 
|  | //     x |   foo(1 + bar(x, | 
|  | //       |  _________^              < MultilineStart | 
|  | //     x | |             y),        < MultilineLine | 
|  | //       | |______________^ label   < MultilineEnd | 
|  | //     x |       z); | 
|  | /// Annotation marking the first character of a fully shown multiline span | 
|  | MultilineStart(usize), | 
|  | /// Annotation marking the last character of a fully shown multiline span | 
|  | MultilineEnd(usize), | 
|  | /// Line at the left enclosing the lines of a fully shown multiline span | 
|  | // Just a placeholder for the drawing algorithm, to know that it shouldn't skip the first 4 | 
|  | // and last 2 lines of code. The actual line is drawn in `emit_message_default` and not in | 
|  | // `draw_multiline_line`. | 
|  | MultilineLine(usize), | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] | 
|  | pub(crate) struct Annotation { | 
|  | /// Start column. | 
|  | /// Note that it is important that this field goes | 
|  | /// first, so that when we sort, we sort orderings by start | 
|  | /// column. | 
|  | pub start_col: AnnotationColumn, | 
|  |  | 
|  | /// End column within the line (exclusive) | 
|  | pub end_col: AnnotationColumn, | 
|  |  | 
|  | /// Is this annotation derived from primary span | 
|  | pub is_primary: bool, | 
|  |  | 
|  | /// Optional label to display adjacent to the annotation. | 
|  | pub label: Option<String>, | 
|  |  | 
|  | /// Is this a single line, multiline or multiline span minimized down to a | 
|  | /// smaller span. | 
|  | pub annotation_type: AnnotationType, | 
|  | } | 
|  |  | 
|  | impl Annotation { | 
|  | /// Whether this annotation is a vertical line placeholder. | 
|  | pub(crate) fn is_line(&self) -> bool { | 
|  | matches!(self.annotation_type, AnnotationType::MultilineLine(_)) | 
|  | } | 
|  |  | 
|  | /// Length of this annotation as displayed in the stderr output | 
|  | pub(crate) fn len(&self) -> usize { | 
|  | // Account for usize underflows | 
|  | self.end_col.display.abs_diff(self.start_col.display) | 
|  | } | 
|  |  | 
|  | pub(crate) fn has_label(&self) -> bool { | 
|  | if let Some(ref label) = self.label { | 
|  | // Consider labels with no text as effectively not being there | 
|  | // to avoid weird output with unnecessary vertical lines, like: | 
|  | // | 
|  | //     X | fn foo(x: u32) { | 
|  | //       | -------^------ | 
|  | //       | |      | | 
|  | //       | | | 
|  | //       | | 
|  | // | 
|  | // Note that this would be the complete output users would see. | 
|  | !label.is_empty() | 
|  | } else { | 
|  | false | 
|  | } | 
|  | } | 
|  |  | 
|  | pub(crate) fn takes_space(&self) -> bool { | 
|  | // Multiline annotations always have to keep vertical space. | 
|  | matches!( | 
|  | self.annotation_type, | 
|  | AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_) | 
|  | ) | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Debug)] | 
|  | pub(crate) struct StyledString { | 
|  | pub text: String, | 
|  | pub style: Style, | 
|  | } | 
|  |  | 
|  | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] | 
|  | pub enum Style { | 
|  | MainHeaderMsg, | 
|  | HeaderMsg, | 
|  | LineAndColumn, | 
|  | LineNumber, | 
|  | Quotation, | 
|  | UnderlinePrimary, | 
|  | UnderlineSecondary, | 
|  | LabelPrimary, | 
|  | LabelSecondary, | 
|  | NoStyle, | 
|  | Level(Level), | 
|  | Highlight, | 
|  | Addition, | 
|  | Removal, | 
|  | } |