Comparison with markdownlint¶
This document provides a detailed comparison between rumdl and markdownlint, covering rule compatibility, intentional design differences, and features unique to each tool.
Quick Summary¶
rumdl offers high markdownlint compatibility with intentional differences while also adding performance improvements and newer features. All 53 markdownlint rules are implemented, but rumdl prefers predictable CommonMark-oriented behavior over bug-for-bug compatibility in a few documented areas.
Key Differences:
- Performance: rumdl is significantly faster (30-100x in many cases) thanks to Rust and intelligent caching
- Rule Coverage: All 53 markdownlint rules are implemented, with a small number of intentional behavioral differences documented below
- Unique Features: 18 additional rules (MD057, MD061-MD077), built-in LSP server, VS Code extension, 6 Markdown flavors
- Configuration: Automatic markdownlint config discovery and conversion
Rule Coverage¶
Implemented Rules¶
rumdl implements 71 rules total: all 53 markdownlint rules plus 18 unique rules.
Markdownlint-compatible rules (53): All markdownlint rules are implemented with full compatibility. See the Rules Reference for the complete list.
Note: Rule numbers MD001-MD060 have gaps (MD002, MD006, MD008, MD015-MD017 were never implemented in markdownlint). rumdl maintains these gaps for compatibility.
Rules Unique to rumdl¶
rumdl implements 18 additional rules not found in markdownlint:
| Rule | Name | Description |
|---|---|---|
| MD057 | Relative links | Validates that relative file links point to existing files |
| MD061 | Forbidden terms | Flags usage of configurable forbidden terms |
| MD062 | Link destination whitespace | No whitespace in link destinations |
| MD063 | Heading capitalization | Enforces consistent heading capitalization style |
| MD064 | No multiple consecutive spaces | Flags multiple consecutive spaces in content |
| MD065 | Blanks around horizontal rules | Horizontal rules should have surrounding blank lines |
| MD066 | Footnote validation | Validates footnote references have definitions |
| MD067 | Footnote definition order | Footnotes should appear in order of reference |
| MD068 | Empty footnote definitions | Footnote definitions should not be empty |
| MD069 | No duplicate list markers | Flags duplicate markers like - - text from copy-paste |
| MD070 | Nested code fence | Detects nested fence collisions (opt-in) |
| MD071 | Blank line after frontmatter | Frontmatter should be followed by a blank line |
| MD072 | Frontmatter key sort | Frontmatter keys should be sorted (opt-in) |
| MD073 | TOC validation | Table of Contents should match headings (opt-in) |
| MD074 | MkDocs nav validation | Validates MkDocs nav entries against the docs tree |
| MD075 | Orphaned table rows | Detects headerless pipe tables and orphaned table rows |
| MD076 | List item spacing | Enforces consistent blank lines between list items |
| MD077 | List continuation indent | Enforces indentation for list continuation content |
Opt-in rules: MD060, MD063, MD070, MD072, MD073, and MD074 are disabled by default. Enable them explicitly in your configuration.
Intentional Design Differences¶
1. CommonMark Specification Compliance¶
rumdl prioritizes CommonMark specification compliance over bug-for-bug compatibility with markdownlint's parsing.
Example - List Continuation vs Code Blocks:
1. List item
This is a continuation paragraph (4 spaces = continuation)
This is a code block (8 spaces = continuation indent + 4)
- markdownlint: May incorrectly treat 4-space indented paragraphs as code blocks
- rumdl: Follows CommonMark: 4 spaces = list continuation, 8 spaces = code block within list
- Rationale: Reduces false positives and aligns with the official Markdown spec
References:
- CommonMark List Specification
- rumdl Issue #128 - False positive fix
2. Performance Architecture¶
rumdl uses Rust and intelligent caching for significant performance gains:
- Cold start: 30-100x faster than markdownlint on large repositories
- Incremental: Only re-lints changed files (Ruff-style caching)
- Parallel processing: Multi-threaded file processing and rule execution
- Zero dependencies: Single binary, no Node.js runtime required
Benchmark: See the performance comparison in the main README, which shows detailed benchmarks on the Rust Book repository (478 markdown files).
3. Auto-fix Mode Differences¶
Both tools support auto-fixing, but with different philosophies:
markdownlint:
- Fixes issues in-place
- Requires
--fixflag
rumdl:
- Two modes:
rumdl fmt(formatter-style, exits 0) andrumdl check --fix(linter-style, exits 0 if all violations fixed, 1 if violations remain) --diffmode to preview changes- Parallel file fixing (4.8x faster on multi-file projects)
Why two modes?
fmt: Designed for editor integration (doesn't fail on unfixable issues)check --fix: Designed for CI/CD (fails if violations remain after fixing)
4. Configuration Philosophy¶
Automatic Discovery:
rumdl automatically discovers and loads both markdownlint and markdownlint-cli2 config files:
# rumdl automatically finds and uses these (in precedence order):
.markdownlint-cli2.jsonc
.markdownlint-cli2.yaml
.markdownlint-cli2.yml
.markdownlint.json
.markdownlint.yaml
markdownlint.json
For markdownlint-cli2 files, rumdl extracts rule configuration from the config: key and ignores cli2-specific keys like globs and ignores.
Conversion Tool:
Multiple Formats:
- Native:
.rumdl.toml(TOML, with JSON schema support) - Python projects:
pyproject.tomlwith[tool.rumdl]section - Markdownlint: Automatic compatibility mode
5. Editor Integration¶
rumdl includes a built-in Language Server Protocol (LSP) implementation:
Features:
- Real-time linting as you type
- Quick fixes for supported rules
- Hover documentation for rules
- Zero configuration required
6. Markdown Flavors¶
rumdl supports 6 Markdown flavors to adapt rule behavior for different documentation systems:
| Flavor | Use Case | Key Adjustments |
|---|---|---|
standard |
Default Markdown | CommonMark + GFM extensions |
gfm |
GitHub Flavored Markdown | Security-sensitive HTML, autolinks |
mkdocs |
MkDocs / Material for MkDocs | Admonitions, tabs, mkdocstrings |
mdx |
MDX (JSX in Markdown) | JSX components, ESM imports |
obsidian |
Obsidian knowledge base | Callouts, Dataview, Templater, wikilinks |
quarto |
Quarto / RMarkdown | Citations, shortcodes, executable code |
Configuration:
markdownlint does not have built-in flavor support; users must configure individual rules manually.
Configuration Compatibility¶
Markdownlint Config Auto-Detection¶
rumdl automatically discovers and loads markdownlint and markdownlint-cli2 configurations:
# .markdownlint-cli2.yaml (also automatically loaded)
config:
MD013: false
MD033:
allowed_elements: ['br', 'img']
Equivalent rumdl Configuration¶
Configuration Mapping¶
Most markdownlint options map directly to rumdl:
| markdownlint | rumdl |
|---|---|
default: true |
[global] section |
Rule by number (MD013) |
Same ([MD013]) |
Rule by name (line-length) |
Same ([line-length]) |
Disabling: "MD013": false |
disable = ["MD013"] |
Per-File Ignores¶
Both support per-file rule configuration:
# rumdl
[per-file-ignores]
"README.md" = ["MD033"] # Allow HTML in README
"docs/api/**/*.md" = ["MD013"] # Relax line length in API docs
markdownlint uses glob patterns in separate config files or inline comments.
Inline Configuration Compatibility¶
rumdl supports both rumdl and markdownlint inline comment styles:
<!-- markdownlint-disable MD013 -->
This line can be as long as needed
<!-- markdownlint-enable MD013 -->
<!-- rumdl-disable MD013 -->
Alternative syntax also supported
<!-- rumdl-enable MD013 -->
Both syntaxes work identically in rumdl for seamless migration.
CLI Differences¶
Command Structure¶
markdownlint:
rumdl:
Output Formats¶
Both support:
- Text output (colored, human-readable)
- JSON output (for tool integration)
rumdl additionally supports:
- Source line display with caret underlines (
--output-format full) - GitHub Actions annotations (
--output-format github) - GitLab, Azure, SARIF, JUnit, and Pylint formats
- Statistics summary (
--statistics) - Profiling information (
--profile)
Exit Codes¶
markdownlint-cli:
0: No violations1: Violations found2: Unable to write output3: Unable to load custom rules4: Unexpected error
rumdl:
0: Success (orrumdl fmtcompleted successfully)1: Violations found (or remain after--fix)2: Tool error
Migration Guide¶
From markdownlint to rumdl¶
-
Install rumdl:
bash uv tool install rumdl # or: cargo install rumdl # or: pip install rumdl # or: brew install rumdl (See README.md) -
Test with existing config:
bash # rumdl automatically discovers .markdownlint.json rumdl check . -
(Optional) Convert config:
bash rumdl import .markdownlint.json -
Update CI/CD:
```yaml # Before (markdownlint) - run: markdownlint '*/.md'
# After (rumdl) - run: rumdl check . ```
Known Behavioral Differences¶
These are intentional deviations where rumdl produces different results than markdownlint. They are design decisions, not bugs.
MD004 (unordered-list-style): In consistent mode, rumdl uses prevalence-based detection (most common marker wins, ties prefer dash). markdownlint uses the first marker as the standard.
MD005/MD007 (list-indent / ul-indent): rumdl uses parent-based dynamic indentation, properly handling ordered lists with variable marker widths (e.g., 1. vs 10.). markdownlint may treat
children at different indentation levels as inconsistent.
MD012 (no-multiple-blanks): rumdl uses the filtered_lines() architecture to skip frontmatter, code blocks, and flavor-specific constructs. This may produce slightly different counts near block
boundaries.
MD013 (line-length): rumdl exempts entire lines that are completely unbreakable (URLs with no spaces, long code spans). It also supports line_length = 0 to mean unlimited. markdownlint exempts
more selectively.
MD029 (ordered-list-prefix): rumdl uses CommonMark AST start values. A list starting at 11 expects items 11, 12, 13. rumdl only auto-fixes when start_value == 1 to preserve explicit numbering
intent.
If you encounter other compatibility issues, please file an issue.
Feature Comparison Table¶
| Feature | markdownlint | rumdl |
|---|---|---|
| Core Functionality | ||
| Rule count | 53 implemented | 71 (53 compatible + 18 new) |
| Auto-fix | ✅ | ✅ |
| Configuration file | ✅ JSON/YAML | ✅ TOML/JSON/JSONC/YAML/cli2 |
| Inline config | ✅ | ✅ (compatible) |
| Custom rules | ✅ (JavaScript) | ❌ |
| Markdown flavors | ❌ | ✅ 6 flavors |
| Performance | ||
| Single file | Fast | Very Fast (10-30x) |
| Large repos (100+ files) | Slow | Very Fast (30-100x) |
| Incremental mode | ❌ | ✅ (caching) |
| Parallel processing | Partial | ✅ Full |
| Developer Experience | ||
| Built-in LSP | ❌ | ✅ |
| VS Code extension | ✅ (separate) | ✅ (built-in) |
| Watch mode | Via external tools | ✅ --watch |
| Stdin/stdout | ✅ | ✅ |
| Diff preview | ❌ | ✅ --diff |
| Installation | ||
| Node.js required | ✅ | ❌ |
| Python pip | ❌ | ✅ |
| Rust cargo | ❌ | ✅ |
| Single binary | ❌ | ✅ |
| Homebrew | ✅ | ✅ |
| Output & Integration | ||
| Text format | ✅ | ✅ |
| JSON format | ✅ | ✅ |
| GitHub Actions | ✅ | ✅ Enhanced |
| Statistics | ❌ | ✅ |
| Profiling | ❌ | ✅ |
CommonMark Compliance¶
rumdl prioritizes CommonMark specification compliance to reduce false positives and align with modern Markdown standards:
| Aspect | markdownlint | rumdl |
|---|---|---|
| List continuation indent | Custom logic | CommonMark spec |
| Code blocks in lists | May misdetect | Spec-compliant (8 spaces) |
| Heading anchor generation | GitHub-flavored | Multiple styles (GitHub, GitLab, etc.) |
| Reference definitions | Basic | Full spec support |
Reporting Compatibility Issues¶
If you find a compatibility issue with markdownlint:
- Check existing issues
- Verify with both tools:
markdownlint file.mdandrumdl check file.md - File an issue with:
- Markdown sample
- Expected behavior (markdownlint output)
- Actual behavior (rumdl output)
- Versions of both tools
See Also¶
- Tool Comparison Matrix - Broad comparison of Markdown linters and formatters
- Comparison with mdformat - For users coming from the mdformat formatter