---
title: "Comparison with Other Tools"
description: >
  This page compares Panache to other tools that overlap with parts of its
  scope -- formatting, linting, and language-server features for Markdown,
  Pandoc, and Quarto. The goal is to clarify what each tool is built for, so
  you can pick the right one (or combine them) for your project.
---

## At a Glance

Panache aims to cover the formatter, linter, and language-server roles for
Pandoc-flavoured Markdown from a single, lossless concrete syntax tree. Most
other tools in this space focus on one of those roles, and most do not parse
Pandoc/Quarto-specific constructs (fenced divs, bracketed spans, block
attributes, raw blocks, definition lists, line blocks, table captions, citation
keys, math environments).

The tables below cover the same set of programs as the
[Performance](performance.qmd) page, plus marksman -- a Markdown language server
that does not appear in the benchmarks but is the closest neighbour to Panache's
LSP role.

### Roles

  | Tool              | Formatter | Linter | LSP | Implementation |
  | ----------------- | :-------- | :----- | :-- | -------------- |
  | Panache           | ●         | ●      | ●   | Rust           |
  | mado              | ○         | ●      | ○   | Rust           |
  | markdownlint      | ○         | ●      | ○   | Ruby           |
  | markdownlint-cli2 | ◐[^1]     | ●      | ○   | JavaScript     |
  | marksman          | ○         | ◐[^2]  | ●   | F# / .NET      |
  | mdformat          | ●         | ○      | ○   | Python         |
  | Pandoc            | ◐[^3]     | ○      | ○   | Haskell        |
  | Prettier          | ●         | ○      | ○   | JavaScript     |
  | rumdl             | ◐[^4]     | ●      | ●   | Rust           |

  : Tooling roles covered by each project. Marks: ● full, ◐ partial, ○ none.

[^1]: markdownlint-cli2's `--fix` applies whichever fixes the enabled rules
    produce; there is no separate reflow pass, so output depends on which rules
    are active.

[^2]: marksman emits diagnostics for broken wikilinks, duplicate headings, and a
    handful of structural issues, but it is not a general style linter.

[^3]: Pandoc is a document converter; `pandoc -f markdown -t markdown`
    round-trips through Pandoc's AST, which normalises but is not designed as a
    formatter and is not in general lossless.

[^4]: `rumdl fmt` reuses the lint engine to apply auto-fixes; it is not a
    separate reflow pass, and the output depends on rule configuration.

### Markdown Flavors

Markdown is not one language but a family of dialects, and the differences
matter: a "format" pass that does not understand a construct will, at best,
preserve it verbatim and, at worst, rewrite it into something the renderer no
longer accepts.

::: {.column-page}
  | Tool              | CommonMark | GFM   | Pandoc | Quarto | R Markdown | MultiMarkdown | MDX |
  | ----------------- | :--------- | :---- | :----- | :----- | :--------- | :------------ | :-- |
  | Panache           | ●          | ●     | ●      | ●      | ●          | ●             | ○   |
  | mado              | ●          | ●     | ○      | ○      | ○          | ○             | ○   |
  | markdownlint      | ◐[^6]      | ◐[^6] | ○      | ○      | ○          | ○             | ○   |
  | markdownlint-cli2 | ●          | ●     | ○      | ○      | ○          | ○             | ○   |
  | marksman          | ●          | ●     | ◐[^7]  | ○      | ○          | ○             | ○   |
  | mdformat          | ●          | ◐[^8] | ○      | ○      | ○          | ○             | ○   |
  | Pandoc            | ●          | ●     | ●      | ◐[^9]  | ◐[^9]      | ◐[^10]        | ○   |
  | Prettier          | ●          | ●     | ○      | ○      | ○          | ○             | ●   |
  | rumdl             | ●          | ●     | ○      | ◐[^12] | ◐[^12]     | ○             | ○   |

  : Markdown dialect coverage across the tools.
:::

[^6]: markdownlint (the Ruby `mdl` gem) targets generic Markdown style rather
    than a specific spec; rules apply equally to CommonMark and GFM input but
    the tool does not parse to either spec.

[^7]: marksman recognises YAML frontmatter and code fences but does not parse
    Pandoc-specific block constructs (fenced divs, bracketed spans, attribute
    syntax, definition lists, table captions, etc.).

[^8]: mdformat is CommonMark-only by default; GFM, frontmatter, and similar
    constructs require loading the matching plugin (`mdformat-gfm`,
    `mdformat-frontmatter`, etc.).

[^9]: Pandoc reads `.qmd`/`.Rmd` as Markdown but does not interpret Quarto or
    knitr executable code blocks, shortcodes, or cross-reference syntax; those
    round-trip as plain text.

[^10]: Pandoc reads `markdown_mmd` but its writer emits its own dialect;
    round-tripping a MultiMarkdown document does not preserve MMD-specific
    syntax.

[^12]: rumdl will lint `.qmd` and `.Rmd` files and skip YAML frontmatter, but it
    does not parse Quarto/R Markdown executable code blocks, shortcodes, or
    cross-reference syntax -- those are treated as plain Markdown.

