panache 1.0.0

A formatter for Pandoc, Quarto, and RMarkdown documents
docs.rs failed to build panache-1.0.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

panache

Build and Test

A CLI formatter for Quarto (.qmd), Pandoc, and Markdown files.

Work in Progress

This project is in very early development. Expect bugs, missing features, and breaking changes.

Installation

From Source

cargo install --path .

Pre-built Binaries

Download pre-built binaries from the releases page. Available for:

  • Linux (x86_64, ARM64)
  • macOS (Intel, Apple Silicon)
  • Windows (x86_64)

Each archive includes the binary, man pages, and shell completions.

Linux Packages

For Debian/Ubuntu systems:

# Download the .deb from releases
sudo dpkg -i panache_*.deb

For Fedora/RHEL/openSUSE systems:

# Download the .rpm from releases
sudo rpm -i panache-*.rpm

Packages include:

  • Binary at /usr/bin/panache
  • Man pages for all subcommands
  • Shell completions (bash, fish, zsh)

Usage

CLI Formatting

# Format a file and output to stdout
panache format document.qmd

# Format a file in place
panache format --write document.qmd

# Check if a file is formatted
panache format --check document.qmd

# Format from stdin
cat document.qmd | panache format

# Parse and inspect the AST (for debugging)
panache parse document.qmd

Language Server (LSP)

panache includes a built-in Language Server Protocol implementation for editor integration.

Start the server:

panache lsp

Editor Configuration:

The LSP communicates over stdin/stdout and provides document formatting capabilities.

-- Add to your LSP config
local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')

-- Define panache LSP
if not configs.panache then
  configs.panache = {
    default_config = {
      cmd = { 'panache', 'lsp' },
      filetypes = { 'quarto', 'markdown', 'rmarkdown' },
      root_dir = lspconfig.util.root_pattern('.panache.toml', 'panache.toml', '.git'),
      settings = {},
    },
  }
end

-- Enable it
lspconfig.panache.setup{}

Format on save:

vim.api.nvim_create_autocmd('BufWritePre', {
  pattern = { '*.qmd', '*.md', '*.rmd' },
  callback = function()
    vim.lsp.buf.format({ async = false })
  end,
})

Install a generic LSP client extension like vscode-languageserver-node, then configure in settings.json:

{
  "languageServerExample.server": {
    "command": "panache",
    "args": ["lsp"],
    "filetypes": ["quarto", "markdown", "rmarkdown"]
  },
  "editor.formatOnSave": true
}

Or use the Custom LSP extension.

Add to ~/.config/helix/languages.toml:

[[language]]
name = "markdown"
language-servers = ["panache-lsp"]
auto-format = true

[language-server.panache-lsp]
command = "panache"
args = ["lsp"]

Configuration: The LSP automatically discovers .panache.toml from your workspace root.

Configuration

panache looks for a configuration in:

  1. .panache.toml or panache.toml in current directory or parent directories
  2. ~/.config/panache/config.toml

Example config

# Markdown flavor and line width
flavor = "quarto"
line_width = 80
line-ending = "auto"
wrap = "reflow"

# External code formatters (new!)
[formatters.r]
cmd = "styler"
args = ["--scope=spaces"]

[formatters.python]
cmd = "black"
args = ["-", "--line-length=88"]

[formatters.rust]
cmd = "rustfmt"
args = []

See .panache.toml.example for a complete configuration reference.

External Code Formatters

panache can invoke external formatters for code blocks:

  • Formatters run in true parallel: External formatters execute simultaneously with panache's markdown formatting for maximum performance
  • Each formatter must accept code via stdin and output to stdout
  • Formatters respect their own config files (.prettierrc, pyproject.toml, etc.)
  • On error, original code is preserved with a warning logged
  • 30-second timeout per formatter invocation

Performance: If your document has 3 code blocks and each formatter takes 1 second, all 3 will complete in ~1 second (not 3 seconds sequentially).

Example: Format R code with styler and Python with black:

[formatters.r]
cmd = "styler"
args = ["--scope=spaces"]

[formatters.python]
cmd = "black"
args = ["-"]

Supported formatters (any CLI tool that reads stdin/writes stdout):

  • R: styler, formatR
  • Python: black, ruff format, autopep8
  • Rust: rustfmt
  • JavaScript/TypeScript: prettier, deno fmt
  • JSON: jq
  • And any other stdin/stdout formatter!

Motivation

I wanted a formatter that understands Quarto and Pandoc syntax. I have tried to use Prettier as well as mdformat, but both fail to handle some of the particular syntax used in Quarto documents, such as fenced divs and some of the table syntax.

Design Goals

  • Support Quarto, Pandoc, and Markdown syntax
  • Be fast
  • Be configurable, but have sane defaults (that most people can agree on)
  • Format math
  • ✅ Hook into external formatters for code blocks (e.g. styler for R, black for Python)