From 7c12669ee7ae4add85bd2dcc4f2484ad06233a09 Mon Sep 17 00:00:00 2001 From: hippietrail Date: Mon, 8 Dec 2025 01:03:35 +0800 Subject: [PATCH 1/2] feat: print linter match with context --- harper-core/src/linting/mod.rs | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/harper-core/src/linting/mod.rs b/harper-core/src/linting/mod.rs index 20639a87f..b2583b464 100644 --- a/harper-core/src/linting/mod.rs +++ b/harper-core/src/linting/mod.rs @@ -253,6 +253,44 @@ where } } +pub mod debug { + use crate::{Token, token_string_ext::TokenStringExt}; + + /// Formats a lint match with surrounding context for debug output. + /// + /// The function takes the same `matched_tokens` and `source`, and `context` parameters + /// passed to `[match_to_lint_with_context]`. + /// + /// # Arguments + /// * `log` - `matched_tokens` + /// * `ctx` - `context`, or `None` if calling from `[match_to_lint]` + /// * `src` - `source` from `[match_to_lint]` / `[match_to_lint_with_context]` + /// + /// # Returns + /// A string with ANSI escape codes where: + /// - Context tokens are dimmed before and after the matched tokens in normal weight. + /// - Markup and formatting text hidden in whitespace tokens is filtered out. + pub fn format_lint_match( + log: &[Token], + ctx: Option<(&[Token], &[Token])>, + src: &[char], + ) -> String { + if let Some((pro, epi)) = ctx { + let [pro, log, epi] = [pro, log, epi].map(|tt| { + tt.iter() + .filter(|t| !t.kind.is_whitespace() && !t.kind.is_newline()) + .map(|t| t.span.get_content_string(src)) + .collect::>() + .join(" ") + }); + format!("\x1b[2m{}\x1b[0m {} \x1b[2m{}\x1b[0m", pro, log, epi,) + } else { + log.span() + .map_or_else(String::new, |span| span.get_content_string(src)) + } + } +} + #[cfg(test)] pub mod tests { use crate::parsers::Markdown; From e280dba3211ba281ca607a223385a2dc0aeaa13e Mon Sep 17 00:00:00 2001 From: hippietrail Date: Mon, 8 Dec 2025 02:42:39 +0800 Subject: [PATCH 2/2] refactor: cleaner, better markup filtering --- harper-core/src/linting/mod.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/harper-core/src/linting/mod.rs b/harper-core/src/linting/mod.rs index b2583b464..06482766b 100644 --- a/harper-core/src/linting/mod.rs +++ b/harper-core/src/linting/mod.rs @@ -254,7 +254,7 @@ where } pub mod debug { - use crate::{Token, token_string_ext::TokenStringExt}; + use crate::Token; /// Formats a lint match with surrounding context for debug output. /// @@ -275,18 +275,23 @@ pub mod debug { ctx: Option<(&[Token], &[Token])>, src: &[char], ) -> String { + let fmt = |tokens: &[Token]| { + tokens + .iter() + .filter(|t| !t.kind.is_unlintable()) + .map(|t| t.span.get_content_string(src)) + .collect::() + }; + if let Some((pro, epi)) = ctx { - let [pro, log, epi] = [pro, log, epi].map(|tt| { - tt.iter() - .filter(|t| !t.kind.is_whitespace() && !t.kind.is_newline()) - .map(|t| t.span.get_content_string(src)) - .collect::>() - .join(" ") - }); - format!("\x1b[2m{}\x1b[0m {} \x1b[2m{}\x1b[0m", pro, log, epi,) + format!( + "\x1b[2m{}\x1b[0m{}\x1b[2m{}\x1b[0m", + fmt(pro), + fmt(log), + fmt(epi) + ) } else { - log.span() - .map_or_else(String::new, |span| span.get_content_string(src)) + fmt(log) } } }