Configuration
Panache aims to provide sensible defaults, but also be configurable enough to accommodate common formatting preferences.
The configuration system is built around a TOML configuration file, which allows you to customize a range of options, including formatting preferences, linting rules, external linter and formatter integrations>
Panache searches for a configuration file in the following order:
- Explicit path:
--config <path>(errors if invalid) - Project config:
.panache.tomlorpanache.tomlin current or parent directories - User config:
~/$XDG_CONFIG_HOME/panache/config.toml(typically~/.config/panache/config.toml)
Basic Options
Here, we list the top-level configuration options that control general behavior.
Flavor
Choose the Markdown flavor, which determines default extension settings:
flavor = "pandoc"The available flavors are:
pandoc- Standard Pandoc Markdown (default)
quarto- Quarto-flavored Markdown (Pandoc + Quarto extensions)
rmarkdown- R Markdown (Pandoc + R-specific extensions, including Bookdown): R Markdown (Pandoc + R-specific extensions)
gfm- GitHub-Flavored Markdownb Flavored Markdown
commonmark- CommonMark (Note that we are currently not fully CommonMark-compliant, and use this option only as a means to setup the correct set of extensions): CommonMark (minimal extensions)
Line Width
Set the maximum line width for text wrapping:
line-width = 80Flavor Overrides
Use flavor-overrides to pick flavor by path pattern for Markdown-family files (.md, .markdown, .mdown, .mkd):
flavor = "quarto"
[flavor-overrides]
"README.md" = "gfm"
"docs/**/*.md" = "quarto"Panache applies flavor in this order:
.qmdfiles always usequarto.Rmdfiles always usermarkdown- Markdown-family files use the most specific matching
flavor-overridespattern - Otherwise, Panache falls back to top-level
flavor
File Selection (Directory Traversal)
When you run commands on directories (panache format ., panache lint ., panache debug format .), Panache supports include/exclude selectors:
exclude = [".git/", "build/"]
extend-exclude = ["tests/testthat/_snaps/"]
include = ["*.qmd", "*.md"]
extend-include = ["*.Rmd"]exclude-
Base exclude patterns. If set, replaces Panache’s default exclude base. The default exclude base includes common build/cache directories and
**/LICENSE.md. extend-exclude- Additional exclude patterns appended to the base set.
include- Base include patterns. If set, replaces Panache’s default include base.
extend-include- Additional include patterns appended to the base set.
If a path matches both include and exclude, exclude wins.
Formatting Style
Formatting style preferences are organized under the [format] section:
[format]
wrap = "reflow"
blank-lines = "collapse"
math-delimiter-style = "preserve"
math-indent = 0
tab-stops = "normalize"
tab-width = 4Wrapping Mode
Control how text is wrapped:
[format]
wrap = "reflow"reflow- Reformat paragraphs to fit within line width (default)
sentence- Wrap after each sentence.
preserve- Keep existing line breaks
Blank Lines
Control blank line handling:
[format]
blank-lines = "collapse"collapse- Collapse multiple blank lines into one (default)
preserve- Keep all existing blank lines
Math Formatting
Configure how math delimiters are formatted:
[format]
math-delimiter-style = "preserve"
math-indent = 0Math delimiter styles:
preserve- Keep original delimiter style (default)
dollars-
Normalize to
$...$and$$...$$ backslash-
Normalize to
\(...\)and\[...\]
The math-indent field specifies indentation (in spaces) for display math blocks. Default is 0.
Tab Stops
Control how tabs are handled during formatting:
[format]
tab-stops = "normalize"
tab-width = 4normalize-
Convert tabs to spaces using
tab-width(default 4). preserve- Preserve tabs in literal code spans and fenced/indented code blocks. Tabs in regular text are always normalized to spaces.
tab-width- Number of spaces per tab when normalizing (default 4).
Extensions
Panache supports most of the Pandoc extensions. Defaults vary by flavor, but you can override any extension:
flavor = "quarto"
[extensions]
hard-line-breaks = false
citations = true
task-lists = trueYou can also scope extension overrides to a specific flavor:
[extensions]
citations = true
task-lists = false
[extensions.gfm]
task-lists = truePrecedence for the active flavor is:
- flavor defaults
- global
[extensions] [extensions.<flavor>]
Block-Level Extensions
Headings
[extensions]
blank-before-header = true
header-attributes = true
implicit-header-references = trueblank-before-header- Require blank line before headers (default: enabled)
header-attributes-
Full attribute syntax on headers
{#id .class key=value}(default: enabled) implicit-header-references-
Allow
[Heading]links to reference headers (default: enabled)
Block Quotes
[extensions]
blank-before-blockquote = trueblank-before-blockquote- Require blank line before blockquotes (default: enabled)
Lists
[extensions]
fancy-lists = true
startnum = true
example-lists = true
task-lists = true
definition-lists = truefancy-lists- Roman numerals, letters, and fancy list markers (default: enabled)
startnum- Start ordered lists at arbitrary numbers (default: enabled)
example-lists-
Example lists with
(@)markers (default: enabled) task-lists-
GitHub-style task lists
- [ ]and- [x](default: enabled) definition-lists- Term/definition syntax (default: enabled)
Code Blocks
[extensions]
backtick-code-blocks = true
fenced-code-blocks = true
fenced-code-attributes = true
inline-code-attributes = truebacktick-code-blocks- Fenced code blocks with ``` fences (default: enabled)
fenced-code-blocks-
Fenced code blocks with
~~~fences (default: enabled) fenced-code-attributes-
Attributes on fenced code blocks
{.language #id}(default: enabled) inline-code-attributes-
Attributes on inline code
`code`{.class}(default: enabled)
Tables
[extensions]
simple-tables = true
multiline-tables = true
grid-tables = true
pipe-tables = true
table-captions = truesimple-tables- Simple table syntax (default: enabled)
multiline-tables- Multiline cells (default: enabled)
grid-tables- Grid-style tables (default: enabled)
pipe-tables-
GitHub-style
|tables (default: enabled) table-captions- Table captions (default: enabled)
Divs
[extensions]
fenced-divs = true
native-divs = truefenced-divs-
Fenced divs
::: {.class}(default: enabled) native-divs-
HTML
<div>elements (default: enabled)
Other Blocks
[extensions]
line-blocks = trueline-blocks-
Line blocks for poetry with
|prefix (default: enabled)
Inline Extensions
Emphasis
[extensions]
intraword-underscores = true
strikeout = true
superscript = true
subscript = trueintraword-underscores-
Don’t trigger emphasis in
snake_case(default: enabled) strikeout-
Strikethrough
~~text~~(default: enabled) superscript-
Superscript
^super^(default: enabled) subscript-
Subscript
~sub~(default: enabled)
Links
[extensions]
inline-links = true
reference-links = true
shortcut-reference-links = true
link-attributes = true
autolinks = true
autolink-bare-uris = falseinline-links-
Inline links
[text](url)(default: enabled) reference-links-
Reference links
[text][ref](default: enabled) shortcut-reference-links-
Shortcut reference links
[ref](default: enabled) link-attributes-
Attributes on links
[text](url){.class}(default: enabled) autolinks-
Automatic links
<http://example.com>(default: enabled) autolink-bare-uris- Bare URLs become links (default: disabled, non-default extension)
Images
[extensions]
inline-images = true
implicit-figures = trueinline-images-
Inline images
(default: enabled) implicit-figures- Single image becomes figure (default: enabled)
Math
[extensions]
tex-math-dollars = true
tex-math-gfm = false
tex-math-single-backslash = false
tex-math-double-backslash = falsetex-math-dollars-
Dollar-delimited math
$x$and$$equation$$(default: enabled) tex-math-gfm-
GFM math: inline
$…$and fenced``` mathblocks (default: disabled, enabled for GFM flavor) tex-math-single-backslash-
Single backslash math
\(...\)and\[...\](default: disabled, enabled for RMarkdown) tex-math-double-backslash-
Double backslash math
\\(...\\)and\\[...\\](default: disabled)
Footnotes
[extensions]
inline-footnotes = true
footnotes = trueinline-footnotes-
Inline footnotes
^[text](default: enabled) footnotes-
Reference footnotes
[^1]and[^1]: content(default: enabled)
Citations
[extensions]
citations = truecitations-
Citation syntax
[@cite](default: enabled)
Spans
[extensions]
bracketed-spans = true
native-spans = truebracketed-spans-
Bracketed spans
[text]{.class}(default: enabled) native-spans-
HTML
<span>elements (default: enabled)
Metadata Extensions
[extensions]
yaml-metadata-block = true
pandoc-title-block = trueyaml-metadata-block-
YAML frontmatter with
---delimiters (default: enabled) pandoc-title-block-
Pandoc title block
% Title,% Author,% Date(default: enabled)
Raw Content Extensions
[extensions]
raw-html = true
markdown-in-html-blocks = false
raw-tex = true
raw-attribute = trueraw-html- HTML blocks and inline HTML (default: enabled)
markdown-in-html-blocks- Markdown inside HTML blocks (default: disabled)
raw-tex- LaTeX commands and environments (default: enabled)
raw-attribute-
Generic raw blocks with
{=format}syntax (default: enabled)
Special Character Extensions
[extensions]
all-symbols-escapable = true
escaped-line-breaks = true
hard-line-breaks = false
emoji = false
mark = falseall-symbols-escapable- Backslash escapes any symbol (default: enabled)
escaped-line-breaks- Backslash at line end creates hard line break (default: enabled)
hard-line-breaks- Newline creates hard line break (default: disabled, non-default extension)
emoji-
Emoji syntax
:emoji:(default: disabled, non-default extension) mark-
Highlighted text
==highlighted==(default: disabled, non-default extension)
Quarto-Specific Extensions
[extensions]
quarto-callouts = true
quarto-crossrefs = true
quarto-shortcodes = truequarto-callouts-
Quarto callout blocks
.callout-note,.callout-warning, etc. (default: disabled, enabled for Quarto flavor) quarto-crossrefs-
Quarto cross-references
@fig-id,@tbl-id(default: disabled, enabled for Quarto flavor) quarto-shortcodes-
Quarto shortcodes
{{< name args >}}(default: disabled, enabled for Quarto flavor)
Bookdown Extensions
[extensions]
bookdown-references = truebookdown-references-
Bookdown references
\@ref(label)and(\#label)(default: disabled, enabled for RMarkdown flavor)
External Code Formatters
Panache can invoke external formatters for code blocks. Formatters are opt-in—you choose which languages to format.
YAML is special: Panache has built-in YAML formatting for frontmatter and hashpipe chunk options. External YAML formatters apply only to fenced code blocks with YAML language. The YAML parser/formatter path is implemented in Panache-owned in-repo components.
Basic Usage
Map languages to formatters using built-in presets:
[formatters]
r = "air"
python = "ruff"
javascript = "prettier"
typescript = "prettier"Available presets:
| Language | Preset | Command | Type |
|---|---|---|---|
| R | air |
air format {} |
File-based |
| R | styler |
Rscript -e "styler::style_file('{}')" |
File-based |
| Python | ruff |
ruff format |
Stdin |
| Python | black |
black - |
Stdin |
| YAML | yamlfmt |
yamlfmt - |
Stdin |
| YAML | prettier |
prettier --parser=yaml |
Stdin |
| TOML | taplo |
taplo format - |
Stdin |
| Shell | shfmt |
shfmt - |
Stdin |
| C/C++ | clang-format |
clang-format - |
Stdin |
Sequential Formatting
Run multiple formatters in order by using an array:
[formatters]
python = ["isort", "black"]Output from the first formatter is piped to the second.
Custom Formatters
Define custom formatter configurations with the [formatters.NAME] syntax:
[formatters]
python = ["isort", "black"]
javascript = "prettier"
[formatters.prettier]
cmd = "prettier"
args = ["--parser=babel", "--print-width=100"]
stdin = true
[formatters.isort]
cmd = "isort"
args = ["-"]
stdin = trueFormatter definition fields:
cmd- Command to execute (required for custom formatters)
args- Command-line arguments (optional, defaults to empty list)
stdin-
Use stdin/stdout mode (default:
true) or file-based mode (false)
Preset Inheritance
When a [formatters.NAME] section matches a built-in preset name, unspecified fields are automatically inherited from the preset. This allows partial overrides:
[formatters]
r = "air"
[formatters.air]
args = ["format", "--preset=tidyverse"]In this example, cmd and stdin are inherited from the built-in air preset, while args is customized.
How it works:
- If the formatter name matches a built-in preset (
air,black,ruff, etc.), that preset’s defaults are used as a base - Any fields you specify (
cmd,args,stdin) override the preset defaults - Unspecified fields keep the preset values
Examples:
Override only args (inherits cmd = "air", stdin = false):
[formatters.air]
args = ["format", "--custom-flag", "{}"]Override only cmd (inherits default args and stdin):
[formatters.ruff]
cmd = "ruff-custom"Override everything (complete replacement):
[formatters.black]
cmd = "my-black"
args = ["--fast"]
stdin = falseIncremental Argument Modification
Instead of completely overriding args, you can append and prepend arguments to the preset’s base args using append-args and prepend-args:1
[formatters]
r = "air"
[formatters.air]
append-args = ["-i", "2"]This adds ["-i", "2"] after the preset’s base args ["format", "{}"], resulting in final args ["format", "{}", "-i", "2"].
Available modifiers:
prepend-args- Arguments added before base args
append-args- Arguments added after base args
Multiple modifiers example:
[formatters.air]
prepend-args = ["--verbose"]
append-args = ["-i", "2", "--check"]This produces final args: ["--verbose", "format", "{}", "-i", "2", "--check"].
With explicit args (no preset):
[formatters.shfmt]
cmd = "shfmt"
args = ["-filename", "$FILENAME"]
append-args = ["-i", "2", "-bn"]Final args: ["-filename", "$FILENAME", "-i", "2", "-bn"].
File-Based Formatters
For formatters that modify files in place:
[formatters]
r = "air-file"
[formatters.air-file]
cmd = "air"
args = ["format", "{}"]
stdin = falseThe {} placeholder controls where the file path is inserted. If omitted, it’s appended at the end.
Behavior
- Language matching
-
Code block language (e.g.,
```python) is matched to formatter key (case-insensitive) - Parallel execution
- All formatters run concurrently for speed
- Sequential chains
- Multiple formatters per language run in order
- Error handling
- Failed formatters preserve original code with a warning
- Timeout
- 30 seconds per formatter (not per chain)
- Config files
-
Formatters respect their own config files (
.prettierrc,pyproject.toml, etc.)
External Code Linters
Panache can invoke external linters for code blocks. Linters are opt-in—you choose which languages to lint.
Quick Start
Enable linters for specific languages:
[linters]
r = "jarl"Available linters:
| Language | Linter | Command | Notes |
|---|---|---|---|
| R | jarl |
jarl |
R linter with JSON diagnostics |
How It Works
External linters analyze code blocks within your document:
- Collection - Gathers all code blocks of the configured language
- Concatenation - Combines blocks with blank-line preservation to maintain original line numbers
- Analysis - Runs the external linter on the concatenated code
- Mapping - Maps diagnostics back to exact line/column positions in your document
This approach handles stateful code correctly. For example, if an R variable is defined in one code block and used in another, the linter sees both blocks together and won’t report false “undefined variable” errors.
Where Linters Run
- CLI
-
panache lintshows external linter diagnostics - LSP
- Diagnostics appear inline in your editor as you type
Behavior
- Language matching
-
Code block language (e.g.,
```{r}) is matched to linter key (case-insensitive) - Error handling
- Missing linters are gracefully ignored with a warning
- Timeout
- 30 seconds per linter invocation
- Line-accurate
- Diagnostics report exact line/column locations
- Auto-fixes
- Currently disabled due to byte offset mapping complexity (diagnostics work perfectly)
Example
Enable R linting with jarl while also formatting with air:
[linters]
r = "jarl"
[formatters]
r = "air"Example Configuration
Complete example with some common options:
flavor = "quarto"
line-width = 80
[format]
wrap = "reflow"
blank-lines = "collapse"
math-delimiter-style = "preserve"
math-indent = 0
[extensions]
hard-line-breaks = false
citations = true
task-lists = true
emoji = false
[formatters]
r = "air"
python = "ruff"Footnotes
The idea of
append-argsandprepend-argsis taken from the conform.nvim Neovim plugin.↩︎