Formatting

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 runs1

Text Wrapping

Paragraphs

Panache supports four modes of text wrapping for paragraphs, which are controlled via the configuration file:

[format]
wrap = "reflow" # or "preserve", "sentence", or "semantic"

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.

If wrap is set to semantic, Panache enforces semantic line breaks: it keeps the line breaks you already placed (for example after a comma or clause) and adds a break at every sentence boundary you missed. Unlike sentence, it never reflows away an existing break, and like sentence it ignores line-width—a long sentence with no internal break is left on a single line. This makes it the non-destructive companion to sentence: use sentence to normalize an arbitrarily-wrapped document into one-sentence-per-line, and semantic to augment a document you are already hand-breaking.

In sentence and semantic modes, 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.

A sentence break is also suppressed when it would push the next token to the start of a line where the parser would read it as the opening of a block—an ordered or unordered list marker (1., -, +, 1)), an ATX heading (#), a blockquote (>), or a setext/thematic rule (---, ===). For example, prose that embeds an inline enumeration stays one paragraph: Hear from us. 1. Tell us your name. 2. Describe it. keeps each marker inline rather than promoting the text into a list. This guarantees wrapping never changes how the document parses, which in turn keeps sentence/semantic formatting idempotent (panache format followed by panache format --check always succeeds). Genuine, well-formed lists are unaffected and continue to wrap normally.

The abbreviation list follows the document language. When metadata provides lang (as in Quarto and Pandoc frontmatter), Panache selects a built-in profile for it; profiles ship for English, Czech, German, Spanish, and French, and unknown languages fall back to English. You can also set a project-wide fallback language and extend the list with your own tokens—see Sentence-Wrapping Abbreviations in the configuration guide.

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

For semantic, consider input where the author has already broken a line after a clause:

Input
A longstanding problem asks: whether the bound is tight. It is,
as the appendix shows.
Output ("semantic")
A longstanding problem asks: whether the bound is tight.
It is,
as the appendix shows.

The break after It is, is preserved (the author put it there), while a new break is added after tight. (a sentence boundary).

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)
First line  
Second line
Output
First line\
Second line

YAML frontmatter and chunk options

