From 336a2d88acfcb785329ec04cb8253f14c9c31a81 Mon Sep 17 00:00:00 2001 From: Ben Date: Sat, 4 Oct 2025 12:14:02 +0100 Subject: [PATCH 01/22] Changes to CI --- .github/workflows/ci.yml | 56 ++++++++++++---------------------------- 1 file changed, 17 insertions(+), 39 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 04300a2a..c6149be8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,8 +1,8 @@ name: ci on: - push: { 'branches': [master] } - pull_request: { 'branches': ["*"] } + push: { branches: [master] } + pull_request: { branches: ["*"] } jobs: check: @@ -15,44 +15,17 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - - # TODO features here + with: { toolchain: "${{ matrix.rust }}" } - name: Run cargo check for codespan - run: cargo check --manifest-path "codespan/Cargo.toml" --features "serialization" + run: cargo check - name: Run cargo check for codespan-reporting - run: cargo check --manifest-path "codespan-reporting/Cargo.toml" --features "serialization" + run: cargo check -p codespan-reporting - name: Run cargo check for codespan-lsp - run: cargo check --manifest-path "codespan-lsp/Cargo.toml" + run: cargo check -p codespan-lsp - check-no-std: - needs: check - - name: Check no_std ${{ matrix.rust }} / ${{ matrix.target }} - - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - rust: ["stable", "nightly"] - target: ["x86_64-unknown-none", "wasm32v1-none", "thumbv6m-none-eabi"] - - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master - with: { toolchain: '${{ matrix.rust }}', targets: '${{ matrix.target }}' } - - - name: Run cargo check for codespan-reporting - run: cargo check --manifest-path "codespan-reporting/Cargo.toml" --no-default-features --features "serialization" --target ${{ matrix.target }} - - - name: Run cargo check for codespan - run: cargo check --manifest-path "codespan/Cargo.toml" --no-default-features --features "serialization" --target ${{ matrix.target }} - test: needs: check @@ -70,17 +43,22 @@ jobs: - uses: dtolnay/rust-toolchain@master with: { toolchain: '${{ matrix.rust }}' } - # Test each crate individually to work around rust-lang/cargo#4942 - name: Run cargo test for codespan - run: cargo test -p codespan --features "serialization" + run: cargo test -p codespan - name: Run cargo test for codespan-reporting - run: cargo test -p codespan-reporting --features "serialization" + run: cargo test -p codespan-reporting + + - name: Run cargo test for codespan-reporting (no features) + run: cargo test -p codespan-reporting --no-default-features + + - name: Run cargo test for codespan-reporting (only std feature) + run: cargo test -p codespan-reporting --no-default-features --features std - name: Run cargo test for codespan-lsp run: cargo test -p codespan-lsp - fmt: + format: name: rustfmt runs-on: ubuntu-latest @@ -113,8 +91,7 @@ jobs: strategy: fail-fast: false - matrix: - rust: ["stable"] + matrix: { rust: ["stable"] } steps: - uses: actions/checkout@v4 @@ -126,6 +103,7 @@ jobs: publish-ability: runs-on: ubuntu-latest + steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable From 867e3938ccda9135ee6b9d3eac99f50371d612e1 Mon Sep 17 00:00:00 2001 From: Ben Date: Sat, 4 Oct 2025 12:15:30 +0100 Subject: [PATCH 02/22] Add documentation + `Writer::new` --- codespan-reporting/src/term/config.rs | 4 ++++ codespan-reporting/src/term/renderer.rs | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/codespan-reporting/src/term/config.rs b/codespan-reporting/src/term/config.rs index f22e52b6..9ac2be47 100644 --- a/codespan-reporting/src/term/config.rs +++ b/codespan-reporting/src/term/config.rs @@ -234,6 +234,9 @@ impl Default for Styles { } #[cfg(feature = "termcolor")] +/// A [`WriteStyle`](crate::term::renderer::WriteStyle) implementation that applies custom [`Styles`]. +/// +/// Use this to render diagnostics with custom colors and formatting. pub struct StylesWriter<'a, W> { writer: W, style: &'a Styles, @@ -241,6 +244,7 @@ pub struct StylesWriter<'a, W> { #[cfg(feature = "termcolor")] impl<'a, W> StylesWriter<'a, W> { + /// Creates a new `StylesWriter` with the given writer and styles. pub fn new(writer: W, style: &'a Styles) -> Self { Self { writer, style } } diff --git a/codespan-reporting/src/term/renderer.rs b/codespan-reporting/src/term/renderer.rs index 095494a9..1153473b 100644 --- a/codespan-reporting/src/term/renderer.rs +++ b/codespan-reporting/src/term/renderer.rs @@ -13,7 +13,13 @@ type WriteResult = io::Result<()>; #[cfg(not(feature = "std"))] use core::fmt::{Arguments, Result as WriteResult, Write}; -/// A writer that can apply and reset styling for different parts of a diagnostic renderer. +/// A writer that can apply styling for different parts of a diagnostic renderer. +/// +/// # Implementations +/// +/// - [`Writer`](Writer) - no-op styling, plain text output +/// - [`StylesWriter`](crate::term::StylesWriter) - custom styles (requires `termcolor` feature) +/// - `T: WriteColor` - blanket impl using default styles (requires `termcolor` feature) pub trait WriteStyle: Write { fn set_header(&mut self, severity: Severity) -> WriteResult; @@ -30,10 +36,17 @@ pub trait WriteStyle: Write { fn reset(&mut self) -> WriteResult; } +/// A [`WriteStyle`] implementation that ignores all styling calls. useful for non color output. pub struct Writer { w: W, } +impl Writer { + pub fn new(writer: W) -> Self { + Self { w: writer } + } +} + #[cfg(not(feature = "std"))] impl Write for Writer { fn write_str(&mut self, s: &str) -> core::fmt::Result { From bf6b418e06c4c6be8e0be2da135affa9117a4686 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 8 Oct 2025 12:34:49 +0100 Subject: [PATCH 03/22] Move some items around and add separate methods for *emitting* to `String` --- codespan-reporting/src/files.rs | 19 +- codespan-reporting/src/lib.rs | 5 +- codespan-reporting/src/term.rs | 65 ---- codespan-reporting/src/term/config.rs | 399 ++++++++++++------------ codespan-reporting/src/term/mod.rs | 143 +++++++++ codespan-reporting/src/term/renderer.rs | 116 +++---- 6 files changed, 410 insertions(+), 337 deletions(-) delete mode 100644 codespan-reporting/src/term.rs create mode 100644 codespan-reporting/src/term/mod.rs diff --git a/codespan-reporting/src/files.rs b/codespan-reporting/src/files.rs index 457fdac4..db61c276 100644 --- a/codespan-reporting/src/files.rs +++ b/codespan-reporting/src/files.rs @@ -26,12 +26,6 @@ use alloc::vec::Vec; use core::ops::Range; -#[cfg(feature = "std")] -use std::error; - -#[cfg(not(feature = "std"))] -use core::error; - /// An enum representing an error that happened while looking up a file or a piece of content in that file. #[derive(Debug)] #[non_exhaustive] @@ -42,7 +36,8 @@ pub enum Error { IndexTooLarge { given: usize, max: usize }, /// The file is present, but does not contain the specified line index. LineTooLarge { given: usize, max: usize }, - /// The file is present and contains the specified line index, but the line does not contain the specified column index. + /// The file is present and contains the specified line index, but the line does not contain + /// the specified column index. ColumnTooLarge { given: usize, max: usize }, /// The given index is contained in the file, but is not a boundary of a UTF-8 code point. InvalidCharBoundary { given: usize }, @@ -87,8 +82,14 @@ impl core::fmt::Display for Error { } } -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { +#[cfg(feature = "std")] +use std::error::Error as RustError; + +#[cfg(not(feature = "std"))] +use core::error::Error as RustError; + +impl RustError for Error { + fn source(&self) -> Option<&(dyn RustError + 'static)> { match &self { #[cfg(feature = "std")] Error::Io(err) => Some(err), diff --git a/codespan-reporting/src/lib.rs b/codespan-reporting/src/lib.rs index 76af4fd4..c7beab48 100644 --- a/codespan-reporting/src/lib.rs +++ b/codespan-reporting/src/lib.rs @@ -1,13 +1,10 @@ //! Diagnostic reporting support for the codespan crate. #![forbid(unsafe_code)] -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; -#[cfg(feature = "std")] -extern crate std; - pub mod diagnostic; pub mod files; pub mod term; diff --git a/codespan-reporting/src/term.rs b/codespan-reporting/src/term.rs deleted file mode 100644 index fcc17fe1..00000000 --- a/codespan-reporting/src/term.rs +++ /dev/null @@ -1,65 +0,0 @@ -//! Terminal back-end for emitting diagnostics. - -use crate::diagnostic::Diagnostic; -use crate::files::Files; - -mod config; -mod renderer; -mod views; - -#[cfg(feature = "termcolor")] -pub use termcolor; - -pub use self::config::{Chars, Config, DisplayStyle}; - -#[cfg(feature = "termcolor")] -pub use self::config::Styles; - -#[cfg(feature = "termcolor")] -pub use config::StylesWriter; - -pub use self::renderer::WriteStyle; - -pub use self::renderer::{Renderer, Writer}; -pub use self::views::{RichDiagnostic, ShortDiagnostic}; - -/// Emit a diagnostic using the given writer, context, config, and files. -/// -/// The return value covers all error cases. These error case can arise if: -/// * a file was removed from the file database. -/// * a file was changed so that it is too small to have an index -/// * IO fails -pub fn emit<'files, F: Files<'files> + ?Sized, W: WriteStyle>( - writer: &mut W, - config: &Config, - files: &'files F, - diagnostic: &Diagnostic, -) -> Result<(), super::files::Error> { - let mut renderer = Renderer::new(writer, config); - match config.display_style { - DisplayStyle::Rich => RichDiagnostic::new(diagnostic, config).render(files, &mut renderer), - DisplayStyle::Medium => ShortDiagnostic::new(diagnostic, true).render(files, &mut renderer), - DisplayStyle::Short => ShortDiagnostic::new(diagnostic, false).render(files, &mut renderer), - } -} - -#[cfg(all(test, feature = "termcolor"))] -mod tests { - use alloc::{vec, vec::Vec}; - - use super::*; - - use crate::diagnostic::Label; - use crate::files::SimpleFiles; - - #[test] - fn unsized_emit() { - let mut files = SimpleFiles::new(); - - let id = files.add("test", ""); - let mut writer = termcolor::NoColor::new(Vec::::new()); - let diagnostic = Diagnostic::bug().with_labels(vec![Label::primary(id, 0..0)]); - - emit(&mut writer, &Config::default(), &files, &diagnostic).unwrap(); - } -} diff --git a/codespan-reporting/src/term/config.rs b/codespan-reporting/src/term/config.rs index 9ac2be47..81e40381 100644 --- a/codespan-reporting/src/term/config.rs +++ b/codespan-reporting/src/term/config.rs @@ -1,23 +1,5 @@ use alloc::string::String; -#[cfg(feature = "termcolor")] -use termcolor::WriteColor; - -#[cfg(feature = "termcolor")] -use super::renderer::WriteStyle; - -#[cfg(feature = "termcolor")] -use { - crate::diagnostic::{LabelStyle, Severity}, - termcolor::{Color, ColorSpec}, -}; - -#[cfg(not(feature = "std"))] -use core::fmt::{Arguments, Result as WriteResult, Write}; - -#[cfg(feature = "std")] -use std::io; - /// Configures how a diagnostic is rendered. #[derive(Clone, Debug)] pub struct Config { @@ -106,242 +88,253 @@ pub enum DisplayStyle { Short, } -/// Styles to use when rendering the diagnostic. #[cfg(feature = "termcolor")] -#[derive(Clone, Debug)] -pub struct Styles { - /// The style to use when rendering bug headers. - /// Defaults to `fg:red bold intense`. - pub header_bug: ColorSpec, - /// The style to use when rendering error headers. - /// Defaults to `fg:red bold intense`. - pub header_error: ColorSpec, - /// The style to use when rendering warning headers. - /// Defaults to `fg:yellow bold intense`. - pub header_warning: ColorSpec, - /// The style to use when rendering note headers. - /// Defaults to `fg:green bold intense`. - pub header_note: ColorSpec, - /// The style to use when rendering help headers. - /// Defaults to `fg:cyan bold intense`. - pub header_help: ColorSpec, - /// The style to use when the main diagnostic message. - /// Defaults to `bold intense`. - pub header_message: ColorSpec, - - /// The style to use when rendering bug labels. - /// Defaults to `fg:red`. - pub primary_label_bug: ColorSpec, - /// The style to use when rendering error labels. - /// Defaults to `fg:red`. - pub primary_label_error: ColorSpec, - /// The style to use when rendering warning labels. - /// Defaults to `fg:yellow`. - pub primary_label_warning: ColorSpec, - /// The style to use when rendering note labels. - /// Defaults to `fg:green`. - pub primary_label_note: ColorSpec, - /// The style to use when rendering help labels. - /// Defaults to `fg:cyan`. - pub primary_label_help: ColorSpec, - /// The style to use when rendering secondary labels. - /// Defaults `fg:blue` (or `fg:cyan` on windows). - pub secondary_label: ColorSpec, - - /// The style to use when rendering the line numbers. - /// Defaults `fg:blue` (or `fg:cyan` on windows). - pub line_number: ColorSpec, - /// The style to use when rendering the source code borders. - /// Defaults `fg:blue` (or `fg:cyan` on windows). - pub source_border: ColorSpec, - /// The style to use when rendering the note bullets. - /// Defaults `fg:blue` (or `fg:cyan` on windows). - pub note_bullet: ColorSpec, -} +pub mod styles { + use super::super::renderer::{self, WriteStyle}; + + use crate::diagnostic::{LabelStyle, Severity}; + use termcolor::{Color, ColorSpec, WriteColor}; + + /// Styles to use when rendering the diagnostic. + #[derive(Clone, Debug)] + pub struct Styles { + /// The style to use when rendering bug headers. + /// Defaults to `fg:red bold intense`. + pub header_bug: ColorSpec, + /// The style to use when rendering error headers. + /// Defaults to `fg:red bold intense`. + pub header_error: ColorSpec, + /// The style to use when rendering warning headers. + /// Defaults to `fg:yellow bold intense`. + pub header_warning: ColorSpec, + /// The style to use when rendering note headers. + /// Defaults to `fg:green bold intense`. + pub header_note: ColorSpec, + /// The style to use when rendering help headers. + /// Defaults to `fg:cyan bold intense`. + pub header_help: ColorSpec, + /// The style to use when the main diagnostic message. + /// Defaults to `bold intense`. + pub header_message: ColorSpec, + + /// The style to use when rendering bug labels. + /// Defaults to `fg:red`. + pub primary_label_bug: ColorSpec, + /// The style to use when rendering error labels. + /// Defaults to `fg:red`. + pub primary_label_error: ColorSpec, + /// The style to use when rendering warning labels. + /// Defaults to `fg:yellow`. + pub primary_label_warning: ColorSpec, + /// The style to use when rendering note labels. + /// Defaults to `fg:green`. + pub primary_label_note: ColorSpec, + /// The style to use when rendering help labels. + /// Defaults to `fg:cyan`. + pub primary_label_help: ColorSpec, + /// The style to use when rendering secondary labels. + /// Defaults `fg:blue` (or `fg:cyan` on windows). + pub secondary_label: ColorSpec, + + /// The style to use when rendering the line numbers. + /// Defaults `fg:blue` (or `fg:cyan` on windows). + pub line_number: ColorSpec, + /// The style to use when rendering the source code borders. + /// Defaults `fg:blue` (or `fg:cyan` on windows). + pub source_border: ColorSpec, + /// The style to use when rendering the note bullets. + /// Defaults `fg:blue` (or `fg:cyan` on windows). + pub note_bullet: ColorSpec, + } + + impl Styles { + /// The style used to mark a header at a given severity. + pub fn header(&self, severity: Severity) -> &ColorSpec { + match severity { + Severity::Bug => &self.header_bug, + Severity::Error => &self.header_error, + Severity::Warning => &self.header_warning, + Severity::Note => &self.header_note, + Severity::Help => &self.header_help, + } + } -#[cfg(feature = "termcolor")] -impl Styles { - /// The style used to mark a header at a given severity. - pub fn header(&self, severity: Severity) -> &ColorSpec { - match severity { - Severity::Bug => &self.header_bug, - Severity::Error => &self.header_error, - Severity::Warning => &self.header_warning, - Severity::Note => &self.header_note, - Severity::Help => &self.header_help, + pub fn header_message(&self) -> &ColorSpec { + &self.header_message } - } - pub fn header_message(&self) -> &ColorSpec { - &self.header_message - } + pub fn line_number(&self) -> &ColorSpec { + &self.line_number + } - pub fn line_number(&self) -> &ColorSpec { - &self.line_number - } + pub fn note_bullet(&self) -> &ColorSpec { + &self.note_bullet + } - pub fn note_bullet(&self) -> &ColorSpec { - &self.note_bullet - } + pub fn source_border(&self) -> &ColorSpec { + &self.source_border + } - pub fn source_border(&self) -> &ColorSpec { - &self.source_border - } + /// The style used to mark a primary or secondary label at a given severity. + pub fn label(&self, severity: Severity, label_style: LabelStyle) -> &ColorSpec { + match (label_style, severity) { + (LabelStyle::Primary, Severity::Bug) => &self.primary_label_bug, + (LabelStyle::Primary, Severity::Error) => &self.primary_label_error, + (LabelStyle::Primary, Severity::Warning) => &self.primary_label_warning, + (LabelStyle::Primary, Severity::Note) => &self.primary_label_note, + (LabelStyle::Primary, Severity::Help) => &self.primary_label_help, + (LabelStyle::Secondary, _) => &self.secondary_label, + } + } - /// The style used to mark a primary or secondary label at a given severity. - pub fn label(&self, severity: Severity, label_style: LabelStyle) -> &ColorSpec { - match (label_style, severity) { - (LabelStyle::Primary, Severity::Bug) => &self.primary_label_bug, - (LabelStyle::Primary, Severity::Error) => &self.primary_label_error, - (LabelStyle::Primary, Severity::Warning) => &self.primary_label_warning, - (LabelStyle::Primary, Severity::Note) => &self.primary_label_note, - (LabelStyle::Primary, Severity::Help) => &self.primary_label_help, - (LabelStyle::Secondary, _) => &self.secondary_label, + #[doc(hidden)] + pub fn with_blue(blue: Color) -> Styles { + let header = ColorSpec::new().set_bold(true).set_intense(true).clone(); + + Styles { + header_bug: header.clone().set_fg(Some(Color::Red)).clone(), + header_error: header.clone().set_fg(Some(Color::Red)).clone(), + header_warning: header.clone().set_fg(Some(Color::Yellow)).clone(), + header_note: header.clone().set_fg(Some(Color::Green)).clone(), + header_help: header.clone().set_fg(Some(Color::Cyan)).clone(), + header_message: header, + + primary_label_bug: ColorSpec::new().set_fg(Some(Color::Red)).clone(), + primary_label_error: ColorSpec::new().set_fg(Some(Color::Red)).clone(), + primary_label_warning: ColorSpec::new().set_fg(Some(Color::Yellow)).clone(), + primary_label_note: ColorSpec::new().set_fg(Some(Color::Green)).clone(), + primary_label_help: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(), + secondary_label: ColorSpec::new().set_fg(Some(blue)).clone(), + + line_number: ColorSpec::new().set_fg(Some(blue)).clone(), + source_border: ColorSpec::new().set_fg(Some(blue)).clone(), + note_bullet: ColorSpec::new().set_fg(Some(blue)).clone(), + } } } - #[doc(hidden)] - pub fn with_blue(blue: Color) -> Styles { - let header = ColorSpec::new().set_bold(true).set_intense(true).clone(); - - Styles { - header_bug: header.clone().set_fg(Some(Color::Red)).clone(), - header_error: header.clone().set_fg(Some(Color::Red)).clone(), - header_warning: header.clone().set_fg(Some(Color::Yellow)).clone(), - header_note: header.clone().set_fg(Some(Color::Green)).clone(), - header_help: header.clone().set_fg(Some(Color::Cyan)).clone(), - header_message: header, - - primary_label_bug: ColorSpec::new().set_fg(Some(Color::Red)).clone(), - primary_label_error: ColorSpec::new().set_fg(Some(Color::Red)).clone(), - primary_label_warning: ColorSpec::new().set_fg(Some(Color::Yellow)).clone(), - primary_label_note: ColorSpec::new().set_fg(Some(Color::Green)).clone(), - primary_label_help: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(), - secondary_label: ColorSpec::new().set_fg(Some(blue)).clone(), - - line_number: ColorSpec::new().set_fg(Some(blue)).clone(), - source_border: ColorSpec::new().set_fg(Some(blue)).clone(), - note_bullet: ColorSpec::new().set_fg(Some(blue)).clone(), + impl Default for Styles { + fn default() -> Styles { + Self::with_blue(Color::Cyan) } } -} -#[cfg(feature = "termcolor")] -impl Default for Styles { - fn default() -> Styles { - Self::with_blue(Color::Cyan) + /// A [`WriteStyle`](crate::term::renderer::WriteStyle) implementation that applies custom [`Styles`]. + /// + /// Use this to render diagnostics with custom colors and formatting. + pub struct StylesWriter<'a, W> { + writer: W, + style: &'a Styles, } -} - -#[cfg(feature = "termcolor")] -/// A [`WriteStyle`](crate::term::renderer::WriteStyle) implementation that applies custom [`Styles`]. -/// -/// Use this to render diagnostics with custom colors and formatting. -pub struct StylesWriter<'a, W> { - writer: W, - style: &'a Styles, -} -#[cfg(feature = "termcolor")] -impl<'a, W> StylesWriter<'a, W> { - /// Creates a new `StylesWriter` with the given writer and styles. - pub fn new(writer: W, style: &'a Styles) -> Self { - Self { writer, style } + impl<'a, W> StylesWriter<'a, W> { + /// Creates a new `StylesWriter` with the given writer and styles. + pub fn new(writer: W, style: &'a Styles) -> Self { + Self { writer, style } + } } -} -#[cfg(feature = "termcolor")] -#[cfg(feature = "std")] -impl<'a, W: WriteColor> io::Write for StylesWriter<'a, W> { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.writer.write(buf) - } + #[cfg(feature = "std")] + impl<'a, W: WriteColor> std::io::Write for StylesWriter<'a, W> { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.writer.write(buf) + } - fn flush(&mut self) -> io::Result<()> { - self.writer.flush() + fn flush(&mut self) -> std::io::Result<()> { + self.writer.flush() + } } -} -#[cfg(feature = "termcolor")] -#[cfg(not(feature = "std"))] -impl Write for StylesWriter<'_, '_> { - fn write_str(&mut self, s: &str) -> core::fmt::Result { - self.writer.write_str(s) - } + #[cfg(not(feature = "std"))] + impl core::fmt::Write for StylesWriter<'_, '_> { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + self.writer.write_str(s) + } - fn write_char(&mut self, c: char) -> core::fmt::Result { - self.writer.write_char(c) - } + fn write_char(&mut self, c: char) -> core::fmt::Result { + self.writer.write_char(c) + } - fn write_fmt(&mut self, args: Arguments<'_>) -> core::fmt::Result { - self.writer.write_fmt(args) + fn write_fmt(&mut self, args: core::fmt::Arguments<'_>) -> core::fmt::Result { + self.writer.write_fmt(args) + } } -} -#[cfg(feature = "termcolor")] -impl<'a, W: WriteColor> WriteStyle for StylesWriter<'a, W> { - fn set_header(&mut self, severity: Severity) -> io::Result<()> { - self.writer.set_color(self.style.header(severity)) - } + impl<'a, W: WriteColor> WriteStyle for StylesWriter<'a, W> { + fn set_header(&mut self, severity: Severity) -> renderer::GeneralWriteResult { + self.writer.set_color(self.style.header(severity)) + } - fn set_header_message(&mut self) -> io::Result<()> { - self.writer.set_color(&self.style.header_message) - } + fn set_header_message(&mut self) -> renderer::GeneralWriteResult { + self.writer.set_color(&self.style.header_message) + } - fn set_line_number(&mut self) -> io::Result<()> { - self.writer.set_color(&self.style.line_number) - } + fn set_line_number(&mut self) -> renderer::GeneralWriteResult { + self.writer.set_color(&self.style.line_number) + } - fn set_note_bullet(&mut self) -> io::Result<()> { - self.writer.set_color(&self.style.note_bullet) - } + fn set_note_bullet(&mut self) -> renderer::GeneralWriteResult { + self.writer.set_color(&self.style.note_bullet) + } - fn set_source_border(&mut self) -> io::Result<()> { - self.writer.set_color(&self.style.source_border) - } + fn set_source_border(&mut self) -> renderer::GeneralWriteResult { + self.writer.set_color(&self.style.source_border) + } - fn set_label(&mut self, severity: Severity, label_style: LabelStyle) -> io::Result<()> { - let spec = self.style.label(severity, label_style); - self.writer.set_color(spec) - } + fn set_label( + &mut self, + severity: Severity, + label_style: LabelStyle, + ) -> renderer::GeneralWriteResult { + let spec = self.style.label(severity, label_style); + self.writer.set_color(spec) + } - fn reset(&mut self) -> io::Result<()> { - self.writer.reset() + fn reset(&mut self) -> renderer::GeneralWriteResult { + self.writer.reset() + } } } #[cfg(feature = "termcolor")] -impl WriteStyle for T +impl super::renderer::WriteStyle for T where - T: WriteColor, + T: termcolor::WriteColor, { - fn set_header(&mut self, severity: Severity) -> io::Result<()> { - self.set_color(Styles::default().header(severity)) + fn set_header( + &mut self, + severity: crate::diagnostic::Severity, + ) -> super::renderer::GeneralWriteResult { + self.set_color(styles::Styles::default().header(severity)) } - fn set_header_message(&mut self) -> io::Result<()> { - self.set_color(&Styles::default().header_message) + fn set_header_message(&mut self) -> super::renderer::GeneralWriteResult { + self.set_color(&styles::Styles::default().header_message) } - fn set_line_number(&mut self) -> io::Result<()> { - self.set_color(&Styles::default().line_number) + fn set_line_number(&mut self) -> super::renderer::GeneralWriteResult { + self.set_color(&styles::Styles::default().line_number) } - fn set_note_bullet(&mut self) -> io::Result<()> { - self.set_color(&Styles::default().note_bullet) + fn set_note_bullet(&mut self) -> super::renderer::GeneralWriteResult { + self.set_color(&styles::Styles::default().note_bullet) } - fn set_source_border(&mut self) -> io::Result<()> { - self.set_color(&Styles::default().source_border) + fn set_source_border(&mut self) -> super::renderer::GeneralWriteResult { + self.set_color(&styles::Styles::default().source_border) } - fn set_label(&mut self, severity: Severity, label_style: LabelStyle) -> io::Result<()> { - let styles = Styles::default(); + fn set_label( + &mut self, + severity: crate::diagnostic::Severity, + label_style: crate::diagnostic::LabelStyle, + ) -> super::renderer::GeneralWriteResult { + let styles = styles::Styles::default(); let spec = styles.label(severity, label_style); self.set_color(spec) } - fn reset(&mut self) -> io::Result<()> { + fn reset(&mut self) -> super::renderer::GeneralWriteResult { self.reset() } } diff --git a/codespan-reporting/src/term/mod.rs b/codespan-reporting/src/term/mod.rs new file mode 100644 index 00000000..caecbf70 --- /dev/null +++ b/codespan-reporting/src/term/mod.rs @@ -0,0 +1,143 @@ +//! Terminal back-end for emitting diagnostics. + +use crate::diagnostic::Diagnostic; +use crate::files::Files; + +mod config; +mod renderer; +mod views; + +#[cfg(not(feature = "std"))] +use alloc::string::String; + +pub use config::{Chars, Config, DisplayStyle}; + +// re-export +#[cfg(feature = "termcolor")] +pub use config::styles::{Styles, StylesWriter}; + +pub use renderer::{Renderer, WriteStyle}; +pub use views::{RichDiagnostic, ShortDiagnostic}; + +pub fn emit_into_string<'files, F: Files<'files> + ?Sized>( + config: &Config, + files: &'files F, + diagnostic: &Diagnostic, +) -> Result { + let mut writer = String::new(); + emit_to_string(&mut writer, config, files, diagnostic)?; + Ok(writer) +} + +pub fn emit_to_string<'files, F: Files<'files> + ?Sized>( + writer: &mut String, + config: &Config, + files: &'files F, + diagnostic: &Diagnostic, +) -> Result<(), super::files::Error> { + // std::io::Write used under `feature = "std"` + #[cfg(feature = "std")] + { + let mut buffer = Vec::new(); + emit_with_style( + &mut renderer::PlainWriter::new(&mut buffer), + config, + files, + diagnostic, + )?; + let buffer_str: &str = str::from_utf8(&buffer).expect("buffer not utf8"); + writer.push_str(buffer_str); + Ok(()) + } + + // core::fmt::Write used not `not(feature = "std")` + #[cfg(not(feature = "std"))] + { + emit_with_style( + &mut renderer::PlainWriter::new(writer), + config, + files, + diagnostic, + ) + } +} + +#[cfg(feature = "std")] +pub fn emit_to_io_write<'files, F: Files<'files> + ?Sized, W: std::io::Write>( + writer: &mut W, + config: &Config, + files: &'files F, + diagnostic: &Diagnostic, +) -> Result<(), super::files::Error> { + emit_with_style( + &mut renderer::PlainWriter::new(writer), + config, + files, + diagnostic, + ) +} + +pub fn emit_to_write_style<'files, F: Files<'files> + ?Sized, W: WriteStyle>( + writer: &mut W, + config: &Config, + files: &'files F, + diagnostic: &Diagnostic, +) -> Result<(), super::files::Error> { + emit_with_style(writer, config, files, diagnostic) +} + +#[deprecated(since = "0.12.0", note = "Use emit_to_io_write instead")] +/// Emit a diagnostic using the given writer, context, config, and files. +/// +/// The return value covers all error cases. These error case can arise if: +/// * a file was removed from the file database. +/// * a file was changed so that it is too small to have an index +/// * IO fails +pub fn emit<'files, F: Files<'files> + ?Sized, W: renderer::GeneralWrite>( + writer: &mut W, + config: &Config, + files: &'files F, + diagnostic: &Diagnostic, +) -> Result<(), super::files::Error> { + emit_with_style( + &mut renderer::PlainWriter::new(writer), + config, + files, + diagnostic, + ) +} + +fn emit_with_style<'files, F: Files<'files> + ?Sized, W: WriteStyle>( + writer: &mut W, + config: &Config, + files: &'files F, + diagnostic: &Diagnostic, +) -> Result<(), super::files::Error> { + let mut renderer = Renderer::new(writer, config); + match config.display_style { + DisplayStyle::Rich => RichDiagnostic::new(diagnostic, config).render(files, &mut renderer), + DisplayStyle::Medium => ShortDiagnostic::new(diagnostic, true).render(files, &mut renderer), + DisplayStyle::Short => ShortDiagnostic::new(diagnostic, false).render(files, &mut renderer), + } +} + +#[cfg(all(test, feature = "termcolor"))] +mod tests { + use alloc::vec::Vec; + + use super::*; + + use crate::diagnostic::Label; + use crate::files::SimpleFiles; + + #[test] + fn unsized_emit() { + let mut files = SimpleFiles::new(); + + let id = files.add("test", ""); + let mut writer = termcolor::NoColor::new(Vec::::new()); + let diagnostic = Diagnostic::bug().with_labels(vec![Label::primary(id, 0..0)]); + + emit(&mut writer, &Config::default(), &files, &diagnostic).unwrap(); + } +} diff --git a/codespan-reporting/src/term/renderer.rs b/codespan-reporting/src/term/renderer.rs index 1153473b..7078ca6b 100644 --- a/codespan-reporting/src/term/renderer.rs +++ b/codespan-reporting/src/term/renderer.rs @@ -6,49 +6,64 @@ use crate::files::{Error, Location}; use crate::term::{Chars, Config}; #[cfg(feature = "std")] -use std::io::{self, Write}; +pub(crate) use std::io::Write as GeneralWrite; + #[cfg(feature = "std")] -type WriteResult = io::Result<()>; +pub type GeneralWriteResult = std::io::Result<()>; + +#[cfg(not(feature = "std"))] +pub(crate) use core::fmt::Write as GeneralWrite; #[cfg(not(feature = "std"))] -use core::fmt::{Arguments, Result as WriteResult, Write}; +pub use core::fmt::Result as GeneralWriteResult; /// A writer that can apply styling for different parts of a diagnostic renderer. /// /// # Implementations /// -/// - [`Writer`](Writer) - no-op styling, plain text output +/// - [`PlainWriter`](PlainWriter) - no-op styling, plain text output /// - [`StylesWriter`](crate::term::StylesWriter) - custom styles (requires `termcolor` feature) /// - `T: WriteColor` - blanket impl using default styles (requires `termcolor` feature) -pub trait WriteStyle: Write { - fn set_header(&mut self, severity: Severity) -> WriteResult; +pub trait WriteStyle: GeneralWrite { + fn set_header(&mut self, severity: Severity) -> GeneralWriteResult; - fn set_header_message(&mut self) -> WriteResult; + fn set_header_message(&mut self) -> GeneralWriteResult; - fn set_line_number(&mut self) -> WriteResult; + fn set_line_number(&mut self) -> GeneralWriteResult; - fn set_note_bullet(&mut self) -> WriteResult; + fn set_note_bullet(&mut self) -> GeneralWriteResult; - fn set_source_border(&mut self) -> WriteResult; + fn set_source_border(&mut self) -> GeneralWriteResult; - fn set_label(&mut self, severity: Severity, label_style: LabelStyle) -> WriteResult; + fn set_label(&mut self, severity: Severity, label_style: LabelStyle) -> GeneralWriteResult; - fn reset(&mut self) -> WriteResult; + fn reset(&mut self) -> GeneralWriteResult; } /// A [`WriteStyle`] implementation that ignores all styling calls. useful for non color output. -pub struct Writer { +pub struct PlainWriter { w: W, } -impl Writer { +impl PlainWriter { pub fn new(writer: W) -> Self { Self { w: writer } } } +#[cfg(feature = "std")] +impl std::io::Write for PlainWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.w.write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.w.flush() + } +} + #[cfg(not(feature = "std"))] -impl Write for Writer { +impl core::fmt::Write for PlainWriter { fn write_str(&mut self, s: &str) -> core::fmt::Result { self.w.write_str(s) } @@ -57,48 +72,37 @@ impl Write for Writer { self.w.write_char(c) } - fn write_fmt(&mut self, args: Arguments<'_>) -> core::fmt::Result { + fn write_fmt(&mut self, args: core::fmt::Arguments<'_>) -> core::fmt::Result { self.w.write_fmt(args) } } -#[cfg(feature = "std")] -impl Write for Writer { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.w.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.w.flush() - } -} - -impl WriteStyle for Writer { - fn set_header(&mut self, _severity: Severity) -> WriteResult { +impl WriteStyle for PlainWriter { + fn set_header(&mut self, _severity: Severity) -> GeneralWriteResult { Ok(()) } - fn set_header_message(&mut self) -> WriteResult { + fn set_header_message(&mut self) -> GeneralWriteResult { Ok(()) } - fn set_line_number(&mut self) -> WriteResult { + fn set_line_number(&mut self) -> GeneralWriteResult { Ok(()) } - fn set_note_bullet(&mut self) -> WriteResult { + fn set_note_bullet(&mut self) -> GeneralWriteResult { Ok(()) } - fn set_source_border(&mut self) -> WriteResult { + fn set_source_border(&mut self) -> GeneralWriteResult { Ok(()) } - fn set_label(&mut self, _severity: Severity, _label_style: LabelStyle) -> WriteResult { + fn set_label(&mut self, _severity: Severity, _label_style: LabelStyle) -> GeneralWriteResult { Ok(()) } - fn reset(&mut self) -> WriteResult { + fn reset(&mut self) -> GeneralWriteResult { Ok(()) } } @@ -204,7 +208,7 @@ type Underline = (LabelStyle, VerticalBound); /// empty ── │ /// ``` /// -/// Filler text from http://www.cupcakeipsum.com +/// > Filler text from http://www.cupcakeipsum.com pub struct Renderer<'writer, 'config> { writer: &'writer mut dyn WriteStyle, config: &'config Config, @@ -1048,8 +1052,19 @@ impl<'writer, 'config> Renderer<'writer, 'config> { } } +#[cfg(feature = "std")] +impl std::io::Write for Renderer<'_, '_> { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.writer.write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.writer.flush() + } +} + #[cfg(not(feature = "std"))] -impl Write for Renderer<'_, '_> { +impl core::fmt::Write for Renderer<'_, '_> { fn write_str(&mut self, s: &str) -> core::fmt::Result { self.writer.write_str(s) } @@ -1058,47 +1073,36 @@ impl Write for Renderer<'_, '_> { self.writer.write_char(c) } - fn write_fmt(&mut self, args: Arguments<'_>) -> core::fmt::Result { + fn write_fmt(&mut self, args: core::fmt::Arguments<'_>) -> core::fmt::Result { self.writer.write_fmt(args) } } -#[cfg(feature = "std")] -impl Write for Renderer<'_, '_> { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.writer.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.writer.flush() - } -} - impl WriteStyle for Renderer<'_, '_> { - fn set_header(&mut self, severity: Severity) -> WriteResult { + fn set_header(&mut self, severity: Severity) -> GeneralWriteResult { self.writer.set_header(severity) } - fn set_header_message(&mut self) -> WriteResult { + fn set_header_message(&mut self) -> GeneralWriteResult { self.writer.set_header_message() } - fn set_line_number(&mut self) -> WriteResult { + fn set_line_number(&mut self) -> GeneralWriteResult { self.writer.set_line_number() } - fn set_note_bullet(&mut self) -> WriteResult { + fn set_note_bullet(&mut self) -> GeneralWriteResult { self.writer.set_note_bullet() } - fn set_source_border(&mut self) -> WriteResult { + fn set_source_border(&mut self) -> GeneralWriteResult { self.writer.set_source_border() } - fn set_label(&mut self, severity: Severity, label_style: LabelStyle) -> WriteResult { + fn set_label(&mut self, severity: Severity, label_style: LabelStyle) -> GeneralWriteResult { self.writer.set_label(severity, label_style) } - fn reset(&mut self) -> WriteResult { + fn reset(&mut self) -> GeneralWriteResult { self.writer.reset() } } From 6d4bfd534c629ff637430ca097f3812fb20abf70 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 10 Oct 2025 10:20:17 +0100 Subject: [PATCH 04/22] remove ::{self} --- codespan-reporting/examples/custom_files.rs | 2 +- codespan-reporting/examples/peg_calculator.rs | 2 +- codespan-reporting/examples/readme_preview.rs | 2 +- codespan-reporting/examples/reusable_diagnostic.rs | 2 +- codespan-reporting/examples/term.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/codespan-reporting/examples/custom_files.rs b/codespan-reporting/examples/custom_files.rs index b2e6af48..303a36a2 100644 --- a/codespan-reporting/examples/custom_files.rs +++ b/codespan-reporting/examples/custom_files.rs @@ -11,7 +11,7 @@ use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -use codespan_reporting::term::{self}; +use codespan_reporting::term; use core::ops::Range; fn main() -> anyhow::Result<()> { diff --git a/codespan-reporting/examples/peg_calculator.rs b/codespan-reporting/examples/peg_calculator.rs index ebf936c1..395787a7 100644 --- a/codespan-reporting/examples/peg_calculator.rs +++ b/codespan-reporting/examples/peg_calculator.rs @@ -10,7 +10,7 @@ use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFile; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -use codespan_reporting::term::{self}; +use codespan_reporting::term; use rustyline::error::ReadlineError; use rustyline::Editor; diff --git a/codespan-reporting/examples/readme_preview.rs b/codespan-reporting/examples/readme_preview.rs index 243c0402..e68c28af 100644 --- a/codespan-reporting/examples/readme_preview.rs +++ b/codespan-reporting/examples/readme_preview.rs @@ -9,8 +9,8 @@ use codespan_reporting::diagnostic::{Diagnostic, Label, LabelStyle, Severity}; use codespan_reporting::files::SimpleFile; +use codespan_reporting::term; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -use codespan_reporting::term::{self}; use std::io::{self, Write}; #[derive(Debug)] diff --git a/codespan-reporting/examples/reusable_diagnostic.rs b/codespan-reporting/examples/reusable_diagnostic.rs index d1ec55fa..aaf9e4c2 100644 --- a/codespan-reporting/examples/reusable_diagnostic.rs +++ b/codespan-reporting/examples/reusable_diagnostic.rs @@ -1,7 +1,7 @@ use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFile; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -use codespan_reporting::term::{self}; +use codespan_reporting::term; use core::ops::Range; #[derive(Debug)] diff --git a/codespan-reporting/examples/term.rs b/codespan-reporting/examples/term.rs index 07217207..b0336bc1 100644 --- a/codespan-reporting/examples/term.rs +++ b/codespan-reporting/examples/term.rs @@ -8,7 +8,7 @@ use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -use codespan_reporting::term::{self}; +use codespan_reporting::term; #[derive(Debug)] pub struct Opts { From 3fd85a8d22dbe3ab8efcedb2fac786aebcbb77aa Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 10 Oct 2025 11:33:17 +0100 Subject: [PATCH 05/22] Add Styles::no_color --- codespan-reporting/src/lib.rs | 1 + codespan-reporting/src/term/config.rs | 69 ++++++++++++++----------- codespan-reporting/src/term/mod.rs | 4 +- codespan-reporting/src/term/renderer.rs | 5 +- 4 files changed, 45 insertions(+), 34 deletions(-) diff --git a/codespan-reporting/src/lib.rs b/codespan-reporting/src/lib.rs index c7beab48..08580a07 100644 --- a/codespan-reporting/src/lib.rs +++ b/codespan-reporting/src/lib.rs @@ -3,6 +3,7 @@ #![forbid(unsafe_code)] #![cfg_attr(not(feature = "std"), no_std)] +// for no_std extern crate alloc; pub mod diagnostic; diff --git a/codespan-reporting/src/term/config.rs b/codespan-reporting/src/term/config.rs index 81e40381..26e4a7f9 100644 --- a/codespan-reporting/src/term/config.rs +++ b/codespan-reporting/src/term/config.rs @@ -95,6 +95,9 @@ pub mod styles { use crate::diagnostic::{LabelStyle, Severity}; use termcolor::{Color, ColorSpec, WriteColor}; + // re-export + pub use termcolor; + /// Styles to use when rendering the diagnostic. #[derive(Clone, Debug)] pub struct Styles { @@ -186,9 +189,35 @@ pub mod styles { (LabelStyle::Secondary, _) => &self.secondary_label, } } + } - #[doc(hidden)] - pub fn with_blue(blue: Color) -> Styles { + impl Styles { + pub fn no_color() -> Styles { + Styles { + header_bug: ColorSpec::new(), + header_error: ColorSpec::new(), + header_warning: ColorSpec::new(), + header_note: ColorSpec::new(), + header_help: ColorSpec::new(), + header_message: ColorSpec::new(), + + primary_label_bug: ColorSpec::new(), + primary_label_error: ColorSpec::new(), + primary_label_warning: ColorSpec::new(), + primary_label_note: ColorSpec::new(), + primary_label_help: ColorSpec::new(), + secondary_label: ColorSpec::new(), + + line_number: ColorSpec::new(), + source_border: ColorSpec::new(), + note_bullet: ColorSpec::new(), + } + } + } + + impl Default for Styles { + fn default() -> Styles { + // Default style let header = ColorSpec::new().set_bold(true).set_intense(true).clone(); Styles { @@ -204,24 +233,19 @@ pub mod styles { primary_label_warning: ColorSpec::new().set_fg(Some(Color::Yellow)).clone(), primary_label_note: ColorSpec::new().set_fg(Some(Color::Green)).clone(), primary_label_help: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(), - secondary_label: ColorSpec::new().set_fg(Some(blue)).clone(), + secondary_label: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(), - line_number: ColorSpec::new().set_fg(Some(blue)).clone(), - source_border: ColorSpec::new().set_fg(Some(blue)).clone(), - note_bullet: ColorSpec::new().set_fg(Some(blue)).clone(), + line_number: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(), + source_border: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(), + note_bullet: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(), } } } - impl Default for Styles { - fn default() -> Styles { - Self::with_blue(Color::Cyan) - } - } - - /// A [`WriteStyle`](crate::term::renderer::WriteStyle) implementation that applies custom [`Styles`]. + /// A [`WriteStyle`](crate::term::renderer::WriteStyle) implementation that applies custom [`Styles`] + /// using termcolor. /// - /// Use this to render diagnostics with custom colors and formatting. + /// Use this to render diagnostics with custom colors pub struct StylesWriter<'a, W> { writer: W, style: &'a Styles, @@ -234,7 +258,7 @@ pub mod styles { } } - #[cfg(feature = "std")] + // Always true here #[cfg(feature = "std")] impl<'a, W: WriteColor> std::io::Write for StylesWriter<'a, W> { fn write(&mut self, buf: &[u8]) -> std::io::Result { self.writer.write(buf) @@ -245,21 +269,6 @@ pub mod styles { } } - #[cfg(not(feature = "std"))] - impl core::fmt::Write for StylesWriter<'_, '_> { - fn write_str(&mut self, s: &str) -> core::fmt::Result { - self.writer.write_str(s) - } - - fn write_char(&mut self, c: char) -> core::fmt::Result { - self.writer.write_char(c) - } - - fn write_fmt(&mut self, args: core::fmt::Arguments<'_>) -> core::fmt::Result { - self.writer.write_fmt(args) - } - } - impl<'a, W: WriteColor> WriteStyle for StylesWriter<'a, W> { fn set_header(&mut self, severity: Severity) -> renderer::GeneralWriteResult { self.writer.set_color(self.style.header(severity)) diff --git a/codespan-reporting/src/term/mod.rs b/codespan-reporting/src/term/mod.rs index caecbf70..53e4fa52 100644 --- a/codespan-reporting/src/term/mod.rs +++ b/codespan-reporting/src/term/mod.rs @@ -14,9 +14,9 @@ pub use config::{Chars, Config, DisplayStyle}; // re-export #[cfg(feature = "termcolor")] -pub use config::styles::{Styles, StylesWriter}; +pub use config::styles::{Styles, StylesWriter, termcolor}; -pub use renderer::{Renderer, WriteStyle}; +pub use renderer::{Renderer, WriteStyle, GeneralWrite, GeneralWriteResult}; pub use views::{RichDiagnostic, ShortDiagnostic}; pub fn emit_into_string<'files, F: Files<'files> + ?Sized>( diff --git a/codespan-reporting/src/term/renderer.rs b/codespan-reporting/src/term/renderer.rs index 7078ca6b..d9365915 100644 --- a/codespan-reporting/src/term/renderer.rs +++ b/codespan-reporting/src/term/renderer.rs @@ -6,13 +6,13 @@ use crate::files::{Error, Location}; use crate::term::{Chars, Config}; #[cfg(feature = "std")] -pub(crate) use std::io::Write as GeneralWrite; +pub use std::io::Write as GeneralWrite; #[cfg(feature = "std")] pub type GeneralWriteResult = std::io::Result<()>; #[cfg(not(feature = "std"))] -pub(crate) use core::fmt::Write as GeneralWrite; +pub use core::fmt::Write as GeneralWrite; #[cfg(not(feature = "std"))] pub use core::fmt::Result as GeneralWriteResult; @@ -41,6 +41,7 @@ pub trait WriteStyle: GeneralWrite { } /// A [`WriteStyle`] implementation that ignores all styling calls. useful for non color output. +/// Available on all targets pub struct PlainWriter { w: W, } From 2fe29ba2600ccc82998a217fe56f457cdadd2f9d Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 10 Oct 2025 11:33:43 +0100 Subject: [PATCH 06/22] Update examples to be work across feature selection --- codespan-reporting/examples/custom_files.rs | 15 ++-- codespan-reporting/examples/peg_calculator.rs | 16 +++-- codespan-reporting/examples/readme_preview.rs | 69 +++++++++++++------ .../examples/reusable_diagnostic.rs | 39 +++++++---- codespan-reporting/examples/term.rs | 39 ++++++----- 5 files changed, 119 insertions(+), 59 deletions(-) diff --git a/codespan-reporting/examples/custom_files.rs b/codespan-reporting/examples/custom_files.rs index 303a36a2..8ff0204a 100644 --- a/codespan-reporting/examples/custom_files.rs +++ b/codespan-reporting/examples/custom_files.rs @@ -10,11 +10,18 @@ //! ``` use codespan_reporting::diagnostic::{Diagnostic, Label}; -use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -use codespan_reporting::term; +use codespan_reporting::term::{self, Config}; use core::ops::Range; +#[cfg(not(feature = "termcolor"))] +fn main() { + panic!("this example requires termcolor feature"); +} + +#[cfg(feature = "termcolor")] fn main() -> anyhow::Result<()> { + use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; + let mut files = files::Files::new(); let file_id0 = files.add("0.greeting", "hello world!").unwrap(); @@ -30,9 +37,9 @@ fn main() -> anyhow::Result<()> { ]; let writer = StandardStream::stderr(ColorChoice::Always); - let config = term::Config::default(); + let config = Config::default(); for message in &messages { - term::emit( + term::emit_to_write_style( &mut writer.lock(), &config, &files, diff --git a/codespan-reporting/examples/peg_calculator.rs b/codespan-reporting/examples/peg_calculator.rs index 395787a7..2f2c9d1c 100644 --- a/codespan-reporting/examples/peg_calculator.rs +++ b/codespan-reporting/examples/peg_calculator.rs @@ -9,8 +9,8 @@ use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFile; -use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -use codespan_reporting::term; +use codespan_reporting::term::{self, Config}; + use rustyline::error::ReadlineError; use rustyline::Editor; @@ -36,9 +36,17 @@ peg::parser! { } } +#[cfg(not(feature = "termcolor"))] +fn main() { + panic!("this example requires termcolor feature"); +} + +#[cfg(feature = "termcolor")] fn main() -> anyhow::Result<()> { + use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; + let writer = StandardStream::stderr(ColorChoice::Always); - let config = codespan_reporting::term::Config::default(); + let config = Config::default(); let mut editor = Editor::<()>::new(); loop { @@ -61,7 +69,7 @@ fn main() -> anyhow::Result<()> { ]) .with_notes(vec![format!("expected: {}", error.expected)]); - term::emit(&mut writer.lock(), &config, &file, &diagnostic)?; + term::emit_to_write_style(&mut writer.lock(), &config, &file, &diagnostic)?; } } } diff --git a/codespan-reporting/examples/readme_preview.rs b/codespan-reporting/examples/readme_preview.rs index e68c28af..509fb9c7 100644 --- a/codespan-reporting/examples/readme_preview.rs +++ b/codespan-reporting/examples/readme_preview.rs @@ -9,15 +9,19 @@ use codespan_reporting::diagnostic::{Diagnostic, Label, LabelStyle, Severity}; use codespan_reporting::files::SimpleFile; -use codespan_reporting::term; +use codespan_reporting::term::{self, Config}; + +use codespan_reporting::term::{GeneralWrite, GeneralWriteResult}; + +#[cfg(feature = "termcolor")] use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -use std::io::{self, Write}; #[derive(Debug)] pub enum Opts { /// Render SVG output Svg, /// Render Stderr output + #[cfg(feature = "termcolor")] Stderr { /// Configure coloring of output color: ColorChoice, @@ -29,6 +33,7 @@ fn parse_args() -> Result { match pargs.subcommand()? { Some(value) => match value.as_str() { "svg" => Ok(Opts::Svg), + #[cfg(feature = "termcolor")] "stderr" => { let color = pargs .opt_value_from_str("--color")? @@ -91,10 +96,10 @@ fn main() -> anyhow::Result<()> { Opts::Svg => { let mut buffer = Vec::new(); let mut writer = SvgWriter::new(&mut buffer); - let config = codespan_reporting::term::Config::default(); + let config = Config::default(); for diagnostic in &diagnostics { - term::emit(&mut writer, &config, &file, diagnostic)?; + term::emit_to_write_style(&mut writer, &config, &file, diagnostic)?; } let num_lines = buffer.iter().filter(|byte| **byte == b'\n').count() + 1; @@ -204,11 +209,12 @@ fn main() -> anyhow::Result<()> { " )?; } + #[cfg(feature = "termcolor")] Opts::Stderr { color } => { let writer = StandardStream::stderr(color); - let config = codespan_reporting::term::Config::default(); + let config = Config::default(); for diagnostic in &diagnostics { - term::emit(&mut writer.lock(), &config, &file, diagnostic)?; + term::emit_to_write_style(&mut writer.lock(), &config, &file, diagnostic)?; } } } @@ -221,7 +227,7 @@ pub struct SvgWriter { span_open: bool, } -impl SvgWriter { +impl SvgWriter { pub fn new(upstream: W) -> Self { SvgWriter { upstream, @@ -230,7 +236,7 @@ impl SvgWriter { } /// Close any open span - fn close_span(&mut self) -> io::Result<()> { + fn close_span(&mut self) -> GeneralWriteResult { if self.span_open { write!(self.upstream, "")?; self.span_open = false; @@ -239,7 +245,7 @@ impl SvgWriter { } /// Open a new span with the given CSS class - fn open_span(&mut self, class: &str) -> io::Result<()> { + fn open_span(&mut self, class: &str) -> GeneralWriteResult { // close existing first self.close_span()?; write!(self.upstream, "", class)?; @@ -248,8 +254,9 @@ impl SvgWriter { } } -impl Write for SvgWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { +#[cfg(feature = "std")] +impl std::io::Write for SvgWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { let mut last = 0; for (i, &b) in buf.iter().enumerate() { let escape = match b { @@ -265,13 +272,35 @@ impl Write for SvgWriter { self.upstream.write_all(&buf[last..])?; Ok(buf.len()) } - fn flush(&mut self) -> io::Result<()> { + + fn flush(&mut self) -> std::io::Result<()> { self.upstream.flush() } } -impl codespan_reporting::term::WriteStyle for SvgWriter { - fn set_header(&mut self, severity: Severity) -> io::Result<()> { +#[cfg(not(feature = "std"))] +impl core::fmt::Write for SvgWriter { + fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { + let mut last = 0; + // TODO match indices + for (i, &b) in s.chars().enumerate() { + let escape = match b { + '<' => "<", + '>' => ">", + '&' => "&", + _ => continue, + }; + self.upstream.write_str(&buf[last..i])?; + self.upstream.write_str(escape)?; + last = i + 1; + } + self.upstream.write_str(&buf[last..])?; + Ok(buf.len()) + } +} + +impl codespan_reporting::term::WriteStyle for SvgWriter { + fn set_header(&mut self, severity: Severity) -> GeneralWriteResult { let class = match severity { Severity::Bug => "header-bug", Severity::Error => "header-error", @@ -282,23 +311,23 @@ impl codespan_reporting::term::WriteStyle for SvgWriter { self.open_span(class) } - fn set_header_message(&mut self) -> io::Result<()> { + fn set_header_message(&mut self) -> GeneralWriteResult { self.open_span("header-message") } - fn set_line_number(&mut self) -> io::Result<()> { + fn set_line_number(&mut self) -> GeneralWriteResult { self.open_span("line-number") } - fn set_note_bullet(&mut self) -> io::Result<()> { + fn set_note_bullet(&mut self) -> GeneralWriteResult { self.open_span("note-bullet") } - fn set_source_border(&mut self) -> io::Result<()> { + fn set_source_border(&mut self) -> GeneralWriteResult { self.open_span("source-border") } - fn set_label(&mut self, severity: Severity, label_style: LabelStyle) -> io::Result<()> { + fn set_label(&mut self, severity: Severity, label_style: LabelStyle) -> GeneralWriteResult { let sev = match severity { Severity::Bug => "bug", Severity::Error => "error", @@ -313,7 +342,7 @@ impl codespan_reporting::term::WriteStyle for SvgWriter { self.open_span(&format!("label-{}-{}", typ, sev)) } - fn reset(&mut self) -> io::Result<()> { + fn reset(&mut self) -> GeneralWriteResult { self.close_span() } } diff --git a/codespan-reporting/examples/reusable_diagnostic.rs b/codespan-reporting/examples/reusable_diagnostic.rs index aaf9e4c2..94fd3a38 100644 --- a/codespan-reporting/examples/reusable_diagnostic.rs +++ b/codespan-reporting/examples/reusable_diagnostic.rs @@ -1,23 +1,30 @@ use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFile; -use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -use codespan_reporting::term; +use codespan_reporting::term::{self, Config}; use core::ops::Range; -#[derive(Debug)] -pub struct Opts { - color: ColorChoice, -} - -fn parse_args() -> Result { - let mut pargs = pico_args::Arguments::from_env(); - let color = pargs - .opt_value_from_str("--color")? - .unwrap_or(ColorChoice::Auto); - Ok(Opts { color }) +#[cfg(not(feature = "termcolor"))] +fn main() { + panic!("this example requires termcolor feature"); } +#[cfg(feature = "termcolor")] fn main() -> anyhow::Result<()> { + use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; + + #[derive(Debug)] + pub struct Opts { + color: ColorChoice, + } + + fn parse_args() -> Result { + let mut pargs = pico_args::Arguments::from_env(); + let color = pargs + .opt_value_from_str("--color")? + .unwrap_or(ColorChoice::Auto); + Ok(Opts { color }) + } + let file = SimpleFile::new( "main.rs", unindent::unindent( @@ -39,10 +46,12 @@ fn main() -> anyhow::Result<()> { ]; let Opts { color } = parse_args()?; + let writer = StandardStream::stderr(color); - let config = codespan_reporting::term::Config::default(); + let config = Config::default(); + for diagnostic in errors.iter().map(Error::report) { - term::emit(&mut writer.lock(), &config, &file, &diagnostic)?; + term::emit_to_write_style(&mut writer.lock(), &config, &file, &diagnostic)?; } Ok(()) diff --git a/codespan-reporting/examples/term.rs b/codespan-reporting/examples/term.rs index b0336bc1..8b30471b 100644 --- a/codespan-reporting/examples/term.rs +++ b/codespan-reporting/examples/term.rs @@ -7,24 +7,31 @@ use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFiles; -use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -use codespan_reporting::term; +use codespan_reporting::term::{self, Config}; -#[derive(Debug)] -pub struct Opts { - /// Configure coloring of output - pub color: ColorChoice, -} - -fn parse_args() -> Result { - let mut pargs = pico_args::Arguments::from_env(); - let color = pargs - .opt_value_from_str("--color")? - .unwrap_or(ColorChoice::Auto); - Ok(Opts { color }) +#[cfg(not(feature = "termcolor"))] +fn main() { + panic!("this example requires termcolor feature"); } +#[cfg(feature = "termcolor")] fn main() -> anyhow::Result<()> { + use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; + + #[derive(Debug)] + pub struct Opts { + /// Configure coloring of output + pub color: ColorChoice, + } + + fn parse_args() -> Result { + let mut pargs = pico_args::Arguments::from_env(); + let color = pargs + .opt_value_from_str("--color")? + .unwrap_or(ColorChoice::Auto); + Ok(Opts { color }) + } + let Opts { color } = parse_args()?; let mut files = SimpleFiles::new(); @@ -166,9 +173,9 @@ fn main() -> anyhow::Result<()> { ]; let writer = StandardStream::stderr(color); - let config = codespan_reporting::term::Config::default(); + let config = Config::default(); for diagnostic in &diagnostics { - term::emit(&mut writer.lock(), &config, &files, diagnostic)?; + term::emit_to_write_style(&mut writer.lock(), &config, &files, diagnostic)?; } Ok(()) From c326eb1c25fd4364549a3edb0d3f34605526c6f0 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 10 Oct 2025 11:41:48 +0100 Subject: [PATCH 07/22] Update test now config is correct --- .../snapshots/term__surrounding_lines__rich_no_color.snap | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/codespan-reporting/tests/snapshots/term__surrounding_lines__rich_no_color.snap b/codespan-reporting/tests/snapshots/term__surrounding_lines__rich_no_color.snap index 2ebdcd30..d6371c4d 100644 --- a/codespan-reporting/tests/snapshots/term__surrounding_lines__rich_no_color.snap +++ b/codespan-reporting/tests/snapshots/term__surrounding_lines__rich_no_color.snap @@ -1,5 +1,6 @@ --- source: codespan-reporting/tests/term.rs +assertion_line: 1157 expression: TEST_DATA.emit_no_color(& config) --- error: Unknown attribute macro @@ -7,17 +8,23 @@ error: Unknown attribute macro │ 1 │ #[foo] │ ^^^ No attribute macro `foo` known +2 │ fn main() { error: Missing argument for format ┌─ surroundingLines.fun:5:9 │ +2 │ fn main() { +3 │ println!( 4 │ "{}", │ -- Unable to use `{}`-directive to display `Foo` 5 │ Foo │ ^^^ No instance of std::fmt::Display exists for type Foo +6 │ ); error: Syntax error ┌─ surroundingLines.fun:9:11 │ +7 │ } +8 │ 9 │ struct Foo │ ^ Missing a semicolon From b122904dee54dd239882ea5490918044fe5996d7 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 10 Oct 2025 11:42:33 +0100 Subject: [PATCH 08/22] Fixes to tests to work across selected features --- codespan-reporting/src/term/mod.rs | 12 ++++++------ codespan-reporting/tests/support/mod.rs | 25 ++++++++++++------------- codespan-reporting/tests/term.rs | 7 +++++-- codespan/src/file.rs | 4 ++-- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/codespan-reporting/src/term/mod.rs b/codespan-reporting/src/term/mod.rs index 53e4fa52..d7a33f0c 100644 --- a/codespan-reporting/src/term/mod.rs +++ b/codespan-reporting/src/term/mod.rs @@ -86,7 +86,7 @@ pub fn emit_to_write_style<'files, F: Files<'files> + ?Sized, W: WriteStyle>( emit_with_style(writer, config, files, diagnostic) } -#[deprecated(since = "0.12.0", note = "Use emit_to_io_write instead")] +#[deprecated(since = "0.12.0", note = "Use `emit_to_write_style` instead")] /// Emit a diagnostic using the given writer, context, config, and files. /// /// The return value covers all error cases. These error case can arise if: @@ -121,7 +121,7 @@ fn emit_with_style<'files, F: Files<'files> + ?Sized, W: WriteStyle>( } } -#[cfg(all(test, feature = "termcolor"))] +#[cfg(test)] mod tests { use alloc::vec::Vec; @@ -130,14 +130,14 @@ mod tests { use crate::diagnostic::Label; use crate::files::SimpleFiles; + /// Test range of 0 to 0 and check does not crash #[test] fn unsized_emit() { let mut files = SimpleFiles::new(); let id = files.add("test", ""); - let mut writer = termcolor::NoColor::new(Vec::::new()); - let diagnostic = Diagnostic::bug().with_labels(vec![Label::primary(id, 0..0)]); - - emit(&mut writer, &Config::default(), &files, &diagnostic).unwrap(); + let zero_range = 0..0; + let diagnostic = Diagnostic::bug().with_labels(vec![Label::primary(id, zero_range)]); + emit_into_string(&Config::default(), &files, &diagnostic).unwrap(); } } diff --git a/codespan-reporting/tests/support/mod.rs b/codespan-reporting/tests/support/mod.rs index c674b31d..e3ee5a64 100644 --- a/codespan-reporting/tests/support/mod.rs +++ b/codespan-reporting/tests/support/mod.rs @@ -1,31 +1,30 @@ use codespan_reporting::diagnostic::Diagnostic; use codespan_reporting::files::Files; -use codespan_reporting::term::{emit, Config}; -use termcolor::{Buffer, WriteColor}; +use codespan_reporting::term::{self, Config}; +#[cfg(feature = "termcolor")] mod color_buffer; -use self::color_buffer::ColorBuffer; - pub struct TestData<'files, F: Files<'files>> { pub files: F, pub diagnostics: Vec>, } impl<'files, F: Files<'files>> TestData<'files, F> { - fn emit(&'files self, mut writer: W, config: &Config) -> W { + #[cfg(feature = "termcolor")] + pub fn emit_color(&'files self, config: &Config) -> String { + let mut writer = color_buffer::ColorBuffer::new(); for diagnostic in &self.diagnostics { - emit(&mut writer, config, &self.files, diagnostic).unwrap(); + term::emit_to_write_style(&mut writer, config, &self.files, diagnostic).unwrap(); } - writer - } - - pub fn emit_color(&'files self, config: &Config) -> String { - self.emit(ColorBuffer::new(), config).into_string() + writer.into_string() } pub fn emit_no_color(&'files self, config: &Config) -> String { - let buffer = self.emit(Buffer::no_color(), config); - String::from_utf8_lossy(buffer.as_slice()).into_owned() + let mut writer = String::new(); + for diagnostic in &self.diagnostics { + term::emit_to_string(&mut writer, config, &self.files, diagnostic).unwrap(); + } + writer } } diff --git a/codespan-reporting/tests/term.rs b/codespan-reporting/tests/term.rs index 2787e500..019c5910 100644 --- a/codespan-reporting/tests/term.rs +++ b/codespan-reporting/tests/term.rs @@ -14,6 +14,7 @@ type LazyTestData<'a, T> = LazyLock>; macro_rules! test_emit { (rich_color) => { #[test] + #[cfg(feature = "termcolor")] fn rich_color() { let config = Config { display_style: DisplayStyle::Rich, @@ -25,6 +26,7 @@ macro_rules! test_emit { }; (medium_color) => { #[test] + #[cfg(feature = "termcolor")] fn medium_color() { let config = Config { display_style: DisplayStyle::Medium, @@ -36,6 +38,7 @@ macro_rules! test_emit { }; (short_color) => { #[test] + #[cfg(feature = "termcolor")] fn short_color() { let config = Config { display_style: DisplayStyle::Short, @@ -1103,8 +1106,8 @@ mod surrounding_lines { use super::*; static TEST_CONFIG: LazyLock = LazyLock::new(|| Config { - start_context_lines: 2, - end_context_lines: 1, + before_label_lines: 2, + after_label_lines: 1, ..Config::default() }); diff --git a/codespan/src/file.rs b/codespan/src/file.rs index 1eac0cae..1915d348 100644 --- a/codespan/src/file.rs +++ b/codespan/src/file.rs @@ -450,7 +450,7 @@ mod test { #[test] fn interoperability() { extern crate termcolor; - use codespan_reporting::{diagnostic::*, term::emit}; + use codespan_reporting::{diagnostic::*, term::emit_to_write_style}; use termcolor::{ColorChoice, StandardStream}; let mut files = Files::::new(); @@ -462,6 +462,6 @@ mod test { let config = codespan_reporting::term::Config::default(); let writer = StandardStream::stdout(ColorChoice::Auto); - emit(&mut writer.lock(), &config, &files, &diagnostic).unwrap(); + emit_to_write_style(&mut writer.lock(), &config, &files, &diagnostic).unwrap(); } } From 8ab1842ebf66d19854030cf9304751777b1000be Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 10 Oct 2025 11:43:14 +0100 Subject: [PATCH 09/22] Minor changes to CI --- .github/workflows/ci.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6149be8..8a8392e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,8 +6,10 @@ on: jobs: check: - name: Check ${{ matrix.rust }} + name: Check (${{ matrix.rust }}) + runs-on: ubuntu-latest + strategy: matrix: rust: ["stable", "nightly"] # "1.67.0", @@ -46,27 +48,24 @@ jobs: - name: Run cargo test for codespan run: cargo test -p codespan - - name: Run cargo test for codespan-reporting - run: cargo test -p codespan-reporting - - name: Run cargo test for codespan-reporting (no features) run: cargo test -p codespan-reporting --no-default-features - name: Run cargo test for codespan-reporting (only std feature) run: cargo test -p codespan-reporting --no-default-features --features std + - name: Run cargo test for codespan-reporting (all default features) + run: cargo test -p codespan-reporting + - name: Run cargo test for codespan-lsp run: cargo test -p codespan-lsp format: - name: rustfmt - runs-on: ubuntu-latest strategy: fail-fast: false - matrix: - rust: ["stable"] + matrix: { rust: ["stable"] } steps: - uses: actions/checkout@v4 From 725089208a8eec0379922bb3cd46fa0d5de120f7 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 10 Oct 2025 12:06:26 +0100 Subject: [PATCH 10/22] Fixes to readme example --- codespan-reporting/examples/readme_preview.rs | 156 ++++++++++-------- 1 file changed, 90 insertions(+), 66 deletions(-) diff --git a/codespan-reporting/examples/readme_preview.rs b/codespan-reporting/examples/readme_preview.rs index 509fb9c7..9f1cab90 100644 --- a/codespan-reporting/examples/readme_preview.rs +++ b/codespan-reporting/examples/readme_preview.rs @@ -10,46 +10,51 @@ use codespan_reporting::diagnostic::{Diagnostic, Label, LabelStyle, Severity}; use codespan_reporting::files::SimpleFile; use codespan_reporting::term::{self, Config}; - use codespan_reporting::term::{GeneralWrite, GeneralWriteResult}; #[cfg(feature = "termcolor")] use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -#[derive(Debug)] -pub enum Opts { - /// Render SVG output - Svg, - /// Render Stderr output - #[cfg(feature = "termcolor")] - Stderr { - /// Configure coloring of output - color: ColorChoice, - }, +#[cfg(not(feature = "std"))] +fn main() { + panic!("example requires std feature"); } -fn parse_args() -> Result { - let mut pargs = pico_args::Arguments::from_env(); - match pargs.subcommand()? { - Some(value) => match value.as_str() { - "svg" => Ok(Opts::Svg), - #[cfg(feature = "termcolor")] - "stderr" => { - let color = pargs - .opt_value_from_str("--color")? - .unwrap_or(ColorChoice::Auto); - Ok(Opts::Stderr { color }) - } - _ => Err(pico_args::Error::Utf8ArgumentParsingFailed { - value, - cause: "not a valid subcommand".to_owned(), - }), +#[cfg(feature = "std")] +fn main() -> anyhow::Result<()> { + #[derive(Debug)] + pub enum Opts { + /// Render SVG output + Svg, + /// Render Stderr output + #[cfg(feature = "termcolor")] + Stderr { + /// Configure coloring of output + color: ColorChoice, }, - None => Err(pico_args::Error::MissingArgument), } -} -fn main() -> anyhow::Result<()> { + fn parse_args() -> Result { + let mut pargs = pico_args::Arguments::from_env(); + match pargs.subcommand()? { + Some(value) => match value.as_str() { + "svg" => Ok(Opts::Svg), + #[cfg(feature = "termcolor")] + "stderr" => { + let color = pargs + .opt_value_from_str("--color")? + .unwrap_or(ColorChoice::Auto); + Ok(Opts::Stderr { color }) + } + _ => Err(pico_args::Error::Utf8ArgumentParsingFailed { + value, + cause: "not a valid subcommand".to_owned(), + }), + }, + None => Err(pico_args::Error::MissingArgument), + } + } + let file = SimpleFile::new( "FizzBuzz.fun", unindent::unindent( @@ -94,15 +99,14 @@ fn main() -> anyhow::Result<()> { match parse_args()? { Opts::Svg => { - let mut buffer = Vec::new(); - let mut writer = SvgWriter::new(&mut buffer); + let mut writer = SvgWriter::new(); let config = Config::default(); for diagnostic in &diagnostics { term::emit_to_write_style(&mut writer, &config, &file, diagnostic)?; } - let num_lines = buffer.iter().filter(|byte| **byte == b'\n').count() + 1; + let num_lines = writer.line_count(); let padding = 10; let font_size = 12; @@ -110,6 +114,8 @@ fn main() -> anyhow::Result<()> { let width = 882; let height = padding + num_lines * (font_size + line_spacing) + padding; + let content = writer.into_string(); + let stdout = std::io::stdout(); let writer = &mut stdout.lock(); @@ -191,22 +197,12 @@ fn main() -> anyhow::Result<()> {
-
"#,
-                padding = padding,
-                font_size = font_size,
-                width = width,
-                height = height,
-            )?;
-
-            writer.write_all(&buffer)?;
-
-            write!(
-                writer,
-                "
+
+        {content}
+      
- -" +"# )?; } #[cfg(feature = "termcolor")] @@ -222,23 +218,51 @@ fn main() -> anyhow::Result<()> { Ok(()) } -pub struct SvgWriter { - upstream: W, +// This whole example requires the std feature, but below is feature agnostic for reference + +#[cfg(feature = "std")] +type WriterBuffer = Vec; + +#[cfg(not(feature = "std"))] +type WriterBuffer = String; + +pub struct SvgWriter { + buffer: WriterBuffer, span_open: bool, } -impl SvgWriter { - pub fn new(upstream: W) -> Self { +impl SvgWriter { + pub fn new() -> Self { SvgWriter { - upstream, + buffer: WriterBuffer::default(), span_open: false, } } + #[cfg(feature = "std")] + pub fn into_string(self) -> String { + String::from_utf8(self.buffer).unwrap() + } + + #[cfg(not(feature = "std"))] + pub fn into_string(self) -> String { + self.buffer + } + + #[cfg(feature = "std")] + pub fn line_count(&self) -> usize { + self.buffer.iter().filter(|byte| **byte == b'\n').count() + 1 + } + + #[cfg(not(feature = "std"))] + pub fn line_count(&self) -> usize { + self.buffer.lines().count() + } + /// Close any open span fn close_span(&mut self) -> GeneralWriteResult { if self.span_open { - write!(self.upstream, "
")?; + write!(self.buffer, "")?; self.span_open = false; } Ok(()) @@ -248,14 +272,14 @@ impl SvgWriter { fn open_span(&mut self, class: &str) -> GeneralWriteResult { // close existing first self.close_span()?; - write!(self.upstream, "", class)?; + write!(self.buffer, "", class)?; self.span_open = true; Ok(()) } } #[cfg(feature = "std")] -impl std::io::Write for SvgWriter { +impl std::io::Write for SvgWriter { fn write(&mut self, buf: &[u8]) -> std::io::Result { let mut last = 0; for (i, &b) in buf.iter().enumerate() { @@ -265,41 +289,41 @@ impl std::io::Write for SvgWriter { b'&' => b"&"[..].as_ref(), _ => continue, }; - self.upstream.write_all(&buf[last..i])?; - self.upstream.write_all(escape)?; + self.buffer.write_all(&buf[last..i])?; + self.buffer.write_all(escape)?; last = i + 1; } - self.upstream.write_all(&buf[last..])?; + self.buffer.write_all(&buf[last..])?; Ok(buf.len()) } fn flush(&mut self) -> std::io::Result<()> { - self.upstream.flush() + self.buffer.flush() } } #[cfg(not(feature = "std"))] -impl core::fmt::Write for SvgWriter { +impl core::fmt::Write for SvgWriter { fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { let mut last = 0; // TODO match indices - for (i, &b) in s.chars().enumerate() { + for (i, b) in s.chars().enumerate() { let escape = match b { '<' => "<", '>' => ">", '&' => "&", _ => continue, }; - self.upstream.write_str(&buf[last..i])?; - self.upstream.write_str(escape)?; + self.buffer.write_str(&s[last..i])?; + self.buffer.write_str(escape)?; last = i + 1; } - self.upstream.write_str(&buf[last..])?; - Ok(buf.len()) + self.buffer.write_str(&s[last..])?; + Ok(()) } } -impl codespan_reporting::term::WriteStyle for SvgWriter { +impl codespan_reporting::term::WriteStyle for SvgWriter { fn set_header(&mut self, severity: Severity) -> GeneralWriteResult { let class = match severity { Severity::Bug => "header-bug", From a78fd50131f06d5b9c588e78b7f2972ff1b3ec2a Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 10 Oct 2025 12:06:32 +0100 Subject: [PATCH 11/22] Formatting --- codespan-reporting/src/term/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codespan-reporting/src/term/mod.rs b/codespan-reporting/src/term/mod.rs index d7a33f0c..99c3391d 100644 --- a/codespan-reporting/src/term/mod.rs +++ b/codespan-reporting/src/term/mod.rs @@ -14,9 +14,9 @@ pub use config::{Chars, Config, DisplayStyle}; // re-export #[cfg(feature = "termcolor")] -pub use config::styles::{Styles, StylesWriter, termcolor}; +pub use config::styles::{termcolor, Styles, StylesWriter}; -pub use renderer::{Renderer, WriteStyle, GeneralWrite, GeneralWriteResult}; +pub use renderer::{GeneralWrite, GeneralWriteResult, Renderer, WriteStyle}; pub use views::{RichDiagnostic, ShortDiagnostic}; pub fn emit_into_string<'files, F: Files<'files> + ?Sized>( From 01eae666a8692a28d06eb9658306be71afa552d2 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 10 Oct 2025 12:12:36 +0100 Subject: [PATCH 12/22] Change to labels --- codespan-reporting/examples/peg_calculator.rs | 4 +--- codespan-reporting/examples/term.rs | 8 ++------ codespan-reporting/src/term/mod.rs | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/codespan-reporting/examples/peg_calculator.rs b/codespan-reporting/examples/peg_calculator.rs index 2f2c9d1c..c6737748 100644 --- a/codespan-reporting/examples/peg_calculator.rs +++ b/codespan-reporting/examples/peg_calculator.rs @@ -64,9 +64,7 @@ fn main() -> anyhow::Result<()> { let start = error.location.offset; let diagnostic = Diagnostic::error() .with_message("parse error") - .with_labels(vec![ - Label::primary((), start..start).with_message("parse error") - ]) + .with_label(Label::primary((), start..start).with_message("parse error")) .with_notes(vec![format!("expected: {}", error.expected)]); term::emit_to_write_style(&mut writer.lock(), &config, &file, &diagnostic)?; diff --git a/codespan-reporting/examples/term.rs b/codespan-reporting/examples/term.rs index 8b30471b..001055c3 100644 --- a/codespan-reporting/examples/term.rs +++ b/codespan-reporting/examples/term.rs @@ -102,18 +102,14 @@ fn main() -> anyhow::Result<()> { // Unknown builtin error Diagnostic::error() .with_message("unknown builtin: `NATRAL`") - .with_labels(vec![ - Label::primary(file_id1, 96..102).with_message("unknown builtin") - ]) + .with_label(Label::primary(file_id1, 96..102).with_message("unknown builtin")) .with_notes(vec![ "there is a builtin with a similar name: `NATURAL`".to_owned() ]), // Unused parameter warning Diagnostic::warning() .with_message("unused parameter pattern: `n₂`") - .with_labels(vec![ - Label::primary(file_id1, 285..289).with_message("unused parameter") - ]) + .with_label(Label::primary(file_id1, 285..289).with_message("unused parameter")) .with_notes(vec!["consider using a wildcard pattern: `_`".to_owned()]), // Unexpected type error Diagnostic::error() diff --git a/codespan-reporting/src/term/mod.rs b/codespan-reporting/src/term/mod.rs index 99c3391d..f4bfb897 100644 --- a/codespan-reporting/src/term/mod.rs +++ b/codespan-reporting/src/term/mod.rs @@ -137,7 +137,7 @@ mod tests { let id = files.add("test", ""); let zero_range = 0..0; - let diagnostic = Diagnostic::bug().with_labels(vec![Label::primary(id, zero_range)]); + let diagnostic = Diagnostic::bug().with_label(Label::primary(id, zero_range)); emit_into_string(&Config::default(), &files, &diagnostic).unwrap(); } } From 8552ae1332c797ece7d22cebb742ef4af5ec8465 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 15 Oct 2025 10:18:57 +0100 Subject: [PATCH 13/22] Some minor internal changes --- codespan-reporting/src/diagnostic.rs | 16 +++--- codespan-reporting/src/files.rs | 2 +- codespan-reporting/src/term/config.rs | 2 +- codespan-reporting/src/term/renderer.rs | 12 ++--- codespan-reporting/src/term/views.rs | 71 ++++++++++++------------- codespan/src/file.rs | 13 ++--- codespan/src/span.rs | 5 ++ 7 files changed, 61 insertions(+), 60 deletions(-) diff --git a/codespan-reporting/src/diagnostic.rs b/codespan-reporting/src/diagnostic.rs index 1b19f4b2..c4d496dc 100644 --- a/codespan-reporting/src/diagnostic.rs +++ b/codespan-reporting/src/diagnostic.rs @@ -1,9 +1,9 @@ //! Diagnostic data structures. -use alloc::{ - string::{String, ToString}, - vec::Vec, -}; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; + +use core::fmt::Display; use core::ops::Range; #[cfg(feature = "serialization")] @@ -90,7 +90,7 @@ impl Label { } /// Add a message to the diagnostic. - pub fn with_message(mut self, message: impl ToString) -> Label { + pub fn with_message(mut self, message: &impl Display) -> Label { self.message = message.to_string(); self } @@ -170,13 +170,13 @@ impl Diagnostic { } /// Set the error code of the diagnostic. - pub fn with_code(mut self, code: impl ToString) -> Diagnostic { + pub fn with_code(mut self, code: &impl Display) -> Diagnostic { self.code = Some(code.to_string()); self } /// Set the message of the diagnostic. - pub fn with_message(mut self, message: impl ToString) -> Diagnostic { + pub fn with_message(mut self, message: &impl Display) -> Diagnostic { self.message = message.to_string(); self } @@ -203,7 +203,7 @@ impl Diagnostic { } /// Add a note to the diagnostic. - pub fn with_note(mut self, note: impl ToString) -> Diagnostic { + pub fn with_note(mut self, note: &impl ToString) -> Diagnostic { self.notes.push(note.to_string()); self } diff --git a/codespan-reporting/src/files.rs b/codespan-reporting/src/files.rs index db61c276..45391ea7 100644 --- a/codespan-reporting/src/files.rs +++ b/codespan-reporting/src/files.rs @@ -324,7 +324,7 @@ where Ordering::Less => Ok(self .line_starts .get(line_index) - .cloned() + .copied() .expect("failed despite previous check")), Ordering::Equal => Ok(self.source.as_ref().len()), Ordering::Greater => Err(Error::LineTooLarge { diff --git a/codespan-reporting/src/term/config.rs b/codespan-reporting/src/term/config.rs index 26e4a7f9..1faf3366 100644 --- a/codespan-reporting/src/term/config.rs +++ b/codespan-reporting/src/term/config.rs @@ -269,7 +269,7 @@ pub mod styles { } } - impl<'a, W: WriteColor> WriteStyle for StylesWriter<'a, W> { + impl WriteStyle for StylesWriter<'_, W> { fn set_header(&mut self, severity: Severity) -> renderer::GeneralWriteResult { self.writer.set_color(self.style.header(severity)) } diff --git a/codespan-reporting/src/term/renderer.rs b/codespan-reporting/src/term/renderer.rs index d9365915..27089ce0 100644 --- a/codespan-reporting/src/term/renderer.rs +++ b/codespan-reporting/src/term/renderer.rs @@ -137,7 +137,7 @@ pub enum MultiLabel<'diagnostic> { /// Can also be rendered at the beginning of the line /// if there is only whitespace before the label starts. /// - /// /// ```text + /// ```text /// ╭ /// ``` Top(usize), @@ -209,7 +209,7 @@ type Underline = (LabelStyle, VerticalBound); /// empty ── │ /// ``` /// -/// > Filler text from http://www.cupcakeipsum.com +/// > Filler text from pub struct Renderer<'writer, 'config> { writer: &'writer mut dyn WriteStyle, config: &'config Config, @@ -524,7 +524,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { let current_label_style = single_labels .iter() .filter(|(_, range, _)| is_overlapping(range, &column_range)) - .map(|(label_style, _, _)| *label_style) + .map(|(label_style, _, _)| label_style.clone()) .max_by_key(label_priority_key); // Update writer style if necessary @@ -666,7 +666,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { } MultiLabel::Top(..) if multi_label_index == *i => { underline = Some((*ls, VerticalBound::Top)); - self.label_multi_top_left(severity, label_style)? + self.label_multi_top_left(severity, label_style)?; } MultiLabel::Bottom(..) if multi_label_index == *i => { underline = Some((*ls, VerticalBound::Bottom)); @@ -686,7 +686,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { match bottom_message { None => self.label_multi_top_caret(severity, label_style, source, *range)?, Some(message) => { - self.label_multi_bottom_caret(severity, label_style, source, *range, message)? + self.label_multi_bottom_caret(severity, label_style, source, *range, message)?; } } } @@ -847,7 +847,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { let column_range = metrics.byte_index..(metrics.byte_index + ch.len_utf8()); let label_style = hanging_labels(single_labels, trailing_label) .filter(|(_, range, _)| column_range.contains(&range.start)) - .map(|(label_style, _, _)| *label_style) + .map(|(label_style, _, _)| label_style.clone()) .max_by_key(label_priority_key); let mut spaces = match label_style { diff --git a/codespan-reporting/src/term/views.rs b/codespan-reporting/src/term/views.rs index 364cd65c..e1424b5a 100644 --- a/codespan-reporting/src/term/views.rs +++ b/codespan-reporting/src/term/views.rs @@ -1,8 +1,5 @@ -use alloc::{ - string::{String, ToString}, - vec, - vec::Vec, -}; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use core::ops::Range; use crate::diagnostic::{Diagnostic, LabelStyle}; @@ -100,44 +97,41 @@ where // NOTE: This could be made more efficient by using an associative // data structure like a hashmap or B-tree, but we use a vector to // preserve the order that unique files appear in the list of labels. - let labeled_file = match labeled_files + let labeled_file = labeled_files .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 - } - None => { - // 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)?, - 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 - .last_mut() - .expect("just pushed an element that disappeared") + .find(|labeled_file| label.file_id == labeled_file.file_id); + let labeled_file = if let Some(labeled_file) = 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 + } else { + // 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)?, + 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 + .last_mut() + .expect("just pushed an element that disappeared") }; // insert context lines before label // start from 1 because 0 would be the start of the label itself - for offset in 1..self.config.before_label_lines + 1 { + for offset in 1..=self.config.before_label_lines { let index = if let Some(index) = start_line_index.checked_sub(offset) { index } else { @@ -158,7 +152,7 @@ where // insert context lines after label // start from 1 because 0 would be the end of the label itself - for offset in 1..self.config.after_label_lines + 1 { + for offset in 1..=self.config.after_label_lines { let index = end_line_index .checked_add(offset) .expect("line index too big"); @@ -445,6 +439,7 @@ impl<'diagnostic, FileId> ShortDiagnostic<'diagnostic, FileId> where FileId: Copy + PartialEq, { + #[must_use] pub fn new( diagnostic: &'diagnostic Diagnostic, show_notes: bool, diff --git a/codespan/src/file.rs b/codespan/src/file.rs index 1915d348..54a9b2ca 100644 --- a/codespan/src/file.rs +++ b/codespan/src/file.rs @@ -1,8 +1,6 @@ -use alloc::{ - string::{String, ToString}, - vec, - vec::Vec, -}; +use alloc::string::{String, ToString}; +use alloc::vec; +use alloc::vec::Vec; use core::num::NonZeroU32; use codespan_reporting::files::Error; @@ -30,6 +28,7 @@ impl FileId { /// `Option` is 4 bytes) const OFFSET: u32 = 1; + #[must_use] fn new(index: usize) -> FileId { FileId(NonZeroU32::new(index as u32 + Self::OFFSET).expect("file index cannot be stored")) } @@ -84,7 +83,7 @@ where /// This will mean that any outstanding byte indexes will now point to /// invalid locations. pub fn update(&mut self, file_id: FileId, source: Source) { - self.get_mut(file_id).update(source) + self.get_mut(file_id).update(source); } /// Get a the source file using the file id. @@ -111,6 +110,7 @@ where /// /// assert_eq!(files.name(file_id), name); /// ``` + #[must_use] pub fn name(&self, file_id: FileId) -> &OsStr { self.get(file_id).name() } @@ -197,6 +197,7 @@ where /// /// assert_eq!(*files.source(file_id), source); /// ``` + #[must_use] pub fn source(&self, file_id: FileId) -> &Source { self.get(file_id).source() } diff --git a/codespan/src/span.rs b/codespan/src/span.rs index 2fa95160..54acaefd 100644 --- a/codespan/src/span.rs +++ b/codespan/src/span.rs @@ -15,6 +15,7 @@ pub struct Span { impl Span { /// Create a new span from a starting and ending span. + #[must_use] pub fn new(start: impl Into, end: impl Into) -> Span { let start = start.into(); let end = end.into(); @@ -25,6 +26,7 @@ impl Span { } /// Gives an empty span at the start of a source. + #[must_use] pub const fn initial() -> Span { Span { start: ByteIndex(0), @@ -42,6 +44,7 @@ impl Span { /// assert_eq!(span, Span::new(0, 5)); /// ``` #[allow(clippy::should_implement_trait)] + #[must_use] pub fn from_str(s: &str) -> Span { Span::new(0, s.len() as u32) } @@ -61,6 +64,7 @@ impl Span { /// /// assert_eq!(Span::merge(span1, span2), Span::new(0, 16)); /// ``` + #[must_use] pub fn merge(self, other: Span) -> Span { use core::cmp::{max, min}; @@ -77,6 +81,7 @@ impl Span { /// let span2 = Span::new(10, 16); /// assert!(span1.disjoint(span2)); /// ``` + #[must_use] pub fn disjoint(self, other: Span) -> bool { let (first, last) = if self.end < other.end { (self, other) From d77f767e2032766d028209d65de6fb02d637d209 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 15 Oct 2025 10:44:40 +0100 Subject: [PATCH 14/22] Add dependants check on PRs with API changes --- .github/workflows/ci.yml | 56 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a8392e6..16637f8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,8 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: - matrix: - rust: ["stable", "nightly"] # "1.67.0", + matrix: { rust: ["stable", "nightly"] } # "1.67.0", steps: - uses: actions/checkout@v4 @@ -37,8 +36,7 @@ jobs: strategy: fail-fast: false - matrix: - rust: ["stable", "nightly"] + matrix: { rust: ["stable", "nightly"] } steps: - uses: actions/checkout@v4 @@ -95,10 +93,58 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master - with: { toolchain: '${{ matrix.rust }}', components: 'clippy' } + with: { toolchain: "${{ matrix.rust }}", components: 'clippy' } - name: Run cargo clippy run: cargo clippy --all + + dependants: + runs-on: ubuntu-latest + + if: contains(github.event.pull_request.labels.*.name, 'api') + + timeout-minutes: 5 + + strategy: + matrix: + # Test dependenents of the codespan project that + repository: + - dtolnay/cxx + - gfx-rs/wgpu + - bevyengine/bevy + - EmbarkStudios/toml-span + - bevyengine/naga_oil + include: + - repository: gfx-rs/wgpu + path: naga + - repository: bevyengine/bevy + path: crates/bevy_render + - repository: EmbarkStudios/toml-span + path: toml-span + fail-fast: false + + steps: + - uses: dtolnay/rust-toolchain@stable + + - name: Checkout repository + uses: actions/checkout@v4 + with: { repository: "${{ matrix.repository }}" } + + - name: Run check that it works + run: cargo locate-project + working-directory: ${{ matrix.path }} + + - name: Update dependency to ${{ github.repository }}/refs/${{ github.ref }} + run: cargo add codespan-reporting --git ${{ github.repository }} --branch ${{ github.ref }} + working-directory: ${{ matrix.path }} + + - name: Run cargo tree + run: cargo tree + working-directory: ${{ matrix.path }} + + - name: Run check + run: cargo check; cargo check --examples + working-directory: ${{ matrix.path }} publish-ability: runs-on: ubuntu-latest From 06ddcb539bb6fc64e47c6ef8d99c0dd3a48c1926 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 15 Oct 2025 10:55:26 +0100 Subject: [PATCH 15/22] Revert ref parameter --- codespan-reporting/src/diagnostic.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/codespan-reporting/src/diagnostic.rs b/codespan-reporting/src/diagnostic.rs index c4d496dc..98924d65 100644 --- a/codespan-reporting/src/diagnostic.rs +++ b/codespan-reporting/src/diagnostic.rs @@ -90,7 +90,7 @@ impl Label { } /// Add a message to the diagnostic. - pub fn with_message(mut self, message: &impl Display) -> Label { + pub fn with_message(mut self, message: impl Display) -> Label { self.message = message.to_string(); self } @@ -170,13 +170,13 @@ impl Diagnostic { } /// Set the error code of the diagnostic. - pub fn with_code(mut self, code: &impl Display) -> Diagnostic { + pub fn with_code(mut self, code: impl Display) -> Diagnostic { self.code = Some(code.to_string()); self } /// Set the message of the diagnostic. - pub fn with_message(mut self, message: &impl Display) -> Diagnostic { + pub fn with_message(mut self, message: impl Display) -> Diagnostic { self.message = message.to_string(); self } From 0f27b6850e49a20fe1b62ac969d9d9473df62951 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 15 Oct 2025 10:55:44 +0100 Subject: [PATCH 16/22] ref -> ref_name in CI --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16637f8f..82bebee5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,6 +103,9 @@ jobs: if: contains(github.event.pull_request.labels.*.name, 'api') + # Just a warning for now + continue-on-error: true + timeout-minutes: 5 strategy: @@ -135,7 +138,7 @@ jobs: working-directory: ${{ matrix.path }} - name: Update dependency to ${{ github.repository }}/refs/${{ github.ref }} - run: cargo add codespan-reporting --git ${{ github.repository }} --branch ${{ github.ref }} + run: cargo add codespan-reporting --git ${{ github.repository }} --branch ${{ github.ref_name }} working-directory: ${{ matrix.path }} - name: Run cargo tree From f14a37c35048f6ce2125b01ad6e2f7c57ff5c8c7 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 15 Oct 2025 10:58:17 +0100 Subject: [PATCH 17/22] Change publish to publish crates under same version --- .github/workflows/publish.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a3e908b1..43c32083 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -6,15 +6,7 @@ on: version: description: "major/minor/patch or semver" required: false - default: "patch" - lsp-version: - description: "major/minor/patch or semver for codespan-lsp crate (or none if not releasing derive crate)" - required: false - default: "none" - reporting-version: - description: "major/minor/patch or semver for codespan-reporting crate (or none if not releasing derive crate)" - required: false - default: "none" + default: patch jobs: publish: @@ -34,7 +26,7 @@ jobs: uses: kaleidawave/crates-release-gh-action@improvements id: release with: - version: codespan=${{ github.event.inputs.version }},codespan-reporting=${{ github.event.inputs.lsp-version }},codespan-lsp=${{ github.event.inputs.reporting-version }} + version: codespan=${{ github.event.inputs.version }},codespan-reporting=${{ github.event.inputs.version }},codespan-lsp=${{ github.event.inputs.version }} crates-token: ${{ steps.auth.outputs.token }} - name: Push updated Cargo.toml From ea73764369e97797a7e13d31ad33d1da1ac4862d Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 15 Oct 2025 11:16:02 +0100 Subject: [PATCH 18/22] import vec macro --- codespan-reporting/src/term/views.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/codespan-reporting/src/term/views.rs b/codespan-reporting/src/term/views.rs index e1424b5a..caccc3f1 100644 --- a/codespan-reporting/src/term/views.rs +++ b/codespan-reporting/src/term/views.rs @@ -1,4 +1,5 @@ use alloc::string::{String, ToString}; +use alloc::vec; use alloc::vec::Vec; use core::ops::Range; From 552d5685719976c30c363c8a5af55977b927e0a5 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 15 Oct 2025 11:16:59 +0100 Subject: [PATCH 19/22] 'https://github.com' prefix & ref_name -> head_ref in CI --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82bebee5..bcbb2cc7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,8 +137,8 @@ jobs: run: cargo locate-project working-directory: ${{ matrix.path }} - - name: Update dependency to ${{ github.repository }}/refs/${{ github.ref }} - run: cargo add codespan-reporting --git ${{ github.repository }} --branch ${{ github.ref_name }} + - name: Update dependency to ${{ github.repository }} on ${{ github.head_ref }} + run: cargo add codespan-reporting --git https://github.com/${{ github.repository }} --branch ${{ github.head_ref }} working-directory: ${{ matrix.path }} - name: Run cargo tree From 6c51c0c26b626fa2be4f026f16f1f25850f8c507 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 15 Oct 2025 11:36:52 +0100 Subject: [PATCH 20/22] Change `deprecated` message --- codespan-reporting/src/term/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codespan-reporting/src/term/mod.rs b/codespan-reporting/src/term/mod.rs index f4bfb897..86985d78 100644 --- a/codespan-reporting/src/term/mod.rs +++ b/codespan-reporting/src/term/mod.rs @@ -86,7 +86,7 @@ pub fn emit_to_write_style<'files, F: Files<'files> + ?Sized, W: WriteStyle>( emit_with_style(writer, config, files, diagnostic) } -#[deprecated(since = "0.12.0", note = "Use `emit_to_write_style` instead")] +#[deprecated(since = "0.12.0", note = "Use `emit_to_write_style` instead or depending on the writer use `emit_to_io_write` or `emit_to_string`")] /// Emit a diagnostic using the given writer, context, config, and files. /// /// The return value covers all error cases. These error case can arise if: From 65841f9a7f30bdb3e346f52379d273673840d940 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 15 Oct 2025 11:37:13 +0100 Subject: [PATCH 21/22] Run CI on all pull request branches --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bcbb2cc7..62d1b556 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: ci on: push: { branches: [master] } - pull_request: { branches: ["*"] } + pull_request: { branches: ["**"] } jobs: check: From 819dae65d55ba76684cd7258536af2b91cc429b1 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 15 Oct 2025 11:46:01 +0100 Subject: [PATCH 22/22] `deprecated since 0.13.0` --- codespan-reporting/src/term/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/codespan-reporting/src/term/mod.rs b/codespan-reporting/src/term/mod.rs index 86985d78..4c0b5239 100644 --- a/codespan-reporting/src/term/mod.rs +++ b/codespan-reporting/src/term/mod.rs @@ -86,7 +86,10 @@ pub fn emit_to_write_style<'files, F: Files<'files> + ?Sized, W: WriteStyle>( emit_with_style(writer, config, files, diagnostic) } -#[deprecated(since = "0.12.0", note = "Use `emit_to_write_style` instead or depending on the writer use `emit_to_io_write` or `emit_to_string`")] +#[deprecated( + since = "0.13.0", + note = "Use `emit_to_write_style` instead or depending on the writer use `emit_to_io_write` or `emit_to_string`" +)] /// Emit a diagnostic using the given writer, context, config, and files. /// /// The return value covers all error cases. These error case can arise if: