1use core::iter;
4use core::fmt::{self, Formatter};
5use std::error::Error;
6
7use crate::utils;
8use crate::debug::SourceError;
9use crate::debug::symbol::{ResolvedSymbol, DebugSymbolResolver};
10
11pub fn print_source_errors<E>(resolver: &impl DebugSymbolResolver, errors: &[E]) where E: SourceError {
12 let symbols = errors.iter().filter_map(|err| err.debug_symbol());
13
14 let resolved_table = resolver.resolve_symbols(symbols).unwrap();
15
16 let mut render_errors = errors.iter().filter_map(
18 |error| match error.debug_symbol() {
19 None => Some(RenderError(error, None)),
20 Some(symbol) => {
21 let resolved = resolved_table.lookup(symbol).unwrap();
22 match resolved {
23 Ok(resolved) => Some(RenderError(error, Some(resolved))),
24 Err(resolve_error) => {
25 println!("{}", error);
26 println!("Could not resolve symbol: {}", resolve_error);
27 None
28 }
29 }
30 },
31 })
32 .collect::<Vec<RenderError<E>>>();
33
34 render_errors.sort_by_key(|render| render.1.map_or_else(
36 || (1, 0), |resolved| (0, resolved.lineno())
37 ));
38
39 for render in render_errors.iter() {
40 println!("{}", render);
41 }
42}
43
44
45pub struct RenderError<'e, 's, E>(pub &'e E, pub Option<&'s ResolvedSymbol>) where E: Error;
46
47impl<E> fmt::Display for RenderError<'_, '_, E> where E: Error {
48 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
49 let RenderError(error, source_lines) = self;
50
51 if let Some(source_lines) = source_lines {
52 write!(fmt, "{}.\n\n{}", error, source_lines)
53 } else {
54 write!(fmt, "{}.", error)
55 }
56 }
57}
58
59impl fmt::Display for ResolvedSymbol {
60 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
61 fmt_source_lines(fmt, self)
62 }
63}
64
65fn fmt_source_lines(fmt: &mut Formatter<'_>, symbol: &ResolvedSymbol) -> fmt::Result {
66 let mut start_idx = 0;
67 for (num, raw_line) in symbol.iter_whole_lines().enumerate() {
68 let end_index = start_idx + raw_line.len(); let margin = format!("{: >3}", num + symbol.lineno());
71 let source_line = raw_line.trim_end();
72
73 let start_col =
74 if symbol.start() < start_idx { 0 } else { symbol.start() };
76
77 let end_col =
78 if symbol.end() > end_index { source_line.len() } else { symbol.end() - start_idx };
80
81 let mut marker = String::new();
82 marker.extend(iter::repeat(' ').take(margin.len()));
83 marker.push_str(" ");
84
85 marker.extend(iter::repeat(' ').take(start_col));
86 marker.extend(iter::repeat('^').take(usize::max(end_col - start_col, 1))); writeln!(fmt, "{}| {}", margin, source_line)?;
89 writeln!(fmt, "{}", marker)?;
90
91 start_idx += raw_line.len();
92 }
93
94 Ok(())
95}
96
97fn fmt_source_line_single(fmt: &mut Formatter<'_>, symbol: &ResolvedSymbol) -> fmt::Result {
98 let margin = format!("{: >3}| ", symbol.lineno());
99 let source_line = render_symbol_single_line(symbol).to_string();
100
101 let start_col = symbol.start_col();
102 let end_col =
103 if !symbol.is_multiline() {
104 symbol.end_col()
105 } else {
106 source_line.len()
107 };
108
109 let mut marker = String::new();
110 marker.extend(iter::repeat(' ').take(margin.len() + start_col));
111 marker.extend(iter::repeat('^').take(usize::max(end_col - start_col, 1)));
112
113 writeln!(fmt, "{}{}", margin, source_line)?;
114 writeln!(fmt, "{}\n", marker)?;
115
116 Ok(())
117}
118
119
120fn render_symbol_lines(symbol: &ResolvedSymbol) -> impl fmt::Display + '_ {
121 utils::make_display(|fmt| fmt_symbol_lines(fmt, symbol))
122}
123
124fn fmt_symbol_lines(fmt: &mut Formatter<'_>, symbol: &ResolvedSymbol) -> fmt::Result {
126 for line in symbol.iter_whole_lines() {
127 fmt.write_str(line.trim_end())?;
128 fmt.write_str("\n")?;
129 }
130 Ok(())
131}
132
133fn render_symbol_single_line(symbol: &ResolvedSymbol) -> impl fmt::Display + '_ {
136 utils::make_display(|fmt| fmt_symbol_single_line(fmt, symbol))
137}
138
139fn fmt_symbol_single_line(fmt: &mut fmt::Formatter<'_>, symbol: &ResolvedSymbol) -> fmt::Result {
140 if let Some(first_line) = symbol.iter_whole_lines().next() {
141 if symbol.is_multiline() {
142 write!(fmt, "{}...", first_line.trim_end())?;
143 } else {
144 fmt.write_str(first_line.trim_end())?;
145 }
146 }
147 Ok(())
148}