Under a wrapping mode (reflow, sentence, or semantic), Panache also wraps prose held in YAML—both document frontmatter and Quarto/R Markdown hashpipe (#|) chunk options—following the same wrap mode as the document body. Folded block scalars (those introduced with >, including the >- and >+ chomping variants) and plain scalars are reflowed; a folded scalar’s single line breaks fold to spaces, so this never changes the value:

  • reflow rejoins the whole paragraph and refills it to line-width, so a hand-wrapped scalar with uneven lines is tidied up (short lines are joined).
  • sentence puts one sentence per line; semantic keeps your existing line breaks and adds breaks at sentence boundaries.

Blank lines and more-indented lines inside a folded scalar are significant and left in place. Literal block scalars (|) are left untouched—their line breaks are significant content. Under wrap = "preserve", nothing in the YAML is reflowed.

A long double-quoted scalar that overflows line-width is converted to a folded >- block scalar so it can wrap, for example a long description: or title::

description: >-
  This is a very long description that happens to exceed the line-length cap by
  a fair bit, and gets folded so it wraps.

This happens only when it is completely value-preserving. A scalar is left quoted (and may stay over the limit) if its value contains escape sequences (\n, \t, a unicode escape, …), leading or trailing whitespace, or control characters, or if it already spans multiple lines. A single-quoted scalar with simple content is normalized to double quotes first and then folds the same way; one that keeps its single quotes (because it contains an apostrophe) is left untouched.

A few top-level frontmatter keys are exempt even under a wrapping mode, because a downstream non-YAML tool reads their value line by line. Currently that is vignette: R’s vignette engine scans the raw %\VignetteEngine{...} lines, so Panache keeps each directive on its own line rather than folding them together.

Input
```{python}
#| fig-cap: >
#|   Suboptimality versus wall-clock time on the Rosenbrock function (200-iteration cap, `Vec<f64>` backend). Lower and further left is better.
```
Output
```{python}
#| fig-cap: >
#|   Suboptimality versus wall-clock time on the Rosenbrock function
#|   (200-iteration cap, `Vec<f64>` backend). Lower and further left is
#|   better.
```

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
#Heading 1
Text

##  Heading 2

###   Heading 3
Output
# 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
## Heading with classes{.important  .highlight }
Output
## Heading with classes {.important .highlight}

Setext Headings

Setext-style headings are converted to ATX style.

Input
Heading 1
=========

Heading 2
---------
Output
# 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
Single asterisks: *italic text*

Single underscores: _italic text_

Double asterisks: **bold text**

Double underscores: __bold text__

**foo*
Output
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
Code with spaces: ` code with spaces `

`` ```r ``
Output
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:

[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
```haskell
qsort [] = []
```

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

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

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

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

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

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

For a bare (unbraced) info string only the first word is the language class, but the rest of the info string is preserved verbatim rather than dropped. This keeps tool-specific metadata intact—for example Documenter.jl’s @example foo, jldoctest; setup = :(...), or @repl bar:

Note

Multi-word bare info strings are only valid code fences under the CommonMark and GFM flavors. In the Pandoc flavor (matching pandoc -t markdown) a bare info string must be a single word; ```@example foo is parsed as an inline code span instead, so use a CommonMark-family flavor for documents that rely on this syntax.

Indented Code Blocks

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

Input
    a <- 1
    b <- a^2
Output
```
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
* Item 1
  + Nested item
      *  Deeply nested

 +  Item 2
Output:
- 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
+ [ ] Parent task
   *   [ ] Nested unchecked task
   - [x]   Nested checked task
- [X] Another parent task
Output
- [ ] 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
Term 1

: Definition 1

Term 2
:   Definition 2a
 : Definition 2b

Term 3
:   - List
    with lazy continuation
    - > a
      > b
      > c
Output
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
(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
  (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

Four-Space Indentation

With the four-space-rule extension enabled, nested lists and continuation paragraphs (blocks that follow a blank line) are indented a flat four columns (one tab stop) per nesting level instead of lining up with the marker. Markers keep their normal trailing space, and a wrapped paragraph’s own continuation lines still align with its first-line content—only nesting and new blocks move to the tab stop. This produces output that strict Markdown parsers like Python-Markdown accept (they treat two-space-indented nesting as flat sibling items).

Input
* a
    * a1
* b
    * b1
Output
- a
    - a1
- b
    - b1

The four-space nesting is preserved (the default style would collapse it to two spaces). Note the extension assumes four-space-indented input: under it, two-space-indented content is read as a sibling rather than a child—matching the strict-Markdown reading—so it normalizes existing four-space documents but does not re-nest two-space ones.

Block Quotes

Block quotes are formatted with consistent > markers:

Input
>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
> 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. Pipe, simple, and multiline table blocks are indented by two spaces. Grid tables are left at the margin (column 0): Pandoc only recognizes a grid table when its +---+ border starts at column 0, so an indented border would be parsed as a paragraph instead.

The two-space indent is configurable via table-indent under [format], which accepts an integer from 0 to 3 (default 2) and applies to pipe, simple, and multiline tables alike. Setting table-indent = 0 keeps them flush at column 0. Grid tables always stay flush regardless, since Pandoc does not yet recognize an indented grid border. See Table Indentation for details. The examples below show the default indent of 2.

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
| Right | Left | Default | Center|
|------:|:-----|---------|:------:|
| 12  |  12  |    12   |    12  |
|  123  |  123 |   123   |   123  |
|  1  |    1 |     1   |     1 |
Output
  | 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. They are kept at column 0 rather than indented, since Pandoc does not recognize an indented grid border. Unlike pipe and simple tables, their column widths are preserved rather than shrunk to fit the content: Pandoc derives relative column widths from the source border widths and propagates them to output formats (e.g. HTML <col style="width:…">), so resizing a column would change the rendered layout.

Input
+--------+----------------+
| Var    | Description    |
+========+================+
| `A`    | Example value. |
+--------+----------------+
Output
+--------+----------------+
| Var    | Description    |
+========+================+
| `A`    | Example value. |
+--------+----------------+

Plain-prose cell text is reflowed to fill the existing column width and any blank padding lines inside a cell are dropped, following the global wrap setting (on under the default reflow; preserve leaves cell line breaks untouched). Column widths are not resized, so reflowing only re-packs a cell’s text within its column. Cells that carry block content (lists, headings, blockquotes, code) or an explicit hard line break (a trailing \) are kept verbatim so their structure is never collapsed into a paragraph:

Input
+--------------------+
| Lorem ipsum        |
| dolor sit          |
|                    |
+--------------------+
Output
+--------------------+
| Lorem ipsum dolor  |
| sit                |
+--------------------+

Column-spanning cells

A grid table cell may span several columns by omitting the interior |/+ markers at the boundaries it crosses (a Pandoc colspan). Such cells are preserved: every spanned column is kept and the spanning cell is re-laid out across them, so no content is dropped. As with all grid tables the source border widths are preserved (only grown to fit content), and the spanning header keeps its span:

Input
+---------+
|a        |
+:=:+:===:+
| aa|  ab |
+---+-----+
Output
+----------+
|    a     |
+:==:+:===:+
| aa | ab  |
+----+-----+

If a row’s | markers don’t line up with any column boundary the separators define, the table can’t be placed on a single grid; it is then left untouched so nothing is lost.

Row-spanning cells

A cell may span several rows: a content row carries an interior +---+ separator that closes the neighboring cells while the spanning cell continues past it. Row spans and column spans are laid out together in a single pass. Column alignment is taken from the separator colons (:---, ---:, :--:), never guessed from the cell contents, and the source border widths are preserved:

Input
+--------+--------+
| Name   | Value  |
+:======:+=======:+
| group  | 1.5    |
| spans  +--------+
| rows   | 22.0   |
+--------+--------+
Output
+--------+--------+
|  Name  |  Value |
+:======:+=======:+
| group  |    1.5 |
| spans  +--------+
|  rows  |   22.0 |
+--------+--------+

Simple Tables

We normalize simple tables so that cell contents align with the alignment specified in the header row, and the column spacing is recomputed from the content: each column’s dash run is the width of its widest cell plus two, and columns are separated by a single space. The output is therefore independent of the incoming spacing and dash lengths.

Headerless simple tables (delimited by a dash run above and below the data) have no header row, so each column’s alignment is read from the first data row’s position relative to the dash runs, matching how Pandoc parses them.

Input
   Right     Left     Center     Default
 -------     ------ ----------   ---------------
      12       12      12            12
   123       123       123          123
       1     1        1             1

   :  A caption
Output
    Right Left    Center  Default
  ------- ------ -------- ---------
       12 12        12    12
      123 123      123    123
        1 1         1     1

  : A caption

Multiline Tables

Input
-------------------------------------------------------------
 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
  -------------------------------------------------------------
   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.
  -------------------------------------------------------------

Body cell text is reflowed to fit the existing column width and any blank padding lines inside a cell are dropped, following the global wrap setting: under reflow (the default) a cell whose lines could pack more tightly is re-wrapped within its column, while preserve keeps the existing line breaks untouched. As with grid tables, the relative column widths themselves are never changed, since Pandoc derives relative output widths from them. The header row is left as-is, because its layout determines each column’s alignment.

A multiline table column spans from the start of its dashes to the start of the next column’s dashes (the gap belongs to the left column; the last column runs to the end of the line), matching how Pandoc reads the separator. The variable runs of spaces a source file may leave between the dash runs are normalized to a single space, with the dash runs extended to keep every column start fixed. The relative widths are unchanged, so the table renders identically while the separator becomes regular. Because the column boundary is the next column’s start (not the end of its own dashes), cell text that extends past a short dash run into the gap is kept rather than truncated.

Input
-----  ----            ------
Cell   spanning runs   here

Next   more     text     end
-----  ----            ------
Output
------ --------------- ------
Cell   spanning runs   here

Next   more text       end
------ --------------- ------

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
 a   b
--- ---
 1   2

   : A caption with extra spaces
Output
   a   b
  --- ---
   1   2

  : A caption with extra spaces
Output (wrap = "sentence")
   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 2):

Input
$$E = mc^2$$
Output
$$
  E = mc^2
$$

Set math-indent = 0 to keep the content flush against the left margin.

Content-Aware Math Formatting (experimental)

By default the content between math delimiters is emitted verbatim. Enabling the experimental format-math option turns on structural reformatting of that content—inline whitespace collapse, tight sub/superscripts, math-mode group-interior trimming, environment-body indentation, \\ normalization, and &-column alignment:

[experimental]
format-math = true
Input
$$
\begin{aligned}
x &= 1 \\
y &= 22 \\
z &= 333
\end{aligned}
$$
Output
$$
\begin{aligned}
  x & = 1   \\
  y & = 22  \\
  z & = 333
\end{aligned}
$$

The & columns and the trailing \\ are both aligned.

Operators are spaced precedence-aware: a binary or relational operator gets one space on each side (a+ba + b, a<=ba <= b), while a unary minus or plus stays tight (x = -y, f(-x)). Command operators are spaced the same way (a\cdot ba \cdot b, x\leq yx \leq y). Macro contents are not rewritten (no \frac canonicalization, no auto-& insertion). Alignment uses source character widths (it tidies the source, it does not align rendered glyphs), and malformed math is left untouched.

Whitespace that TeX ignores is normalized away: sub/superscript markers (_, ^) are tightened (H _{ 00}^{-1 }H_{00}^{-1}), and the leading/trailing interior whitespace of a math-mode brace group is trimmed ({ 00 }{00}). Text-mode arguments are exempt, so \text{ a } keeps its spaces.

A display equation ($$…$$) wider than line-width is broken at its top-level relations, with each continuation aligned under the first relation (the classic stacked-= layout for an equality chain). If a relation segment is itself too wide, its binary terms break one level deeper, each + term sitting flush under the right-hand side they hang under—the relation/right-hand-side offset alone supplies the nesting. The math-indent shifts the whole block to the right but never changes this internal alignment, so the equation’s shape is the same at any indent. The width budget accounts for math-indent, so a broken line plus its leading indent stays within line-width:

Input
$$
A = aaaaaaaaaa + bbbbbbbbbb = cccccccccc + dddddddddd
$$
Output
$$
  A = aaaaaaaaaa
      + bbbbbbbbbb
    = cccccccccc
      + dddddddddd
$$

An assignment arrow (\gets, \leftarrow, \mapsto, \coloneqq, :=) is treated differently: it defines its left-hand side rather than equating it, so the equality continuations it introduces anchor under the assignment’s right-hand side instead of under the arrow. This keeps a wide arrow from dragging

the continuations far left:
$$
  \beta_0 \gets \beta_0 + \frac{4}{n} \sum_{i = 1}^n (y_i - p_i)
                = \beta_0 - \frac{1}{L_0} \partial_0 F, \qquad L_0
                = 1/4,
$$

The break is source-cosmetic—math ignores whitespace, so the rendered equation is unchanged. The layout is fully deterministic: it depends only on the content, line-width, and math-indent, never on where the author placed line breaks. A relation chain split across forced \\ breaks in a bare $$ is aligned the same way, like an implicit aligned environment. Relations break before binary operators (+, \cdot), which only break further when their segment still overflows; operators inside (…), \left…\right, or a {…} group (including \frac{…}{…} arguments) are never broken at. A unary +/- is never a break point. An over-width row with a single relation breaks its binary right-hand side; one with no relation at all (a standalone binary chain) breaks with its head term first and each following term flush beneath it. Only an equation with no top-level relation or binary operator (e.g. a single wide \frac{…}{…}) is left on one line when it overflows. The option is unstable and may change without a major release—see Experimental Features.

Fenced Divs

Fenced divs are normalized so that blank lines at the boundaries of the div body are stripped (no blank line after the opening fence or before the closing fence) and runs of blank lines inside the body collapse to a single separator, matching pandoc. Attributes are preserved and normalized. With nested divs, the inner div

Input
::: Warning ::::::
This is a warning.

::: Danger
This is a warning within a warning.
:::
::::::::::::::::::
Output
::: Warning
This is a warning.

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

Fence-shape lines pulled into a paragraph

If a line that looks like a fenced div opener or closer (::: foo, :::, etc.) directly follows non-blank content with no separating blank line, pandoc parses it as paragraph text rather than a fence. Reflowing such a paragraph would collapse the structural-looking lines into ordinary prose and hide the cause. Panache instead preserves the source line breaks for that paragraph so the missing blank line stays visible. The stray-fenced-div-markers lint rule flags the same shape diagnostically.

Input
[]{#hmm}
::: {lang=zh-TW}
bla
:::
Output
[]{#hmm}
::: {lang=zh-TW}
bla
:::

MyST Directives

Under the MyST flavor, Panache recognizes MyST directives (```{name}, ~~~{name}, or :::{name}). The opening fence is kept verbatim, the leading :key: value option block is emitted canonically (one option per line, a single space after the closing colon), and a single blank line separates the options from the body.

