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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions crates/interface/src/diagnostics/emitter/human.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ impl HumanEmitter {
.children
.iter()
.filter(|sub| sub.span.is_dummy())
.map(OwnedMessage::from_subdiagnostic)
.map(|sub| OwnedMessage::from_subdiagnostic(sub, self.supports_color()))
.collect();

let snippet = title
Expand Down Expand Up @@ -282,8 +282,12 @@ impl OwnedMessage {
Self { id: diag.id(), label: diag.label().into_owned(), level: to_as_level(diag.level) }
}

fn from_subdiagnostic(sub: &SubDiagnostic) -> Self {
Self { id: None, label: sub.label().into_owned(), level: to_as_level(sub.level) }
fn from_subdiagnostic(sub: &SubDiagnostic, supports_color: bool) -> Self {
Self {
id: None,
label: sub.label_with_style(supports_color).into_owned(),
level: to_as_level(sub.level),
}
}

fn as_ref(&self) -> Message<'_> {
Expand Down
90 changes: 81 additions & 9 deletions crates/interface/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

use crate::Span;
use anstyle::{AnsiColor, Color};
use std::{borrow::Cow, fmt, panic::Location};
use std::{
borrow::Cow,
fmt::{self, Write},
panic::Location,
};

mod builder;
pub use builder::{DiagBuilder, EmissionGuarantee};
Expand Down Expand Up @@ -315,7 +319,12 @@ pub struct SubDiagnostic {
impl SubDiagnostic {
/// Formats the diagnostic messages into a single string.
pub fn label(&self) -> Cow<'_, str> {
flatten_messages(&self.messages)
flatten_messages(&self.messages, false, self.level)
}

/// Formats the diagnostic messages into a single string with ANSI color codes if applicable.
pub fn label_with_style(&self, supports_color: bool) -> Cow<'_, str> {
flatten_messages(&self.messages, supports_color, self.level)
}
}

Expand Down Expand Up @@ -377,7 +386,12 @@ impl Diag {

/// Formats the diagnostic messages into a single string.
pub fn label(&self) -> Cow<'_, str> {
flatten_messages(&self.messages)
flatten_messages(&self.messages, false, self.level)
}

/// Formats the diagnostic messages into a single string with ANSI color codes if applicable.
pub fn label_with_style(&self, supports_color: bool) -> Cow<'_, str> {
flatten_messages(&self.messages, supports_color, self.level)
}

/// Returns the messages of this diagnostic.
Expand Down Expand Up @@ -554,11 +568,69 @@ impl Diag {
}
}

// TODO: Styles?
fn flatten_messages(messages: &[(DiagMsg, Style)]) -> Cow<'_, str> {
match messages {
[] => Cow::Borrowed(""),
[(message, _)] => Cow::Borrowed(message.as_str()),
messages => messages.iter().map(|(msg, _)| msg.as_str()).collect(),
/// Flattens diagnostic messages, applying ANSI styles if requested.
fn flatten_messages(messages: &[(DiagMsg, Style)], with_style: bool, level: Level) -> Cow<'_, str> {
if with_style {
match messages {
[] => Cow::Borrowed(""),
[(msg, Style::NoStyle)] => Cow::Borrowed(msg.as_str()),
[(msg, style)] => {
let mut res = String::new();
write_fmt(&mut res, msg, style, level);
Cow::Owned(res)
}
messages => {
let mut res = String::new();
for (msg, style) in messages {
match style {
Style::NoStyle => res.push_str(msg.as_str()),
_ => write_fmt(&mut res, msg, style, level),
}
}
Cow::Owned(res)
}
}
} else {
match messages {
[] => Cow::Borrowed(""),
[(message, _)] => Cow::Borrowed(message.as_str()),
messages => messages.iter().map(|(msg, _)| msg.as_str()).collect(),
}
}
}

fn write_fmt(output: &mut String, msg: &DiagMsg, style: &Style, level: Level) {
let ansi_style = style.to_color_spec(level);
write!(output, "{}{}{}", ansi_style.render(), msg.as_str(), ansi_style.render_reset()).unwrap();
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_styled_messages() {
// Create a diagnostic with styled messages
let mut diag = Diag::new(Level::Note, "test");

diag.highlighted_note(vec![
("plain text ", Style::NoStyle),
("removed", Style::Removal),
(" middle ", Style::NoStyle),
("added", Style::Addition),
]);

let sub = &diag.children[0];

// Without styles - just concatenated text
let plain = sub.label();
assert_eq!(plain, "plain text removed middle added");

// With styles - includes ANSI escape codes
let styled = sub.label_with_style(true);
assert_eq!(
styled.to_string(),
"plain text \u{1b}[91mremoved\u{1b}[0m middle \u{1b}[92madded\u{1b}[0m".to_string()
);
}
}
Loading