panache 2.37.0

An LSP, formatter, and linter for Pandoc markdown, Quarto, and RMarkdown
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
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
---
title: Formatting
description: >
  This section provides instructions and examples for using Panache to format
  your documents, showing examples of the formatting rules and how to customize
  them.
---

Panache's formatting rules are designed to produce clean, consistent Markdown
output that adheres to common style guidelines while preserving the semantic
meaning of the original document. We often try to stick quite close to the
output of Pandoc's writer, but not always. Panache is an opinionated formatter
and provides a minimal set of options to configure the formatting behavior.

The golden rule is that a markdown document should be readable in its raw form,
which for instance has guided the decision to convert `---` horizontal rules to
a full line of hyphens.

Another important principle is that Panache's formatter is *idempotent*: running
the formatter multiple times on the same document should not produce different
output after the first run. To make this work, Panache therefore needs to escape
certain characters that might otherwise cause semantic drift on subsequent
runs^[This is precisely what Pandoc's writer also does.]

## Text Wrapping

### Paragraphs

Panache supports three modes of text wrapping for paragraphs, which are
controlled via the [configuration](configuration.qmd) file:

```toml
[format]
wrap = "reflow" # or "preserve" or "sentence"
```

If `wrap` is set to `reflow`, Panache will reformat paragraphs to fit within the
number of columns specified by the `line-width` configuration option (default
80). If `wrap` is set to `preserve`, Panache will keep existing line breaks and
not reflow paragraphs. If `wrap` is set to `sentence`, Panache will break lines
at sentence boundaries, ensuring that each sentence starts on a new line.

In sentence mode, Panache applies conservative no-break rules for common
abbreviations (for example `i.e.` and `e.g.`), so these do not trigger sentence
splits by themselves.

When document metadata provides `lang` (as in Quarto and Pandoc frontmatter),
Panache uses it to choose sentence-boundary behavior. Currently, English rules
are applied as the default fallback when language-specific rules are not
available.

Input

:   ```markdown
    First sentence with a [link text](https://example.com) and inline math $a + b = c$. Second sentence; third sentence!
    ```

Output (`"reflow"`)

:   ```markdown
    First sentence with a [link text](https://example.com) and inline math
    $a + b = c$. Second sentence; third sentence!
    ```

Output (`"sentence"`)

:   ```markdown
    First sentence with a [link text](https://example.com) and inline math $a + b = c$.
    Second sentence; third sentence!
    ```

### Hard Line Breaks

Hard line breaks, which are created either by ending a line with two or more
spaces or by using a backslash (`\n`), are preserved regardless of the wrapping
mode:

Input (with two spaces at the end of the first line)

:   ```markdown
    First line  
    Second line
    ```

Output

:   ```markdown
    First line\
    Second line
    ```

## Headings

### ATX Headings

Headings are normalized with consistent spacing. Trailing `#` characters in ATX
headings are removed. Spacing between the `#` characters and the heading text is
normalized to a single space. Headings are separated from surrounding content by
a blank line.

Input

:   ```markdown
    #Heading 1
    Text

    ##  Heading 2

    ###   Heading 3
    ```

Output

:   ```markdown
    # Heading 1

    Text

    ## Heading 2

    ### Heading 3
    ```

### Heading Attributes

Attributes are preserved and formatted to have a single space between the class
names and no space between the class names and the opening `{` character.

Input

:   ```markdown
    ## Heading with classes{.important  .highlight }
    ```

Output

:   ```markdown
    ## Heading with classes {.important .highlight}
    ```

### Setext Headings

Setext-style headings are converted to ATX style.

Input

:   ```markdown
    Heading 1
    =========

    Heading 2
    ---------
    ```

Output

:   ```markdown
    # Heading 1

    ## Heading 2
    ```

## Emphasis and Strong

### Basic Formatting

Emphasis markers are consistently normalized to asterisks (`*`) for italics and
double asterisks (`**`) for bold text. Underscores are converted to asterisks.
Ambiguous cases are resolved by escaping emphasis markers to avoid idempotency
issues.

Input

:   ```markdown
    Single asterisks: *italic text*

    Single underscores: _italic text_

    Double asterisks: **bold text**

    Double underscores: __bold text__

    **foo*
    ```

Output

:   ```markdown
    Single asterisks: *italic text*

    Single underscores: *italic text*

    Double asterisks: **bold text**

    Double underscores: **bold text**

    \*\*foo\*
    ```

## Code

### Inline Code

For inline code, Panache normalizes spacing within the code span and ensures
that the code is enclosed in the appropriate number of backticks to avoid
conflicts with the content. If the code contains spaces, it will be wrapped in
double backticks to preserve the spaces.

Input

:   ````markdown
    Code with spaces: ` code with spaces `

    `` ```r ``
    ````

Output

:   `````markdown
    Code with spaces: `code with spaces`

    ```` ```r ````
    `````

### Tab Stops

Tabs in regular text are always normalized to spaces. To preserve tabs in
literal code spans and code blocks, set:

```toml
[format]
tab-stops = "preserve"
tab-width = 4
```

### Fenced Code Blocks

Fenced code blocks are formatted with consistent fencing and attributes. We
normalize them to the *shortcut* style from Pandoc.

Input

:   ````markdown
    ```haskell
    qsort [] = []
    ```

    ``` {.haskell}
    qsort [] = []
    ```

    ```haskell {.numberLines}
    qsort [] = []
    ```

    ``` {.haskell .numberLines}
    qsort [] = []
    ```
    ````

Output

:   ````markdown
    ```haskell
    qsort [] = []
    ```

    ```haskell
    qsort [] = []
    ```

    ```haskell {.numberLines}
    qsort [] = []
    ```

    ```haskell {.numberLines}
    qsort [] = []
    ```
    ````

### Indented Code Blocks

Indented code blocks are normalized to fenced code blocks for consistency:

Input

:   ```markdown
        a <- 1
        b <- a^2
    ```

Output

:   ````markdown
    ```
    a <- 1
    b <- a^2
    ```
    ````

## Lists

### Unordered Lists

Bullet list markers are standardized to `-`. Indentation is normalized to two
spaces per level. Loose lists are converted to tight lights if they only contain
plain (non-block/paragraph) items.

Input

:   ```markdown
    * Item 1
      + Nested item
          *  Deeply nested

     +  Item 2
    ```

Output:

:   ```markdown
    - Item 1
      - Nested item
        - Deeply nested
    - Item 2
    ```

### Ordered Lists

Ordered lists are handled similarly to [unordered lists].

### Task Lists

GitHub-style task lists use the standardized `-` marker, and indent to the start
of the text.

Input

:   ```markdown
    + [ ] Parent task
       *   [ ] Nested unchecked task
       - [x]   Nested checked task
    - [X] Another parent task
    ```

Output

:   ```markdown
    - [ ] Parent task
           - [ ] Nested unchecked task
           - [x] Nested checked task
    - [x] Another parent task
    ```

### Definition Lists

Definition lists are indented with three spaces after the marker, flush the
marker to the left, and indent following lines to four spaces.

Panache also normalizes compact vs loose definition items by structure, making
them

- compact when each definition is a single plain paragraph-like block and
- loose when a definition contains multiple blocks or starts with a
  non-paragraph block.

Input

:   ```markdown
    Term 1

    : Definition 1

    Term 2
    :   Definition 2a
     : Definition 2b

    Term 3
    :   - List
        with lazy continuation
        - > a
          > b
          > c
    ```

Output

:   ```markdown
    Term 1

    :   Definition 1

    Term 2
    :   Definition 2a
    :   Definition 2b

    Term 3

    :   - List with lazy continuation

        - > a b c
    ```

### Fancy Lists

Fancy lists are indented to line up with the first character of the list item
text.

Input

:   ```markdown
    (i) Parens style
    (ii) Second item
    (iii) Third item

    iv. Starting at four
    v. Five
    vi. Six
    vii. Seven
    viii. Eight
    ix. Nine
    x. Ten
    ```

Output

:   ```markdown
      (i) Parens style
     (ii) Second item
    (iii) Third item

      iv. Starting at four
       v. Five
      vi. Six
     vii. Seven
    viii. Eight
      ix. Nine
       x. Ten
    ```

## Block Quotes

Block quotes are formatted with consistent `>` markers:

Input

:   ```markdown
    >This is a block quote. This
    >paragraph has two lines.
    >
    > 1. This is a list inside a block quote.
    > 2. Second item.

    > This is a block quote. This
    paragraph has two lines.
    ```

Output

:   ```markdown
    > This is a block quote. This paragraph has two lines.
    >
    > 1. This is a list inside a block quote.
    > 2. Second item.

    > This is a block quote. This paragraph has two lines.
    ```

## Tables

Panache supports all Pandoc table types and normalizes alignment and spacing
while preserving content and attributes. Formatted table blocks are indented by
two spaces.

### Pipe Tables

Pipe tables are normalized to have consistent spacing and alignment. The header
separator row is standardized to use hyphens and colons for alignment, and
spacing within cells is normalized.

Input

:   ```markdown
    | Right | Left | Default | Center|
    |------:|:-----|---------|:------:|
    | 12  |  12  |    12   |    12  |
    |  123  |  123 |   123   |   123  |
    |  1  |    1 |     1   |     1 |
    ```

Output

:   ```markdown
      | Right | Left | Default | Center |
      | ----: | :--- | ------- | :----: |
      |    12 | 12   | 12      |   12   |
      |   123 | 123  | 123     |  123   |
      |     1 | 1    | 1       |   1    |
    ```

### Grid Tables

Grid tables are normalized to have consistent spacing and alignment.

Input

:   ```markdown
    +--------+----------------+
    | Var    | Description    |
    +========+================+
    | `A`    | Example value. |
    +--------+----------------+
    ```

Output

:   ```markdown
      +-----+----------------+
      | Var | Description    |
      +=====+================+
      | `A` | Example value. |
      +-----+----------------+
    ```

### Simple Tables

We normalize simple tables so that cell contents align with the alignment
specified in the header row, and spacing within cells is normalized.

Input

:   ```markdown
       Right     Left     Center     Default
     -------     ------ ----------   ---------------
          12       12      12            12
       123       123       123          123
           1     1        1             1

       :  A caption
    ```

Output

:   ```markdown
        Right     Left     Center     Default
      -------     ------ ----------   -------
           12     12         12       12
          123     123       123       123
            1     1          1        1

      : A caption
    ```

### Multiline Tables

Input

:   ```markdown
    -------------------------------------------------------------
     Centered   Default           Right Left
      Header    Aligned         Aligned Aligned
    ----------- ------- --------------- -------------------------
      First     row              12.0   Example of a row that
                                        spans multiple lines.


      Second    row                 5.0 Here's another one. Note
                                        the blank line between
                                        rows.
    -------------------------------------------------------------
    ```

Output

:   ```markdown
      -------------------------------------------------------------
       Centered   Default           Right Left
        Header    Aligned         Aligned Aligned
      ----------- ------- --------------- -------------------------
         First    row                12.0 Example of a row that
                                          spans multiple lines.

        Second    row                 5.0 Here's another one. Note
                                          the blank line between
                                          rows.
      -------------------------------------------------------------
    ```

### Table Captions

Table captions are normalized to be on a separate line **below** the table,
prefixed with `:`. Wrapped continuation lines use a hanging indent.

Captions also follow the configured wrapping mode (`reflow`, `preserve`, or
`sentence`). In `reflow` mode, long captions wrap to the configured
`line-width`. In `sentence` mode, each sentence starts on its own line.

Input

:   ```markdown
     a   b
    --- ---
     1   2

       : A caption with extra spaces
    ```

Output

:   ```markdown
       a   b
      --- ---
       1   2

      : A caption with extra spaces
    ```

Output (`wrap = "sentence"`)

:   ```markdown
       a   b
      --- ---
       1   2

      : First caption sentence.
        Second caption sentence.
    ```

## Math

### Inline Math

Inline math is (currently) untouched.

### Display Math

Display math is formatted on its own lines with leading indentation depending on
the `math-indent` configuration option (default 0):

Input

:   ```markdown
    $$E = mc^2$$
    ```

Output

:   ```markdown
    $$
    E = mc^2
    $$
    ```

## Links

For links, Panache preserves the original link text and URL but normalizes the
spacing around attributes, quotes, and parentheses. Descriptions are wrapped if
reflow wrapping is enabled.

Input

:   ```markdown
    [URL and title](/url/ "title").

    [URL and title](/url/  "title preceded by two spaces").

    [URL and title](/url/   "title preceded by a tab").

    [URL and title](/url/ "title with "quotes" in it")

    [URL and title](/url/ 'title with single quotes')
    ```

Output

:   ```markdown
    [URL and title](/url/ "title").

    [URL and title](/url/ "title preceded by two spaces").

    [URL and title](/url/ "title preceded by a tab").

    [URL and title](/url/ "title with "quotes" in it")

    [URL and title](/url/ "title with single quotes")
    ```

## Fenced Divs

Fenced divs are normalized so that there is no blank line between the opening
fence and the content and no space between the closing fence and the content.
Attributes are preserved and normalized. With nested divs, the inner div

Input

:   ```markdown
    ::: Warning ::::::
    This is a warning.

    ::: Danger
    This is a warning within a warning.
    :::
    ::::::::::::::::::
    ```

Output

:   ```markdown
    ::: Warning
    This is a warning.

    ::::: Danger
    This is a warning within a warning.
    :::::
    :::
    ```

## Footnotes

### Reference Footnotes

Input

:   ```markdown
    [^longnote]: Here's one with multiple blocks.

        Subsequent paragraphs are indented to show that they
    belong to the previous footnote.

            { some.code }

        The whole paragraph can be indented, or just the first
        line.  In this way, multi-paragraph footnotes work like
        multi-paragraph list items.
    ```

Output

:   ````markdown
    [^longnote]: Here's one with multiple blocks.

        Subsequent paragraphs are indented to show that they belong to the previous
        footnote.

        ```
        { some.code }
        ```

        The whole paragraph can be indented, or just the first line. In this way,
        multi-paragraph footnotes work like multi-paragraph list items.
    ````

## Horizontal Rulers

Horizontal rulers are normalized to hyphens and extend to `line-width`.

Input

:   ```markdown
    ---
    ***
    ___
    ```

Output (`line-width = 60`):

:   ```markdown
    -----------------------------------------------------------

    -----------------------------------------------------------

    -----------------------------------------------------------
    ```

## Blank Lines

### Collapsing

By default, multiple blank lines are collapsed to one:

Input

:   ```markdown
    Paragraph 1


    Paragraph 2
    ```

Output

:   ```markdown
    Paragraph 1

    Paragraph 2
    ```

If you want to preserve all existing blank lines, set:

```toml
[format]
blank-lines = "preserve"
```

## Frontmatter

### YAML

YAML frontmatter is parsed and normalized by
[`pretty_yaml`](https://github.com/g-plane/pretty_yaml).

Input

:   ```markdown
    ---
    echo:    false
    list:
    -  a
    -     b
    ---
    Text
    ```

Output

:   ```markdown
    ---
    echo: false
    list:
      - a
      - b
    ---

    Text
    ```

## Chunk Options

For executable code blocks, Panache convert options specified in the header
(e.g. ```` ```{r, echo=FALSE} ````) to the hashpipe comment style.

Input

:   ````markdown
    ```{{r foobar, echo=FALSE, dependson = c("foo", "bar"), fig.cap = "A caption"}}
    a <- 1
    b <- 2
    ```
    ````

Output

:   ````markdown
    ```{{r, dependson=c("foo", "bar")}}
    #| label: foobar
    #| echo: false
    #| fig-cap: "A caption"
    a <- 1
    b <- 2
    ```
    ````

Complex structures, like the `dependson` option in the example above, are
preserved as-is without attempting to convert them. Formatting of the options is
handled by [`pretty_yaml`](https://github.com/g-plane/pretty_yaml)---as in the
case of YAML frontmatter. If you want to ensure that your caption is wrapped,
don't use a quoted scalar value for the caption. Instead use a block scalar, via
`>-`.

## Ignore Directives

You can selectively disable formatting for specific regions using HTML comment
directives:

### Ignore Formatting Only

Use `panache-ignore-format-start` and `panache-ignore-format-end` to preserve
specific formatting:

```markdown
Normal paragraph will be wrapped and formatted.

<!-- panache-ignore-format-start -->
This    paragraph   has    custom     spacing
that  will   be   preserved   exactly.
<!-- panache-ignore-format-end -->

Back to normal formatting.
```

This is useful for:

- ASCII art or diagrams
- Tables with specific spacing
- Pre-formatted text blocks
- Code examples that aren't in code blocks

### Ignore Both Formatting and Linting

Use `panache-ignore-start` and `panache-ignore-end` to disable both formatting
and linting:

```markdown
<!-- panache-ignore-start -->
#### Heading with unusual spacing
Custom    formatting   and   linting   rules   ignored
<!-- panache-ignore-end -->
```