1 use crate::config::FileName;
2 use crate::formatting::FormattingError;
3 use crate::{ErrorKind, FormatReport};
4 use annotate_snippets::display_list::DisplayList;
5 use annotate_snippets::formatter::DisplayListFormatter;
6 use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation};
7 use std::fmt::{self, Display};
9 /// A builder for [`FormatReportFormatter`].
10 pub struct FormatReportFormatterBuilder<'a> {
11 report: &'a FormatReport,
15 impl<'a> FormatReportFormatterBuilder<'a> {
16 /// Creates a new [`FormatReportFormatterBuilder`].
17 pub fn new(report: &'a FormatReport) -> Self {
24 /// Enables colors and formatting in the output.
25 pub fn enable_colors(self, enable_colors: bool) -> Self {
32 /// Creates a new [`FormatReportFormatter`] from the settings in this builder.
33 pub fn build(self) -> FormatReportFormatter<'a> {
34 FormatReportFormatter {
36 enable_colors: self.enable_colors,
41 /// Formats the warnings/errors in a [`FormatReport`].
43 /// Can be created using a [`FormatReportFormatterBuilder`].
44 pub struct FormatReportFormatter<'a> {
45 report: &'a FormatReport,
49 impl<'a> Display for FormatReportFormatter<'a> {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 let formatter = DisplayListFormatter::new(self.enable_colors, false);
52 let errors_by_file = &self.report.internal.borrow().0;
54 for (file, errors) in errors_by_file {
56 let snippet = formatting_error_to_snippet(file, error);
57 writeln!(f, "{}\n", formatter.format(&DisplayList::from(snippet)))?;
61 if !errors_by_file.is_empty() {
62 let snippet = formatting_failure_snippet(self.report.warning_count());
63 writeln!(f, "{}", formatter.format(&DisplayList::from(snippet)))?;
70 fn formatting_failure_snippet(warning_count: usize) -> Snippet {
72 title: Some(Annotation {
75 "rustfmt has failed to format. See previous {} errors.",
78 annotation_type: AnnotationType::Warning,
85 fn formatting_error_to_snippet(file: &FileName, error: &FormattingError) -> Snippet {
86 let slices = vec![snippet_code_slice(file, error)];
87 let title = Some(snippet_title(error));
88 let footer = snippet_footer(error).into_iter().collect();
97 fn snippet_title(error: &FormattingError) -> Annotation {
98 let annotation_type = error_kind_to_snippet_annotation_type(&error.kind);
101 id: title_annotation_id(error),
102 label: Some(error.kind.to_string()),
107 fn snippet_footer(error: &FormattingError) -> Option<Annotation> {
108 let message_suffix = error.msg_suffix();
110 if !message_suffix.is_empty() {
113 label: Some(message_suffix.to_string()),
114 annotation_type: AnnotationType::Note,
121 fn snippet_code_slice(file: &FileName, error: &FormattingError) -> Slice {
122 let annotations = slice_annotation(error).into_iter().collect();
123 let origin = Some(format!("{}:{}", file, error.line));
124 let source = error.line_buffer.clone();
128 line_start: error.line,
135 fn slice_annotation(error: &FormattingError) -> Option<SourceAnnotation> {
136 let (range_start, range_length) = error.format_len();
137 let range_end = range_start + range_length;
139 if range_length > 0 {
140 Some(SourceAnnotation {
141 annotation_type: AnnotationType::Error,
142 range: (range_start, range_end),
143 label: String::new(),
150 fn title_annotation_id(error: &FormattingError) -> Option<String> {
151 const INTERNAL_ERROR_ID: &str = "internal";
153 if error.is_internal() {
154 Some(INTERNAL_ERROR_ID.to_string())
160 fn error_kind_to_snippet_annotation_type(error_kind: &ErrorKind) -> AnnotationType {
162 ErrorKind::LineOverflow(..)
163 | ErrorKind::TrailingWhitespace
164 | ErrorKind::IoError(_)
165 | ErrorKind::ModuleResolutionError(_)
166 | ErrorKind::ParseError
167 | ErrorKind::LostComment
168 | ErrorKind::LicenseCheck
170 | ErrorKind::InvalidGlobPattern(_)
171 | ErrorKind::VersionMismatch => AnnotationType::Error,
172 ErrorKind::BadIssue(_) | ErrorKind::DeprecatedAttr => AnnotationType::Warning,