rumdl 0.1.51

A fast Markdown linter written in Rust (Ru(st) MarkDown Linter)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
# MD060 - Table Format

**Aliases:** `table-format`

**Enabled by default:** No (opt-in)

**Fixable:** Yes

## Rationale

Tables in Markdown are significantly easier to read and maintain when their columns are properly aligned in source form. While Markdown tables render the same regardless of spacing, aligned tables in
source code:

- Improve readability during code review
- Make editing cells more intuitive
- Provide better developer experience
- Ensure consistency across documents

This rule enforces consistent column alignment and padding in Markdown tables, matching the behavior of popular formatters like Prettier.

## Configuration

```toml
[MD060]
enabled = false              # Default: opt-in for conservative adoption
style = "aligned"            # Options: "aligned", "aligned-no-space", "compact", "tight", "any"
max-width = 0                # Default: inherit from MD013's line-length
column-align = "auto"        # Options: "auto", "left", "center", "right"
column-align-header = "auto" # Override alignment for header row only
column-align-body = "auto"   # Override alignment for body rows only
loose-last-column = false    # Cap last column at header width when true
```

### Options

#### `enabled`

**Type:** `boolean`
**Default:** `false`

Whether to enable MD060 table formatting. This rule is disabled by default to allow gradual adoption, as it can make significant changes to existing tables.

#### `style`

**Type:** `string`
**Default:** `"any"`
**Values:** `"aligned"`, `"aligned-no-space"`, `"compact"`, `"tight"`, `"any"`

Controls the table formatting style:

- **`aligned`**: Columns are padded with spaces for visual alignment (recommended for readability)
- **`aligned-no-space`**: Like `aligned`, but delimiter row has no spaces around dashes (see below)
- **`compact`**: Minimal spacing with single spaces between pipes and content
- **`tight`**: No spacing, pipes directly adjacent to content
- **`any`**: Preserve existing formatting style (no enforcement)

#### `max-width`

**Type:** `number`
**Default:** `0` (inherit from MD013)

Maximum table width before auto-switching to compact mode.

- **`0`** (default): Smart inheritance from MD013's settings (see below)
- **Non-zero**: Explicit max width threshold, independent of MD013

When a table's aligned width would exceed this limit, MD060 automatically uses compact formatting instead to prevent excessively long lines. This intelligent auto-compacting matches Prettier's table
formatting behavior.

**Smart Inheritance Behavior (when `max-width = 0`):**

When `max-width` is not explicitly set, MD060 intelligently determines whether to apply width limits:

| MD013 Configuration                         | Effective max-width             |
| ------------------------------------------- | ------------------------------- |
| MD013 disabled                              | **Unlimited** (no auto-compact) |
| `MD013.tables = false`                      | **Unlimited** (no auto-compact) |
| `MD013.line-length = 0`                     | **Unlimited** (no auto-compact) |
| `MD013.tables = true` and `line-length = N` | **Inherits N**                  |

This means if you don't care about table line length (by disabling MD013 or setting `tables = false`), MD060 won't force tables to compact. Your aligned tables will stay aligned regardless of width.

**Why this matters:** Wide tables with many columns or long content can produce extremely long lines when aligned. Auto-compacting ensures tables don't violate line length limits while still
maintaining alignment where practical. However, if you've explicitly opted out of line length checks for tables, MD060 respects that choice.

#### `column-align`

**Type:** `string`
**Default:** `"auto"`
**Values:** `"auto"`, `"left"`, `"center"`, `"right"`

Controls how cell text is aligned within the padded column width:

- **`auto`** (default): Respects alignment indicators from the delimiter row (`:---` for left, `:---:` for center, `---:` for right)
- **`left`**: Forces all columns to left-align text (content on left, padding on right)
- **`center`**: Forces all columns to center text (padding split on both sides)
- **`right`**: Forces all columns to right-align text (padding on left, content on right)

**Note:** This option only applies when `style = "aligned"` or `style = "aligned-no-space"`. It has no effect on `compact` or `tight` styles (which have minimal or no padding).

**Example:**

```toml
[MD060]
enabled = true
style = "aligned"
column-align = "right"  # Right-align all cell content
```

#### `column-align-header`

**Type:** `string` (optional)
**Default:** Not set (falls back to `column-align`)
**Values:** `"auto"`, `"left"`, `"center"`, `"right"`

