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.
12use Level;
13
14#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
15pub struct Line {
16 pub line_index: usize,
17 pub annotations: Vec<Annotation>,
18}
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 if let AnnotationType::MultilineLine(_) = self.annotation_type {
123 true
124 } else {
125 false
126 }
127 }
128
129 pub fn is_multiline(&self) -> bool {
130 match self.annotation_type {
131 AnnotationType::Multiline(_) |
132 AnnotationType::MultilineStart(_) |
133 AnnotationType::MultilineLine(_) |
134 AnnotationType::MultilineEnd(_) => true,
135 _ => false,
136 }
137 }
138
139 pub fn len(&self) -> usize {
140 // Account for usize underflows
141 if self.end_col > self.start_col {
142 self.end_col - self.start_col
143 } else {
144 self.start_col - self.end_col
145 }
146 }
147
148 pub fn has_label(&self) -> bool {
149 if let Some(ref label) = self.label {
150 // Consider labels with no text as effectively not being there
151 // to avoid weird output with unnecessary vertical lines, like:
152 //
153 // X | fn foo(x: u32) {
154 // | -------^------
155 // | | |
156 // | |
157 // |
158 //
159 // Note that this would be the complete output users would see.
160 label.len() > 0
161 } else {
162 false
163 }
164 }
165
166 pub fn takes_space(&self) -> bool {
167 // Multiline annotations always have to keep vertical space.
168 match self.annotation_type {
169 AnnotationType::MultilineStart(_) |
170 AnnotationType::MultilineEnd(_) => true,
171 _ => false,
172 }
173 }
174}
175
176#[derive(Debug)]
177pub struct StyledString {
178 pub text: String,
179 pub style: Style,
180}
181
182#[derive(Copy, Clone, Debug, PartialEq)]
183#[allow(dead_code)]
184pub enum Style {
185 HeaderMsg,
186 LineAndColumn,
187 LineNumber,
188 Quotation,
189 UnderlinePrimary,
190 UnderlineSecondary,
191 LabelPrimary,
192 LabelSecondary,
193 NoStyle,
194 Level(Level),
195 Highlight,
196}
197