Support raw HTML <br> line breaks in the Markdown renderer#13870
Open
fbartho wants to merge 9 commits into
Open
Support raw HTML <br> line breaks in the Markdown renderer#13870fbartho wants to merge 9 commits into
fbartho wants to merge 9 commits into
Conversation
…v#13732) Warp's Markdown viewer rendered a raw <br> as literal text in both paragraphs and GFM pipe-table cells. Recognize <br>, <br/>, and <br /> (case-insensitive) as an inline token that produces a forced line break. The break is stored as a newline fragment: both the paragraph and table-cell render paths already turn an embedded newline into a real visual break, so no rendering changes are needed. The newline/tab/pipe delimited serialization paths (inline_to_markdown, to_plain_text) translate the break back to <br> so an in-cell break does not corrupt table row/column structure. Attributes (e.g. <br class="x">) and malformed forms (<br with no >, < br>, </br>) deterministically fall through to literal text.
A raw HTML <br> at the end of a GFM pipe-table cell parses to a trailing "\n" fragment, but layout_run strips a trailing newline out of the cell text so the break was silently dropped — the cell collapsed to a single line instead of rendering the trailing empty line GitHub shows. measure_table_cells now honors layout_run's has_new_line return: when the final run ends in a stripped newline, re-append it to the cell text so both the measure pass and the render pass emit the trailing empty line. Embedded breaks (interior newline) were never stripped and are unchanged.
The editor's table export path (BufferMarkdownParser::serialize_table_to_gfm_markdown) uses a local inline_to_markdown that emitted fragment text unchanged, so an in-cell hard break (raw HTML <br>, stored as an embedded newline) was written as a raw newline that split the pipe-table row. Apply the same newline-to-<br> conversion the markdown_parser serializer already performs, guarded on non-inline-code fragments.
TableCellOffsetMap::from_inline_and_source walked source char-by-char against rendered text, assuming a 1:1 correspondence. A raw HTML <br> is 4+ source chars but a single rendered newline, so the walk desynced: offsets after an in-cell break mapped past the break instead of to their true source positions, corrupting cursor rects, selection highlights, and clipboard slices for post-break text. Walk each fragment in newline-delimited segments, emitting one linear range per segment and consuming the <br>/<br/>/<br /> token as a source gap between segments.
serialize_table_cell_inline wrote a cell fragment's text verbatim, so an authored hard break (stored as an embedded newline) landed as a raw newline inside <td>. HTML collapses that to a space, silently dropping the in-cell break when copying a table as HTML. Split the fragment on newlines and emit a <br> element between the parts, mirroring the sibling guard in inline_to_markdown (ca6a4dd). Inline code is left verbatim since it can never contain such a newline.
clipboard_table_text_in_range flattened each cell with inline_to_text, which emits an authored hard break (stored as an embedded newline) as a raw newline. Inside the tab/newline-delimited clipboard format that raw newline splits the row, so pasting a copied table back into Warp re-parsed a spurious extra row. Translate the break back to a literal <br> when building each cell slice, scoped to the table-cell context rather than changing inline_to_text globally (other callers may legitimately want the newline).
The parser had no coverage combining <br> with inline style delimiters. Guard that a break inside bold, underline, or a link keeps the surrounding style on both sides of the embedded newline.
The plain-text clipboard flavor is rendered text and already strips styling, so an authored hard break must emit a real newline rather than the literal HTML string "<br>" the user never selected. The HTML flavor still carries the break as a real <br> element for Warp-to-Warp round-trip fidelity; the accepted cost is that a plain-text-only re-paste of such a row may split it, a standard TSV-class limitation.
Author
Feedback from original PR:This PR continues #13854 (same branch history; that PR exhausted its automated-review budget). All feedback received there, and everything a deeper self-review pass surfaced, is accounted for below.
|
Author
|
@cla-bot check |
|
The cla-bot has been summoned, and re-checked this pull request! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #13732
The Markdown viewer rendered a raw
<br>as literal text, both mid-paragraph and inside GFM pipe-table cells. Inside a pipe-table cell<br>is the only way to author a forced line break at all, so this was the more painful half of the gap.This teaches the
.mdinline parser to recognize<br>,<br/>, and<br />(case-insensitive) and turn them into a real line break, in ordinary paragraphs and inside GFM table cells.Notes:
<br>so an in-cell break does not corrupt row/column structure on round-trip.<br class="x">) and malformed forms (<brwithout>,< br>,</br>) fall through to literal text.Tested: new unit tests in
markdown_parserpass, clippy and rustfmt clean, and exercised in a local dev build against the issue sample document — paragraphs, consecutive breaks, self-closing/case variants, table cells, and malformed forms all behave as described.Human-focused Test Document + Screenshot