Configuration

Configuration Files

panache searches for configuration in this order:

  1. Explicit path: --config <path> (errors if invalid)
  2. Project config: .panache.toml or panache.toml in current or parent directories
  3. User config: ~/.config/panache/config.toml (XDG)
  4. Built-in defaults: 80 char width, auto line endings, reflow wrap

Basic Options

Flavor

Choose the Markdown flavor, which determines default extension settings:

flavor = "quarto"

Available flavors:

  • pandoc - Standard Pandoc Markdown (default)
  • quarto - Quarto-flavored Markdown (Pandoc + Quarto extensions)
  • rmarkdown - R Markdown (Pandoc + R-specific extensions)
  • gfm - GitHub Flavored Markdown
  • commonmark - CommonMark (minimal extensions)

Line Width

Set the maximum line width for wrapping:

line_width = 80  # Default: 80

Wrapping Mode

Control how text is wrapped:

wrap = "reflow"  # Options: "reflow", "preserve"
  • reflow (default): Reformat paragraphs to fit within line width
  • preserve: Keep existing line breaks

Blank Lines

Control blank line handling:

blank_lines = "collapse"  # Options: "collapse", "preserve"
  • collapse (default): Collapse multiple blank lines into one
  • preserve: Keep all existing blank lines

Extensions

panache supports 60+ Pandoc extensions. Each flavor has sensible defaults, but you can override any extension:

flavor = "quarto"

[extensions]
# Override flavor defaults
hard_line_breaks = false
citations = true
task_lists = true

Block-Level Extensions

Headings

[extensions]
blank_before_header = true       # Require blank line before headers
header_attributes = true         # {#id .class key=value} syntax

Block Quotes

[extensions]
blank_before_blockquote = true   # Require blank line before blockquotes

Lists

[extensions]
fancy_lists = true               # Roman numerals, letters
startnum = true                  # Start at arbitrary numbers
example_lists = true             # (@) example markers
task_lists = true                # - [ ] and - [x]
definition_lists = true          # Term/definition syntax

Code Blocks

[extensions]
backtick_code_blocks = true      # ``` fences
fenced_code_blocks = true        # ~~~ fences
fenced_code_attributes = true    # {.language #id}
inline_code_attributes = true    # `code`{.class}

Tables

[extensions]
simple_tables = true             # Simple table syntax
multiline_tables = true          # Multiline cells
grid_tables = true               # Grid-style tables
pipe_tables = true               # GitHub-style | tables
table_captions = true            # Table captions

Divs

[extensions]
fenced_divs = true               # ::: {.class} divs
native_divs = true               # HTML <div> elements

Inline Extensions

Emphasis

[extensions]
intraword_underscores = true     # Don't trigger emphasis in snake_case
strikeout = true                 # ~~strikethrough~~
superscript = true               # ^super^
subscript = true                 # ~sub~

Images

[extensions]
inline_images = true             # ![alt](url)
implicit_figures = true          # Single image → figure

Math

[extensions]
tex_math_dollars = true          # $x$ and $$equation$$
tex_math_single_backslash = false  # \(...\) and \[...\] (RMarkdown)
tex_math_double_backslash = false  # \\(...\\) and \\[...\\]

Footnotes

[extensions]
inline_footnotes = true          # ^[text]
footnotes = true                 # [^1] references

Citations

[extensions]
citations = true                 # [@cite] syntax

Spans

[extensions]
bracketed_spans = true           # [text]{.class}
native_spans = true              # HTML <span> elements

Metadata Extensions

[extensions]
yaml_metadata_block = true       # --- YAML frontmatter
pandoc_title_block = true        # % Title / % Author / % Date

Raw Content Extensions

[extensions]
raw_html = true                  # HTML blocks and inline
markdown_in_html_blocks = true   # Markdown inside HTML
raw_tex = true                   # LaTeX commands
raw_attribute = true             # {=format} raw blocks

Special Character Extensions

