Skip to content

fix!: Iterate on the API #198

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 16, 2025
Merged
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
6 changes: 3 additions & 3 deletions benches/bench.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use annotate_snippets::{level::Level, AnnotationKind, Group, Renderer, Snippet};
use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};

#[divan::bench]
fn simple() -> String {
@@ -24,7 +24,7 @@ fn simple() -> String {
_ => continue,
}
}"#;
let message = Level::ERROR.message("mismatched types").id("E0308").group(
let message = Level::ERROR.header("mismatched types").id("E0308").group(
Group::new().element(
Snippet::source(source)
.line_start(51)
@@ -69,7 +69,7 @@ fn fold(bencher: divan::Bencher<'_, '_>, context: usize) {
(input, span)
})
.bench_values(|(input, span)| {
let message = Level::ERROR.message("mismatched types").id("E0308").group(
let message = Level::ERROR.header("mismatched types").id("E0308").group(
Group::new().element(
Snippet::source(&input)
.fold(true)
4 changes: 2 additions & 2 deletions examples/custom_error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use annotate_snippets::renderer::OutputTheme;
use annotate_snippets::{level::Level, AnnotationKind, Group, Renderer, Snippet};
use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};

fn main() {
let source = r#"//@ compile-flags: -Ztreat-err-as-bug
@@ -17,7 +17,7 @@ pub static C: u32 = 0 - 1;
"#;
let message = Level::ERROR
.text(Some("error: internal compiler error"))
.message("could not evaluate static initializer")
.header("could not evaluate static initializer")
.id("E0080")
.group(
Group::new().element(
4 changes: 2 additions & 2 deletions examples/custom_level.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use annotate_snippets::renderer::OutputTheme;
use annotate_snippets::{level::Level, AnnotationKind, Group, Patch, Renderer, Snippet};
use annotate_snippets::{AnnotationKind, Group, Level, Patch, Renderer, Snippet};

fn main() {
let source = r#"// Regression test for issue #114529
@@ -30,7 +30,7 @@ fn main() {
}
"#;
let message = Level::ERROR
.message("`break` with value from a `while` loop")
.header("`break` with value from a `while` loop")
.id("E0571")
.group(
Group::new().element(
4 changes: 2 additions & 2 deletions examples/expected_type.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use annotate_snippets::{level::Level, AnnotationKind, Group, Renderer, Snippet};
use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};

fn main() {
let source = r#" annotations: vec![SourceAnnotation {
label: "expected struct `annotate_snippets::snippet::Slice`, found reference"
,
range: <22, 25>,"#;
let message =
Level::ERROR.message("expected type, found `22`").group(
Level::ERROR.header("expected type, found `22`").group(
Group::new().element(
Snippet::source(source)
.line_start(26)
4 changes: 2 additions & 2 deletions examples/footer.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use annotate_snippets::{level::Level, AnnotationKind, Group, Renderer, Snippet};
use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};

fn main() {
let message = Level::ERROR
.message("mismatched types")
.header("mismatched types")
.id("E0308")
.group(
Group::new().element(
4 changes: 2 additions & 2 deletions examples/format.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use annotate_snippets::{level::Level, AnnotationKind, Group, Renderer, Snippet};
use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};

fn main() {
let source = r#") -> Option<String> {
@@ -23,7 +23,7 @@ fn main() {
_ => continue,
}
}"#;
let message = Level::ERROR.message("mismatched types").id("E0308").group(
let message = Level::ERROR.header("mismatched types").id("E0308").group(
Group::new().element(
Snippet::source(source)
.line_start(51)
4 changes: 2 additions & 2 deletions examples/highlight_source.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use annotate_snippets::{level::Level, AnnotationKind, Group, Renderer, Snippet};
use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};

fn main() {
let source = r#"//@ compile-flags: -Z teach
@@ -10,7 +10,7 @@ const CON: Vec<i32> = vec![1, 2, 3]; //~ ERROR E0010
fn main() {}
"#;
let message = Level::ERROR
.message("allocations are not allowed in constants")
.header("allocations are not allowed in constants")
.id("E0010")
.group(
Group::new()
4 changes: 2 additions & 2 deletions examples/highlight_title.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use annotate_snippets::{level::Level, AnnotationKind, Group, Renderer, Snippet};
use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
use anstyle::Effects;

fn main() {
@@ -43,7 +43,7 @@ fn main() {
magenta.render_reset()
);

let message = Level::ERROR.message("mismatched types").id("E0308").group(
let message = Level::ERROR.header("mismatched types").id("E0308").group(
Group::new()
.element(
Snippet::source(source)
4 changes: 2 additions & 2 deletions examples/multislice.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use annotate_snippets::{level::Level, Annotation, Group, Renderer, Snippet};
use annotate_snippets::{Annotation, Group, Level, Renderer, Snippet};

fn main() {
let message = Level::ERROR.message("mismatched types").group(
let message = Level::ERROR.header("mismatched types").group(
Group::new()
.element(
Snippet::<Annotation<'_>>::source("Foo")
36 changes: 23 additions & 13 deletions src/level.rs
Original file line number Diff line number Diff line change
@@ -1,59 +1,61 @@
//! [`Level`] constants for easy importing

use crate::renderer::stylesheet::Stylesheet;
use crate::snippet::{ERROR_TXT, HELP_TXT, INFO_TXT, NOTE_TXT, WARNING_TXT};
use crate::{Element, Group, Message, Title};
use anstyle::Style;

/// Default `error:` [`Level`]
pub const ERROR: Level<'_> = Level {
name: None,
level: LevelInner::Error,
};

/// Default `warning:` [`Level`]
pub const WARNING: Level<'_> = Level {
name: None,
level: LevelInner::Warning,
};

/// Default `info:` [`Level`]
pub const INFO: Level<'_> = Level {
name: None,
level: LevelInner::Info,
};

/// Default `note:` [`Level`]
pub const NOTE: Level<'_> = Level {
name: None,
level: LevelInner::Note,
};

/// Default `help:` [`Level`]
pub const HELP: Level<'_> = Level {
name: None,
level: LevelInner::Help,
};

/// [`Message`] or [`Title`] severity level
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Level<'a> {
pub(crate) name: Option<Option<&'a str>>,
pub(crate) level: LevelInner,
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Level2<'a> {
Builtin(LevelInner),
Custom {
name: Option<&'a str>,
level: LevelInner,
},
None,
}

impl<'a> Level<'a> {
pub const ERROR: Level<'a> = ERROR;
pub const WARNING: Level<'a> = WARNING;
pub const INFO: Level<'a> = INFO;
pub const NOTE: Level<'a> = NOTE;
pub const HELP: Level<'a> = HELP;

/// <div class="warning">
///
/// Text passed to this function is considered "untrusted input", as such
/// all text is passed through a normalization function. Pre-styled text is
/// not allowed to be passed to this function.
///
/// </div>
pub fn text(self, text: Option<&'a str>) -> Level<'a> {
Level {
name: Some(text),
@@ -63,24 +65,32 @@ impl<'a> Level<'a> {
}

impl<'a> Level<'a> {
/// <div class="warning">
///
/// Text passed to this function is considered "untrusted input", as such
/// all text is passed through a normalization function. Pre-styled text is
/// not allowed to be passed to this function.
pub fn message(self, title: &'a str) -> Message<'a> {
///
/// </div>
pub fn header(self, header: &'a str) -> Message<'a> {
Message {
id: None,
groups: vec![Group::new().element(Element::Title(Title {
level: self,
title,
title: header,
primary: true,
}))],
}
}

/// <div class="warning">
///
/// Text passed to this function is allowed to be pre-styled, as such all
/// text is considered "trusted input" and has no normalizations applied to
/// it. [`normalize_untrusted_str`](crate::normalize_untrusted_str) can be
/// used to normalize untrusted text before it is passed to this function.
///
/// </div>
pub fn title(self, title: &'a str) -> Title<'a> {
Title {
level: self,
@@ -107,7 +117,7 @@ impl<'a> Level<'a> {
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum LevelInner {
pub(crate) enum LevelInner {
Error,
Warning,
Info,
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -53,7 +53,8 @@ pub fn normalize_untrusted_str(s: &str) -> String {
renderer::normalize_whitespace(s)
}

#[doc(inline)]
pub use level::Level;
#[doc(inline)]
pub use renderer::Renderer;
pub use snippet::ColumnSeparator;
pub use snippet::*;
30 changes: 15 additions & 15 deletions src/renderer/mod.rs
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
//! # Example
//! ```
//! use annotate_snippets::*;
//! use annotate_snippets::level::Level;
//! use annotate_snippets::Level;
//!
//! let source = r#"
//! use baz::zed::bar;
@@ -19,7 +19,7 @@
//! }
//! "#;
//! Level::ERROR
//! .message("unresolved import `baz::zed`")
//! .header("unresolved import `baz::zed`")
//! .id("E0432")
//! .group(
//! Group::new().element(
@@ -101,7 +101,7 @@ impl Renderer {
info: BRIGHT_BLUE.effects(Effects::BOLD),
note: AnsiColor::BrightGreen.on_default().effects(Effects::BOLD),
help: AnsiColor::BrightCyan.on_default().effects(Effects::BOLD),
line_no: BRIGHT_BLUE.effects(Effects::BOLD),
line_num: BRIGHT_BLUE.effects(Effects::BOLD),
emphasis: if USE_WINDOWS_COLORS {
AnsiColor::BrightWhite.on_default()
} else {
@@ -178,8 +178,8 @@ impl Renderer {
}

/// Set the output style for line numbers
pub const fn line_no(mut self, style: Style) -> Self {
self.stylesheet.line_no = style;
pub const fn line_num(mut self, style: Style) -> Self {
self.stylesheet.line_num = style;
self
}

@@ -383,7 +383,7 @@ impl Renderer {
self.render_origin(buffer, max_line_num_len, origin);
last_was_suggestion = false;
}
Element::ColumnSeparator(_) => {
Element::Padding(_) => {
self.draw_col_separator_no_space(
buffer,
buffer.num_lines(),
@@ -430,7 +430,7 @@ impl Renderer {

let (has_primary_spans, has_span_labels) =
next_section.map_or((false, false), |s| match s {
Element::Title(_) | Element::ColumnSeparator(_) => (false, false),
Element::Title(_) | Element::Padding(_) => (false, false),
Element::Cause(cause) => (
cause.markers.iter().any(|m| m.kind.is_primary()),
cause.markers.iter().any(|m| m.label.is_some()),
@@ -1523,7 +1523,7 @@ impl Renderer {
}
if suggestion.origin != primary_origin {
if let Some(origin) = suggestion.origin {
let (loc, _) = sm.span_to_locations(parts[0].range.clone());
let (loc, _) = sm.span_to_locations(parts[0].span.clone());
// --> file.rs:line:col
// |
let arrow = self.file_start();
@@ -1563,8 +1563,8 @@ impl Renderer {
row_num += 1;
}

let file_lines = sm.span_to_lines(parts[0].range.clone());
let (line_start, line_end) = sm.span_to_locations(parts[0].range.clone());
let file_lines = sm.span_to_lines(parts[0].span.clone());
let (line_start, line_end) = sm.span_to_locations(parts[0].span.clone());
let mut lines = complete.lines();
if lines.clone().next().is_none() {
// Account for a suggestion to completely remove a line(s) with whitespace (#94192).
@@ -1697,8 +1697,8 @@ impl Renderer {
// already existing code, despite the colors and UI elements.
// We special case `#[derive(_)]\n` and other attribute suggestions, because those
// are the ones where context is most useful.
let file_lines = sm.span_to_lines(parts[0].range.end..parts[0].range.end);
let (lo, _) = sm.span_to_locations(parts[0].range.clone());
let file_lines = sm.span_to_lines(parts[0].span.end..parts[0].span.end);
let (lo, _) = sm.span_to_locations(parts[0].span.clone());
let line_num = lo.line;
if let Some(line) = sm.get_line(line_num) {
let line = normalize_whitespace(line);
@@ -1724,7 +1724,7 @@ impl Renderer {
show_code_change
{
for part in parts {
let (span_start, span_end) = sm.span_to_locations(part.range.clone());
let (span_start, span_end) = sm.span_to_locations(part.span.clone());
let span_start_pos = span_start.display;
let span_end_pos = span_end.display;

@@ -1764,7 +1764,7 @@ impl Renderer {
let padding: usize = max_line_num_len + 3;
for p in underline_start..underline_end {
if matches!(show_code_change, DisplaySuggestion::Underline)
&& is_different(sm, part.replacement, part.range.clone())
&& is_different(sm, part.replacement, part.span.clone())
{
// If this is a replacement, underline with `~`, if this is an addition
// underline with `+`.
@@ -2665,7 +2665,7 @@ impl ElementStyle {
ElementStyle::Addition => stylesheet.addition,
ElementStyle::Removal => stylesheet.removal,
ElementStyle::LineAndColumn => stylesheet.none,
ElementStyle::LineNumber => stylesheet.line_no,
ElementStyle::LineNumber => stylesheet.line_num,
ElementStyle::Quotation => stylesheet.none,
ElementStyle::MainHeaderMsg => stylesheet.emphasis,
ElementStyle::UnderlinePrimary | ElementStyle::LabelPrimary => level.style(stylesheet),
Loading