Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/md013.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ and work with in various contexts.
line-length = 100 # Maximum characters per line (default: 80)
code-blocks = false # Don't check code blocks (default: true)
code-spans = false # Don't flag lines whose only overflow is an unbreakable inline code span (default: true)
emphasis-spans = false # Allow reflow breaking inside emphasis/strong/strikethrough spans (default: false)
tables = false # Don't check tables (default: false)
headings = true # Check headings (default: true)
paragraphs = true # Check paragraph/regular text (default: true)
Expand All @@ -67,14 +66,14 @@ reflow-mode = "default" # Reflow mode: "default", "normalize", "sentence-per-li
length-mode = "visual" # How to count line length: "visual", "chars", or "bytes" (default: "visual")
abbreviations = ["Assn", "Univ"] # Add custom abbreviations for sentence-per-line mode
require-sentence-capital = true # Require uppercase after periods for sentence detection (default: true)
atomic-spans = true # Treat code/emphasis spans as atomic units during reflow (default: true)
```

### Configuration options explained

- `line-length`: The maximum number of characters allowed per line (set to `0` to disable all line length checks)
- `code-blocks`: Whether to check line length in code blocks (default: `true`)
- `code-spans`: Whether to check lines whose length comes from an inline code span (default: `true`). Inline code spans (`` `like this` ``) cannot be wrapped, so reflow cannot shorten a line whose excess length is one. When `false`, a line is not reported if it would fit within the limit once its inline code spans are excluded - useful with `reflow` so an unbreakable code incantation does not fail an otherwise-clean file
- `emphasis-spans`: Whether to allow reflow breaking inside emphasis, strong, and strikethrough spans (default: `false`). When `true`, text inside emphasis elements is wrapped word-by-word like normal text; inline code nested inside an emphasis span wraps along with the surrounding words (a code span across a soft line break renders identically). When `false`, emphasis spans are treated as atomic units and kept on a single line unless they exceed the maximum line length on their own
- `tables`: Whether to check line length in tables (default: `false`)
- `headings`: Whether to check line length in headings (default: `true`)
- `paragraphs`: Whether to check line length in regular text/paragraphs (default: `true`). When false, `line-length` is still used for reflow but no warnings are reported. Blockquote content is treated as paragraph text, so `paragraphs = false` also skips blockquotes
Expand All @@ -98,6 +97,7 @@ require-sentence-capital = true # Require uppercase after periods for sentence
- When `true`, only `word. Capital` is treated as a sentence boundary (fewer false positives)
- When `false`, `word. lowercase` is also treated as a sentence boundary (more splitting)
- Does not affect `!` and `?` which are always treated as sentence boundaries
- `atomic-spans`: Whether to hold emphasis/strong/strikethrough and code spans atomic during reflow (default: `true`). When `true`, these spans are treated as atomic units. When `false`, they can be wrapped word-by-word like normal text. Note that if an emphasis span contains a nested code span, the emphasis span is kept atomic to prevent formatting corruption.

## Ignoring inline link URLs (non-strict mode)

Expand Down
4 changes: 2 additions & 2 deletions src/rules/md013_line_length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl MD013LineLength {
abbreviations: Vec::new(),
require_sentence_capital: true,
ignore_link_urls: true,
emphasis_spans: false,
atomic_spans: true,
},
list_spacing: MD030Config::default(),
}
Expand Down Expand Up @@ -135,7 +135,7 @@ impl MD013LineLength {
None
},
defined_references: Some(Self::defined_reference_labels(ctx)),
emphasis_spans: config.emphasis_spans,
atomic_spans: config.atomic_spans,
}
}

Expand Down
45 changes: 20 additions & 25 deletions src/rules/md013_line_length/md013_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,11 @@ pub struct MD013Config {
)]
pub require_sentence_capital: bool,

/// Whether to allow reflow breaking inside emphasis/strong/strikethrough spans.
/// When true, emphasis text is wrapped word-by-word like normal text.
/// When false (default), emphasis spans are treated as atomic units.
#[serde(default = "default_emphasis_spans", alias = "emphasis_spans")]
pub emphasis_spans: bool,
/// Whether to hold emphasis/strong/strikethrough and code spans atomic during reflow.
/// When true (default), these spans are treated as atomic units.
/// When false, they can be wrapped word-by-word like normal text.
#[serde(default = "default_atomic_spans", alias = "atomic_spans")]
pub atomic_spans: bool,
}

fn default_line_length() -> LineLength {
Expand Down Expand Up @@ -209,8 +209,8 @@ fn default_ignore_link_urls() -> bool {
true
}

fn default_emphasis_spans() -> bool {
false
fn default_atomic_spans() -> bool {
true
}

impl Default for MD013Config {
Expand All @@ -233,7 +233,7 @@ impl Default for MD013Config {
length_mode: LengthMode::default(),
abbreviations: Vec::new(),
require_sentence_capital: default_require_sentence_capital(),
emphasis_spans: default_emphasis_spans(),
atomic_spans: default_atomic_spans(),
}
}
}
Expand All @@ -255,21 +255,16 @@ impl MD013Config {
/// pre-filter candidate lines: any line shorter than this can never
/// violate, regardless of which context it falls under.
pub fn min_effective_line_length(&self) -> LineLength {
let mut limits: Vec<LineLength> = vec![self.line_length];
if let Some(h) = self.heading_line_length {
limits.push(h);
}
if let Some(c) = self.code_block_line_length {
limits.push(c);
}
// "Unlimited" (0) is the laxest possible budget, so it must not win
// the minimum unless all budgets are unlimited.
let bounded: Vec<LineLength> = limits.iter().copied().filter(|l| !l.is_unlimited()).collect();
if bounded.is_empty() {
LineLength::from_const(0)
} else {
bounded.into_iter().min_by_key(|l| l.get()).unwrap()
}
[
Some(self.line_length),
self.heading_line_length,
self.code_block_line_length,
]
.into_iter()
.flatten()
.filter(|l| !l.is_unlimited())
.min_by_key(|l| l.get())
.unwrap_or(LineLength::from_const(0))
}

/// Convert abbreviations Vec to Option for ReflowOptions
Expand Down Expand Up @@ -311,7 +306,7 @@ impl MD013Config {
// No document context here (config-only), so shortcut references
// stay atomic. The rule's fix path supplies the defined labels.
defined_references: None,
emphasis_spans: self.emphasis_spans,
atomic_spans: self.atomic_spans,
}
}
}
Expand Down Expand Up @@ -417,7 +412,7 @@ mod tests {
abbreviations: Vec::new(),
require_sentence_capital: true,
ignore_link_urls: true,
emphasis_spans: false,
atomic_spans: true,
};

let toml_str = toml::to_string(&config).unwrap();
Expand Down
Loading
Loading