[extensions]
all_symbols_escapable = true     # Backslash escapes any symbol
escaped_line_breaks = true       # Backslash at line end = <br>
hard_line_breaks = false         # Newline = <br> (non-default)
emoji = false                    # :emoji: syntax (non-default)
mark = false                     # ==highlighted== (non-default)

Quarto-Specific Extensions

[extensions]
quarto_callouts = true           # .callout-note, etc.
quarto_crossrefs = true          # @fig-id, @tbl-id

External Code Formatters

panache can invoke external formatters for code blocks—but they’re opt-in. You choose what gets formatted.

Quick Start with Presets

Enable formatters using convenient presets:

# Enable R formatting with air (fast)
[formatters.r]
preset = "air"

# Enable Python formatting with ruff (fast)
[formatters.python]
preset = "ruff"

Available presets:

Language Preset Command Notes
r air air format {} Fast, modern
r styler Rscript -e "styler::..." Requires styler R package
python ruff ruff format Fast, modern
python black black - Popular alternative

Formatters execute in parallel with panache’s markdown formatting. If the tool isn’t installed, code is preserved unchanged (no errors).

Disabling formatters:

[formatters.r]
enabled = false  # Disable R formatting

[formatters.python]
enabled = false  # Disable Python formatting

Custom Configuration

For full control, define formatters manually. External formatters support two modes: stdin/stdout or file-based.

Stdin Mode (Default)

For formatters that read from stdin and write to stdout:

# Custom Python formatter (overrides built-in ruff default)
[formatters.python]
cmd = "black"
args = ["-", "--line-length=88"]
stdin = true  # Default

# Add formatters for new languages
[formatters.rust]
cmd = "rustfmt"
stdin = true

[formatters.javascript]
cmd = "prettier"
args = ["--parser=babel"]
stdin = true

Note: When you define a [formatters.X] section with cmd, it completely replaces the built-in default for that language (if any). Other languages keep their defaults.

File-Based Mode

For formatters that modify files in place:

[formatters.r]
cmd = "air"
args = ["format"]
stdin = false  # File-based mode

[formatters.go]
cmd = "gofmt"
args = ["-w"]  # -w writes to file
stdin = false

File path placeholder: Use {} in args to control where the file path is inserted. If omitted, it’s appended at the end.

[formatters.r]
cmd = "air"
args = ["format", "{}"]  # Explicit: air format /tmp/file.tmp
stdin = false

[formatters.custom]
cmd = "myformatter"
args = ["--input", "{}", "--output", "{}"]  # Multiple uses
stdin = false

Behavior

  • Language matching: Code block language (e.g., ```python) is matched to formatter key (case-insensitive)
  • Parallel execution: All formatters run concurrently for speed
  • Error handling: Failed formatters preserve original code with a warning
  • Timeout: 30 seconds per formatter invocation
  • Optional: Set enabled = false to disable a formatter
  • Formatters respect their own config files (.prettierrc, pyproject.toml, etc.)

Supported Formatters

Any CLI tool that reads stdin and writes stdout works:

  • R: styler, formatR
  • Python: black, ruff format, autopep8
  • Rust: rustfmt
  • JavaScript/TypeScript: prettier, deno fmt
  • JSON: jq
  • YAML: yamlfmt
  • Go: gofmt
  • SQL: sqlformat, pg_format

Performance

Formatters run in true parallel. If you have 3 code blocks and each formatter takes 1 second, all 3 complete in ~1 second (not 3 seconds sequentially).

Example Configuration

Complete example with all common options:

# Markdown flavor and basic settings
flavor = "quarto"
line_width = 80
wrap = "reflow"
blank_lines = "collapse"

# Extension overrides
[extensions]
hard_line_breaks = false
citations = true
task_lists = true
emoji = false

# External formatters (opt-in)
# Option 1: Use presets
[formatters.r]
preset = "air"

# Option 2: Full custom configuration
[formatters.python]
cmd = "black"
args = ["-", "--line-length=88"]

# Option 3: Add new formatters for other languages
[formatters.rust]
cmd = "rustfmt"
args = []

[formatters.javascript]
cmd = "prettier"
args = ["--parser=babel", "--stdin-filepath=file.js"]

See Also