panache 2.44.0

An LSP, formatter, and linter for Markdown, Quarto, and R Markdown
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
---
title: Lint Rules
description: >
  Reference catalogue of every built-in Panache lint rule, the diagnostic codes
  each rule emits, severity, auto-fix availability, and configuration
  requirements.
---

This page documents every built-in lint rule that Panache ships with. Each
section lists the rule's configuration name (used in `[lint.rules]`), the
diagnostic codes the rule may emit at runtime, severity, auto-fix support, and
any extension or metadata requirements.

For a user-friendly introduction to the linter, CLI usage, and configuration,
see the [Linting guide](../guide/linting.qmd).

## Diagnostic format

Diagnostics are displayed in a compiler-style format:

```
severity[diagnostic-code]: message
  --> file:line:column
```

Components:

`severity`
:   `error`, `warning`, or `info`

`diagnostic-code`
:   The specific code emitted (e.g., `undefined-reference-label`). A single rule
    may emit several distinct codes.

`message`
:   Human-readable description of the issue

`location`
:   File path, line number, and column number

Some diagnostics include additional notes pointing at related locations:

```
warning[duplicate-reference-labels]: Duplicate reference definition 'link1'
  --> document.qmd:4:1
note: First defined here:
  --> document.qmd:3:1
```

## Severity levels

Panache uses three severity levels:

`error`
:   Critical issues that prevent correct parsing or rendering

`warning`
:   Likely mistakes or best practice violations

`info`
:   Informational messages (currently unused, reserved for future use)

## Rules

### `heading-hierarchy` {#heading-hierarchy}

Detects skipped heading levels that violate document structure best practices.

Severity
:   Warning

Auto-fix
:   Yes

