From fcfdc6516d7ab6fe6ac6f34c04e4884128d17f22 Mon Sep 17 00:00:00 2001 From: hl-valdemar Date: Fri, 9 Oct 2026 22:52:07 +0200 Subject: [PATCH] fix: color Markdown heading markers with heading text --- docs/editing.md | 2 ++ src/syntax.rs | 19 +++++++++++++++++++ src/syntax/languages.rs | 20 +++++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/editing.md b/docs/editing.md index eed2a67..e65964f 100644 --- a/docs/editing.md +++ b/docs/editing.md @@ -176,4 +176,6 @@ Buffers highlight automatically using [Tree-sitter](https://tree-sitter.github.i Colors retain the terminal palette: comments dark gray, strings green, keywords magenta, numbers yellow, types/markup cyan, and functions light blue. Grammar queries handle multiline/nested constructs, even when their opening is above the viewport. Markdown uses its inline grammar and named fenced-code injections; HTML script/style content uses JavaScript/CSS. Known tagged-template and raw-string injections use the same selected grammar registry; unknown injected languages retain host highlighting or plain text. Highlighting styles whole graphemes and expanded tab cells; partially clipped wide placeholders remain unchanged. Search, Visual selections, and secondary carets retain foreground/background precedence. +Markdown heading markers (`#` through `######`, or Setext underlines) share the heading text's markup color. Plain-text hashes and other punctuation keep their existing styling. + An enabled supporting LSP server supplies semantic tokens automatically, overriding syntax colors only on recognized spans. Variables/parameters use light cyan, properties blue, and readonly variables/properties yellow; declarations/definitions are bold, documentation comments italic, and deprecated symbols struck through. Tree-sitter fills gaps and remains available while new tokens are pending after edits. Search, Visual selections, diagnostics, and carets keep their existing precedence. See [native LSP configuration](configuration.md#native-lsp) for provider support and server-stack routing. diff --git a/src/syntax.rs b/src/syntax.rs index dc91b12..e18a5b1 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -1263,6 +1263,25 @@ mod tests { assert_token(&cache, source, 3, "42", SyntaxKind::Number); } + #[test] + fn markdown_heading_markers_share_title_colors_without_coloring_plain_hashes() { + let source = "# One\n## Two\n### Three\n#### Four\n##### Five\n###### Six\n\nSetext\n======\n\nplain # text"; + let cache = self::cache(source, "headings.md"); + for row in 0..6 { + assert_token( + &cache, + source, + row, + &"#".repeat(row + 1), + SyntaxKind::Markup, + ); + } + assert_token(&cache, source, 0, "One", SyntaxKind::Markup); + assert_token(&cache, source, 7, "Setext", SyntaxKind::Markup); + assert_token(&cache, source, 8, "======", SyntaxKind::Markup); + assert_eq!(kind_at(&cache, 10, 6), None); + } + #[test] fn markdown_inline_and_fences_inject_known_languages_and_update_incrementally() { let original = "A **bold** and `code` link.\n\n```rust\nfn main() { let n = 42; }\n```\n\n```python\ndef hello():\n return 7\n```\n\n```not-a-language\nfn not_rust() { 99; }\n```"; diff --git a/src/syntax/languages.rs b/src/syntax/languages.rs index 076a3f7..483929f 100644 --- a/src/syntax/languages.rs +++ b/src/syntax/languages.rs @@ -46,6 +46,24 @@ static CPP_HIGHLIGHTS: LazyLock = LazyLock::new(|| { ] .join("\n") }); +static MD_HIGHLIGHTS: LazyLock = LazyLock::new(|| { + [ + tree_sitter_md::HIGHLIGHT_QUERY_BLOCK, + " +[ + (atx_h1_marker) + (atx_h2_marker) + (atx_h3_marker) + (atx_h4_marker) + (atx_h5_marker) + (atx_h6_marker) + (setext_h1_underline) + (setext_h2_underline) +] @text.title +", + ] + .join("\n") +}); // The MIT-licensed upstream C++ crate bundles, but does not export, injections.scm. // This minimal AST query uses its raw-string delimiter/content nodes: @@ -129,7 +147,7 @@ static LANGUAGES: LazyLock<[LanguageSpec; 18]> = LazyLock::new(|| { spec( "Markdown", tree_sitter_md::LANGUAGE.into(), - tree_sitter_md::HIGHLIGHT_QUERY_BLOCK, + MD_HIGHLIGHTS.as_str(), tree_sitter_md::INJECTION_QUERY_BLOCK, "", ),