For raw speed numbers, see [Performance](performance.qmd). This page focuses on
*scope* and *behaviour* differences, not benchmarks.

## mado

[mado](https://github.com/akiomik/mado) is a Rust-native Markdown linter
inspired by the `markdownlint` rule numbering, built on
[comrak](https://github.com/kivikakk/comrak) for parsing.

**What it does well**

- Native compilation gives it fast startup and per-file throughput.
- Implements a sizeable subset of the `markdownlint` rule numbers;
  `mado   check` reports violations.
- Configuration via `mado.toml`.

**Where it differs from panache**

- *Linter only.* mado has no formatter mode and no `--fix`; it reports issues
  but does not modify files.
- *No Pandoc/Quarto syntax model.* Pandoc-only constructs are treated as plain
  Markdown.
- *No language server.*
- *Subset of rules.* A number of `markdownlint`-cli2 rule numbers are not yet
  implemented, and several of the implemented rules are marked unstable
  upstream.

## markdownlint (Ruby)

[markdownlint](https://github.com/markdownlint/markdownlint) -- installed as the
`mdl` gem -- is the original Ruby Markdown style checker by Mark Harrison and
the ancestor of the `markdownlint` rule numbering used elsewhere in this family.

**What it does well**

- Long-established rule catalog covering heading style, list indentation, line
  length, trailing whitespace, and similar style concerns.
- Style files configure which rules apply.

**Where it differs from panache**

- *Linter only.* No formatter or `--fix` mode; `mdl` reports violations and
  leaves files unchanged.
- *No specific Markdown flavor target.* Rules apply to generic Markdown;
  Pandoc/Quarto extensions are not modelled.
- *No language server.*
- *Ruby runtime.* Per-invocation startup is dominated by the Ruby interpreter.

## markdownlint-cli2

[markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) is a
Node.js CLI wrapping David Anson's
[`markdownlint`](https://github.com/DavidAnson/markdownlint) library. The
library is the engine behind the `vscode-markdownlint` extension and the most
widely deployed implementation of the `MD0xx` rule family.

**What it does well**

- Large, actively maintained rule catalog with the canonical `MD0xx` rule
  numbering.
- Configuration via `.markdownlint-cli2.{jsonc,yaml,cjs,mjs}` or
  `.markdownlint.{json,jsonc,yaml,yml,cjs,mjs}`, with per-directory overrides.
- `--fix` applies the library's rule-level auto-fixes.
- Built on `micromark`, with extensions for footnotes, math, and directives on
  top of CommonMark + GFM.

**Where it differs from panache**

- *Linter with rule-level fixes, not a reflowing formatter.* `--fix` applies
  what each rule's fix routine produces; there is no separate formatting pass
  and no line-width reflow.
- *No Pandoc/Quarto syntax model.* Fenced divs, bracketed spans, block
  attributes, and similar constructs are not parsed.
- *No language server.* The companion VS Code extension embeds the library
  directly rather than speaking LSP.
- *Node.js runtime.* Same per-invocation startup characteristics as Prettier.

## Marksman

[Marksman](https://github.com/artempyanykh/marksman) is a language server for
Markdown, with first-class support for wikilink-style cross-file references. It
is a good fit for note-taking workflows (Obsidian/Foam/Zettelkasten-style
vaults) where the document graph matters as much as any individual file.

**What it does well**

- Workspace-wide indexing: jump-to-definition and find-references across
  headings, wikilinks, and footnotes.
- Completion for headings, wikilinks, and reference links.
- Diagnostics for broken or ambiguous links and duplicate headings.
- Document and workspace symbol providers, suitable for outline views.

**Where it differs from panache**

- *Not a formatter.* marksman has no `format` command and no formatting code
  actions; you still need a separate tool to normalise whitespace, wrap
  paragraphs, or align tables.
- *Not a style linter.* It catches structural problems (broken refs, duplicate
  anchors), but it will not warn about, say, skipped heading levels or mixed
  list markers.
- *Markdown-flavoured, not Pandoc-flavoured.* Pandoc-only constructs such as
  `::: callout-note` fenced divs, `[text]{.class}` bracketed spans,
  `{#id .class}` block attributes, definition lists, line blocks, and
  `Table: caption` syntax are treated as plain text. Quarto shortcodes and
  cross-references behave the same way.

## mdformat

[mdformat](https://github.com/hukkin/mdformat) is an opinionated CommonMark
formatter written in Python and built on `markdown-it-py`. It produces a single
canonical form for any CommonMark input.

**What it does well**

- Strict CommonMark compliance and a small surface of options.
- Plugin system for non-core syntax: `mdformat-gfm`, `mdformat-tables`,
  `mdformat-frontmatter`, etc.
- Validates output by re-rendering and comparing, catching cases where
  formatting would change rendered HTML (`--no-validate` to disable).

**Where it differs from panache**

- *Formatter only.* No linter, no language server.
- *CommonMark-first, plugin-extended.* GFM tables, frontmatter, and similar
  constructs require plugins; Pandoc and Quarto syntax is not part of the
  supported set.
- *Escape-on-unknown-syntax.* Constructs outside CommonMark (or whatever plugins
  are loaded) are typically backslash-escaped to keep rendered output stable,
  which can mangle Pandoc-flavoured input.
- *Python runtime.* Adds interpreter startup overhead per invocation.

## Pandoc

[Pandoc](https://pandoc.org) is a document converter that translates between
dozens of markup formats. It is not a formatter or linter in the dedicated
sense, but `pandoc -f markdown -t markdown` is sometimes used as a normalisation
step, which is why it appears on the [Performance](performance.qmd) page.

**What it does well**

- Reads many Markdown dialects directly: `markdown` (Pandoc), `commonmark`,
  `commonmark_x`, `gfm`, `markdown_mmd`, `markdown_phpextra`, `markdown_strict`.
- Comprehensive coverage of Pandoc-specific syntax -- fenced divs, bracketed
  spans, block attributes, definition lists, line blocks, table captions, math,
  citations.
- `--wrap=auto|none|preserve` and `--columns` control line wrapping in the
  Markdown writer.

**Where it differs from panache**

- *Converter, not a formatter.* The Markdown writer normalises output, but this
  is a side effect of round-tripping through Pandoc's AST. Pandoc's manual notes
  that round-tripping is not in general lossless: comments, raw HTML, attribute
  placement, and similar details can move or disappear.
- *No idempotency guarantee.* `format(format(x)) == format(x)` is not part of
  Pandoc's design contract; whether it holds depends on the input.
- *No linter.* No rule-based diagnostics or style enforcement.
- *No language server.*
- *No in-place mutation.* The CLI writes to stdout (or `-o`), not into the
  source file.

## Prettier

[Prettier](https://prettier.io) is a polyglot opinionated formatter for many
languages, with first-party Markdown support. It is the most widely deployed
Markdown formatter in the JavaScript ecosystem.

**What it does well**

- Mature, opinionated formatting with very few knobs to turn.
- Strong CommonMark and GFM support, including tables, task lists, and
  strikethrough.
- Frontmatter (YAML/TOML) is recognised and preserved.
- Wide ecosystem of editor integrations and pre-commit hooks.

**Where it differs from panache**

- *Markdown-only formatter.* Prettier has no linter and no language server
  features. For diagnostics, navigation, or code actions you need other tools.
- *No Pandoc/Quarto extensions.* Fenced divs, bracketed spans, block attributes,
  raw blocks, definition lists, line blocks, math blocks beyond inline `$...$`,
  and Pandoc-style table captions are not part of Prettier's Markdown grammar.
  The default behaviour is to either pass them through verbatim or to rewrap
  them in ways that can change semantics for Pandoc.
- *Node.js startup overhead.* For one-off CLI invocations, the Node.js runtime
  dominates the wall-clock time. See [Performance](performance.qmd) for numbers;
  the gap shrinks for long-running processes (editor integrations, the Prettier
  daemon) but may not close entirely.
- *Opinionated wrapping only.* Prettier has a single `proseWrap` setting
  (`always` / `never` / `preserve`); panache adds `sentence`-wrapping for
  diff-friendly prose, plus configurable line width per file or directory.

## rumdl

[rumdl](https://github.com/rvben/rumdl) is a Rust-native Markdown linter with a
`rumdl fmt` command that reuses the lint engine to apply auto-fixes and a
`rumdl server` subcommand that exposes diagnostics and fixes over LSP. It is
heavily inspired by `markdownlint` and ports many of its rules.

**What it does well**

- Large catalogue of style rules (heading style, list indentation, line length,
  trailing whitespace, blank lines, etc.) compatible with the `markdownlint`
  rule numbering.
- Native compilation gives it fast startup and per-file throughput.
- Configurable per-rule severity and per-file disable comments.
- Ships an LSP server (`rumdl server`) for editor integration.

**Where it differs from panache**

- *Lint-engine formatter, not a reflowing formatter.* `rumdl fmt` shares fix
  logic with `rumdl check --fix` (the documented difference is exit-code
  semantics intended for editor integration). Either way, every "format" run
  goes through the lint engine and applies whichever fixes the enabled rules
  produce; rules that do not produce fixes leave their target untouched.
- *No Pandoc/Quarto syntax model.* rumdl operates on CommonMark + GFM
  extensions. Fenced divs, bracketed spans, block attributes, raw blocks, Pandoc
  tables with captions, and similar constructs are either rewritten as plain
  Markdown or left untouched in ways that can break downstream Pandoc rendering.
- *Idempotency is rule-dependent.* Because formatting is a side effect of fix
  application, idempotency depends on the rule set; panache aims for
  `format(format(x)) == format(x)` regardless of configuration.

## Performance

For head-to-head timing, see [Performance](performance.qmd).
