]>
Commit | Line | Data |
---|---|---|
a7813a04 XL |
1 | // Code for annotating snippets. |
2 | ||
353b0b11 | 3 | use crate::{Level, Loc}; |
a7813a04 | 4 | |
5bcae85e SL |
5 | #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] |
6 | pub struct Line { | |
7 | pub line_index: usize, | |
8 | pub annotations: Vec<Annotation>, | |
a7813a04 XL |
9 | } |
10 | ||
353b0b11 FG |
11 | #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Default)] |
12 | pub struct AnnotationColumn { | |
13 | /// the (0-indexed) column for *display* purposes, counted in characters, not utf-8 bytes | |
14 | pub display: usize, | |
15 | /// the (0-indexed) column in the file, counted in characters, not utf-8 bytes. | |
16 | /// | |
17 | /// this may be different from `self.display`, | |
18 | /// e.g. if the file contains hard tabs, because we convert tabs to spaces for error messages. | |
19 | /// | |
20 | /// for example: | |
21 | /// ```text | |
22 | /// (hard tab)hello | |
23 | /// ^ this is display column 4, but file column 1 | |
24 | /// ``` | |
25 | /// | |
26 | /// we want to keep around the correct file offset so that column numbers in error messages | |
27 | /// are correct. (motivated by <https://github.com/rust-lang/rust/issues/109537>) | |
28 | pub file: usize, | |
29 | } | |
30 | ||
31 | impl AnnotationColumn { | |
32 | pub fn from_loc(loc: &Loc) -> AnnotationColumn { | |
33 | AnnotationColumn { display: loc.col_display, file: loc.col.0 } | |
34 | } | |
35 | } | |
36 | ||
476ff2be SL |
37 | #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] |
38 | pub struct MultilineAnnotation { | |
39 | pub depth: usize, | |
40 | pub line_start: usize, | |
41 | pub line_end: usize, | |
353b0b11 FG |
42 | pub start_col: AnnotationColumn, |
43 | pub end_col: AnnotationColumn, | |
476ff2be SL |
44 | pub is_primary: bool, |
45 | pub label: Option<String>, | |
532ac7d7 | 46 | pub overlaps_exactly: bool, |
476ff2be SL |
47 | } |
48 | ||
49 | impl MultilineAnnotation { | |
50 | pub fn increase_depth(&mut self) { | |
51 | self.depth += 1; | |
52 | } | |
53 | ||
532ac7d7 XL |
54 | /// Compare two `MultilineAnnotation`s considering only the `Span` they cover. |
55 | pub fn same_span(&self, other: &MultilineAnnotation) -> bool { | |
dfeec247 XL |
56 | self.line_start == other.line_start |
57 | && self.line_end == other.line_end | |
58 | && self.start_col == other.start_col | |
59 | && self.end_col == other.end_col | |
532ac7d7 XL |
60 | } |
61 | ||
476ff2be SL |
62 | pub fn as_start(&self) -> Annotation { |
63 | Annotation { | |
64 | start_col: self.start_col, | |
353b0b11 FG |
65 | end_col: AnnotationColumn { |
66 | // these might not correspond to the same place anymore, | |
67 | // but that's okay for our purposes | |
68 | display: self.start_col.display + 1, | |
69 | file: self.start_col.file + 1, | |
70 | }, | |
476ff2be | 71 | is_primary: self.is_primary, |
cc61c64b | 72 | label: None, |
dfeec247 | 73 | annotation_type: AnnotationType::MultilineStart(self.depth), |
476ff2be SL |
74 | } |
75 | } | |
76 | ||
77 | pub fn as_end(&self) -> Annotation { | |
78 | Annotation { | |
353b0b11 FG |
79 | start_col: AnnotationColumn { |
80 | // these might not correspond to the same place anymore, | |
81 | // but that's okay for our purposes | |
82 | display: self.end_col.display.saturating_sub(1), | |
83 | file: self.end_col.file.saturating_sub(1), | |
84 | }, | |
476ff2be SL |
85 | end_col: self.end_col, |
86 | is_primary: self.is_primary, | |
cc61c64b | 87 | label: self.label.clone(), |
dfeec247 | 88 | annotation_type: AnnotationType::MultilineEnd(self.depth), |
476ff2be SL |
89 | } |
90 | } | |
91 | ||
92 | pub fn as_line(&self) -> Annotation { | |
93 | Annotation { | |
353b0b11 FG |
94 | start_col: Default::default(), |
95 | end_col: Default::default(), | |
476ff2be SL |
96 | is_primary: self.is_primary, |
97 | label: None, | |
dfeec247 | 98 | annotation_type: AnnotationType::MultilineLine(self.depth), |
476ff2be SL |
99 | } |
100 | } | |
101 | } | |
102 | ||
103 | #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] | |
104 | pub enum AnnotationType { | |
105 | /// Annotation under a single line of code | |
106 | Singleline, | |
107 | ||
476ff2be SL |
108 | // The Multiline type above is replaced with the following three in order |
109 | // to reuse the current label drawing code. | |
110 | // | |
111 | // Each of these corresponds to one part of the following diagram: | |
112 | // | |
113 | // x | foo(1 + bar(x, | |
cc61c64b XL |
114 | // | _________^ < MultilineStart |
115 | // x | | y), < MultilineLine | |
116 | // | |______________^ label < MultilineEnd | |
476ff2be SL |
117 | // x | z); |
118 | /// Annotation marking the first character of a fully shown multiline span | |
119 | MultilineStart(usize), | |
120 | /// Annotation marking the last character of a fully shown multiline span | |
121 | MultilineEnd(usize), | |
122 | /// Line at the left enclosing the lines of a fully shown multiline span | |
cc61c64b XL |
123 | // Just a placeholder for the drawing algorithm, to know that it shouldn't skip the first 4 |
124 | // and last 2 lines of code. The actual line is drawn in `emit_message_default` and not in | |
125 | // `draw_multiline_line`. | |
476ff2be SL |
126 | MultilineLine(usize), |
127 | } | |
128 | ||
a7813a04 | 129 | #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] |
5bcae85e | 130 | pub struct Annotation { |
353b0b11 FG |
131 | /// Start column. |
132 | /// Note that it is important that this field goes | |
a7813a04 XL |
133 | /// first, so that when we sort, we sort orderings by start |
134 | /// column. | |
353b0b11 | 135 | pub start_col: AnnotationColumn, |
a7813a04 XL |
136 | |
137 | /// End column within the line (exclusive) | |
353b0b11 | 138 | pub end_col: AnnotationColumn, |
a7813a04 XL |
139 | |
140 | /// Is this annotation derived from primary span | |
5bcae85e | 141 | pub is_primary: bool, |
a7813a04 | 142 | |
a7813a04 | 143 | /// Optional label to display adjacent to the annotation. |
5bcae85e | 144 | pub label: Option<String>, |
476ff2be SL |
145 | |
146 | /// Is this a single line, multiline or multiline span minimized down to a | |
147 | /// smaller span. | |
148 | pub annotation_type: AnnotationType, | |
149 | } | |
150 | ||
151 | impl Annotation { | |
3b2f2976 | 152 | /// Whether this annotation is a vertical line placeholder. |
32a655c1 | 153 | pub fn is_line(&self) -> bool { |
1b1a35ee | 154 | matches!(self.annotation_type, AnnotationType::MultilineLine(_)) |
32a655c1 SL |
155 | } |
156 | ||
353b0b11 | 157 | /// Length of this annotation as displayed in the stderr output |
32a655c1 SL |
158 | pub fn len(&self) -> usize { |
159 | // Account for usize underflows | |
353b0b11 FG |
160 | if self.end_col.display > self.start_col.display { |
161 | self.end_col.display - self.start_col.display | |
32a655c1 | 162 | } else { |
353b0b11 | 163 | self.start_col.display - self.end_col.display |
32a655c1 SL |
164 | } |
165 | } | |
166 | ||
167 | pub fn has_label(&self) -> bool { | |
168 | if let Some(ref label) = self.label { | |
169 | // Consider labels with no text as effectively not being there | |
170 | // to avoid weird output with unnecessary vertical lines, like: | |
171 | // | |
172 | // X | fn foo(x: u32) { | |
173 | // | -------^------ | |
174 | // | | | | |
175 | // | | | |
176 | // | | |
177 | // | |
178 | // Note that this would be the complete output users would see. | |
74b04a01 | 179 | !label.is_empty() |
32a655c1 SL |
180 | } else { |
181 | false | |
182 | } | |
183 | } | |
cc61c64b XL |
184 | |
185 | pub fn takes_space(&self) -> bool { | |
186 | // Multiline annotations always have to keep vertical space. | |
5869c6ff XL |
187 | matches!( |
188 | self.annotation_type, | |
189 | AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_) | |
190 | ) | |
cc61c64b | 191 | } |
a7813a04 XL |
192 | } |
193 | ||
194 | #[derive(Debug)] | |
195 | pub struct StyledString { | |
196 | pub text: String, | |
197 | pub style: Style, | |
198 | } | |
199 | ||
3dfed10e | 200 | #[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable)] |
a7813a04 | 201 | pub enum Style { |
8faf50e0 | 202 | MainHeaderMsg, |
5bcae85e | 203 | HeaderMsg, |
a7813a04 XL |
204 | LineAndColumn, |
205 | LineNumber, | |
206 | Quotation, | |
207 | UnderlinePrimary, | |
208 | UnderlineSecondary, | |
209 | LabelPrimary, | |
210 | LabelSecondary, | |
a7813a04 | 211 | NoStyle, |
5bcae85e | 212 | Level(Level), |
32a655c1 | 213 | Highlight, |
94222f64 XL |
214 | Addition, |
215 | Removal, | |
c30ab7b3 | 216 | } |