Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions codespan-reporting/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ This is because some testing dependencies now require this Rust version.
```

</details>
- `FileId`s must now implement `Ord` instead of just `PartialEq`.

## [0.11.1] - 2021-01-18

Expand Down
2 changes: 1 addition & 1 deletion codespan-reporting/examples/custom_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
39 changes: 37 additions & 2 deletions codespan-reporting/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::ops::Range;
use std::string::ToString;

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -95,7 +96,9 @@ impl<FileId> Label<FileId> {
/// 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<FileId> {
Expand Down Expand Up @@ -188,4 +191,36 @@ impl<FileId> Diagnostic<FileId> {
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<FileId, usize>
where
FileId: Copy + Ord,
{
let mut labels = self
.labels
.iter()
.map(|label| (label.file_id, label.style, label.range.start))
.collect::<Vec<_>>();

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()
}
}
2 changes: 1 addition & 1 deletion codespan-reporting/src/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
30 changes: 10 additions & 20 deletions codespan-reporting/src/term/views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FileId>,
Expand All @@ -42,12 +42,10 @@ where

struct LabeledFile<'diagnostic, FileId> {
file_id: FileId,
start: usize,
name: String,
location: Location,
num_multi_labels: usize,
lines: BTreeMap<usize, Line<'diagnostic>>,
max_label_style: LabelStyle,
}

impl<'diagnostic, FileId> LabeledFile<'diagnostic, FileId> {
Expand Down Expand Up @@ -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)?;
Expand All @@ -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
Expand Down Expand Up @@ -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<FileId>,
Expand Down