Skip to content
Merged
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Python linting, rumdl brings similar speed and developer experience improvements
It offers:

- ⚡️ **Built for speed** with Rust - significantly faster than alternatives
- 🔍 **<!-- RULE_COUNT -->76<!-- /RULE_COUNT --> lint rules** covering common Markdown issues
- 🔍 **<!-- RULE_COUNT -->77<!-- /RULE_COUNT --> lint rules** covering common Markdown issues
- 🛠️ **Automatic formatting** with `--fix` for files and stdin/stdout
- 📦 **Zero dependencies** - single binary with no runtime requirements
- 🔧 **Highly configurable** with TOML-based config files
Expand Down Expand Up @@ -526,7 +526,7 @@ The action ref (`rvben/rumdl@v0`) selects the GitHub Action version, while the o

## Rules

rumdl implements <!-- RULE_COUNT -->76<!-- /RULE_COUNT --> lint rules for Markdown files. Here are some key rule categories:
rumdl implements <!-- RULE_COUNT -->77<!-- /RULE_COUNT --> lint rules for Markdown files. Here are some key rule categories:

| Category | Description | Example Rules |
| -------------- | ---------------------------------------- | ------------------- |
Expand Down
8 changes: 4 additions & 4 deletions docs/comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ For detailed comparisons with specific tools, see:

