diff --git a/codespan-reporting/CHANGELOG.md b/codespan-reporting/CHANGELOG.md index 14c47660..72e1211c 100644 --- a/codespan-reporting/CHANGELOG.md +++ b/codespan-reporting/CHANGELOG.md @@ -64,6 +64,7 @@ This is because some testing dependencies now require this Rust version. ``` +- `FileId`s must now implement `Ord` instead of just `PartialEq`. ## [0.11.1] - 2021-01-18 diff --git a/codespan-reporting/examples/custom_files.rs b/codespan-reporting/examples/custom_files.rs index ba3bdb36..4618cce2 100644 --- a/codespan-reporting/examples/custom_files.rs +++ b/codespan-reporting/examples/custom_files.rs @@ -74,7 +74,7 @@ mod files { } /// An opaque file identifier. - #[derive(Copy, Clone, PartialEq, Eq)] + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct FileId(u32); #[derive(Debug, Clone)] diff --git a/codespan-reporting/src/diagnostic.rs b/codespan-reporting/src/diagnostic.rs index 8e3abf4f..cda4bdfb 100644 --- a/codespan-reporting/src/diagnostic.rs +++ b/codespan-reporting/src/diagnostic.rs @@ -2,6 +2,7 @@ #[cfg(feature = "serialization")] use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; use std::ops::Range; use std::string::ToString; @@ -32,7 +33,7 @@ pub enum Severity { Bug, } -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub enum LabelStyle { /// Labels that describe the primary cause of a diagnostic. @@ -95,7 +96,9 @@ impl Label { /// Represents a diagnostic message that can provide information like errors and /// warnings to the user. /// -/// The position of a Diagnostic is considered to be the position of the [`Label`] that has the earliest starting position and has the highest style which appears in all the labels of the diagnostic. +/// The position of a Diagnostic is considered to be the position of the +/// [`Label`] that has the earliest starting position and has the highest style +/// which appears in all the labels of the diagnostic. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub struct Diagnostic { @@ -188,4 +191,36 @@ impl Diagnostic { self.notes.append(&mut notes); self } + + /// Calculate the locus of this diagnostic. + /// + /// If this Diagnostic covers multple files, a Vec with multiple values + /// will be returned. Or contrary, if there are no labels in the + /// Diagnostic, an empty Vec will be returned. + pub fn locuses(&self) -> BTreeMap + where + FileId: Copy + Ord, + { + let mut labels = self + .labels + .iter() + .map(|label| (label.file_id, label.style, label.range.start)) + .collect::>(); + + labels.sort_unstable_by(|a, b| { + a.0.partial_cmp(&b.0) + .unwrap_or(std::cmp::Ordering::Equal) + .then_with(|| a.1.cmp(&b.1)) + .then_with(|| a.2.cmp(&b.2)) + // reverse the sorting so we can use `Extend` to deduplicate + .reverse() + }); + + labels + .drain(..) + // the label style was only needed for sorting + .map(|(file, _, start)| (file, start)) + // this also deduplicates + .collect() + } } diff --git a/codespan-reporting/src/files.rs b/codespan-reporting/src/files.rs index c2de5b7c..d5bb464a 100644 --- a/codespan-reporting/src/files.rs +++ b/codespan-reporting/src/files.rs @@ -85,7 +85,7 @@ impl std::error::Error for Error { pub trait Files<'a> { /// A unique identifier for files in the file provider. This will be used /// for rendering `diagnostic::Label`s in the corresponding source files. - type FileId: 'a + Copy + PartialEq; + type FileId: 'a + Copy + Ord; /// The user-facing name of a file, to be displayed in diagnostics. type Name: 'a + std::fmt::Display; /// The source code of a file. diff --git a/codespan-reporting/src/term/views.rs b/codespan-reporting/src/term/views.rs index ebfce733..66d7c85d 100644 --- a/codespan-reporting/src/term/views.rs +++ b/codespan-reporting/src/term/views.rs @@ -21,7 +21,7 @@ pub struct RichDiagnostic<'diagnostic, 'config, FileId> { impl<'diagnostic, 'config, FileId> RichDiagnostic<'diagnostic, 'config, FileId> where - FileId: Copy + PartialEq, + FileId: Copy + Ord, { pub fn new( diagnostic: &'diagnostic Diagnostic, @@ -42,12 +42,10 @@ where struct LabeledFile<'diagnostic, FileId> { file_id: FileId, - start: usize, name: String, location: Location, num_multi_labels: usize, lines: BTreeMap>, - max_label_style: LabelStyle, } impl<'diagnostic, FileId> LabeledFile<'diagnostic, FileId> { @@ -83,6 +81,8 @@ where // snippets of source code. let mut outer_padding = 0; + let locuses = self.diagnostic.locuses(); + // Group labels by file for label in &self.diagnostic.labels { let start_line_index = files.line_index(label.file_id, label.range.start)?; @@ -102,29 +102,19 @@ where .iter_mut() .find(|labeled_file| label.file_id == labeled_file.file_id) { - Some(labeled_file) => { - // another diagnostic also referenced this file - if labeled_file.max_label_style > label.style - || (labeled_file.max_label_style == label.style - && labeled_file.start > label.range.start) - { - // this label has a higher style or has the same style but starts earlier - labeled_file.start = label.range.start; - labeled_file.location = files.location(label.file_id, label.range.start)?; - labeled_file.max_label_style = label.style; - } - labeled_file - } + Some(labeled_file) => labeled_file, None => { + let location = *locuses + .get(&label.file_id) + .expect("file missing from locuses"); + // no other diagnostic referenced this file yet labeled_files.push(LabeledFile { file_id: label.file_id, - start: label.range.start, name: files.name(label.file_id)?.to_string(), - location: files.location(label.file_id, label.range.start)?, + location: files.location(label.file_id, location)?, num_multi_labels: 0, lines: BTreeMap::new(), - max_label_style: label.style, }); // this unwrap should never fail because we just pushed an element labeled_files @@ -441,7 +431,7 @@ pub struct ShortDiagnostic<'diagnostic, FileId> { impl<'diagnostic, FileId> ShortDiagnostic<'diagnostic, FileId> where - FileId: Copy + PartialEq, + FileId: Copy + Ord, { pub fn new( diagnostic: &'diagnostic Diagnostic,