---
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 -->
```