1 // Code for annotating snippets. 2 3 use crate::Level; 4 5 #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] 6 pub struct Line { 7 pub line_index: usize, 8 pub annotations: Vec<Annotation>, 9 } 10 11 #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] 12 pub struct MultilineAnnotation { 13 pub depth: usize, 14 pub line_start: usize, 15 pub line_end: usize, 16 pub start_col: usize, 17 pub end_col: usize, 18 pub is_primary: bool, 19 pub label: Option<String>, 20 pub overlaps_exactly: bool, 21 } 22 23 impl MultilineAnnotation { increase_depth(&mut self)24 pub fn increase_depth(&mut self) { 25 self.depth += 1; 26 } 27 28 /// Compare two `MultilineAnnotation`s considering only the `Span` they cover. same_span(&self, other: &MultilineAnnotation) -> bool29 pub fn same_span(&self, other: &MultilineAnnotation) -> bool { 30 self.line_start == other.line_start 31 && self.line_end == other.line_end 32 && self.start_col == other.start_col 33 && self.end_col == other.end_col 34 } 35 as_start(&self) -> Annotation36 pub fn as_start(&self) -> Annotation { 37 Annotation { 38 start_col: self.start_col, 39 end_col: self.start_col + 1, 40 is_primary: self.is_primary, 41 label: None, 42 annotation_type: AnnotationType::MultilineStart(self.depth), 43 } 44 } 45 as_end(&self) -> Annotation46 pub fn as_end(&self) -> Annotation { 47 Annotation { 48 start_col: self.end_col.saturating_sub(1), 49 end_col: self.end_col, 50 is_primary: self.is_primary, 51 label: self.label.clone(), 52 annotation_type: AnnotationType::MultilineEnd(self.depth), 53 } 54 } 55 as_line(&self) -> Annotation56 pub fn as_line(&self) -> Annotation { 57 Annotation { 58 start_col: 0, 59 end_col: 0, 60 is_primary: self.is_primary, 61 label: None, 62 annotation_type: AnnotationType::MultilineLine(self.depth), 63 } 64 } 65 } 66 67 #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] 68 pub enum AnnotationType { 69 /// Annotation under a single line of code 70 Singleline, 71 72 /// Annotation enclosing the first and last character of a multiline span 73 Multiline(MultilineAnnotation), 74 75 // The Multiline type above is replaced with the following three in order 76 // to reuse the current label drawing code. 77 // 78 // Each of these corresponds to one part of the following diagram: 79 // 80 // x | foo(1 + bar(x, 81 // | _________^ < MultilineStart 82 // x | | y), < MultilineLine 83 // | |______________^ label < MultilineEnd 84 // x | z); 85 /// Annotation marking the first character of a fully shown multiline span 86 MultilineStart(usize), 87 /// Annotation marking the last character of a fully shown multiline span 88 MultilineEnd(usize), 89 /// Line at the left enclosing the lines of a fully shown multiline span 90 // Just a placeholder for the drawing algorithm, to know that it shouldn't skip the first 4 91 // and last 2 lines of code. The actual line is drawn in `emit_message_default` and not in 92 // `draw_multiline_line`. 93 MultilineLine(usize), 94 } 95 96 #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] 97 pub struct Annotation { 98 /// Start column, 0-based indexing -- counting *characters*, not 99 /// utf-8 bytes. Note that it is important that this field goes 100 /// first, so that when we sort, we sort orderings by start 101 /// column. 102 pub start_col: usize, 103 104 /// End column within the line (exclusive) 105 pub end_col: usize, 106 107 /// Is this annotation derived from primary span 108 pub is_primary: bool, 109 110 /// Optional label to display adjacent to the annotation. 111 pub label: Option<String>, 112 113 /// Is this a single line, multiline or multiline span minimized down to a 114 /// smaller span. 115 pub annotation_type: AnnotationType, 116 } 117 118 impl Annotation { 119 /// Whether this annotation is a vertical line placeholder. is_line(&self) -> bool120 pub fn is_line(&self) -> bool { 121 matches!(self.annotation_type, AnnotationType::MultilineLine(_)) 122 } 123 len(&self) -> usize124 pub fn len(&self) -> usize { 125 // Account for usize underflows 126 if self.end_col > self.start_col { 127 self.end_col - self.start_col 128 } else { 129 self.start_col - self.end_col 130 } 131 } 132 has_label(&self) -> bool133 pub fn has_label(&self) -> bool { 134 if let Some(ref label) = self.label { 135 // Consider labels with no text as effectively not being there 136 // to avoid weird output with unnecessary vertical lines, like: 137 // 138 // X | fn foo(x: u32) { 139 // | -------^------ 140 // | | | 141 // | | 142 // | 143 // 144 // Note that this would be the complete output users would see. 145 !label.is_empty() 146 } else { 147 false 148 } 149 } 150 takes_space(&self) -> bool151 pub fn takes_space(&self) -> bool { 152 // Multiline annotations always have to keep vertical space. 153 matches!( 154 self.annotation_type, 155 AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_) 156 ) 157 } 158 } 159 160 #[derive(Debug)] 161 pub struct StyledString { 162 pub text: String, 163 pub style: Style, 164 } 165 166 #[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable)] 167 pub enum Style { 168 MainHeaderMsg, 169 HeaderMsg, 170 LineAndColumn, 171 LineNumber, 172 Quotation, 173 UnderlinePrimary, 174 UnderlineSecondary, 175 LabelPrimary, 176 LabelSecondary, 177 NoStyle, 178 Level(Level), 179 Highlight, 180 } 181