Overrides `column-align` specifically for the header row. When set, the header row uses this alignment while body rows use `column-align` (unless overridden by `column-align-body`).

**Example:**

```toml
[MD060]
enabled = true
style = "aligned"
column-align-header = "center"  # Center header text
column-align-body = "left"      # Left-align body text
```

#### `column-align-body`

**Type:** `string` (optional)
**Default:** Not set (falls back to `column-align`)
**Values:** `"auto"`, `"left"`, `"center"`, `"right"`

Overrides `column-align` specifically for body rows (non-header, non-delimiter). When set, body rows use this alignment while the header row uses `column-align` (unless overridden by `column-align-header`).

**Example:**

```toml
[MD060]
enabled = true
style = "aligned"
column-align = "left"           # Default for both
column-align-body = "right"     # Override body to right-align
```

#### `loose-last-column`

**Type:** `boolean`
**Default:** `false`

Controls whether the last column width is capped at the header text width. This is useful for tables where the last column contains descriptions or other variable-length content.

- **`false`** (default): All columns are padded to the widest cell across all rows. Every row has equal length.
- **`true`**: The last column width is capped at the header text width. Body cells shorter than the header are padded to the header width. Body cells longer than the header extend beyond without extra padding.

```toml
[MD060]
loose-last-column = true
```

```markdown
| Name   | Status   | Description |
| ------ | -------- | ----------- |
| Foo    | Enabled  | Short       |
| Bar    | Disabled | A much longer description that would waste space if padded |
```

Notice that "Short" is padded to match the "Description" header width, while the longer body cell extends beyond the header.

## Examples

### ❌ Incorrect (unaligned)

```markdown
| Name | Age | City |
|---|---|---|
| Alice | 30 | Seattle |
| Bob | 25 | Portland |
```

### ✅ Correct (aligned)

```markdown
| Name  | Age | City     |
| ----- | --- | -------- |
| Alice | 30  | Seattle  |
| Bob   | 25  | Portland |
```

### Compact Style

When `style = "compact"`:

```markdown
| Name | Age | City |
| --- | --- | --- |
| Alice | 30 | Seattle |
| Bob | 25 | Portland |
```

### Tight Style

When `style = "tight"`:

```markdown
|Name|Age|City|
|---|---|---|
|Alice|30|Seattle|
|Bob|25|Portland|
```

### Column Alignment Examples

When `column-align = "center"`:

```markdown
| Name  | Age |   City   |
| ----- | --- | -------- |
| Alice | 30  | Seattle  |
|  Bob  | 25  | Portland |
```

When `column-align = "right"`:

```markdown
|  Name | Age |     City |
| ----- | --- | -------- |
| Alice |  30 |  Seattle |
|   Bob |  25 | Portland |
```

This is useful for consistently formatting tables with numeric data or when you want a specific visual style across all columns.

### Aligned No-Space Style

When `style = "aligned-no-space"`:

```markdown
| Name  | Age | City     |
|-------|-----|----------|
| Alice | 30  | Seattle  |
| Bob   | 25  | Portland |
```

This style is identical to `aligned` except the delimiter row has no spaces around the dashes. Compare:

- **`aligned`**: `| ----- | --- | -------- |`
- **`aligned-no-space`**: `|-------|-----|----------|`

Both styles produce aligned columns with equal-length rows. The only difference is the delimiter row formatting.
This style is popular in hand-written tables and used by projects like GitLab and Obsidian.

## Auto-Compact Threshold

### Example: Inheriting from MD013

```toml
[MD013]
line-length = 100

[MD060]
enabled = true
style = "aligned"
max-width = 0  # Tables exceeding 100 chars will auto-compact
```

**Behavior:**

- Tables ≤ 100 chars wide: Aligned with full padding
- Tables > 100 chars wide: Automatically compact to stay under limit

### Example: Explicit Threshold

```toml
[MD060]
enabled = true
style = "aligned"
max-width = 120  # Independent of MD013, uses 120 char threshold
```

## Unicode Support

MD060 properly handles various Unicode characters:

### ✅ Supported

**CJK Characters** (Chinese, Japanese, Korean):

```markdown
| Name | City |
| ---- | ---- |
| 中文 | 東京 |
```

**Basic Emoji:**

```markdown
| Status | Name |
| ------ | ---- |
|| Pass |
|| Fail |
```

