rumdl 0.3.0

A fast Markdown linter and formatter written in Rust
Documentation
# MD010 - Use spaces instead of tabs

Aliases: `no-hard-tabs`

## What this rule does

Replaces tab characters with spaces to ensure consistent indentation across all editors and platforms.

## Why this matters

- **Consistency**: Tabs display differently in different editors (2, 4, or 8 spaces wide)
- **Team collaboration**: Mixed tabs and spaces create confusing indentation and merge conflicts
- **Tool compatibility**: Many Markdown tools and platforms expect spaces for proper formatting
- **Readability**: Consistent spacing makes documents easier to read and maintain

## Examples

### ✅ Correct

```markdown
* Shopping list
    * Fruits
        * Apples
        * Bananas
    * Vegetables
        * Carrots
        * Broccoli

    Remember to check for freshness!
```

### ❌ Incorrect

> `→` represents a hard tab character.

```markdown
* Shopping list
→* Fruits
→→* Apples
→→* Bananas
→* Vegetables
→→* Carrots
→→* Broccoli

→Remember to check for freshness!
```

### 🔧 Fixed

```markdown
* Shopping list
    * Fruits
        * Apples
        * Bananas
    * Vegetables
        * Carrots
        * Broccoli

    Remember to check for freshness!
```

## Configuration

```toml
[MD010]
spaces-per-tab = 4                        # Number of spaces to replace each tab with (default: 4)
code-blocks = false                       # Skip tabs inside code blocks and inline code (default: false)
ignore-code-languages = ["makefile"]      # Fence languages to never flag (default: none)
```

### Configuration options explained

- `spaces-per-tab`: How many spaces to use when replacing a tab that sits
  between or after text (alignment). Tabs in leading whitespace are not affected
  by this setting; see [Automatic fixes](#automatic-fixes).
- `code-blocks`: When `false` (default), hard tabs inside fenced and indented
  code blocks and inside inline code spans are skipped - tabs are often required there (Makefiles, Go) and
  rewriting them would corrupt examples. Set to `true` for markdownlint-parity
  behavior that flags tabs everywhere, including code blocks and inline code
  spans. A tab inside an inline code span is only reported, never fixed, since
  replacing it would change the code text.
- `ignore-code-languages`: Fence languages whose tabs are never reported, so you
  can flag tabs in code blocks generally while keeping the languages that need
  them. The language is the first word of the info string, matched
  case-insensitively, so ```` ```makefile title="Makefile" ```` matches
  `makefile`. This only has an effect when `code-blocks = true`, since otherwise
  every code block is already skipped. An indented code block has no info
  string, so it can never match; use `code-blocks = false` to skip those.

  Flavors that write the language somewhere other than the first word are read
  on their own terms:

  | Flavor | Fence | Matches |
  |--------|-------|---------|
  | `pandoc`, `quarto` | ```` ```{#id .makefile} ```` | `makefile` (first `.class`) |
  | `quarto` | ```` ```{r, echo=FALSE} ```` | `r` (the chunk engine) |
  | `azure_devops` | `::: mermaid` | `mermaid` (the colon fence language) |

> **Behavior change:** Earlier versions skipped tabs in fenced code blocks
> while still flagging indented ones. MD010 now treats both code-block types
> consistently and, by default, does not flag tabs in either. Note that a
> document whose content starts with a tab-indented line is a CommonMark
> indented code block, so leading tabs there are no longer flagged by default.
> Set `code-blocks = true` to flag tabs in all code blocks.

## Automatic fixes

This rule automatically replaces tab characters with spaces so your document looks the same in every editor. How many spaces depends on where the tab is:

- **Leading whitespace** (indentation, and the space after a list marker such as `-` or `1.` or after a blockquote `>`): the tab is replaced by the spaces it spans. A tab advances to the next multiple of four columns, counted from the start of the line, so `-<tab>x` becomes `-   x` and `10.<tab>x` becomes `10. x`. Where a list item's content starts, how deep a quote is and where indented code begins all depend on these columns, so a fixed width would change the meaning of the document. `spaces-per-tab` is not used here, and this also applies to code when `code-blocks = true`.
- **Elsewhere in a line** (between words, in table cells, inside a code block when `code-blocks = true`): each tab becomes `spaces-per-tab` spaces (default: 4).
- **Inside an inline code span** (between its backticks, on any line of a span that continues onto the next line, including that line's leading whitespace, which a renderer keeps on a lazy continuation line and strips otherwise): the tab is part of the code text, so it is never replaced or removed. Like a tab in a fenced block it is not reported by default. With `code-blocks = true` it is reported without a fix. A tab on the same line but outside the span is fixed as usual.
- **At the end of a line**: the tab (together with any spaces around it) is removed. A tab followed by at most one space is a soft line break, but four spaces would turn it into a hard line break. A run that already ends in two or more spaces is a hard break and is kept as one. A tab right after a backslash is reported but not fixed, because removing it would turn the literal backslash into a hard line break. Inside a code block (with `code-blocks = true`), and on a line that is only whitespace, the tab is replaced by `spaces-per-tab` spaces.

## Learn more

- [CommonMark specification](https://spec.commonmark.org/0.31.2/#tabs) - Technical details about tab handling

## Related rules

- [MD009](md009.md) - Remove trailing spaces
- [MD007](md007.md) - Keep list indentation consistent
- [MD005](md005.md) - Keep list indentation consistent