Paragraph wrapping

Version 0.6.1 Updated Sep 29, 2026

Pass --wrap to reflow prose paragraphs so that every output line fits within 80 display columns. Width is measured in terminal columns, not bytes, so the wrapper accounts correctly for CJK glyphs, emoji, and accented characters. The flag does not accept a width parameter.

Line fitting is delegated to the textwrap crate using a greedy first-fit algorithm: each word is placed on the current line if it fits, and a new line is started otherwise. This produces predictable, diff-friendly output.

Inline code spans (`…`), Markdown links ([text](url)), and inline GFM footnote references ([^label]) are treated as unbreakable units during normal line fitting. mdtablefix --wrap never introduces a line break within an inline code span; the span otherwise moves as a whole to the next line when it would exceed the target width. The exception for an already cross-line overlong span is described below.

Reference-style links such as [text][reference] are likewise unbreakable. The opening [ always stays with the link label, avoiding leading whitespace inside link text after continuation indentation is applied.

A colon-suffixed footnote reference in running prose, such as subcategories [^96]:, remains attached to the preceding word. The wrapper therefore cannot move [^96]: to column one, where Markdown would reinterpret the reference as a footnote definition.

Common English prose dates, such as 25th December 2025, 19 March 2018, and July 4, 2008, are also treated as unbreakable inline fragments. This applies to ordinal-day, numeric-day, and month-name-first forms with full or abbreviated month names. If a date is wider than the configured wrap width, the existing long-token fallback behaviour applies.

Parenthesized inline citations such as pattern([1](url)) are also treated as unbreakable units, keeping the citation link and its surrounding parentheses together during wrapping.

When an inline code span is split across two or more soft-wrapped source lines, --wrap first joins the continuation lines into a single span before applying the line-length limit. When the joined span fits, the remainder of the paragraph is greedily reflowed during the same pass, including later continuation lines in a list item. Running --wrap again therefore produces no further changes.

Thematic breaks act as block boundaries. A line of three or more -, *, or _ characters is passed through on its own line and never absorbed into the surrounding paragraph, with or without --breaks. This includes spaced runs such as - - - and the seventy-underscore line that --breaks writes. A table separator row such as | --- | --- | still contains pipes and is reflowed with its table rather than treated as a break.

Setext headings pass through --wrap whole. A line of paragraph text directly above an underline of = or - characters is a heading, so both lines are emitted unchanged: the heading text is not wrapped, as ATX heading text is not, and the underline is never joined to the heading or to a paragraph directly below it. --breaks likewise leaves a --- underline alone, because it is not a thematic break. --headings still converts the pair to an ATX heading when it is passed.

Arc Gambit
==========

What is it?
-----------

Both headings above come out of --wrap --breaks exactly as written.

When the first line of a prefixed block spills past the target width, the wrapper keeps that block open so its continuation and lazy continuation lines reflow with the tail in the same pass rather than being joined to it on a later run. The prefixed forms are list items, task items, ordered items, blockquotes, and footnote definitions. Folding only happens when the tail would reparse as paragraph text, so a tail indented by four or more columns (indented code), a tail that repeats its blockquote marker, and a footnote definition tail stay separate blocks. The formatter therefore reaches its final form in one pass, so a second run makes no further changes.

The wrapper never introduces a new line break inside an inline-code span. When joining the span would exceed the configured width and each authored line already fits, however, it may retain the authored boundaries inside that span. Markdown renders those retained soft breaks as spaces, while the physical lines remain within the limit. These rules apply in all prefixed contexts — bulleted lists, ordered lists, blockquotes, and footnote definitions — as well as in plain paragraphs.

An inline code span may itself contain backslash-escaped backticks — for example `pass \`--file\` to the tool`. --wrap keeps the whole span, including its escaped inner backticks, opaque to ordinary line fitting. It does not introduce a split inside the span, and the escaped backticks are preserved verbatim; an already conforming authored boundary may still be retained under the overlong-span rule above.

For list items, deferred inline code continuations use continuation indentation rather than repeating the original list marker. This prevents a wrapped checklist item from being reformatted as several independent checklist entries.

Ambiguous close-and-reopen patterns are preserved verbatim, so the formatter does not introduce Markdownlint MD038 spacing violations or change the intended code-span boundaries.

When --wrap is combined with --renumber, ordered list item boundaries are preserved even when a list item contains a long inline code span. The wrapper may leave the span on its existing continuation line, but it does not split a single list item into new numbered steps or strand code-span fragments as separate list items.

Ordered-list renumbering runs before wrapping. Continuation indentation is therefore measured from the final marker width, including when renumbering changes a marker from one digit to two, such as 9. to 10..

A heading or thematic break ends every ordered list whose current item it is not indented into. One indented to an item's content column, which is three spaces under 1., belongs to that item, and the list keeps counting past it. One at column 0, or indented less than the content column, ends the list, and the next list restarts at one. The content column is measured on the renumbered marker, so a second pass reaches the same result.

Renumbering restarts at one wherever the list ends. The rule for a heading or thematic break above applies to those two blocks. Any other block ends the lists whose item it is not indented into, judged by the same content column: after a blank line a paragraph, a fenced code block, a table, a block quote, an HTML block or a bullet list ends the list. A block that can interrupt a paragraph ends it even without the blank line: a fence, a block quote, a bullet item with text after its marker, or an HTML block of the kinds CommonMark lets interrupt a paragraph (comments, <script>, <pre>, <div> and the other block-level tags). An empty bullet, a bullet whose marker is followed by a non-breaking space, and an inline tag such as <span> are paragraph text and leave the list open. A block indented to the content column belongs to the item, so the list keeps counting past it.

Renumbering never changes whether a line is a list item. Only a list starting at 1 can interrupt a paragraph, so a line such as 12. Evidence directly below paragraph text continues that paragraph and is left exactly as written.

When a footnote reference immediately follows an inline code span or Markdown link without intervening whitespace—for example `code`.[^ref] or [text](url).[^ref]—the reference stays on the same line as the preceding punctuation during wrapping. The same rule applies when opening punctuation is coupled to the span, such as (`code`).[^ref].

Inline GFM footnote references that immediately follow sentence punctuation are also kept attached as unbreakable units. For example, Sentence.[^ref], Sentence,[^ref], Sentence?[^ref], and Sentence"[^ref] remain attached during wrapping, and previously split paragraph text such as Sentence. [^ref] or Sentence. followed by [^ref] is normalized back to Sentence.[^ref]. Footnote definition lines such as [^ref]: note text remain definitions and are not joined to preceding prose.

Opening brackets and other opening punctuation ((, [, and CJK openers such as ( and 「) that immediately precede an inline code span or Markdown link stay coupled to that span during wrapping. This prevents a lone opener from being stranded at the end of a line before the code or link that follows it.

A bare numeric bracket reference such as [1] or [12] is likewise unbreakable under --wrap: the opening bracket is never left stranded at the end of a line, the reference moves as a unit, and trailing punctuation after the closing bracket stays attached. The same rule applies when the reference immediately follows an inline code span. Only ASCII digits qualify, so a short alphabetic label such as [a] remains ordinary breakable prose.

Inflectional affixes and possessives that appear immediately after a closing inline code fence — for example `VarGuard`s, `class`'s, `fetch`ed, or `run`ning — are kept on the same line as the code span during wrapping. The tokenizer treats the backtick fence together with its directly attached suffix as a single unbreakable unit, so no line break is ever inserted between the closing backtick and the following letters.

Hyphenated compounds where a word ends with a hyphen immediately before an inline code span — for example pre-`LLMPort`, LLM-`Port`, or (API-`Foo`) — are also kept on the same line during wrapping. The hyphen-prefix token is coupled to the following code span, so wrapping never strands the prefix at the end of a line or the code span at the start of the next. This mirrors the opening-punctuation coupling rule but applies to any token that ends with a hyphen and contains at least one alphabetic character, including Unicode alphabetic characters such as pré-`code` or 字-`code`. If the compound alone exceeds the target width, it may be broken. Trailing-hyphen compounds such as `code`-style continue to be absorbed by the tokenizer at the closing fence.

When a Markdown link or inline code span is followed by trailing punctuation, such as a full stop or comma, mdtablefix keeps that punctuation attached to the same wrapped unit. It does not leave the punctuation orphaned on a line by itself after wrapping.

Blockquote prefixes (>) are parsed before their inner content at every nesting depth, including compact (>>) and spaced (> >) forms. Fenced code blocks, inline code spans, task-list item markers (- [ ], - [x]), ordinary list markers, and footnote definition labels ([^n]:) therefore retain their usual meaning inside blockquotes. Wrapped prose repeats the complete blockquote prefix. For a list inside a blockquote, continuations repeat the blockquote portion and replace only the list marker with an alignment indent.

Fenced code blocks, HTML blocks, indented code blocks (four or more leading spaces or a leading tab), and table rows are passed through unchanged. Wrapping is applied only to prose paragraphs and prefixed lines.

Link reference definitions — lines of the form [label]: <URL> or [label]: URL (with an optional inline title) are left untouched by the reflow pass. The definition line is preserved verbatim; when a valid standalone title continuation line follows (a separate line containing only the title in quotes or parentheses), that line is also preserved verbatim. Collapsed definitions that place the label on one line and an indented destination on the next line are also preserved, including the destination indentation:

[users-guide]:
  docs/users-guide.md

The indented destination continuation is distinct from a standalone title continuation. It must look like a link destination, so indented Markdown blocks such as lists, blockquotes, and headings are still routed through normal block wrapping.

Two trailing spaces at the end of a line produce a hard line break in rendered Markdown. mdtablefix --wrap preserves those trailing spaces on the final wrapped line, so hard-break semantics are not lost after reformatting. A line break the wrapper makes itself never keeps trailing spaces, so a run of spaces inside a paragraph cannot turn into a hard break.

--wrap breaks lines only where the source already has whitespace. Markdown renders a soft line break as a space, so a break between characters that touch in the source would add a space to the rendered text. A run such as ([Python Packaging][4]), `Mutex`/`MutexGuard` or **code** stays on one line, even when that leaves the line longer than the target width.

A line ending in a backslash is also a Markdown hard break. Because the backslash is content, it stays glued to the last word and is measured by the wrap, so a wrapped line never exceeds the target width by the marker's column. The two-space form is trimmed before measuring and re-applied after, which is why it stays outside the fit.

Lines that consist entirely of whitespace — spaces, tabs, or any mixture — are normalized to empty strings during wrapping. Such lines act as paragraph boundaries and are never passed through with their original whitespace content, so the output uses a single uniform separator between paragraphs regardless of the input's incidental indentation.

When computing the indentation width for continuation lines in prefixed contexts (blockquotes, lists, and footnote definitions), mdtablefix measures the prefix using Unicode display width (UnicodeWidthStr::width) rather than byte or character count. Continuation lines therefore stay correctly aligned when the prefix contains full-width characters such as ideographic spaces or CJK punctuation.