Skip to content

Commit 5431a04

Browse files
authored
Feat(Compiler-Base): Add code snippet component (#190)
* refactor(compiler-base): refactor trait component and add errors. add 'errors.rs' to report error for 'components.rs'. refactor trait `Component` to supports report errors. issue #115 * move test cases from error/diagnostic/tests.rs to error/tests.rs * add component StringWithStyle * feat(compiler-base): Add code snippet pendant. add UnderLine, IndentWithPrefix, StringWithStyle, CodeSnippet in compiler_base/error/src/diagnostic/components.rs to show code context in diagnostic. issue #115 * fix CR comments * add inline * replace label by symbol. * fix typo
1 parent 5164ca6 commit 5431a04

File tree

4 files changed

+449
-3
lines changed

4 files changed

+449
-3
lines changed

kclvm/compiler_base/3rdparty/rustc_errors/src/styled_buffer.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,32 @@ where
2929
pub style: Option<T>,
3030
}
3131

32+
impl<T> StyledString<T>
33+
where
34+
T: Clone + PartialEq + Eq + Style,
35+
{
36+
/// Constructs a new `StyledString` by string and style.
37+
///
38+
/// # Examples
39+
///
40+
/// ```ignore
41+
/// // You need to choose a style for the generic parameter `T` of `StyledString`.
42+
/// #[derive(Clone, PartialEq, Eq)]
43+
/// enum MyStyle{
44+
/// Style_1
45+
/// }
46+
/// impl Style for MyStyle {
47+
/// ...
48+
/// }
49+
///
50+
/// let styled_string = StyledString::<MyStyle>::new("Hello Styled String".to_string(), Some<MyStyle::Style_1>);
51+
/// ```
52+
#[inline]
53+
pub fn new(text: String, style: Option<T>) -> Self {
54+
StyledString { text, style }
55+
}
56+
}
57+
3258
impl<T> StyledChar<T>
3359
where
3460
T: Clone + PartialEq + Eq + Style,

kclvm/compiler_base/error/src/diagnostic/components.rs

Lines changed: 334 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! 'components.rs' defines all components with style `DiagnosticStyle` that builtin in compiler_base_error.
2+
use std::sync::Arc;
3+
24
use super::{style::DiagnosticStyle, Component};
35
use crate::errors::ComponentFormatError;
4-
use rustc_errors::styled_buffer::StyledBuffer;
6+
use compiler_base_span::{span_to_filename_string, SourceMap, Span};
7+
use rustc_errors::styled_buffer::{StyledBuffer, StyledString};
58

