rustc_errors/
snippet.rs

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

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy