From e97c3e26521765148deb888b1acbe328090fd32d Mon Sep 17 00:00:00 2001 From: Mohammad Fawaz Date: Mon, 8 Apr 2024 11:18:10 -0400 Subject: [PATCH 1/2] - cargo clippy and cargo fmt - Clippy and fmt in CI --- .github/workflows/rust.yml | 26 ++++ examples/multifile.rs | 47 ++++--- examples/multiline.rs | 63 +++++---- examples/simple.rs | 15 ++- examples/stresstest.rs | 268 ++++++++++++++++++++++++++++++------- src/display.rs | 8 +- src/draw.rs | 25 +++- src/lib.rs | 154 +++++++++++++++------ src/source.rs | 152 ++++++++++++++------- src/write.rs | 209 +++++++++++++++++------------ 10 files changed, 683 insertions(+), 284 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 79251f5..2916580 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -8,6 +8,7 @@ on: env: CARGO_TERM_COLOR: always + RUST_VERSION: 1.77.1 jobs: build: @@ -20,3 +21,28 @@ jobs: run: cargo build --verbose - name: Run tests run: cargo test --verbose + + cargo-fmt-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} + components: rustfmt + - name: Check Formatting + run: cargo fmt --all -- --check + + cargo-clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} + components: clippy + - uses: Swatinem/rust-cache@v2 + - name: Check Clippy Linter + run: cargo clippy --all-features --all-targets -- -D warnings diff --git a/examples/multifile.rs b/examples/multifile.rs index 201160b..6d9ba0e 100644 --- a/examples/multifile.rs +++ b/examples/multifile.rs @@ -1,4 +1,4 @@ -use ariadne::{Report, ReportKind, Label, ColorGenerator, Fmt, sources}; +use ariadne::{sources, ColorGenerator, Fmt, Label, Report, ReportKind}; fn main() { let mut colors = ColorGenerator::new(); @@ -10,21 +10,36 @@ fn main() { Report::build(ReportKind::Error, "b.tao", 10) .with_code(3) - .with_message(format!("Cannot add types Nat and Str")) - .with_label(Label::new(("b.tao", 10..14)) - .with_message(format!("This is of type {}", "Nat".fg(a))) - .with_color(a)) - .with_label(Label::new(("b.tao", 17..20)) - .with_message(format!("This is of type {}", "Str".fg(b))) - .with_color(b)) - .with_label(Label::new(("b.tao", 15..16)) - .with_message(format!(" {} and {} undergo addition here", "Nat".fg(a), "Str".fg(b))) - .with_color(c) - .with_order(10)) - .with_label(Label::new(("a.tao", 4..8)) - .with_message(format!("Original definition of {} is here", "five".fg(a))) - .with_color(a)) - .with_note(format!("{} is a number and can only be added to other numbers", "Nat".fg(a))) + .with_message("Cannot add types Nat and Str".to_string()) + .with_label( + Label::new(("b.tao", 10..14)) + .with_message(format!("This is of type {}", "Nat".fg(a))) + .with_color(a), + ) + .with_label( + Label::new(("b.tao", 17..20)) + .with_message(format!("This is of type {}", "Str".fg(b))) + .with_color(b), + ) + .with_label( + Label::new(("b.tao", 15..16)) + .with_message(format!( + " {} and {} undergo addition here", + "Nat".fg(a), + "Str".fg(b) + )) + .with_color(c) + .with_order(10), + ) + .with_label( + Label::new(("a.tao", 4..8)) + .with_message(format!("Original definition of {} is here", "five".fg(a))) + .with_color(a), + ) + .with_note(format!( + "{} is a number and can only be added to other numbers", + "Nat".fg(a) + )) .finish() .print(sources(vec![ ("a.tao", include_str!("a.tao")), diff --git a/examples/multiline.rs b/examples/multiline.rs index ad2a77a..e44f508 100644 --- a/examples/multiline.rs +++ b/examples/multiline.rs @@ -1,4 +1,4 @@ -use ariadne::{Report, ReportKind, Label, Source, Color, ColorGenerator, Fmt}; +use ariadne::{Color, ColorGenerator, Fmt, Label, Report, ReportKind, Source}; fn main() { let mut colors = ColorGenerator::new(); @@ -7,36 +7,43 @@ fn main() { let a = colors.next(); let b = colors.next(); let out = Color::Fixed(81); - let out2= colors.next(); + let out2 = colors.next(); Report::build(ReportKind::Error, "sample.tao", 12) .with_code(3) - .with_message(format!("Incompatible types")) - .with_label(Label::new(("sample.tao", 32..33)) - .with_message(format!("This is of type {}", "Nat".fg(a))) - .with_color(a)) - .with_label(Label::new(("sample.tao", 42..45)) - .with_message(format!("This is of type {}", "Str".fg(b))) - .with_color(b)) - .with_label(Label::new(("sample.tao", 11..48)) - .with_message(format!( - "The values are outputs of this {} expression", - "match".fg(out), - )) - .with_color(out)) - .with_label(Label::new(("sample.tao", 0..48)) - .with_message(format!( - "The {} has a problem", - "definition".fg(out2), - )) - .with_color(out2)) - .with_label(Label::new(("sample.tao", 50..76)) - .with_message(format!( - "Usage of {} here", - "definition".fg(out2), - )) - .with_color(out2)) - .with_note(format!("Outputs of {} expressions must coerce to the same type", "match".fg(out))) + .with_message("Incompatible types".to_string()) + .with_label( + Label::new(("sample.tao", 32..33)) + .with_message(format!("This is of type {}", "Nat".fg(a))) + .with_color(a), + ) + .with_label( + Label::new(("sample.tao", 42..45)) + .with_message(format!("This is of type {}", "Str".fg(b))) + .with_color(b), + ) + .with_label( + Label::new(("sample.tao", 11..48)) + .with_message(format!( + "The values are outputs of this {} expression", + "match".fg(out), + )) + .with_color(out), + ) + .with_label( + Label::new(("sample.tao", 0..48)) + .with_message(format!("The {} has a problem", "definition".fg(out2),)) + .with_color(out2), + ) + .with_label( + Label::new(("sample.tao", 50..76)) + .with_message(format!("Usage of {} here", "definition".fg(out2),)) + .with_color(out2), + ) + .with_note(format!( + "Outputs of {} expressions must coerce to the same type", + "match".fg(out) + )) .finish() .print(("sample.tao", Source::from(include_str!("sample.tao")))) .unwrap(); diff --git a/examples/simple.rs b/examples/simple.rs index b78672a..2c8827d 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,4 +1,4 @@ -use ariadne::{Report, ReportKind, Label, Source, Config, Color}; +use ariadne::{Color, Config, Label, Report, ReportKind, Source}; fn main() { Report::build(ReportKind::Error, (), 34) @@ -15,9 +15,18 @@ fn main() { .with_message("Incompatible types") .with_config(Config::default().with_compact(true)) .with_label(Label::new(0..1).with_color(Color::Red)) - .with_label(Label::new(2..3).with_color(Color::Blue).with_message("`b` for banana").with_order(1)) + .with_label( + Label::new(2..3) + .with_color(Color::Blue) + .with_message("`b` for banana") + .with_order(1), + ) .with_label(Label::new(4..5).with_color(Color::Green)) - .with_label(Label::new(7..9).with_color(Color::Cyan).with_message("`e` for emerald")) + .with_label( + Label::new(7..9) + .with_color(Color::Cyan) + .with_message("`e` for emerald"), + ) .finish() .print(Source::from(SOURCE)) .unwrap(); diff --git a/examples/stresstest.rs b/examples/stresstest.rs index 0fc9d5f..3f3064a 100644 --- a/examples/stresstest.rs +++ b/examples/stresstest.rs @@ -1,59 +1,227 @@ -use ariadne::{Report, ReportKind, Label, Source, Config, Color, ColorGenerator}; +use ariadne::{Color, ColorGenerator, Config, Label, Report, ReportKind, Source}; fn main() { let mut colors = ColorGenerator::new(); Report::build(ReportKind::Error, "stresstest.tao", 13) .with_code(3) - .with_message(format!("Incompatible types")) - .with_label(Label::new(("stresstest.tao", 0..1)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 1..2)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 2..3)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 3..4)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 4..5)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 5..6)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 6..7)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 7..8)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 8..9)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 9..10)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 10..11)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 11..12)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 12..13)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 13..14)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 14..15)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 15..16)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 16..17)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 17..18)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 18..19)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 19..20)).with_message("Color").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 20..21)).with_message("Color").with_color(colors.next())) - - .with_label(Label::new(("stresstest.tao", 18..19)).with_message("This is of type Nat").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 13..16)).with_message("This is of type Str").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 40..41)).with_message("This is of type Nat").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 43..47)).with_message("This is of type Bool").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 49..51)).with_message("This is of type ()").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 53..55)).with_message("This is of type [_]").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 25..78)).with_message("This is of type Str").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 81..124)).with_message("This is of type Nat").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 100..126)).with_message("This is an inner multi-line").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 106..120)).with_message("This is another inner multi-line").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 108..122)).with_message("This is *really* nested multi-line").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 110..111)).with_message("This is an inline within the nesting!").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 111..112)).with_message("And another!").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 103..123)).with_message("This is *really* nested multi-line").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 105..125)).with_message("This is *really* nested multi-line").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 112..116)).with_message("This is *really* nested multi-line").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 26..100)).with_message("Hahaha!").with_color(Color::Fixed(75))) - .with_label(Label::new(("stresstest.tao", 85..110)).with_message("Oh god, no more 1").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 84..114)).with_message("Oh god, no more 2").with_color(colors.next())) - .with_label(Label::new(("stresstest.tao", 89..113)).with_message("Oh god, no more 3").with_color(colors.next())) - .with_config(Config::default() - .with_cross_gap(false) - .with_compact(true) - .with_underlines(true) - .with_tab_width(4)) + .with_message("Incompatible types".to_string()) + .with_label( + Label::new(("stresstest.tao", 0..1)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 1..2)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 2..3)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 3..4)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 4..5)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 5..6)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 6..7)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 7..8)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 8..9)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 9..10)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 10..11)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 11..12)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 12..13)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 13..14)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 14..15)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 15..16)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 16..17)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 17..18)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 18..19)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 19..20)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 20..21)) + .with_message("Color") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 18..19)) + .with_message("This is of type Nat") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 13..16)) + .with_message("This is of type Str") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 40..41)) + .with_message("This is of type Nat") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 43..47)) + .with_message("This is of type Bool") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 49..51)) + .with_message("This is of type ()") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 53..55)) + .with_message("This is of type [_]") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 25..78)) + .with_message("This is of type Str") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 81..124)) + .with_message("This is of type Nat") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 100..126)) + .with_message("This is an inner multi-line") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 106..120)) + .with_message("This is another inner multi-line") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 108..122)) + .with_message("This is *really* nested multi-line") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 110..111)) + .with_message("This is an inline within the nesting!") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 111..112)) + .with_message("And another!") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 103..123)) + .with_message("This is *really* nested multi-line") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 105..125)) + .with_message("This is *really* nested multi-line") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 112..116)) + .with_message("This is *really* nested multi-line") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 26..100)) + .with_message("Hahaha!") + .with_color(Color::Fixed(75)), + ) + .with_label( + Label::new(("stresstest.tao", 85..110)) + .with_message("Oh god, no more 1") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 84..114)) + .with_message("Oh god, no more 2") + .with_color(colors.next()), + ) + .with_label( + Label::new(("stresstest.tao", 89..113)) + .with_message("Oh god, no more 3") + .with_color(colors.next()), + ) + .with_config( + Config::default() + .with_cross_gap(false) + .with_compact(true) + .with_underlines(true) + .with_tab_width(4), + ) .finish() - .print(("stresstest.tao", Source::from(include_str!("stresstest.tao")))) + .print(( + "stresstest.tao", + Source::from(include_str!("stresstest.tao")), + )) .unwrap(); } diff --git a/src/display.rs b/src/display.rs index e089b98..d074009 100644 --- a/src/display.rs +++ b/src/display.rs @@ -14,8 +14,8 @@ impl Display for Show> { impl<'a, T, F: Fn(&mut fmt::Formatter, &'a T) -> fmt::Result> Display for Show<(&'a [T], F)> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for x in self.0.0 { - (self.0.1)(f, x)?; + for x in self.0 .0 { + (self.0 .1)(f, x)?; } Ok(()) } @@ -23,8 +23,8 @@ impl<'a, T, F: Fn(&mut fmt::Formatter, &'a T) -> fmt::Result> Display for Show<( impl Display for Show<(T, usize)> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for _ in 0..self.0.1 { - write!(f, "{}", self.0.0)?; + for _ in 0..self.0 .1 { + write!(f, "{}", self.0 .0)?; } Ok(()) } diff --git a/src/draw.rs b/src/draw.rs index a2a12b3..d732dec 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -222,7 +222,9 @@ pub struct ColorGenerator { } impl Default for ColorGenerator { - fn default() -> Self { Self::from_state([30000, 15000, 35000], 0.5) } + fn default() -> Self { + Self::from_state([30000, 15000, 35000], 0.5) + } } impl ColorGenerator { @@ -230,7 +232,10 @@ impl ColorGenerator { /// /// The minimum brightness can be used to control the colour brightness (0.0 - 1.0). The default is 0.5. pub fn from_state(state: [u16; 3], min_brightness: f32) -> Self { - Self { state, min_brightness: min_brightness.max(0.0).min(1.0) } + Self { + state, + min_brightness: min_brightness.max(0.0).min(1.0), + } } /// Create a new [`ColorGenerator`] with the default state. @@ -239,14 +244,22 @@ impl ColorGenerator { } /// Generate the next colour in the sequence. + #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Color { for i in 0..3 { // magic constant, one of only two that have this property! self.state[i] = (self.state[i] as usize).wrapping_add(40503 * (i * 4 + 1130)) as u16; } - Color::Fixed(16 - + ((self.state[2] as f32 / 65535.0 * (1.0 - self.min_brightness) + self.min_brightness) * 5.0 - + (self.state[1] as f32 / 65535.0 * (1.0 - self.min_brightness) + self.min_brightness) * 30.0 - + (self.state[0] as f32 / 65535.0 * (1.0 - self.min_brightness) + self.min_brightness) * 180.0) as u8) + Color::Fixed( + 16 + ((self.state[2] as f32 / 65535.0 * (1.0 - self.min_brightness) + + self.min_brightness) + * 5.0 + + (self.state[1] as f32 / 65535.0 * (1.0 - self.min_brightness) + + self.min_brightness) + * 30.0 + + (self.state[0] as f32 / 65535.0 * (1.0 - self.min_brightness) + + self.min_brightness) + * 180.0) as u8, + ) } } diff --git a/src/lib.rs b/src/lib.rs index 691ce60..39eb602 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,14 @@ #![doc = include_str!("../README.md")] #![deny(missing_docs)] -mod source; mod display; mod draw; +mod source; mod write; pub use crate::{ - source::{Line, Source, Cache, FileCache, FnCache, sources}, - draw::{Fmt, ColorGenerator}, + draw::{ColorGenerator, Fmt}, + source::{sources, Cache, FileCache, FnCache, Line, Source}, }; pub use yansi::Color; @@ -17,11 +17,11 @@ pub use crate::draw::StdoutFmt; use crate::display::*; use std::{ - ops::Range, - io::{self, Write}, - hash::Hash, - cmp::{PartialEq, Eq}, + cmp::{Eq, PartialEq}, fmt, + hash::Hash, + io::{self, Write}, + ops::Range, }; use unicode_width::UnicodeWidthChar; @@ -46,26 +46,47 @@ pub trait Span { fn end(&self) -> usize; /// Get the length of this span (difference between the start of the span and the end of the span). - fn len(&self) -> usize { self.end().saturating_sub(self.start()) } + fn len(&self) -> usize { + self.end().saturating_sub(self.start()) + } + + /// Returns `true` if this span has length zero. + fn is_empty(&self) -> bool { + self.len() == 0 + } /// Determine whether the span contains the given offset. - fn contains(&self, offset: usize) -> bool { (self.start()..self.end()).contains(&offset) } + fn contains(&self, offset: usize) -> bool { + (self.start()..self.end()).contains(&offset) + } } impl Span for Range { type SourceId = (); - fn source(&self) -> &Self::SourceId { &() } - fn start(&self) -> usize { self.start } - fn end(&self) -> usize { self.end } + fn source(&self) -> &Self::SourceId { + &() + } + fn start(&self) -> usize { + self.start + } + fn end(&self) -> usize { + self.end + } } impl Span for (Id, Range) { type SourceId = Id; - fn source(&self) -> &Self::SourceId { &self.0 } - fn start(&self) -> usize { self.1.start } - fn end(&self) -> usize { self.1.end } + fn source(&self) -> &Self::SourceId { + &self.0 + } + fn start(&self) -> usize { + self.1.start + } + fn end(&self) -> usize { + self.1.end + } } /// A type that represents the way a label should be displayed. @@ -92,19 +113,16 @@ impl Label { /// /// Panics if the given span is backwards. pub fn new(span: S) -> Self { - assert!( - span.start() <= span.end(), - "Label start is after its end" - ); + assert!(span.start() <= span.end(), "Label start is after its end"); Self { span, - display_info: LabelDisplay{ + display_info: LabelDisplay { msg: None, color: None, order: 0, priority: 0, - } + }, } } @@ -164,7 +182,11 @@ pub struct Report<'a, S: Span = Range> { impl Report<'_, S> { /// Begin building a new [`Report`]. - pub fn build::Owned>>(kind: ReportKind, src_id: Id, offset: usize) -> ReportBuilder { + pub fn build::Owned>>( + kind: ReportKind, + src_id: Id, + offset: usize, + ) -> ReportBuilder { ReportBuilder { kind, code: None, @@ -289,7 +311,10 @@ impl<'a, S: Span> ReportBuilder<'a, S> { /// Add multiple labels to the report. pub fn add_labels>>(&mut self, labels: L) { let config = &self.config; // This would not be necessary in Rust 2021 edition - self.labels.extend(labels.into_iter().map(|mut label| { label.display_info.color = config.filter_color(label.display_info.color); label })); + self.labels.extend(labels.into_iter().map(|mut label| { + label.display_info.color = config.filter_color(label.display_info.color); + label + })); } /// Add a label to the report. @@ -378,7 +403,7 @@ pub struct Config { color: bool, tab_width: usize, char_set: CharSet, - index_type : IndexType, + index_type: IndexType, } impl Config { @@ -387,48 +412,91 @@ impl Config { /// The alternative to this is to insert crossing characters. However, these interact poorly with label colours. /// /// If unspecified, this defaults to [`false`]. - pub fn with_cross_gap(mut self, cross_gap: bool) -> Self { self.cross_gap = cross_gap; self } + pub fn with_cross_gap(mut self, cross_gap: bool) -> Self { + self.cross_gap = cross_gap; + self + } /// Where should inline labels attach to their spans? /// /// If unspecified, this defaults to [`LabelAttach::Middle`]. - pub fn with_label_attach(mut self, label_attach: LabelAttach) -> Self { self.label_attach = label_attach; self } + pub fn with_label_attach(mut self, label_attach: LabelAttach) -> Self { + self.label_attach = label_attach; + self + } /// Should the report remove gaps to minimise used space? /// /// If unspecified, this defaults to [`false`]. - pub fn with_compact(mut self, compact: bool) -> Self { self.compact = compact; self } + pub fn with_compact(mut self, compact: bool) -> Self { + self.compact = compact; + self + } /// Should underlines be used for label span where possible? /// /// If unspecified, this defaults to [`true`]. - pub fn with_underlines(mut self, underlines: bool) -> Self { self.underlines = underlines; self } + pub fn with_underlines(mut self, underlines: bool) -> Self { + self.underlines = underlines; + self + } /// Should arrows be used to point to the bounds of multi-line spans? /// /// If unspecified, this defaults to [`true`]. - pub fn with_multiline_arrows(mut self, multiline_arrows: bool) -> Self { self.multiline_arrows = multiline_arrows; self } + pub fn with_multiline_arrows(mut self, multiline_arrows: bool) -> Self { + self.multiline_arrows = multiline_arrows; + self + } /// Should colored output should be enabled? /// /// If unspecified, this defaults to [`true`]. - pub fn with_color(mut self, color: bool) -> Self { self.color = color; self } + pub fn with_color(mut self, color: bool) -> Self { + self.color = color; + self + } /// How many characters width should tab characters be? /// /// If unspecified, this defaults to `4`. - pub fn with_tab_width(mut self, tab_width: usize) -> Self { self.tab_width = tab_width; self } + pub fn with_tab_width(mut self, tab_width: usize) -> Self { + self.tab_width = tab_width; + self + } /// What character set should be used to display dynamic elements such as boxes and arrows? /// /// If unspecified, this defaults to [`CharSet::Unicode`]. - pub fn with_char_set(mut self, char_set: CharSet) -> Self { self.char_set = char_set; self } + pub fn with_char_set(mut self, char_set: CharSet) -> Self { + self.char_set = char_set; + self + } /// Should this report use byte spans instead of char spans? /// /// If unspecified, this defaults to 'false' - pub fn with_index_type(mut self, index_type : IndexType) -> Self { self.index_type = index_type; self } + pub fn with_index_type(mut self, index_type: IndexType) -> Self { + self.index_type = index_type; + self + } - fn error_color(&self) -> Option { Some(Color::Red).filter(|_| self.color) } - fn warning_color(&self) -> Option { Some(Color::Yellow).filter(|_| self.color) } - fn advice_color(&self) -> Option { Some(Color::Fixed(147)).filter(|_| self.color) } - fn margin_color(&self) -> Option { Some(Color::Fixed(246)).filter(|_| self.color) } - fn skipped_margin_color(&self) -> Option { Some(Color::Fixed(240)).filter(|_| self.color) } - fn unimportant_color(&self) -> Option { Some(Color::Fixed(249)).filter(|_| self.color) } - fn note_color(&self) -> Option { Some(Color::Fixed(115)).filter(|_| self.color) } - fn filter_color(&self, color: Option) -> Option { color.filter(|_| self.color) } + fn error_color(&self) -> Option { + Some(Color::Red).filter(|_| self.color) + } + fn warning_color(&self) -> Option { + Some(Color::Yellow).filter(|_| self.color) + } + fn advice_color(&self) -> Option { + Some(Color::Fixed(147)).filter(|_| self.color) + } + fn margin_color(&self) -> Option { + Some(Color::Fixed(246)).filter(|_| self.color) + } + fn skipped_margin_color(&self) -> Option { + Some(Color::Fixed(240)).filter(|_| self.color) + } + fn unimportant_color(&self) -> Option { + Some(Color::Fixed(249)).filter(|_| self.color) + } + fn note_color(&self) -> Option { + Some(Color::Fixed(115)).filter(|_| self.color) + } + fn filter_color(&self, color: Option) -> Option { + color.filter(|_| self.color) + } // Find the character that should be drawn and the number of times it should be drawn for each char fn char_width(&self, c: char, col: usize) -> (char, usize) { @@ -436,8 +504,8 @@ impl Config { '\t' => { // Find the column that the tab should end at let tab_end = (col / self.tab_width + 1) * self.tab_width; - (' ', tab_end - col) - }, + (' ', tab_end - col) + } c if c.is_whitespace() => (' ', 1), _ => (c, c.width().unwrap_or(1)), } diff --git a/src/source.rs b/src/source.rs index 3f198a7..95c60ac 100644 --- a/src/source.rs +++ b/src/source.rs @@ -1,9 +1,10 @@ use super::*; use std::{ + collections::{hash_map::Entry, HashMap}, + fs, + mem::replace, path::{Path, PathBuf}, - collections::{HashMap, hash_map::Entry}, - fs, mem::replace, }; /// A trait implemented by [`Source`] caches. @@ -29,15 +30,23 @@ pub trait Cache { impl<'b, C: Cache, Id: ?Sized> Cache for &'b mut C { type Storage = C::Storage; - fn fetch(&mut self, id: &Id) -> Result<&Source, Box> { C::fetch(self, id) } - fn display<'a>(&self, id: &'a Id) -> Option> { C::display(self, id) } + fn fetch(&mut self, id: &Id) -> Result<&Source, Box> { + C::fetch(self, id) + } + fn display<'a>(&self, id: &'a Id) -> Option> { + C::display(self, id) + } } impl, Id: ?Sized> Cache for Box { type Storage = C::Storage; - fn fetch(&mut self, id: &Id) -> Result<&Source, Box> { C::fetch(self, id) } - fn display<'a>(&self, id: &'a Id) -> Option> { C::display(self, id) } + fn fetch(&mut self, id: &Id) -> Result<&Source, Box> { + C::fetch(self, id) + } + fn display<'a>(&self, id: &'a Id) -> Option> { + C::display(self, id) + } } /// A type representing a single line of a [`Source`]. @@ -51,17 +60,30 @@ pub struct Line { impl Line { /// Get the offset of this line in the original [`Source`] (i.e: the number of characters that precede it). - pub fn offset(&self) -> usize { self.offset } + pub fn offset(&self) -> usize { + self.offset + } /// Get the character length of this line. - pub fn len(&self) -> usize { self.char_len } + pub fn len(&self) -> usize { + self.char_len + } + + /// Returns `true` if this line contains no characters. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } /// Get the offset span of this line in the original [`Source`]. - pub fn span(&self) -> Range { self.offset..self.offset + self.char_len } + pub fn span(&self) -> Range { + self.offset..self.offset + self.char_len + } /// Get the byte offset span of this line in the original [`Source`]. This can be used to /// directly slice into its source text. - fn byte_span(&self) -> Range { self.byte_offset..self.byte_offset + self.byte_len } + fn byte_span(&self) -> Range { + self.byte_offset..self.byte_offset + self.byte_len + } } /// A type representing a single source that may be referred to by [`Span`]s. @@ -72,7 +94,7 @@ pub struct Source = String> { text: I, lines: Vec, len: usize, - byte_len : usize + byte_len: usize, } impl> Source { @@ -94,13 +116,13 @@ impl> From for Source { let mut lines: Vec = input .as_ref() .split_inclusive([ - '\r', // Carriage return - '\n', // Line feed - '\x0B', // Vertical tab - '\x0C', // Form feed + '\r', // Carriage return + '\n', // Line feed + '\x0B', // Vertical tab + '\x0C', // Form feed '\u{0085}', // Next line '\u{2028}', // Line separator - '\u{2029}' // Paragraph separator + '\u{2029}', // Paragraph separator ]) .flat_map(|line| { // Returns last line and set `last_line` to current `line` @@ -112,7 +134,7 @@ impl> From for Source { last.byte_len += 1; char_offset += 1; byte_offset += 1; - return replace(&mut last_line, None).map(|(l, _)| l); + return last_line.take().map(|(l, _)| l); } } @@ -138,23 +160,36 @@ impl> From for Source { text: input, lines, len: char_offset, - byte_len: byte_offset + byte_len: byte_offset, } } } impl> Source { /// Get the length of the total number of characters in the source. - pub fn len(&self) -> usize { self.len } + pub fn len(&self) -> usize { + self.len + } + + /// Returns `true` if this source contains no characters. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } /// Return an iterator over the characters in the source. - pub fn chars(&self) -> impl Iterator + '_ { self.text.as_ref().chars() } + pub fn chars(&self) -> impl Iterator + '_ { + self.text.as_ref().chars() + } /// Get access to a specific, zero-indexed [`Line`]. - pub fn line(&self, idx: usize) -> Option { self.lines.get(idx).copied() } + pub fn line(&self, idx: usize) -> Option { + self.lines.get(idx).copied() + } /// Return an iterator over the [`Line`]s in this source. - pub fn lines(&self) -> impl ExactSizeIterator + '_ { self.lines.iter().copied() } + pub fn lines(&self) -> impl ExactSizeIterator + '_ { + self.lines.iter().copied() + } /// Get the line that the given offset appears on, and the line/column numbers of the offset. /// @@ -221,8 +256,12 @@ impl> Source { impl> Cache<()> for Source { type Storage = I; - fn fetch(&mut self, _: &()) -> Result<&Source, Box> { Ok(self) } - fn display(&self, _: &()) -> Option> { None } + fn fetch(&mut self, _: &()) -> Result<&Source, Box> { + Ok(self) + } + fn display(&self, _: &()) -> Option> { + None + } } impl, Id: fmt::Display + Eq> Cache for (Id, Source) { @@ -235,7 +274,9 @@ impl, Id: fmt::Display + Eq> Cache for (Id, Source) { Err(Box::new(format!("Failed to fetch source '{}'", id))) } } - fn display<'a>(&self, id: &'a Id) -> Option> { Some(Box::new(id)) } + fn display<'a>(&self, id: &'a Id) -> Option> { + Some(Box::new(id)) + } } /// A [`Cache`] that fetches [`Source`]s from the filesystem. @@ -248,25 +289,32 @@ impl Cache for FileCache { type Storage = String; fn fetch(&mut self, path: &Path) -> Result<&Source, Box> { - Ok(match self.files.entry(path.to_path_buf()) { // TODO: Don't allocate here + Ok(match self.files.entry(path.to_path_buf()) { + // TODO: Don't allocate here Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.insert(Source::from(fs::read_to_string(path).map_err(|e| Box::new(e) as _)?)), + Entry::Vacant(entry) => entry.insert(Source::from( + fs::read_to_string(path).map_err(|e| Box::new(e) as _)?, + )), }) } - fn display<'a>(&self, path: &'a Path) -> Option> { Some(Box::new(path.display())) } + fn display<'a>(&self, path: &'a Path) -> Option> { + Some(Box::new(path.display())) + } } /// A [`Cache`] that fetches [`Source`]s using the provided function. #[derive(Debug, Clone)] pub struct FnCache - where I: AsRef +where + I: AsRef, { sources: HashMap>, get: F, } impl FnCache - where I: AsRef +where + I: AsRef, { /// Create a new [`FnCache`] with the given fetch function. pub fn new(get: F) -> Self { @@ -278,7 +326,8 @@ impl FnCache /// Pre-insert a selection of [`Source`]s into this cache. pub fn with_sources(mut self, sources: HashMap>) -> Self - where Id: Eq + Hash + where + Id: Eq + Hash, { self.sources.reserve(sources.len()); for (id, src) in sources { @@ -289,8 +338,9 @@ impl FnCache } impl Cache for FnCache - where I: AsRef, - F: for<'a> FnMut(&'a Id) -> Result>, +where + I: AsRef, + F: for<'a> FnMut(&'a Id) -> Result>, { type Storage = I; @@ -300,20 +350,26 @@ impl Cache for FnCac Entry::Vacant(entry) => entry.insert(Source::from((self.get)(id)?)), }) } - fn display<'a>(&self, id: &'a Id) -> Option> { Some(Box::new(id)) } + fn display<'a>(&self, id: &'a Id) -> Option> { + Some(Box::new(id)) + } } /// Create a [`Cache`] from a collection of ID/strings, where each corresponds to a [`Source`]. pub fn sources(iter: I) -> impl Cache - where Id: fmt::Display + Hash + PartialEq + Eq + Clone + 'static, - I: IntoIterator, - S: AsRef, +where + Id: fmt::Display + Hash + PartialEq + Eq + Clone + 'static, + I: IntoIterator, + S: AsRef, { - FnCache::new((move |id| Err(Box::new(format!("Failed to fetch source '{}'", id)) as _)) as fn(&_) -> _) - .with_sources(iter - .into_iter() + FnCache::new( + (move |id| Err(Box::new(format!("Failed to fetch source '{}'", id)) as _)) as fn(&_) -> _, + ) + .with_sources( + iter.into_iter() .map(|(id, s)| (id, Source::from(s))) - .collect()) + .collect(), + ) } #[cfg(test)] @@ -324,7 +380,7 @@ mod tests { use super::Source; fn test_with_lines(lines: Vec<&str>) { - let source: String = lines.iter().map(|s| *s).collect(); + let source: String = lines.iter().copied().collect(); let source = Source::from(source); assert_eq!(source.lines.len(), lines.len()); @@ -333,10 +389,7 @@ mod tests { for (source_line, raw_line) in zip(source.lines.iter().copied(), lines.into_iter()) { assert_eq!(source_line.offset, offset); assert_eq!(source_line.char_len, raw_line.chars().count()); - assert_eq!( - source.get_line_text(source_line).unwrap(), - raw_line - ); + assert_eq!(source.get_line_text(source_line).unwrap(), raw_line); offset += source_line.char_len; } @@ -396,10 +449,7 @@ mod tests { { assert_eq!(source_line.offset, offset); assert_eq!(source_line.char_len, raw_line.chars().count()); - assert_eq!( - source.get_line_text(source_line).unwrap(), - raw_line - ); + assert_eq!(source.get_line_text(source_line).unwrap(), raw_line); offset += source_line.char_len; } @@ -412,7 +462,7 @@ mod tests { with multiple lines"#; - fn non_owning_source<'a>(input: &'a str) -> Source<&'a str> { + fn non_owning_source(input: &str) -> Source<&str> { Source::from(input) } diff --git a/src/write.rs b/src/write.rs index b2ae739..166559d 100644 --- a/src/write.rs +++ b/src/write.rs @@ -23,12 +23,15 @@ enum LabelKind { struct LabelInfo<'a> { kind: LabelKind, char_span: Range, - display_info : &'a LabelDisplay, + display_info: &'a LabelDisplay, } impl<'a> LabelInfo<'a> { fn last_offset(&self) -> usize { - self.char_span.end.saturating_sub(1).max(self.char_span.start) + self.char_span + .end + .saturating_sub(1) + .max(self.char_span.start) } } @@ -57,17 +60,25 @@ impl Report<'_, S> { let (label_char_span, start_line, end_line) = match self.config.index_type { IndexType::Char => { - let Some(start_line) = src.get_offset_line(given_label_span.start) else {continue}; + let Some(start_line) = src.get_offset_line(given_label_span.start) else { + continue; + }; let end_line = if given_label_span.start >= given_label_span.end { start_line.1 } else { - let Some(end_line) = src.get_offset_line(given_label_span.end - 1) else {continue}; + let Some(end_line) = src.get_offset_line(given_label_span.end - 1) else { + continue; + }; end_line.1 }; (given_label_span, start_line.1, end_line) - }, + } IndexType::Byte => { - let Some((start_line_obj, start_line, start_byte_col)) = src.get_byte_line(given_label_span.start) else {continue;}; + let Some((start_line_obj, start_line, start_byte_col)) = + src.get_byte_line(given_label_span.start) + else { + continue; + }; let line_text = src.get_line_text(start_line_obj).unwrap(); let num_chars_before_start = line_text[..start_byte_col.min(line_text.len())] @@ -78,12 +89,17 @@ impl Report<'_, S> { if given_label_span.start >= given_label_span.end { (start_char_offset..start_char_offset, start_line, start_line) } else { - // We can subtract 1 from end, because get_byte_line doesn't actually index into the text. + // We can subtract 1 from end, because get_byte_line doesn't actually index into the text. let end_pos = given_label_span.end - 1; - let Some((end_line_obj, end_line, end_byte_col)) = src.get_byte_line(end_pos) else {continue}; + let Some((end_line_obj, end_line, end_byte_col)) = + src.get_byte_line(end_pos) + else { + continue; + }; let end_line_text = src.get_line_text(end_line_obj).unwrap(); - // Have to add 1 back now, so we don't cut a char in two. - let num_chars_before_end = end_line_text[..end_byte_col+1].chars().count(); + // Have to add 1 back now, so we don't cut a char in two. + let num_chars_before_end = + end_line_text[..end_byte_col + 1].chars().count(); let end_char_offset = end_line_obj.offset() + num_chars_before_end; (start_char_offset..end_char_offset, start_line, end_line) @@ -169,29 +185,33 @@ impl Report<'_, S> { // Line number maximum width let line_no_width = groups .iter() - .filter_map(|SourceGroup { char_span, src_id, .. }| { - let src_name = cache - .display(src_id) - .map(|d| d.to_string()) - .unwrap_or_else(|| "".to_string()); - - let src = match cache.fetch(src_id) { - Ok(src) => src, - Err(e) => { - eprintln!("Unable to fetch source {}: {:?}", src_name, e); - return None; - } - }; + .filter_map( + |SourceGroup { + char_span, src_id, .. + }| { + let src_name = cache + .display(src_id) + .map(|d| d.to_string()) + .unwrap_or_else(|| "".to_string()); + + let src = match cache.fetch(src_id) { + Ok(src) => src, + Err(e) => { + eprintln!("Unable to fetch source {}: {:?}", src_name, e); + return None; + } + }; - let line_range = src.get_line_range(char_span); - Some( - (1..) - .map(|x| 10u32.pow(x)) - .take_while(|x| line_range.end as u32 / x != 0) - .count() - + 1, - ) - }) + let line_range = src.get_line_range(char_span); + Some( + (1..) + .map(|x| 10u32.pow(x)) + .take_while(|x| line_range.end as u32 / x != 0) + .count() + + 1, + ) + }, + ) .max() .unwrap_or(0); @@ -321,7 +341,9 @@ impl Report<'_, S> { // Multi-line margins if draw_labels { - for col in 0..multi_labels_with_message.len() + (multi_labels_with_message.len() > 0) as usize { + for col in 0..multi_labels_with_message.len() + + (!multi_labels_with_message.is_empty()) as usize + { let mut corner = None; let mut hbar: Option<&LabelInfo> = None; let mut vbar: Option<&LabelInfo> = None; @@ -330,7 +352,8 @@ impl Report<'_, S> { let multi_label = multi_labels_with_message.get(col); let line_span = src.line(idx).unwrap().span(); - for (i, label) in multi_labels_with_message[0..(col + 1).min(multi_labels_with_message.len())] + for (i, label) in multi_labels_with_message + [0..(col + 1).min(multi_labels_with_message.len())] .iter() .enumerate() { @@ -381,8 +404,8 @@ impl Report<'_, S> { } if let (Some((margin, _is_start)), true) = (margin_ptr, is_line) { - let is_col = multi_label - .map_or(false, |ml| std::ptr::eq(*ml, margin.label)); + let is_col = + multi_label.map_or(false, |ml| std::ptr::eq(*ml, margin.label)); let is_limit = col + 1 == multi_labels_with_message.len(); if !is_col && !is_limit { hbar = hbar.or(Some(margin.label)); @@ -398,15 +421,22 @@ impl Report<'_, S> { let (a, b) = if let Some((label, is_start)) = corner { ( - if is_start { draw.ltop } else { draw.lbot }.fg(label.display_info.color, s), + if is_start { draw.ltop } else { draw.lbot } + .fg(label.display_info.color, s), draw.hbar.fg(label.display_info.color, s), ) } else if let Some(label) = hbar.filter(|_| vbar.is_some() && !self.config.cross_gap) { - (draw.xbar.fg(label.display_info.color, s), draw.hbar.fg(label.display_info.color, s)) + ( + draw.xbar.fg(label.display_info.color, s), + draw.hbar.fg(label.display_info.color, s), + ) } else if let Some(label) = hbar { - (draw.hbar.fg(label.display_info.color, s), draw.hbar.fg(label.display_info.color, s)) + ( + draw.hbar.fg(label.display_info.color, s), + draw.hbar.fg(label.display_info.color, s), + ) } else if let Some(label) = vbar { ( if is_ellipsis { @@ -418,8 +448,8 @@ impl Report<'_, S> { ' '.fg(None, s), ) } else if let (Some((margin, is_start)), true) = (margin_ptr, is_line) { - let is_col = multi_label - .map_or(false, |ml| std::ptr::eq(*ml, margin.label)); + let is_col = + multi_label.map_or(false, |ml| std::ptr::eq(*ml, margin.label)); let is_limit = col == multi_labels_with_message.len(); ( if is_limit { @@ -434,7 +464,8 @@ impl Report<'_, S> { draw.hbar } .fg(margin.label.display_info.color, s), - if !is_limit { draw.hbar } else { ' ' }.fg(margin.label.display_info.color, s), + if !is_limit { draw.hbar } else { ' ' } + .fg(margin.label.display_info.color, s), ) } else { (' '.fg(None, s), ' '.fg(None, s)) @@ -467,14 +498,14 @@ impl Report<'_, S> { // TODO: Check to see whether multi is the first on the start line or first on the end line Some(LineLabel { col: label.char_span.start - line.offset(), - label: *label, + label, multi: true, draw_msg: false, // Multi-line spans don;t have their messages drawn at the start }) } else if is_end { Some(LineLabel { col: label.last_offset() - line.offset(), - label: *label, + label, multi: true, draw_msg: true, // Multi-line spans have their messages drawn at the end }) @@ -499,14 +530,14 @@ impl Report<'_, S> { // TODO: Check to see whether multi is the first on the start line or first on the end line Some(LineLabel { col: label.char_span.start - line.offset(), - label: *label, + label, multi: true, draw_msg: false, // Multi-line spans don;t have their messages drawn at the start }) } else if is_end { Some(LineLabel { col: label.last_offset() - line.offset(), - label: *label, + label, multi: true, draw_msg: true, // Multi-line spans have their messages drawn at the end }) @@ -517,16 +548,14 @@ impl Report<'_, S> { .collect::>(); for label_info in labels.iter().filter(|l| { - l.char_span.start >= line.span().start - && l.char_span.end <= line.span().end + l.char_span.start >= line.span().start && l.char_span.end <= line.span().end }) { if matches!(label_info.kind, LabelKind::Inline) { line_labels.push(LineLabel { col: match &self.config.label_attach { LabelAttach::Start => label_info.char_span.start, LabelAttach::Middle => { - (label_info.char_span.start + label_info.char_span.end) - / 2 + (label_info.char_span.start + label_info.char_span.end) / 2 } LabelAttach::End => label_info.last_offset(), } @@ -540,7 +569,7 @@ impl Report<'_, S> { } // Skip this line if we don't have labels for it - if line_labels.len() == 0 && margin_label.is_none() { + if line_labels.is_empty() && margin_label.is_none() { let within_label = multi_labels .iter() .any(|label| label.char_span.contains(&line.span().start())); @@ -549,7 +578,7 @@ impl Report<'_, S> { } else { if !self.config.compact && !is_ellipsis { write_margin(&mut w, idx, false, is_ellipsis, false, None, &[], &None)?; - write!(w, "\n")?; + writeln!(w)?; } is_ellipsis = true; continue; @@ -559,7 +588,13 @@ impl Report<'_, S> { } // Sort the labels by their columns - line_labels.sort_by_key(|ll| (ll.label.display_info.order, ll.col, !ll.label.char_span.start)); + line_labels.sort_by_key(|ll| { + ( + ll.label.display_info.order, + ll.col, + !ll.label.char_span.start, + ) + }); // Determine label bounds so we know where to put error messages let arrow_end_space = if self.config.compact { 1 } else { 2 }; @@ -583,9 +618,7 @@ impl Report<'_, S> { .as_ref() .map_or(true, |m| !std::ptr::eq(ll.label, m.label)) }) - .find(|(j, ll)| { - ll.col == col && ((row <= *j && !ll.multi) || (row <= *j && ll.multi)) - }) + .find(|(j, ll)| ll.col == col && row <= *j) .map(|(_, ll)| ll) }; @@ -593,11 +626,16 @@ impl Report<'_, S> { margin_label .iter() .map(|ll| &ll.label) - .chain(multi_labels.iter().map(|l| l)) + .chain(multi_labels.iter()) .chain(line_labels.iter().map(|l| &l.label)) .filter(|l| l.char_span.contains(&(line.offset() + col))) // Prioritise displaying smaller spans - .min_by_key(|l| (-l.display_info.priority, ExactSizeIterator::len(&l.char_span))) + .min_by_key(|l| { + ( + -l.display_info.priority, + ExactSizeIterator::len(&l.char_span), + ) + }) }; let get_underline = |col| { @@ -610,7 +648,12 @@ impl Report<'_, S> { && ll.label.char_span.contains(&(line.offset() + col)) }) // Prioritise displaying smaller spans - .min_by_key(|ll| (-ll.label.display_info.priority, ExactSizeIterator::len(&ll.label.char_span))) + .min_by_key(|ll| { + ( + -ll.label.display_info.priority, + ExactSizeIterator::len(&ll.label.char_span), + ) + }) }; // Margin @@ -627,7 +670,13 @@ impl Report<'_, S> { // Line if !is_ellipsis { - for (col, c) in src.get_line_text(line).unwrap().trim_end().chars().enumerate() { + for (col, c) in src + .get_line_text(line) + .unwrap() + .trim_end() + .chars() + .enumerate() + { let color = if let Some(highlight) = get_highlight(col) { highlight.display_info.color } else { @@ -643,14 +692,14 @@ impl Report<'_, S> { }; } } - write!(w, "\n")?; + writeln!(w)?; // Arrows for row in 0..line_labels.len() { let line_label = &line_labels[row]; //No message to draw thus no arrow to draw if line_label.label.display_info.msg.is_none() { - continue + continue; } if !self.config.compact { // Margin alternate @@ -674,16 +723,7 @@ impl Report<'_, S> { let underline = get_underline(col).filter(|_| row == 0); let [c, tail] = if let Some(vbar_ll) = vbar { let [c, tail] = if underline.is_some() { - // TODO: Is this good? - if ExactSizeIterator::len(&vbar_ll.label.char_span) <= 1 || true { - [draw.underbar, draw.underline] - } else if line.offset() + col == vbar_ll.label.char_span.start { - [draw.ltop, draw.underbar] - } else if line.offset() + col == vbar_ll.label.last_offset() { - [draw.rtop, draw.underbar] - } else { - [draw.underbar, draw.underline] - } + [draw.underbar, draw.underline] } else if vbar_ll.multi && row == 0 && self.config.multiline_arrows { [draw.uarrow, ' '] @@ -704,7 +744,7 @@ impl Report<'_, S> { write!(w, "{}", if i == 0 { c } else { tail })?; } } - write!(w, "\n")?; + writeln!(w)?; } // Margin @@ -730,7 +770,10 @@ impl Report<'_, S> { && line_label.label.display_info.msg.is_some(); let [c, tail] = if col == line_label.col && line_label.label.display_info.msg.is_some() - && margin_label.as_ref().map_or(true, |m| !std::ptr::eq(line_label.label, m.label)) { + && margin_label + .as_ref() + .map_or(true, |m| !std::ptr::eq(line_label.label, m.label)) + { [ if line_label.multi { if line_label.draw_msg { @@ -744,9 +787,9 @@ impl Report<'_, S> { .fg(line_label.label.display_info.color, s), draw.hbar.fg(line_label.label.display_info.color, s), ] - } else if let Some(vbar_ll) = get_vbar(col, row) - .filter(|_| (col != line_label.col || line_label.label.display_info.msg.is_some())) - { + } else if let Some(vbar_ll) = get_vbar(col, row).filter(|_| { + col != line_label.col || line_label.label.display_info.msg.is_some() + }) { if !self.config.cross_gap && is_hbar { [ draw.xbar.fg(line_label.label.display_info.color, s), @@ -781,7 +824,7 @@ impl Report<'_, S> { if line_label.draw_msg { write!(w, " {}", Show(line_label.label.display_info.msg.as_ref()))?; } - write!(w, "\n")?; + writeln!(w)?; } } @@ -791,20 +834,20 @@ impl Report<'_, S> { if let (Some(note), true) = (&self.help, is_final_group) { if !self.config.compact { write_margin(&mut w, 0, false, false, true, Some((0, false)), &[], &None)?; - write!(w, "\n")?; + writeln!(w)?; } write_margin(&mut w, 0, false, false, true, Some((0, false)), &[], &None)?; - write!(w, "{}: {}\n", "Help".fg(self.config.note_color(), s), note)?; + writeln!(w, "{}: {}", "Help".fg(self.config.note_color(), s), note)?; } // Note if let (Some(note), true) = (&self.note, is_final_group) { if !self.config.compact { write_margin(&mut w, 0, false, false, true, Some((0, false)), &[], &None)?; - write!(w, "\n")?; + writeln!(w)?; } write_margin(&mut w, 0, false, false, true, Some((0, false)), &[], &None)?; - write!(w, "{}: {}\n", "Note".fg(self.config.note_color(), s), note)?; + writeln!(w, "{}: {}", "Note".fg(self.config.note_color(), s), note)?; } // Tail of report @@ -842,7 +885,7 @@ mod tests { use insta::assert_snapshot; - use crate::{Cache, CharSet, Config, Label, Report, ReportKind, Source, Span, IndexType}; + use crate::{Cache, CharSet, Config, IndexType, Label, Report, ReportKind, Source, Span}; impl Report<'_, S> { fn write_to_string>(&self, cache: C) -> String { From 89c9aba898fdc89760b51269790557c2f747b1d6 Mon Sep 17 00:00:00 2001 From: Mohammad Fawaz Date: Tue, 9 Apr 2024 10:08:37 -0400 Subject: [PATCH 2/2] Fix one more clippy issue and add back some code I thought was dead --- src/lib.rs | 1 + src/write.rs | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 39eb602..a887c5e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -530,6 +530,7 @@ impl Default for Config { #[test] #[should_panic] +#[allow(clippy::reversed_empty_ranges)] fn backwards_label_should_panic() { Label::new(1..0); } diff --git a/src/write.rs b/src/write.rs index 166559d..11ca399 100644 --- a/src/write.rs +++ b/src/write.rs @@ -723,7 +723,20 @@ impl Report<'_, S> { let underline = get_underline(col).filter(|_| row == 0); let [c, tail] = if let Some(vbar_ll) = vbar { let [c, tail] = if underline.is_some() { - [draw.underbar, draw.underline] + // TODO: Is this good? + // The `true` is used here because it's temporarily disabling a + // feature that might be reenabled later. + #[allow(clippy::overly_complex_bool_expr)] + if ExactSizeIterator::len(&vbar_ll.label.char_span) <= 1 || true + { + [draw.underbar, draw.underline] + } else if line.offset() + col == vbar_ll.label.char_span.start { + [draw.ltop, draw.underbar] + } else if line.offset() + col == vbar_ll.label.last_offset() { + [draw.rtop, draw.underbar] + } else { + [draw.underbar, draw.underline] + } } else if vbar_ll.multi && row == 0 && self.config.multiline_arrows { [draw.uarrow, ' ']