69
/// `Label` can be considered as a component of diagnostic to display a short label message in `Diagnositc`.
710
/// `Label` provides "error", "warning", "note" and "Help" four kinds of labels.
@@ -54,3 +57,333 @@ impl Component<DiagnosticStyle> for Label {
5457
}
5558
}
5659
}
60+
61+
// Make `StyledString` into a component of diagnostic to display a string with style.
62+
// For more information about `StyledString`, see doc in `/compiler_base/3rdparty/rustc_errors/src/styled_buffer.rs`.
63+
impl Component<DiagnosticStyle> for StyledString<DiagnosticStyle> {
64+
#[inline]
65+
fn format(&self, sb: &mut StyledBuffer<DiagnosticStyle>, _: &mut Vec<ComponentFormatError>) {
66+
sb.appendl(&self.text, self.style);
67+
}
68+
}
69+
70+
/// `IndentWithPrefix` is a component of diagnostic to display an indent with prefix.
71+
/// An indent is a whitespace.
72+
/// ```ignore
73+
/// "| " is three indent with prefix "|".
74+
/// ```
75+
pub struct IndentWithPrefix {
76+
indent: usize,
77+
prefix: StyledString<DiagnosticStyle>,
78+
}
79+
80+
const DEFAULT_INDENT_PREFIX_LABEL: &str = "|";
81+
82+
impl IndentWithPrefix {
83+
/// Constructs a new `IndentWithPrefix` by default label with 0 indent.
84+
///
85+
/// # Examples
86+
///
87+
/// ```rust
88+
/// # use compiler_base_error::Component;
89+
/// # use compiler_base_error::components::IndentWithPrefix;
90+
/// # use compiler_base_error::DiagnosticStyle;
91+
/// # use rustc_errors::styled_buffer::StyledBuffer;
92+
///
93+
/// let mut sb = StyledBuffer::<DiagnosticStyle>::new();
94+
/// let mut errs = vec![];
95+
///
96+
/// // If you want to render default text: "|"
97+
/// let indent = IndentWithPrefix::default();
98+
/// indent.format(&mut sb, &mut errs);
99+
/// ```
100+
#[inline]
101+
pub fn default() -> Self {
102+
Self {
103+
indent: 0,
104+
prefix: StyledString::<DiagnosticStyle> {
105+
text: DEFAULT_INDENT_PREFIX_LABEL.to_string(),
106+
style: None,
107+
},
108+
}
109+
}
110+
111+
/// Constructs a new `IndentWithPrefix` by default label with custom indents.
112+
///
113+
/// # Examples
114+
///
115+
/// ```rust
116+
/// # use compiler_base_error::Component;
117+
/// # use compiler_base_error::components::IndentWithPrefix;
118+
/// # use compiler_base_error::DiagnosticStyle;
119+
/// # use rustc_errors::styled_buffer::StyledBuffer;
120+
///
121+
/// let mut sb = StyledBuffer::<DiagnosticStyle>::new();
122+
/// let mut errs = vec![];
123+
///
124+
/// // If you want to add 3 indents and render text: " |"
125+
/// let indent = IndentWithPrefix::new_with_default_label(3, None);
126+
/// indent.format(&mut sb, &mut errs);
127+
/// ```
128+
#[inline]
129+
pub fn new_with_default_label(indent: usize, style: Option<DiagnosticStyle>) -> Self {
130+
Self {
131+
indent,
132+
prefix: StyledString::<DiagnosticStyle>::new(
133+
DEFAULT_INDENT_PREFIX_LABEL.to_string(),
134+
style,
135+
),
136+
}
137+
}
138+
139+
/// Constructs a new `IndentWithPrefix` by custom label with custom indents.
140+
///
141+
/// # Examples
142+
///
143+
/// ```rust
144+
/// # use compiler_base_error::Component;
145+
/// # use compiler_base_error::components::IndentWithPrefix;
146+
/// # use compiler_base_error::DiagnosticStyle;
147+
/// # use rustc_errors::styled_buffer::StyledBuffer;
148+
///
149+
/// let mut sb = StyledBuffer::<DiagnosticStyle>::new();
150+
/// let mut errs = vec![];
151+
/// // If you want to add 3 indents and rendering text: " ^"
152+
/// let indent = IndentWithPrefix::new("^".to_string(), 3, None);
153+
/// indent.format(&mut sb, &mut errs);
154+
/// ```
155+
#[inline]
156+
pub fn new(prefix: String, indent: usize, prefix_style: Option<DiagnosticStyle>) -> Self {
157+
Self {
158+
indent,
159+
prefix: StyledString::<DiagnosticStyle>::new(prefix, prefix_style),
160+
}
161+
}
162+
}
163+
164+
impl Component<DiagnosticStyle> for IndentWithPrefix {
165+
#[inline]
166+
fn format(&self, sb: &mut StyledBuffer<DiagnosticStyle>, errs: &mut Vec<ComponentFormatError>) {
167+
let indent = self.indent;
168+
sb.appendl(&format!("{:>indent$}", ""), None);
169+
self.prefix.format(sb, errs)
170+
}
171+
}
172+
173+
/// `UnderLine` is a component of diagnostic to display an underline.
174+
///
175+
/// ```ignore
176+
/// int test = 0;
177+
/// ^^^^ This is an underline under variable `test`
178+
/// ```
179+
pub struct UnderLine {
180+
start: usize,
181+
end: usize,
182+
symbol: StyledString<DiagnosticStyle>,
183+
}
184+
185+
const DEFAULT_UNDERLINE_LABEL: &str = "^";
186+
impl UnderLine {
187+
/// Constructs a new `UnderLine` with a default label.
188+
///
189+
/// # Examples
190+
///
191+
/// ```rust
192+
/// # use compiler_base_error::Component;
193+
/// # use compiler_base_error::components::UnderLine;
194+
/// # use compiler_base_error::DiagnosticStyle;
195+
/// # use rustc_errors::styled_buffer::StyledBuffer;
196+
///
197+
/// let mut sb = StyledBuffer::<DiagnosticStyle>::new();
198+
/// let mut errs = vec![];
199+
///
200+
/// // rendering text: "^^^^^^^^^^"
201+
/// let ul = UnderLine::new_with_default_label(0, 10, None);
202+
/// ul.format(&mut sb, &mut errs);
203+
///
204+
/// // rendering text: "^^^^^^^^^^" in `DiagnosticStyle::NeedFix`.
205+
/// let ul_need_fix = UnderLine::new_with_default_label(0, 10, Some(DiagnosticStyle::NeedFix));
206+
/// ul_need_fix.format(&mut sb, &mut errs);
207+
/// ```
208+
#[inline]
209+
pub fn new_with_default_label(
210+
start: usize,
211+
end: usize,
212+
style: Option<DiagnosticStyle>,
213+
) -> Self {
214+
Self {
215+
start,
216+
end,
217+
symbol: StyledString::<DiagnosticStyle>::new(
218+
DEFAULT_UNDERLINE_LABEL.to_string(),
219+
style,
220+
),
221+
}
222+
}
223+
224+
/// Constructs a new `UnderLine` with a custom label.
225+
///
226+
/// # Examples
227+
///
228+
/// ```rust
229+
/// # use compiler_base_error::Component;
230+
/// # use compiler_base_error::components::UnderLine;
231+
/// # use compiler_base_error::DiagnosticStyle;
232+
/// # use rustc_errors::styled_buffer::StyledBuffer;
233+
///
234+
/// let mut sb = StyledBuffer::<DiagnosticStyle>::new();
235+
/// let mut errs = vec![];
236+
///
237+
/// // rendering text: "__________"
238+
/// let ul = UnderLine::new(0, 10, "_".to_string(), None);
239+
/// ul.format(&mut sb, &mut errs);
240+
///
241+
/// // rendering text: "~~" in `DiagnosticStyle::NeedFix`.
242+
/// let ul_need_fix = UnderLine::new(0, 2, "~".to_string(), Some(DiagnosticStyle::NeedFix));
243+
/// ul_need_fix.format(&mut sb, &mut errs);
244+
/// ```
245+
#[inline]
246+
pub fn new(start: usize, end: usize, label: String, style: Option<DiagnosticStyle>) -> Self {
247+
Self {
248+
start,
249+
end,
250+
symbol: StyledString::<DiagnosticStyle>::new(label.to_string(), style),
251+
}
252+
}
253+
}
254+
255+
impl Component<DiagnosticStyle> for UnderLine {
256+
fn format(&self, sb: &mut StyledBuffer<DiagnosticStyle>, errs: &mut Vec<ComponentFormatError>) {
257+
if self.start < self.end {
258+
IndentWithPrefix::new("".to_string(), self.start, None).format(sb, errs);
259+
for _ in self.start..self.end {
260+
self.symbol.format(sb, errs);
261+
}
262+
} else if self.start > self.end {
263+
errs.push(ComponentFormatError::new(
264+
"UnderLine",
265+
"Failed to Format UnderLine in One Line.",
266+
))
267+
}
268+
}
269+
}
270+
271+
/// `CodeSnippet` is a component of diagnostic to display code snippets.
272+
pub struct CodeSnippet {
273+
code_span: Span,
274+
source_map: Arc<SourceMap>,
275+
}
276+
277+
impl CodeSnippet {
278+
/// # Examples
279+
///
280+
/// If you want to get one line code snippet from 'compiler_base/error/src/diagnostic/test_datas/code_snippet' file
281+
/// ```ignore
282+
/// Line 1 Code Snippet.
283+
/// Line 2 Code Snippet.
284+
/// ```
285+
///
286+
/// ```rust
287+
/// # use compiler_base_error::{
288+
/// # Component,
289+
/// # DiagnosticStyle,
290+
/// # };
291+
/// # use compiler_base_span::{
292+
/// # SourceMap,
293+
/// # FilePathMapping,
294+
/// # span_to_filename_string
295+
/// # };
296+
/// # use rustc_errors::styled_buffer::StyledBuffer;
297+
/// # use compiler_base_span::{span::new_byte_pos, SpanData};
298+
/// # use compiler_base_error::components::CodeSnippet;
299+
/// # use std::{path::PathBuf, sync::Arc, fs};
300+
///
301+
/// let mut sb = StyledBuffer::<DiagnosticStyle>::new();
302+
/// let mut errs = vec![];
303+
///
304+
/// // 1. You shouled load the file and create the `SourceFile`
305+
/// let filename = fs::canonicalize(&PathBuf::from("./src/diagnostic/test_datas/code_snippet"))
306+
/// .unwrap()
307+
/// .display()
308+
/// .to_string();
309+
///
310+
/// let src = std::fs::read_to_string(filename.clone()).unwrap();
311+
/// let sm = SourceMap::new(FilePathMapping::empty());
312+
/// sm.new_source_file(PathBuf::from(filename.clone()).into(), src.to_string());
313+
///
314+
/// // 2. You should create a code span for the code snippet.
315+
/// let code_span = SpanData {
316+
/// lo: new_byte_pos(22),
317+
/// hi: new_byte_pos(42),
318+
/// }.span();
319+
///
320+
/// // 3. You can create the `CodeSnippet` by the `SourceFile`,
321+
/// // and render text "Line 2 Code Snippet.".
322+
/// let code_snippet = CodeSnippet::new(code_span, Arc::new(sm));
323+
/// code_snippet.format(&mut sb, &mut errs);
324+
/// ```
325+
#[inline]
326+
pub fn new(code_span: Span, source_map: Arc<SourceMap>) -> Self {
327+
Self {
328+
code_span,
329+
source_map,
330+
}
331+
}
332+
}
333+
334+
const DEFAULT_FILE_PATH_PREFIX: &str = "---> File: ";
335+
336+
impl Component<DiagnosticStyle> for CodeSnippet {
337+
fn format(&self, sb: &mut StyledBuffer<DiagnosticStyle>, errs: &mut Vec<ComponentFormatError>) {
338+
sb.pushs(DEFAULT_FILE_PATH_PREFIX, Some(DiagnosticStyle::Url));
339+
let file_info = self.source_map.span_to_diagnostic_string(self.code_span);
340+
sb.appendl(&file_info, Some(DiagnosticStyle::Url));
341+
sb.appendl("\n", None);
342+
match self.source_map.span_to_lines(self.code_span) {
343+
Ok(affected_lines) => {
344+
match self
345+
.source_map
346+
.source_file_by_filename(&span_to_filename_string(
347+
&self.code_span,
348+
&self.source_map,
349+
)) {
350+
Some(sf) => {
351+
for line in affected_lines.lines {
352+
let line_index = line.line_index.to_string();
353+
let indent = line_index.len() + 1;
354+
IndentWithPrefix::new(line_index, indent, Some(DiagnosticStyle::Url))
355+
.format(sb, errs);
356+
IndentWithPrefix::default().format(sb, errs);
357+
if let Some(line) = sf.get_line(line.line_index) {
358+
sb.appendl(&line.to_string(), None);
359+
} else {
360+
errs.push(ComponentFormatError::new(
361+
"CodeSnippet",
362+
"Failed to Display Code Snippet.",
363+
))
364+
}
365+
sb.appendl("\n", None);
366+
IndentWithPrefix::new_with_default_label(indent + 1, None)
367+
.format(sb, errs);
368+
UnderLine::new_with_default_label(
369+
line.start_col.0,
370+
line.end_col.0,
371+
Some(DiagnosticStyle::NeedFix),
372+
)
373+
.format(sb, errs);
374+
sb.appendl("\n", None);
375+
}
376+
}
377+
None => errs.push(ComponentFormatError::new(
378+
"CodeSnippet",
379+
"Failed to Load Source File",
380+
)),
381+
};
382+
}
383+
Err(_) => errs.push(ComponentFormatError::new(
384+
"CodeSnippet",
385+
"Failed to Display Code Snippet Lines",
386+
)),
387+
};
388+
}
389+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Line 1 Code Snippet.
2+
Line 2 Code Snippet.

0 commit comments

Comments
 (0)