Skip to content

Commit b2dbd3c

Browse files
committed
feat(highlight): syntax-highlight qmd comments in Monaco
HTML `<!-- -->` and editorial `[>> ...]` comments now emit a qmd.markup.comment semantic token (muted italic) via a new highlights.scm capture, legend entry (Rust + TS), and theme rule. Snapshots: 1 modified (structural_corpus, +2 lines) — the two corpus comments now resolve to qmd.markup.comment; no other tokens changed.
1 parent b2b1292 commit b2dbd3c

9 files changed

Lines changed: 88 additions & 20 deletions

File tree

crates/quarto-lsp-core/src/snapshots/quarto_lsp_core__tokens__tests__structural_corpus.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,5 @@ expression: rendered
5555
33:0-6 qmd.code.keyword
5656
33:7-9 qmd.code.variable
5757
34:0-3 qmd.punctuation.delimiter.fence
58+
36:2-26 qmd.markup.comment
59+
36:32-52 qmd.markup.comment

crates/quarto-lsp-core/src/tokens.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,47 @@ mod tests {
497497
assert!(has_type(&toks, "qmd.attribute.specifier"), "got {toks:?}");
498498
}
499499

500+
#[test]
501+
fn tokens_for_html_comment() {
502+
// `<!-- ... -->` parses to a `comment` node; the whole span is coloured.
503+
// The grammar's comment token swallows the preceding space, so assert
504+
// the meaningful edge: a comment token reaching the end of `-->` (col 20)
505+
// and starting no later than the `<` (col 5).
506+
let toks = tokens("Text <!-- hidden --> more\n");
507+
assert!(
508+
toks.iter().any(|t| type_name(t) == "qmd.markup.comment"
509+
&& t.line == 0
510+
&& t.character <= 5
511+
&& t.character + t.length == 20),
512+
"expected a markup.comment over `<!-- hidden -->`, got {toks:?}"
513+
);
514+
}
515+
516+
#[test]
517+
fn tokens_for_multiline_html_comment_split_at_lines() {
518+
// A comment spanning lines must be split into per-line tokens.
519+
let toks = tokens("<!--\nhidden\n-->\n");
520+
let comments: Vec<_> = toks
521+
.iter()
522+
.filter(|t| type_name(t) == "qmd.markup.comment")
523+
.collect();
524+
assert_eq!(
525+
comments.len(),
526+
3,
527+
"expected one comment token per line, got {toks:?}"
528+
);
529+
}
530+
531+
#[test]
532+
fn tokens_for_edit_comment() {
533+
// Quarto editorial comment `[>> ...]` parses to an `edit_comment` node.
534+
let toks = tokens("Para [>> editorial note]\n");
535+
assert!(
536+
has_type(&toks, "qmd.markup.comment"),
537+
"expected a markup.comment for `[>> ...]`, got {toks:?}"
538+
);
539+
}
540+
500541
#[test]
501542
fn tokens_are_non_overlapping_and_sorted() {
502543
// A link inside a heading deliberately nests, exercising the flatten.

crates/quarto-lsp-core/src/types.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@ pub static QMD_TOKEN_LEGEND: &[&str] = &[
644644
"qmd.markup.shortcode",
645645
"qmd.markup.list",
646646
"qmd.markup.quote",
647+
"qmd.markup.comment",
647648
"qmd.punctuation.special",
648649
"qmd.punctuation.special.image",
649650
"qmd.punctuation.bracket",

crates/quarto-lsp-core/tests/fixtures/highlight-corpus.qmd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ x <- 1
3333
```python
3434
import os
3535
```
36+
37+
An <!-- inline comment --> and a [>> editorial note].

crates/tree-sitter-qmd/tree-sitter-markdown/queries/highlights.scm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@
8080
; --- Raw inline/block HTML ---------------------------------------------------
8181
(html_element) @markup.raw
8282

83+
; --- Comments (HTML `<!-- -->` and editorial `[>> ...]`) ---------------------
84+
[
85+
(comment)
86+
(edit_comment)
87+
] @markup.comment
88+
8389
; --- Fenced code: delimiter + info string (interior left to zone 3) ----------
8490
(fenced_code_block_delimiter) @punctuation.delimiter.fence
8591
(info_string) @markup.raw.info

crates/wasm-quarto-hub-client/Cargo.lock

Lines changed: 20 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

hub-client/src/components/quartoTheme.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export const quartoThemeRules: Monaco.editor.ITokenThemeRule[] = [
6666
{ token: 'qmd.markup.shortcode', foreground: FUNCTION },
6767
{ token: 'qmd.markup.list', foreground: PUNCT },
6868
{ token: 'qmd.markup.quote', foreground: COMMENT, fontStyle: 'italic' },
69+
{ token: 'qmd.markup.comment', foreground: COMMENT, fontStyle: 'italic' },
6970
{ token: 'qmd.punctuation.special', foreground: PUNCT },
7071
{ token: 'qmd.punctuation.special.image', foreground: IMAGE_ACCENT },
7172
{ token: 'qmd.punctuation.bracket', foreground: BRACKET },

hub-client/src/services/intelligenceService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export const QMD_TOKEN_LEGEND: readonly string[] = [
5656
'qmd.markup.shortcode',
5757
'qmd.markup.list',
5858
'qmd.markup.quote',
59+
'qmd.markup.comment',
5960
'qmd.punctuation.special',
6061
'qmd.punctuation.special.image',
6162
'qmd.punctuation.bracket',

hub-client/src/services/semanticTokens.wasm.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,20 @@ describe('lsp_get_semantic_tokens', () => {
7979
expect(codeOnBody.length).toBeGreaterThan(0);
8080
});
8181

82+
it('produces a comment token for an HTML comment', () => {
83+
const result = tokensFor('Text <!-- hidden --> more\n');
84+
expect(result.success, result.error).toBe(true);
85+
const names = (result.tokens ?? []).map(typeName);
86+
expect(names).toContain('qmd.markup.comment');
87+
});
88+
89+
it('produces a comment token for an editorial `[>> ...]` comment', () => {
90+
const result = tokensFor('Para [>> editorial note]\n');
91+
expect(result.success, result.error).toBe(true);
92+
const names = (result.tokens ?? []).map(typeName);
93+
expect(names).toContain('qmd.markup.comment');
94+
});
95+
8296
it('returns an empty token list for an empty document', () => {
8397
const result = tokensFor('');
8498
expect(result.success).toBe(true);

0 commit comments

Comments
 (0)