Diagnostic codes
:   [`heading-hierarchy`](#heading-hierarchy)

Description
:   Headings should increment by at most one level (e.g., H1 → H2 → H3).
    Skipping levels (H1 → H3) makes the document structure unclear and can break
    table-of-contents generation.

**Example violation:**

```markdown
# Main Title

### Subsection
```

**Diagnostic:**

```
warning[heading-hierarchy]: Heading level skipped from h1 to h3; expected h2
 --> document.qmd:3:1
  |
3 | ### Subsection
  | ^^^^^^^^^^^^^^
```

**Auto-fix:** Changes `### Subsection` to `## Subsection`.

### `duplicate-reference-labels` {#duplicate-reference-labels}

Detects duplicate reference link and footnote definitions.

Severity
:   Warning

Auto-fix
:   No

Diagnostic codes
:   [`duplicate-reference-labels`](#duplicate-reference-labels)

Description
:   Each reference label and footnote ID must be unique within a document.
    Duplicate definitions cause ambiguity---only the first definition is used,
    making the others ineffective.

**Example violation:**

```markdown
See [link1] and [link2].

[link1]: https://example.com
[link1]: https://different.com
```

**Diagnostic:**

```
warning[duplicate-reference-labels]: Duplicate reference definition 'link1'
  --> document.qmd:4:1
note: First defined here:
  --> document.qmd:3:1
```

**Resolution:** Rename or remove the duplicate definition.

### `undefined-references` {#undefined-references}

Detects reference links and footnotes that point to missing definitions.

Severity
:   Warning

Auto-fix
:   No

Diagnostic codes
:   [`undefined-reference-label`](#undefined-reference-label),
    [`undefined-footnote-id`](#undefined-footnote-id)

Description
:   Flags unresolved reference-style links (including shortcut/collapsed forms)
    and unresolved footnote references. This helps catch broken cross-references
    early in editing and CI.

**Example violation:**

```markdown
See [missing][nope] and note[^missing].

[ok]: https://example.com
```

**Diagnostic:**

```
warning[undefined-reference-label]: Reference label '[nope]' not found
  --> document.qmd:1:15
warning[undefined-footnote-id]: Footnote '[^missing]' not found
  --> document.qmd:1:31
```

#### `undefined-reference-label` {#undefined-reference-label}

Emitted when a reference-style link points to a label that has no matching
definition.

#### `undefined-footnote-id` {#undefined-footnote-id}

Emitted when a footnote reference points to an ID that has no matching footnote
definition.

### `undefined-anchor` {#undefined-anchor}

Detects inline links whose `#fragment` destination has no matching anchor in the
document.

Severity
:   Warning

Auto-fix
:   No

Diagnostic codes
:   [`undefined-anchor`](#undefined-anchor)

Description

:   Flags `[text](#fragment)` links where `#fragment` does not match any anchor
    that will exist in the rendered output. Anchor sources include explicit
    `{#id}` attributes on headings, fenced divs, code blocks, spans, and chunk
    labels, plus auto-generated heading IDs (when the `auto_identifiers`
    extension is enabled). Matching is case-sensitive, mirroring how browsers
    resolve URL fragments.

    Links with a path component (`other.qmd#frag`), absolute URLs
    (`https://example.com#frag`), and bare back-to-top links (`#`) are not flagged.
    In bookdown projects, sibling chapters are scanned because bookdown's gitbook
    renderer rewrites cross-chapter anchors. Quarto books render each chapter to a
    separate HTML page and are not scanned cross-chapter.

**Example violation:**

```markdown
# Real Heading {#real}

See [the typo](#reel).
```

**Diagnostic:**

```
warning[undefined-anchor]: Anchor '#reel' not found in document
  --> document.qmd:3:16
```

### `unused-definitions` {#unused-definitions}

Detects reference labels and footnote definitions that are declared but never
referenced.

Severity
:   Warning

Auto-fix
:   No

Diagnostic codes
:   [`unused-definition-label`](#unused-definition-label),
    [`unused-footnote-id`](#unused-footnote-id)

Description
:   Flags unused reference definitions (`[label]: ...`) and unused footnote
    definitions (`[^id]: ...`). This helps keep documents tidy and avoids dead
    references that can accumulate over time. When project metadata is available
    (for example in Quarto/Bookdown project lint runs), usage is resolved across
    project documents to reduce cross-file false positives.

**Example violation:**

```markdown
Text with one note[^1].

[^1]: Used note.
[^2]: Unused note.

[used]: https://example.com
[unused]: https://unused.example.com
```

**Diagnostic:**

```
warning[unused-footnote-id]: Footnote '[^2]' is never used
  --> document.qmd:4:1
warning[unused-definition-label]: Reference definition '[unused]' is never used
  --> document.qmd:7:1
```

#### `unused-definition-label` {#unused-definition-label}

Emitted when a reference definition (`[label]: ...`) is declared but never
referenced anywhere in the document (or project, when project metadata is
available).

#### `unused-footnote-id` {#unused-footnote-id}

Emitted when a footnote definition (`[^id]: ...`) is declared but never
referenced.

### `citation-keys` {#citation-keys}

Validates citation keys against loaded bibliographies and detects conflicts in
inline bibliography entries.

Severity
:   Error for bibliography load/parse failures, Warning for undefined keys and
    duplicates

Auto-fix
:   No

Requirements
:   Requires `extensions.citations = true` in configuration

Diagnostic codes
:   [`bibliography-load-error`](#bibliography-load-error),
    [`bibliography-parse-error`](#bibliography-parse-error),
    [`missing-bibliography-key`](#missing-bibliography-key),
    [`duplicate-bibliography-key`](#duplicate-bibliography-key),
    [`duplicate-inline-reference-id`](#duplicate-inline-reference-id)

Description
:   Checks that all cited keys (`[@key]`) exist in the configured bibliography
    files. Also validates inline bibliography entries for duplicates and
    conflicts.

**Example violation (undefined key):**

```markdown
---
bibliography: refs.bib
---

See @smith2020 and @jones2021.
```

If `jones2021` doesn't exist in `refs.bib`:

```
warning[missing-bibliography-key]: Citation key 'jones2021' not found in bibliography
  --> document.qmd:5:17
```

**Example violation (bibliography load error):**

```markdown
---
bibliography: nonexistent.bib
---
```

```
error[bibliography-load-error]: Failed to load bibliography nonexistent.bib: File not found
  --> document.qmd:1:1
```

**When it runs:** Only when document metadata includes bibliography
configuration and the citation extension is enabled.

#### `bibliography-load-error` {#bibliography-load-error}

Emitted when a configured bibliography file cannot be opened (missing,
unreadable, etc.).

#### `bibliography-parse-error` {#bibliography-parse-error}

Emitted when a bibliography file is opened successfully but contains entries
that fail to parse.

#### `missing-bibliography-key` {#missing-bibliography-key}

Emitted when a `@cite` reference does not match any key in the loaded
bibliography.

#### `duplicate-bibliography-key` {#duplicate-bibliography-key}

Emitted when the same key appears more than once across loaded bibliography
files.

#### `duplicate-inline-reference-id` {#duplicate-inline-reference-id}

Emitted when an inline bibliography entry collides with another inline entry or
with a key from a loaded bibliography file.

### `chunk-label-spaces` {#chunk-label-spaces}

Detects executable chunk labels containing whitespace (for example
`{r several words}` or `label="several words"`).

Severity
:   Warning

Auto-fix
:   No

Diagnostic codes
:   [`chunk-label-spaces`](#chunk-label-spaces)

Description
:   Labels with spaces are accepted by Quarto execution, but cross-references
    often fail to resolve reliably. Use a stable identifier such as
    `several-words` or `several_words` instead.

### `missing-chunk-labels` {#missing-chunk-labels}

Detects executable chunks that do not define a `label` (either inline or
hashpipe style).

Severity
:   Warning

Auto-fix
:   No

Diagnostic codes
:   [`missing-chunk-labels`](#missing-chunk-labels)

Description
:   Labels facilitate debugging. Add a label with either `#| label: my-chunk` or
    inline `label=my-chunk`.

### `figure-crossref-captions` {#figure-crossref-captions}

Detects figure cross-references that point to chunk labels without a figure
caption option.

Severity
:   Warning

Auto-fix
:   No

Diagnostic codes
:   [`figure-crossref-captions`](#figure-crossref-captions)

Description
:   Bookdown figure cross-references (`\@ref(fig:...)`) require a captioned
    chunk to create a resolvable figure label at render time. When the target
    chunk has a `label` but no `fig-cap`/`fig.cap`, the crossref will not
    resolve.

### `unknown-emoji-alias` {#unknown-emoji-alias}

Detects `:alias:` emoji shortcodes that are not recognized.

Severity
:   Warning

Auto-fix
:   No

Requirements
:   Requires `extensions.emoji = true` in configuration

Diagnostic codes
:   [`unknown-emoji-alias`](#unknown-emoji-alias)

Description
:   Checks parsed emoji aliases against the emoji shortcode dataset and warns
    when an alias is unknown.

**Example violation:**

```markdown
Looks good :smile:, but this one is wrong :not-a-real-emoji:.
```

**Diagnostic:**

```
warning[unknown-emoji-alias]: Unknown emoji alias ':not-a-real-emoji:'
  --> document.qmd:1:40
```

### `html-entities` {#html-entities}

Detects malformed HTML named entity references in inline prose.

Severity
:   Warning

Auto-fix
:   No

Diagnostic codes
:   [`html-entities`](#html-entities)

Description

:   Pandoc and Quarto pass HTML named entities like `…` through to the
    output unchanged, so a typo (`&ellips;`) or a missing trailing semicolon
    (`&numero` instead of `№`) silently produces wrong output. This rule
    flags three conservative cases:

    - `&NAME;` where `NAME` is not in the [HTML5 named-entity
      table](https://html.spec.whatwg.org/multipage/named-characters.html).
    - `&NAME` (no semicolon) where adding the semicolon would produce a known
      entity. This avoids firing on plain prose like "Tom & Jerry" or "AT&T",
      since those words are not entity names.
    - `&NAME` (no semicolon, length ≥ 4) where `NAME` is one edit away from a
      known entity (e.g. `&hellp` → `…`). Far-from-anything words are
      left alone to keep prose like "Procter &Gamble" quiet.

    The rule deliberately ignores numeric character references (`{`,
    `ꯍ`) for now and does not scan code spans, code blocks, raw HTML,
    inline/display math, link destinations, attributes, YAML metadata, or comments.

**Example violations:**

```markdown
This is &ellips; wrong.

Section &numero 5 of the report.
```

**Diagnostics:**

```
warning[html-entities]: Unknown HTML entity '&ellips;'
 --> document.qmd:1:9
  = help: did you mean '…'?

warning[html-entities]: HTML entity '&numero' is missing a trailing ';'
 --> document.qmd:3:9
  = help: write '№' to encode the character
```

### `adjacent-footnote-refs` {#adjacent-footnote-refs}

Detects footnote references placed back-to-back (`[^a][^b]`) where the rendered
superscripts run together (e.g. footnotes 7 and 8 look like footnote 78).

Severity
:   Warning

Auto-fix
:   Yes (inserts a space between the references)

Requirements
:   Requires `extensions.footnotes = true` in configuration

Diagnostic codes
:   [`adjacent-footnote-refs`](#adjacent-footnote-refs)

Description
:   When two footnote references appear with no intervening character, most
    renderers emit the superscript markers as a single visually-merged run.
    Inserting a single space between them keeps the markers distinct without
    changing the prose.

**Example violation:**

```markdown
See the prior reports[^a][^b] for context.
```

**Auto-fix output:**

```markdown
See the prior reports[^a] [^b] for context.
```

### `stray-fenced-div-markers` {#stray-fenced-div-markers}

Detects stray closing fenced div markers (`:::` or longer runs of colons) that
do not pair with an opening fence and consequently land inside paragraph text.

Severity
:   Warning

Auto-fix
:   No

Requirements
:   Requires `extensions.fenced-divs = true` in configuration (default for
    Pandoc, Quarto, and R Markdown flavors).

Diagnostic codes
:   [`stray-fenced-div-markers`](#stray-fenced-div-markers)

Description

:   Pandoc fenced divs use `:::` (or longer colon runs) for both openers (with
    an attribute or class) and closers (bare colons). When a closer-shaped line
    has no matching opener in scope, Pandoc treats it as ordinary paragraph text
    instead of as a div marker. This is almost always a typo---a mismatched
    fence count between opener and closer, an accidentally deleted opener, or a
    copy/paste artifact---and the document silently renders with `:::` showing
    up in the prose.

    Quarto's runtime emits a warning when it sees stray `:::`, but exits 0, which
    makes the issue invisible to CI/Makefile workflows. This rule fills that gap by
    flagging the same condition at lint time.

    The rule fires when, after up to three leading spaces, a line in a paragraph
    consists of three or more colons followed only by trailing whitespace. It does
    **not** flag mid-line `:::`, code spans containing `:::`, or indented code
    blocks---those are not closing-fence shapes.

**Example violation:**

```markdown
::: warning
The fence count on the opener and closer don't match.
::::
```

**Diagnostic:**

```
warning[stray-fenced-div-markers]: Stray fenced div marker '::::' has no matching opener
 --> document.qmd:3:1
  = help: if this is meant to close a div, check the opener's class/attributes;
          otherwise escape the colons or rewrite the line
```

## YAML diagnostics

Panache emits YAML diagnostics when embedded YAML content is invalid. These
apply to both document frontmatter (`--- ... ---`) and executable chunk hashpipe
options (`#| ...`).

### `yaml-parse-error` {#yaml-parse-error}

Severity
:   Warning

Auto-fix
:   No

Description
:   The YAML lexer/parser could not interpret the content (malformed flow
    sequences, unterminated strings, etc.).

**Example (hashpipe):**

````markdown
```{{r}}
#| echo: [
1 + 1
```
````

**Diagnostic:**

```
warning[yaml-parse-error]: YAML parse error: ...
  --> document.qmd:2:10
```

### `yaml-structure-error` {#yaml-structure-error}

Severity
:   Warning

Auto-fix
:   No

Description
:   The YAML parsed successfully but its top-level shape is not valid for the
    context (for example, frontmatter that is not a mapping, or a hashpipe block
    that does not produce a mapping of options).