diff --git a/TODO.md b/TODO.md index 8074f1f..8c9cd26 100644 --- a/TODO.md +++ b/TODO.md @@ -12,6 +12,7 @@ - [x] `:` currently overrides the diff selector input (and maybe other input fields) and spawns the command input on top. this is of course not intended behavior and should be fixed. - [x] diff view: lsp actions should act as they normally would. if an lsp action would open another file, then the file should be opened in another tab outside the diff view - if the referenced file/symbol is part of the changes in the diff, it should just be opened in the diff view. - changed symbol ranges stay in the comparison; other targets open ordinary tabs. Historical text stays immutable; source freshness and failure atomicity are preserved. +- [x] better syntax highlighting for markdown files. color `-` and `1.` from lists. - [ ] diff view: change diff view modes; choose between split and unified diff view (split by default). - [ ] diff view: for split view, change split orientation (left/right vs above/below) - left/right by default. - [ ] diff view: github integration (code review) - add comments on a pr, add review comments at specific lines of files, request changes, approve pr, etc. diff --git a/docs/editing.md b/docs/editing.md index 8bebfe6..fc6b0b9 100644 --- a/docs/editing.md +++ b/docs/editing.md @@ -398,6 +398,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. +Markdown heading markers (`#` through `######`, or Setext underlines) share the heading text's markup color. List markers (`-`, `+`, `*`, and ordered prefixes such as `1.` or `1)`) also use cyan markup, including nested and blockquoted lists. 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. Semantic colors also use the terminal palette: namespaces neutral gray, types cyan, variables/parameters light cyan, and properties blue. Readonly variables/parameters/properties keep those role colors and add italics; declarations/definitions are bold, documentation comments italic, and deprecated symbols struck through. Italic emphasis depends on terminal support. 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 dd1ec57..b573e2c 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -1730,6 +1730,35 @@ mod tests { assert_eq!(kind_at(&cache, 10, 6), None); } + #[test] + fn markdown_list_markers_color_bullets_and_numbers_without_coloring_plain_text() { + let source = "- item\n - nested\n 12. ordered\n\n+ item\n\n* item\n\n1. ordered\n2. next\n\n3) ordered\n\n> - quoted\n> 4. ordered\n\nplain - text and 1. text\n\n\\- escaped"; + let mut cache = self::cache(source, "lists.md"); + for (row, marker) in [ + (0, "-"), + (1, "-"), + (2, "12."), + (4, "+"), + (6, "*"), + (8, "1."), + (9, "2."), + (11, "3)"), + (13, "-"), + (14, "4."), + ] { + assert_token(&cache, source, row, marker, SyntaxKind::Markup); + } + assert_eq!(kind_at(&cache, 0, 2), None); + assert_eq!(kind_at(&cache, 16, 6), None); + assert_eq!(kind_at(&cache, 16, 17), None); + assert_ne!(kind_at(&cache, 18, 1), Some(SyntaxKind::Markup)); + + let edited = source.replacen("- item", "plain - item", 1); + cache.update(&lines(&edited), Some(Path::new("lists.md")), 1); + assert_eq!(kind_at(&cache, 0, 6), None); + assert_fresh(&cache, &edited, "lists.md"); + } + #[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 483929f..899d116 100644 --- a/src/syntax/languages.rs +++ b/src/syntax/languages.rs @@ -60,6 +60,14 @@ static MD_HIGHLIGHTS: LazyLock = LazyLock::new(|| { (setext_h1_underline) (setext_h2_underline) ] @text.title + +[ + (list_marker_minus) + (list_marker_plus) + (list_marker_star) + (list_marker_dot) + (list_marker_parenthesis) +] @markup.list ", ] .join("\n")