diff --git a/Cargo.lock b/Cargo.lock index 34302a319b..5acc1c77d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2865,6 +2865,7 @@ dependencies = [ name = "ruff_db" version = "0.0.0" dependencies = [ + "anstyle", "camino", "countme", "dashmap 6.1.0", diff --git a/crates/ruff_db/Cargo.toml b/crates/ruff_db/Cargo.toml index b37af0f978..3507efa545 100644 --- a/crates/ruff_db/Cargo.toml +++ b/crates/ruff_db/Cargo.toml @@ -20,6 +20,7 @@ ruff_python_trivia = { workspace = true } ruff_source_file = { workspace = true } ruff_text_size = { workspace = true } +anstyle = { workspace = true } camino = { workspace = true } countme = { workspace = true } dashmap = { workspace = true } diff --git a/crates/ruff_db/src/diagnostic/mod.rs b/crates/ruff_db/src/diagnostic/mod.rs index ffcdf2a026..412eec7530 100644 --- a/crates/ruff_db/src/diagnostic/mod.rs +++ b/crates/ruff_db/src/diagnostic/mod.rs @@ -11,6 +11,7 @@ use crate::Db; use self::render::FileResolver; mod render; +mod stylesheet; /// A collection of information that can be rendered into a diagnostic. /// diff --git a/crates/ruff_db/src/diagnostic/render.rs b/crates/ruff_db/src/diagnostic/render.rs index 56e3ea4eaa..e20e8f48aa 100644 --- a/crates/ruff_db/src/diagnostic/render.rs +++ b/crates/ruff_db/src/diagnostic/render.rs @@ -7,6 +7,7 @@ use ruff_annotate_snippets::{ use ruff_source_file::{LineIndex, OneIndexed, SourceCode}; use ruff_text_size::{TextRange, TextSize}; +use crate::diagnostic::stylesheet::{fmt_styled, DiagnosticStylesheet}; use crate::{ files::File, source::{line_index, source_text, SourceText}, @@ -48,6 +49,7 @@ impl<'a> DisplayDiagnostic<'a> { } else { AnnotateRenderer::plain() }; + DisplayDiagnostic { config, resolver, @@ -59,31 +61,64 @@ impl<'a> DisplayDiagnostic<'a> { impl std::fmt::Display for DisplayDiagnostic<'_> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - if matches!(self.config.format, DiagnosticFormat::Concise) { - match self.diag.severity() { - Severity::Info => f.write_str("info")?, - Severity::Warning => f.write_str("warning")?, - Severity::Error => f.write_str("error")?, - Severity::Fatal => f.write_str("fatal")?, - } + let stylesheet = if self.config.color { + DiagnosticStylesheet::styled() + } else { + DiagnosticStylesheet::plain() + }; + + if matches!(self.config.format, DiagnosticFormat::Concise) { + let (severity, severity_style) = match self.diag.severity() { + Severity::Info => ("info", stylesheet.info), + Severity::Warning => ("warning", stylesheet.warning), + Severity::Error => ("error", stylesheet.error), + Severity::Fatal => ("fatal", stylesheet.error), + }; + + write!( + f, + "{severity}[{id}]", + severity = fmt_styled(severity, severity_style), + id = fmt_styled(self.diag.id(), stylesheet.emphasis) + )?; - write!(f, "[{rule}]", rule = self.diag.id())?; if let Some(span) = self.diag.primary_span() { - write!(f, " {path}", path = self.resolver.path(span.file()))?; + write!( + f, + " {path}", + path = fmt_styled(self.resolver.path(span.file()), stylesheet.emphasis) + )?; if let Some(range) = span.range() { let input = self.resolver.input(span.file()); let start = input.as_source_code().line_column(range.start()); - write!(f, ":{line}:{col}", line = start.line, col = start.column)?; + + write!( + f, + ":{line}:{col}", + line = fmt_styled(start.line, stylesheet.emphasis), + col = fmt_styled(start.column, stylesheet.emphasis), + )?; } write!(f, ":")?; } - return writeln!(f, " {}", self.diag.concise_message()); + return writeln!(f, " {message}", message = self.diag.concise_message()); } + let mut renderer = self.annotate_renderer.clone(); + renderer = renderer + .error(stylesheet.error) + .warning(stylesheet.warning) + .info(stylesheet.info) + .note(stylesheet.note) + .help(stylesheet.help) + .line_no(stylesheet.line_no) + .emphasis(stylesheet.emphasis) + .none(stylesheet.none); + let resolved = Resolved::new(&self.resolver, self.diag); let renderable = resolved.to_renderable(self.config.context); for diag in renderable.diagnostics.iter() { - writeln!(f, "{}", self.annotate_renderer.render(diag.to_annotate()))?; + writeln!(f, "{}", renderer.render(diag.to_annotate()))?; } writeln!(f) } diff --git a/crates/ruff_db/src/diagnostic/stylesheet.rs b/crates/ruff_db/src/diagnostic/stylesheet.rs new file mode 100644 index 0000000000..f3d451030b --- /dev/null +++ b/crates/ruff_db/src/diagnostic/stylesheet.rs @@ -0,0 +1,80 @@ +use anstyle::{AnsiColor, Effects, Style}; +use std::fmt::Formatter; + +pub(super) const fn fmt_styled<'a, T>( + content: T, + style: anstyle::Style, +) -> impl std::fmt::Display + 'a +where + T: std::fmt::Display + 'a, +{ + struct FmtStyled { + content: T, + style: anstyle::Style, + } + + impl std::fmt::Display for FmtStyled + where + T: std::fmt::Display, + { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{style_start}{content}{style_end}", + style_start = self.style.render(), + content = self.content, + style_end = self.style.render_reset() + ) + } + } + + FmtStyled { content, style } +} + +#[derive(Clone, Debug)] +pub struct DiagnosticStylesheet { + pub(crate) error: Style, + pub(crate) warning: Style, + pub(crate) info: Style, + pub(crate) note: Style, + pub(crate) help: Style, + pub(crate) line_no: Style, + pub(crate) emphasis: Style, + pub(crate) none: Style, +} + +impl Default for DiagnosticStylesheet { + fn default() -> Self { + Self::plain() + } +} + +impl DiagnosticStylesheet { + /// Default terminal styling + pub fn styled() -> Self { + let bright_blue = AnsiColor::BrightBlue.on_default(); + Self { + error: AnsiColor::BrightRed.on_default().effects(Effects::BOLD), + warning: AnsiColor::Yellow.on_default().effects(Effects::BOLD), + info: bright_blue.effects(Effects::BOLD), + note: AnsiColor::BrightGreen.on_default().effects(Effects::BOLD), + help: AnsiColor::BrightCyan.on_default().effects(Effects::BOLD), + line_no: bright_blue.effects(Effects::BOLD), + emphasis: Style::new().effects(Effects::BOLD), + none: Style::new(), + } + } + + pub fn plain() -> Self { + Self { + error: Style::new(), + warning: Style::new(), + info: Style::new(), + note: Style::new(), + help: Style::new(), + line_no: Style::new(), + emphasis: Style::new(), + none: Style::new(), + } + } +}