| Tool | Type | Language | Rules | Auto-fix | Flavors | Config format | Plugins | LSP |
| --------------------- | ------------- | -------- | ----------------------------------------- | -------- | ------- | ----------------------- | ------------ | --- |
| **rumdl** | Lint + Format | Rust | <!-- RULE_COUNT -->76<!-- /RULE_COUNT --> | Yes | 9 | TOML, JSON, YAML | No | Yes |
| **rumdl** | Lint + Format | Rust | <!-- RULE_COUNT -->77<!-- /RULE_COUNT --> | Yes | 9 | TOML, JSON, YAML | No | Yes |
| **markdownlint-cli** | Lint | Node.js | 53 | Yes | No | JSON, JSONC, YAML, TOML | Yes (JS) | No |
| **markdownlint-cli2** | Lint | Node.js | 53 | Yes | No | JSONC, YAML, JS | Yes (JS) | No |
| **remark-lint** | Lint | Node.js | ~80 (via presets) | No | No | JS, JSON, YAML | Yes (JS) | No |
Expand All @@ -33,7 +33,7 @@ cover common cases. No built-in auto-fix for lint violations (remark itself can

**mado** is a Rust-based linter with 38 rules (33 stable, 5 unstable). It has no auto-fix and no plugin system.

**rumdl** implements all 53 markdownlint rules plus <!-- RULE_COUNT_ADDITIONAL -->23<!-- /RULE_COUNT_ADDITIONAL --> additional rules (<!-- RULE_COUNT -->76<!-- /RULE_COUNT --> total). It supports
**rumdl** implements all 53 markdownlint rules plus <!-- RULE_COUNT_ADDITIONAL -->24<!-- /RULE_COUNT_ADDITIONAL --> additional rules (<!-- RULE_COUNT -->77<!-- /RULE_COUNT --> total). It supports
auto-fix for most rules and includes rules not found in other tools, such as relative link validation (MD057), footnote checks (MD066-MD068), nested code fence detection (MD070), and TOC validation
(MD073).

Expand Down Expand Up @@ -136,7 +136,7 @@ Cold start benchmark on the Rust Book (478 Markdown files), measured with [hyper

**Notes:**

- **mado** is faster in cold-start benchmarks because it does less work per file: fewer rules (38 vs <!-- RULE_COUNT -->76<!-- /RULE_COUNT -->), no fix generation, and no flavor detection.
- **mado** is faster in cold-start benchmarks because it does less work per file: fewer rules (38 vs <!-- RULE_COUNT -->77<!-- /RULE_COUNT -->), no fix generation, and no flavor detection.
The gap reflects feature surface area, not implementation quality.
- **rumdl** supports result caching (`rumdl check` without `--no-cache`), which skips unchanged files on subsequent runs — typically under 50 ms, faster than mado's cold start.
- **pymarkdown** performs well for a Python tool due to its efficient scanner architecture.
Expand All @@ -147,5 +147,5 @@ Cold start benchmark on the Rust Book (478 Markdown files), measured with [hyper

- [Comparison with markdownlint](markdownlint-comparison.md) — detailed rule-by-rule comparison and migration guide
- [Comparison with mdformat](mdformat-comparison.md) — formatting feature comparison and migration guide
- [Rules Reference](rules.md) — complete list of rumdl's <!-- RULE_COUNT -->76<!-- /RULE_COUNT --> rules
- [Rules Reference](rules.md) — complete list of rumdl's <!-- RULE_COUNT -->77<!-- /RULE_COUNT --> rules
- [Markdown Flavors](flavors.md) — flavor configuration and per-rule adjustments
2 changes: 1 addition & 1 deletion docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,5 +145,5 @@ Each line shows:
## Next Steps

- [CLI Commands](../usage/cli.md) - Full command reference
- [Rules Reference](../rules.md) - Explore all <!-- RULE_COUNT -->76<!-- /RULE_COUNT --> rules
- [Rules Reference](../rules.md) - Explore all <!-- RULE_COUNT -->77<!-- /RULE_COUNT --> rules
- [Configuration](../global-settings.md) - Advanced configuration options
6 changes: 3 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ icon: lucide/file-check

[:octicons-arrow-right-24: Benchmarks](#performance)

- :mag:{ .lg .middle } **<!-- RULE_COUNT -->76<!-- /RULE_COUNT --> lint rules**
- :mag:{ .lg .middle } **<!-- RULE_COUNT -->77<!-- /RULE_COUNT --> lint rules**

---

Expand Down Expand Up @@ -76,7 +76,7 @@ With intelligent caching, subsequent runs are even faster - rumdl only re-lints
## Features

- :zap: **Built for speed** with Rust - significantly faster than alternatives
- :mag: **<!-- RULE_COUNT -->76<!-- /RULE_COUNT --> lint rules** covering common Markdown issues
- :mag: **<!-- RULE_COUNT -->77<!-- /RULE_COUNT --> lint rules** covering common Markdown issues
- :wrench: **Automatic formatting** with `--fix` for files and stdin/stdout
- :package: **Zero dependencies** - single binary with no runtime requirements
- :gear: **Highly configurable** with TOML-based config files
Expand All @@ -100,7 +100,7 @@ With intelligent caching, subsequent runs are even faster - rumdl only re-lints

- [:octicons-book-24: **Rules Reference**](rules.md)

Explore all <!-- RULE_COUNT -->76<!-- /RULE_COUNT --> linting rules with examples.
Explore all <!-- RULE_COUNT -->77<!-- /RULE_COUNT --> linting rules with examples.

- [:octicons-gear-24: **Configuration**](global-settings.md)

Expand Down
6 changes: 3 additions & 3 deletions docs/markdownlint-comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ rumdl offers **high markdownlint compatibility with intentional differences** wh

- **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**: <!-- RULE_COUNT_ADDITIONAL -->23<!-- /RULE_COUNT_ADDITIONAL --> additional rules (MD057, MD061-<!-- RULE_MAX -->MD082<!-- /RULE_MAX -->), built-in LSP server, VS Code extension, 6 Markdown flavors
- **Unique Features**: <!-- RULE_COUNT_ADDITIONAL -->24<!-- /RULE_COUNT_ADDITIONAL --> additional rules (MD057, MD061-<!-- RULE_MAX -->MD083<!-- /RULE_MAX -->), built-in LSP server, VS Code extension, 6 Markdown flavors
- **Configuration**: Automatic markdownlint config discovery and conversion

## Rule Coverage

### Implemented Rules

rumdl implements **<!-- RULE_COUNT -->76<!-- /RULE_COUNT --> rules total**: all 53 markdownlint rules plus <!-- RULE_COUNT_ADDITIONAL -->23<!-- /RULE_COUNT_ADDITIONAL --> unique rules.
rumdl implements **<!-- RULE_COUNT -->77<!-- /RULE_COUNT --> rules total**: all 53 markdownlint rules plus <!-- RULE_COUNT_ADDITIONAL -->24<!-- /RULE_COUNT_ADDITIONAL --> unique rules.

**Markdownlint-compatible rules (53):** All markdownlint rules are implemented with full compatibility. See the [Rules Reference](rules.md) 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 <!-- RULE_COUNT_ADDITIONAL -->23<!-- /RULE_COUNT_ADDITIONAL --> additional rules not found in markdownlint:
rumdl implements <!-- RULE_COUNT_ADDITIONAL -->24<!-- /RULE_COUNT_ADDITIONAL --> additional rules not found in markdownlint:

| Rule | Name | Description |
| ------ | ------------------------------ | ---------------------------------------------------------- |
Expand Down
70 changes: 70 additions & 0 deletions docs/md083.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# MD083 - Detect mojibake due to encoding issues

Aliases: `mojibake`

**Disabled by default.** This rule is opt-in: enable it explicitly with
`extend-enable`. There is no markdownlint equivalent; this is a rumdl-specific
structure rule.

## What this rule does

Mojibake is text that appears corrupted because it was decoded using a wrong character encoding.

This rule detects common Mojibake patterns, such as UTF-8 text that has been misread as Windows-1252 or ISO-8859-1.

Examples of Mojibake include:

- `Café` instead of `Café`
- `–` instead of `–`
- `’` instead of `’`

## Why this matters

Encoding problems may replace valid Unicode characters such as ✓, ©, §, or ✅ by weird uncommonly used characters such as ✓, ©, §, or ✅

Such issues typically happen when several people work collaboratively using different editors, IDEs, OS and configurations.
Detecting encoding problems early allows the team to identify the causes of the issues and to fix them before they spread.

This rule uses regex-based detection for suspicious character sequences commonly associated with encoding issues.

To fix existing issues in a documentation base, you can use tools such as [`ftfy`](https://github.com/rspeer/python-ftfy/) (in Python)
or [`plsfix`](https://github.com/kevinhu/plsfix) (a reimplementation in Rust).

## Examples

### Incorrect

```text
# Café menu

This line contains a broken dash – and a broken apostrophe ’.
```

### Correct

```text
# Café menu

This line contains an en dash – and a typographic apostrophe ’.
```

## Configuration

```toml
[MD083]
ignore = []
ignore-code-blocks = true
```

- `ignore`: A list of exact mojibake sequences to ignore, such as `"–"` or `"’"`. Default: `[]`.
- `ignore-code-blocks`: Whether to ignore findings inside code, meaning fenced and indented code blocks as well as inline code spans. Default: `true`.

Example:

```toml
[MD083]
ignore = ["–", "’"]
ignore-code-blocks = false
```

With this configuration, MD083 skips findings for those exact mojibake sequences and also reports mojibake inside code blocks and inline code spans.
8 changes: 4 additions & 4 deletions docs/mdformat-comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This document compares rumdl and mdformat, focusing on their formatting capabili
Both tools format Markdown files, but serve different purposes:

- **mdformat**: Pure formatter focused on consistent Markdown output
- **rumdl**: Combined linter and formatter with <!-- RULE_COUNT -->76<!-- /RULE_COUNT --> rules plus formatting
- **rumdl**: Combined linter and formatter with <!-- RULE_COUNT -->77<!-- /RULE_COUNT --> rules plus formatting

**Key Differences:**

Expand All @@ -16,7 +16,7 @@ Both tools format Markdown files, but serve different purposes:
| Primary purpose | Formatting only | Linting + formatting |
| Language | Python | Rust |
| Performance | Good | Faster (native + caching) |
| Linting rules | ❌ | ✅ <!-- RULE_COUNT -->76<!-- /RULE_COUNT --> rules |
| Linting rules | ❌ | ✅ <!-- RULE_COUNT -->77<!-- /RULE_COUNT --> rules |
| Extensibility | Plugin ecosystem | Built-in flavors |
| CommonMark | Strict compliance | Strict compliance |

Expand Down Expand Up @@ -47,7 +47,7 @@ Both tools format Markdown to a consistent style:

### Linting (rumdl only)

rumdl provides <!-- RULE_COUNT -->76<!-- /RULE_COUNT --> linting rules that mdformat does not have:
rumdl provides <!-- RULE_COUNT -->77<!-- /RULE_COUNT --> linting rules that mdformat does not have:

- **Broken link detection** (MD051, MD052, MD057)
- **Accessibility checks** (MD045 - image alt text)
Expand Down Expand Up @@ -299,7 +299,7 @@ style = "atx"
| Capability | mdformat | rumdl |
| ----------------------- | ------------------ | ---------------------- |
| Markdown formatting | ✅ Primary focus | ✅ Via `rumdl fmt` |
| Markdown linting | ❌ | ✅ <!-- RULE_COUNT -->76<!-- /RULE_COUNT --> rules |
| Markdown linting | ❌ | ✅ <!-- RULE_COUNT -->77<!-- /RULE_COUNT --> rules |
| Performance | Good | Faster (native binary) |
| Extended syntax | Plugins | Built-in flavors |
| Editor integration | Basic | LSP + VS Code |
Expand Down
3 changes: 2 additions & 1 deletion docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Introduction

rumdl implements <!-- RULE_COUNT -->76<!-- /RULE_COUNT --> rules for checking Markdown files. This document provides a comprehensive reference of all available rules, organized by category.
rumdl implements <!-- RULE_COUNT -->77<!-- /RULE_COUNT --> rules for checking Markdown files. This document provides a comprehensive reference of all available rules, organized by category.
Each rule has a brief description and a link to its detailed documentation.

For information on global configuration settings (file selection, rule enablement, etc.), see the [Global Settings Reference](global-settings.md).
Expand Down Expand Up @@ -40,6 +40,7 @@ The following rules are **disabled by default** because they enforce opinionated
| [MD074](md074.md) | MkDocs nav validation | Requires `flavor = "mkdocs"` to activate |
| [MD080](md080.md) | Heading anchor collision | Collisions are functional under platform auto-suffixing |
| [MD082](md082.md) | No empty sections | Empty sections are sometimes intentional stubs |
| [MD083](md083.md) | Detect Mojibake | Turn it on once you encounter encoding issues |

### Enabling Opt-in Rules

Expand Down
10 changes: 10 additions & 0 deletions rules.json
Original file line number Diff line number Diff line change
Expand Up @@ -764,5 +764,15 @@
"fix": "Fix is not available.",
"fix_availability": "None",
"url": "https://rumdl.dev/md082/"
},
{
"code": "MD083",
"name": "mojibake",
"aliases": [],
"summary": "Detects common encoding issues (aka mojibake).",
"category": "other",
"fix": "Fix is not available.",
"fix_availability": "None",
"url": "https://rumdl.dev/md083/"
}
]
2 changes: 2 additions & 0 deletions src/config/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ pub static RULE_ALIAS_MAP: phf::Map<&'static str, &'static str> = phf::phf_map!
"MD080" => "MD080",
"MD081" => "MD081",
"MD082" => "MD082",
"MD083" => "MD083",

// Aliases (hyphen format)
"HEADING-INCREMENT" => "MD001",
Expand Down Expand Up @@ -342,6 +343,7 @@ pub static RULE_ALIAS_MAP: phf::Map<&'static str, &'static str> = phf::phf_map!
"HEADING-ANCHOR-COLLISION" => "MD080",
"NO-EXCESSIVE-EMPHASIS" => "MD081",
"NO-EMPTY-SECTIONS" => "MD082",
"MOJIBAKE" => "MD083",
};

/// Resolve a rule name alias to its canonical form with O(1) perfect hash lookup
Expand Down
Loading
Loading