swc_common/errors/snippet.rs
1// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11// Code for annotating snippets.
12
13use super::Level;
14
15#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
16pub struct Line {
17 pub line_index: usize,
18 pub annotations: Vec<Annotation>,
19}
20
21#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
22pub struct MultilineAnnotation {
23 pub depth: usize,
24 pub line_start: usize,
25 pub line_end: usize,
26 pub start_col: usize,
27 pub end_col: usize,
28 pub is_primary: bool,
29 pub label: Option<String>,
30}
31
32impl MultilineAnnotation {
33 pub fn increase_depth(&mut self) {
34 self.depth += 1;
35 }
36
37 pub fn as_start(&self) -> Annotation {
38 Annotation {
39 start_col: self.start_col,
40 end_col: self.start_col + 1,
41 is_primary: self.is_primary,
42 label: None,
43 annotation_type: AnnotationType::MultilineStart(self.depth),
44 }
45 }
46
47 pub fn as_end(&self) -> Annotation {
48 Annotation {
49 start_col: self.end_col.saturating_sub(1),
50 end_col: self.end_col,
51 is_primary: self.is_primary,
52 label: self.label.clone(),
53 annotation_type: AnnotationType::MultilineEnd(self.depth),
54 }
55 }
56
57 pub fn as_line(&self) -> Annotation {
58 Annotation {
59 start_col: 0,
60 end_col: 0,
61 is_primary: self.is_primary,
62 label: None,
63 annotation_type: AnnotationType::MultilineLine(self.depth),
64 }
65 }
66}
67
68#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
69pub enum AnnotationType {
70 /// Annotation under a single line of code
71 Singleline,
72
73 /// Annotation enclosing the first and last character of a multiline span
74 Multiline(MultilineAnnotation),
75
76 // The Multiline type above is replaced with the following three in order
77 // to reuse the current label drawing code.
78 //
79 // Each of these corresponds to one part of the following diagram:
80 //
81 // x | foo(1 + bar(x,
82 // | _________^ < MultilineStart
83 // x | | y), < MultilineLine
84 // | |______________^ label < MultilineEnd
85 // x | z);
86 /// Annotation marking the first character of a fully shown multiline span
87 MultilineStart(usize),
88 /// Annotation marking the last character of a fully shown multiline span
89 MultilineEnd(usize),
90 /// Line at the left enclosing the lines of a fully shown multiline span
91 // Just a placeholder for the drawing algorithm, to know that it shouldn't skip the first 4
92 // and last 2 lines of code. The actual line is drawn in `emit_message_default` and not in
93 // `draw_multiline_line`.
94 MultilineLine(usize),
95}
96
97#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
98pub struct Annotation {
99 /// Start column, 0-based indexing -- counting *characters*, not
100 /// utf-8 bytes. Note that it is important that this field goes
101 /// first, so that when we sort, we sort orderings by start
102 /// column.
103 pub start_col: usize,
104
105 /// End column within the line (exclusive)
106 pub end_col: usize,
107
108 /// Is this annotation derived from primary span
109 pub is_primary: bool,
110
111 /// Optional label to display adjacent to the annotation.
112 pub label: Option<String>,
113
114 /// Is this a single line, multiline or multiline span minimized down to a
115 /// smaller span.
116 pub annotation_type: AnnotationType,
117}
118
119impl Annotation {
120 /// Whether this annotation is a vertical line placeholder.
121 pub fn is_line(&self) -> bool {
122 matches!(self.annotation_type, AnnotationType::MultilineLine(_))
123 }
124
125 pub fn is_multiline(&self) -> bool {
126 matches!(
127 self.annotation_type,
128 AnnotationType::Multiline(_)
129 | AnnotationType::MultilineStart(_)
130 | AnnotationType::MultilineLine(_)
131 | AnnotationType::MultilineEnd(_)
132 )
133 }
134
135 pub fn len(&self) -> usize {
136 // Account for usize underflows
137 self.end_col.abs_diff(self.start_col)
138 }
139
140 pub fn has_label(&self) -> bool {
141 if let Some(ref label) = self.label {
142 // Consider labels with no text as effectively not being there
143 // to avoid weird output with unnecessary vertical lines, like:
144 //
145 // X | fn foo(x: u32) {
146 // | -------^------
147 // | | |
148 // | |
149 // |
150 //
151 // Note that this would be the complete output users would see.
152 !label.is_empty()
153 } else {
154 false
155 }
156 }
157
158 pub fn takes_space(&self) -> bool {
159 // Multiline annotations always have to keep vertical space.
160 matches!(
161 self.annotation_type,
162 AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_)
163 )
164 }
165}
166
167#[derive(Debug)]
168pub struct StyledString {
169 pub text: String,
170 pub style: Style,
171}
172
173#[allow(clippy::enum_variant_names)]
174#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
175#[cfg_attr(
176 feature = "diagnostic-serde",
177 derive(serde::Serialize, serde::Deserialize)
178)]
179#[cfg_attr(
180 feature = "encoding-impl",
181 derive(::ast_node::Encode, ::ast_node::Decode)
182)]
183pub enum Style {
184 MainHeaderMsg,
185 HeaderMsg,
186 LineAndColumn,
187 LineNumber,
188 Quotation,
189 UnderlinePrimary,
190 UnderlineSecondary,
191 LabelPrimary,
192 LabelSecondary,
193 OldSchoolNoteText,
194 NoStyle,
195 Level(Level),
196 Highlight,
197}