The rule correctly measures CJK characters as double-width and aligns columns accordingly.

### ⚠️ Automatically Skipped

Tables containing complex Unicode sequences are automatically skipped to prevent alignment corruption:

- **Zero-Width Joiner (ZWJ) emoji:** 👨‍👩‍👧‍👦, 👩‍💻
- **Zero-Width Space (ZWS):** Invisible word break opportunities
- **Zero-Width Non-Joiner (ZWNJ):** Ligature prevention marks
- **Word Joiner (WJ):** Non-breaking invisible characters

These characters have inconsistent or zero display widths across terminals and fonts, making accurate alignment impossible. The rule preserves these tables as-is rather than risk corrupting them.

**Example (automatically skipped):**

```markdown
| Emoji    | Name      |
| -------- | --------- |
| 👨‍👩‍👧‍👦 | Family    |
| 👩‍💻     | Developer |
```

This is an honest limitation of terminal display technology, similar to what other tools like markdownlint experience.

## Fix Behavior

When applying automatic fixes (`rumdl fmt`), this rule:

1. **Calculates proper display width** for each column using Unicode width measurements
2. **Pads cells** with trailing spaces to align columns
3. **Preserves cell content** exactly (only spacing is modified)
4. **Respects alignment indicators** in delimiter rows:
   - `:---` → Left-aligned
   - `:---:` → Center-aligned
   - `---:` → Right-aligned
5. **Auto-compacts** tables exceeding `max-width` to prevent line length violations
6. **Skips tables with ZWJ emoji** to prevent alignment corruption
7. **Masks inline code blocks** to avoid treating code pipes as table delimiters

## Common Use Cases

### Recommended Configuration (Conservative)

For most projects, start with this conservative configuration:

```toml
[MD013]
line-length = 100

[MD060]
enabled = true        # Enable table formatting
style = "aligned"     # Align columns for readability
max-width = 0         # Auto-compact tables exceeding 100 chars
```

This provides readable tables without creating excessively long lines.

### Strict Compact Mode

For projects preferring minimal formatting:

```toml
[MD060]
enabled = true
style = "compact"
```

### Allow Any Style

To check without enforcing:

```toml
[MD060]
enabled = true
style = "any"  # No enforcement, preserves existing formatting
```

### Unlimited Table Width

For projects that want aligned tables without any width limits:

```toml
[global]
disable = ["MD013"]  # No line length checks = no table compacting

[MD060]
enabled = true
style = "aligned"
max-width = 0  # Will be unlimited since MD013 is disabled
```

Or, if you want MD013 for code but not tables:

```toml
[MD013]
line-length = 80
tables = false  # Skip table line length checks

[MD060]
enabled = true
style = "aligned"
max-width = 0  # Will be unlimited since MD013.tables = false
```

## Integration with Other Rules

**MD013 (Line Length):**

- MD060's `max-width = 0` uses smart inheritance from MD013
- If MD013 is disabled or `tables = false`, tables have **unlimited width** (no auto-compact)
- If MD013 is enabled with `tables = true`, tables are auto-compacted at `line-length`
- Can override with explicit `max-width` value to ignore MD013 entirely

**MD056 (Table Column Count):**

- MD056 validates column count consistency
- MD060 formats tables after MD056 validates structure
- Use both rules together for comprehensive table linting

## Performance Notes

Table formatting is computationally intensive due to Unicode width calculations. MD060 is optimized with:

- Efficient Unicode width caching
- Early bailout for complex Unicode sequences
- Parallel processing in fix mode (when multiple tables exist)

For large documents with many tables, expect formatting to take a few hundred milliseconds.

## Rationale for Default Disabled

MD060 is disabled by default because:

1. **Large diffs:** Enabling on existing codebases can produce massive formatting changes
2. **Opt-in adoption:** Teams should consciously decide to adopt table formatting
3. **Performance:** Unicode width calculations add overhead
4. **Conservative approach:** Matches rumdl's philosophy of opt-in for aggressive formatting

We recommend enabling MD060 for new projects or when doing a formatting cleanup pass.

## See Also

- [MD056]md056.md - Table Column Count (validates table structure)
- [MD013]md013.md - Line Length (works with MD060's auto-compact)
- [MD055]md055.md - Table Pipe Style (validates pipe placement)