Directives whose body is literal content—{code}, {code-block}, {code-cell}, and {math}—are treated as verbatim: their body is passed through unchanged, so indentation and line breaks are never reflowed. The {toctree} directive is also verbatim, since its body is a line-oriented list of entries (one document reference per line) rather than prose; reflowing it would fuse the entries onto a single line and break the table of contents.

Input
```{code-block} python
:caption:   Fibonacci

def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)
```
Output
```{code-block} python
:caption: Fibonacci

def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)
```

Other directives ({note}, {figure}, …) keep markdown bodies, which are formatted recursively like ordinary content.

Admonitions

When the python-markdown-admonitions extension is enabled, Panache recognizes python-markdown admonitions (!!! type "title" followed by 4-space-indented content). The companion pymdownx-details extension adds the collapsible ??? (collapsed) and ???+ (expanded) variants. Both are disabled by default for every flavor, since the indented body would otherwise parse as an indented code block.

The marker line is kept on a single line (never wrapped or sentence-split), with the type, any extra classes, and the optional quoted title normalized to single spaces. The body is re-indented by four spaces and reflowed like ordinary content; blank lines at the body boundaries are stripped (matching fenced divs).

Input
!!! note "Heads up"

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod nulla.

???+ tip

    - first
    - second
Output
!!! note "Heads up"
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et
    euismod nulla.

???+ tip
    - first
    - second

Footnotes

Reference Footnotes

Input
[^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
[^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
---
***
___
Output (line-width = 60):
-----------------------------------------------------------

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

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

Blank Lines

Multiple blank lines are collapsed to one:

Input
Paragraph 1


Paragraph 2
Output
Paragraph 1

Paragraph 2

Frontmatter

YAML

YAML frontmatter is parsed and normalized by pretty_yaml. The deterministic style rules Panache emits are documented in STYLE.md.

Input
---
echo:    false
list:
-  a
-     b
---
Text
Output
---
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
```{r foobar, echo=FALSE, dependson = c("foo", "bar"), fig.cap = "A caption"}
a <- 1
b <- 2
```
Output
```{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—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:

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:

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

Footnotes

  1. This is precisely what Pandoc’s writer also does.↩︎