rslint_errors/
formatters.rs1use crate::termcolor::{ColorChoice, StandardStream, WriteColor};
2use crate::*;
3use codespan::files::Error;
4use colored::*;
5use file::Files;
6use std::collections::HashSet;
7use std::io;
8
9pub trait Formatter {
14 fn emit_stdout(&mut self, diagnostics: &[Diagnostic], files: &dyn Files) -> io::Result<()> {
15 let stderr = StandardStream::stderr(ColorChoice::Always);
16 let mut out = stderr.lock();
17 self.emit_with_writer(diagnostics, files, &mut out)
18 }
19
20 fn emit_stderr(&mut self, diagnostics: &[Diagnostic], files: &dyn Files) -> io::Result<()> {
21 let stderr = StandardStream::stderr(ColorChoice::Always);
22 let mut out = stderr.lock();
23 self.emit_with_writer(diagnostics, files, &mut out)
24 }
25
26 fn emit_with_writer(
27 &mut self,
28 diagnostics: &[Diagnostic],
29 files: &dyn Files,
30 writer: &mut dyn WriteColor,
31 ) -> io::Result<()>;
32}
33
34#[derive(Debug, Copy, Clone)]
35pub struct ShortFormatter;
36
37impl Formatter for ShortFormatter {
38 fn emit_with_writer(
39 &mut self,
40 diagnostics: &[Diagnostic],
41 files: &dyn Files,
42 writer: &mut dyn WriteColor,
43 ) -> io::Result<()> {
44 let mut ids = HashSet::new();
45 diagnostics.iter().for_each(|d| {
46 ids.insert(d.file_id);
47 });
48 for id in ids {
49 let cur_diags = diagnostics
50 .iter()
51 .filter(|x| x.file_id == id && x.primary.is_some());
52 if cur_diags.clone().count() == 0 {
53 continue;
54 }
55
56 let name = files.name(id).expect("Invalid file id");
57 writeln!(writer, "{}", name.white().underline())?;
58 let mut line_starts = vec![];
59
60 for diag in cur_diags.clone() {
61 let line_index = files
62 .line_index(id, diag.primary.as_ref().unwrap().span.range.start)
63 .expect("Line index out of bounds");
64 let line_span = files.line_range(id, line_index).unwrap();
65 let column = diag.primary.as_ref().unwrap().span.range.start - line_span.start;
66 line_starts.push((line_index, column));
67 }
68 let max_msg_len = cur_diags
69 .clone()
70 .max_by_key(|x| x.title.trim().len())
71 .map(|x| x.title.trim().len())
72 .unwrap();
73
74 let max_severity_len = cur_diags
75 .clone()
76 .map(|x| format!("{:?}", x.severity).len())
77 .max()
78 .unwrap();
79
80 let max_loc = line_starts
81 .iter()
82 .map(|x| x.0.to_string().len() + x.1.to_string().len() + 1)
83 .max()
84 .unwrap();
85 for (diag, (line, column)) in cur_diags.zip(line_starts) {
86 write!(writer, " ")?;
87 write!(
88 writer,
89 "{} ",
90 " ".repeat(max_loc - (line.to_string().len() + column.to_string().len() + 1))
91 )?;
92 write!(
93 writer,
94 "{}{}{} ",
95 line.to_string().truecolor(140, 140, 140),
96 ":".truecolor(140, 140, 140),
97 column.to_string().truecolor(140, 140, 140)
98 )?;
99 let color = match diag.severity {
100 Severity::Bug | Severity::Error => Color::BrightRed,
101 Severity::Note => Color::BrightCyan,
102 Severity::Warning => Color::BrightYellow,
103 Severity::Help => Color::BrightGreen,
104 };
105 let severity_string = format!("{:?}", diag.severity).to_ascii_lowercase();
106 write!(
107 writer,
108 "{}{} ",
109 " ".repeat(max_severity_len - severity_string.len()),
110 severity_string.color(color)
111 )?;
112 write!(
113 writer,
114 "{}{} ",
115 diag.title.trim(),
116 " ".repeat(max_msg_len - diag.title.trim().len())
117 )?;
118 if let Some(code) = diag.code.clone() {
119 write!(writer, "{}", code.white())?;
120 }
121 writeln!(writer)?;
122 }
123 writeln!(writer)?;
124 }
125 Ok(())
126 }
127}
128
129#[derive(Debug, Copy, Clone)]
130pub struct LongFormatter;
131
132impl Formatter for LongFormatter {
133 fn emit_with_writer(
134 &mut self,
135 diagnostics: &[Diagnostic],
136 files: &dyn Files,
137 writer: &mut dyn WriteColor,
138 ) -> io::Result<()> {
139 for diag in diagnostics {
140 match Emitter::new(files).emit_with_writer(diag, writer) {
141 Ok(_) => {}
142 Err(err) => {
143 if let Error::Io(io_err) = err {
144 return Err(io_err);
145 } else {
146 panic!("{}", err)
147 }
148 }
149 }
150 }
151 Ok(())
152 }
153}