174 lines
5.0 KiB
Rust
174 lines
5.0 KiB
Rust
use crate::config::FileName;
|
|
use crate::formatting::FormattingError;
|
|
use crate::{ErrorKind, FormatReport};
|
|
use annotate_snippets::display_list::DisplayList;
|
|
use annotate_snippets::formatter::DisplayListFormatter;
|
|
use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation};
|
|
use std::fmt::{self, Display};
|
|
|
|
/// A builder for [`FormatReportFormatter`].
|
|
pub struct FormatReportFormatterBuilder<'a> {
|
|
report: &'a FormatReport,
|
|
enable_colors: bool,
|
|
}
|
|
|
|
impl<'a> FormatReportFormatterBuilder<'a> {
|
|
/// Creates a new [`FormatReportFormatterBuilder`].
|
|
pub fn new(report: &'a FormatReport) -> Self {
|
|
Self {
|
|
report,
|
|
enable_colors: false,
|
|
}
|
|
}
|
|
|
|
/// Enables colors and formatting in the output.
|
|
pub fn enable_colors(self, enable_colors: bool) -> Self {
|
|
Self {
|
|
enable_colors,
|
|
..self
|
|
}
|
|
}
|
|
|
|
/// Creates a new [`FormatReportFormatter`] from the settings in this builder.
|
|
pub fn build(self) -> FormatReportFormatter<'a> {
|
|
FormatReportFormatter {
|
|
report: self.report,
|
|
enable_colors: self.enable_colors,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Formats the warnings/errors in a [`FormatReport`].
|
|
///
|
|
/// Can be created using a [`FormatReportFormatterBuilder`].
|
|
pub struct FormatReportFormatter<'a> {
|
|
report: &'a FormatReport,
|
|
enable_colors: bool,
|
|
}
|
|
|
|
impl<'a> Display for FormatReportFormatter<'a> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let formatter = DisplayListFormatter::new(self.enable_colors);
|
|
let errors_by_file = &self.report.internal.borrow().0;
|
|
|
|
for (file, errors) in errors_by_file {
|
|
for error in errors {
|
|
let snippet = formatting_error_to_snippet(file, error);
|
|
writeln!(f, "{}\n", formatter.format(&DisplayList::from(snippet)))?;
|
|
}
|
|
}
|
|
|
|
if !errors_by_file.is_empty() {
|
|
let snippet = formatting_failure_snippet(self.report.warning_count());
|
|
writeln!(f, "{}", formatter.format(&DisplayList::from(snippet)))?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn formatting_failure_snippet(warning_count: usize) -> Snippet {
|
|
Snippet {
|
|
title: Some(Annotation {
|
|
id: None,
|
|
label: Some(format!(
|
|
"rustfmt has failed to format. See previous {} errors.",
|
|
warning_count
|
|
)),
|
|
annotation_type: AnnotationType::Warning,
|
|
}),
|
|
footer: Vec::new(),
|
|
slices: Vec::new(),
|
|
}
|
|
}
|
|
|
|
fn formatting_error_to_snippet(file: &FileName, error: &FormattingError) -> Snippet {
|
|
let slices = vec![snippet_code_slice(file, error)];
|
|
let title = Some(snippet_title(error));
|
|
let footer = snippet_footer(error).into_iter().collect();
|
|
|
|
Snippet {
|
|
title,
|
|
footer,
|
|
slices,
|
|
}
|
|
}
|
|
|
|
fn snippet_title(error: &FormattingError) -> Annotation {
|
|
let annotation_type = error_kind_to_snippet_annotation_type(&error.kind);
|
|
|
|
Annotation {
|
|
id: title_annotation_id(error),
|
|
label: Some(error.kind.to_string()),
|
|
annotation_type,
|
|
}
|
|
}
|
|
|
|
fn snippet_footer(error: &FormattingError) -> Option<Annotation> {
|
|
let message_suffix = error.msg_suffix();
|
|
|
|
if !message_suffix.is_empty() {
|
|
Some(Annotation {
|
|
id: None,
|
|
label: Some(message_suffix.to_string()),
|
|
annotation_type: AnnotationType::Note,
|
|
})
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn snippet_code_slice(file: &FileName, error: &FormattingError) -> Slice {
|
|
let annotations = slice_annotation(error).into_iter().collect();
|
|
let origin = Some(format!("{}:{}", file, error.line));
|
|
let source = error.line_buffer.clone();
|
|
|
|
Slice {
|
|
source,
|
|
line_start: error.line,
|
|
origin,
|
|
fold: false,
|
|
annotations,
|
|
}
|
|
}
|
|
|
|
fn slice_annotation(error: &FormattingError) -> Option<SourceAnnotation> {
|
|
let (range_start, range_length) = error.format_len();
|
|
let range_end = range_start + range_length;
|
|
|
|
if range_length > 0 {
|
|
Some(SourceAnnotation {
|
|
annotation_type: AnnotationType::Error,
|
|
range: (range_start, range_end),
|
|
label: String::new(),
|
|
})
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn title_annotation_id(error: &FormattingError) -> Option<String> {
|
|
const INTERNAL_ERROR_ID: &str = "internal";
|
|
|
|
if error.is_internal() {
|
|
Some(INTERNAL_ERROR_ID.to_string())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn error_kind_to_snippet_annotation_type(error_kind: &ErrorKind) -> AnnotationType {
|
|
match error_kind {
|
|
ErrorKind::LineOverflow(..)
|
|
| ErrorKind::TrailingWhitespace
|
|
| ErrorKind::IoError(_)
|
|
| ErrorKind::ParseError
|
|
| ErrorKind::LostComment
|
|
| ErrorKind::LicenseCheck
|
|
| ErrorKind::BadAttr
|
|
| ErrorKind::InvalidGlobPattern(_)
|
|
| ErrorKind::VersionMismatch => AnnotationType::Error,
|
|
ErrorKind::BadIssue(_) | ErrorKind::DeprecatedAttr => AnnotationType::Warning,
|
|
}
|
|
}
|