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 is fully compatible with markdownlint while offering significant performance improvements and design enhancements. All 53 markdownlint rules are implemented, making migration seamless.
Key Differences:
- Performance: rumdl is significantly faster (30-100x in many cases) thanks to Rust and intelligent caching
- Rule Coverage: 100% compatible - all 53 markdownlint rules are implemented with the same behavior
- Unique Features: 14 additional rules (MD057, MD061-MD073), built-in LSP server, VS Code extension, 6 Markdown flavors
- Configuration: Automatic markdownlint config discovery and conversion
Rule Coverage¶
Implemented Rules¶
rumdl implements 68 rules total: all 53 markdownlint rules plus 15 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 14 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) |
Opt-in rules: MD060, MD063, MD070, MD072, and MD073 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 markdownlint config files:
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 configurations:
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 | 68 (53 compatible + 15 new) |
| Auto-fix | ✅ | ✅ |
| Configuration file | ✅ JSON/YAML | ✅ TOML/JSON/YAML |
| 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