Fix pasting text with line breaks into a code block#905
Fix pasting text with line breaks into a code block#905jorgemanrubia wants to merge 2 commits intomainfrom
Conversation
When pasting plain text containing line breaks into a code block, the text after the first line break would escape the code block and land in a new paragraph. This happened because the clipboard handler bailed out entirely when inside a code block, letting Lexical's default paste handler create paragraph nodes that broke out of the code node. Instead of deferring to Lexical, handle code block paste explicitly by splitting the plain text on newlines and inserting TextNode/LineBreakNode pairs directly. This keeps all pasted lines inside the code block.
There was a problem hiding this comment.
Pull request overview
Fixes a paste-handling gap where pasting multi-line plain text into a Lexical code block could break out into a sibling paragraph by explicitly inserting TextNode + LineBreakNode sequences into the active code block.
Changes:
- Add explicit “paste into code block” path in the clipboard handler (inserts line breaks as
LineBreakNodes). - Add a Playwright regression test ensuring multi-line paste remains contained within the code block.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| test/browser/tests/paste/paste.test.js | Adds a regression test for multi-line plain-text paste inside a code block. |
| src/editor/clipboard.js | Implements custom code-block paste handling to keep newline-separated text within the code node. |
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/editor/clipboard.js
Outdated
| if (this.#isPastingIntoCodeBlock()) { | ||
| this.#pasteIntoCodeBlock(clipboardData) | ||
| event.preventDefault() | ||
| return true | ||
| } |
There was a problem hiding this comment.
paste() unconditionally calls preventDefault() and returns true when inside a code block, even if #pasteIntoCodeBlock() ends up doing nothing (e.g., clipboard has no text/plain). This can swallow pastes like files/HTML and result in no-op paste behavior in code blocks. Consider having #pasteIntoCodeBlock() return a boolean (handled vs not), and only preventDefault()/return true when it actually inserts nodes; otherwise fall through to the existing rich text / file handling.
| const lines = text.split(/\n/) | ||
| const nodes = [] | ||
|
|
||
| for (let i = 0; i < lines.length; i++) { | ||
| if (i > 0) nodes.push($createLineBreakNode()) | ||
| if (lines[i]) nodes.push($createTextNode(lines[i])) | ||
| } |
There was a problem hiding this comment.
Splitting pasted text with text.split(/\n/) will leave stray \r characters in the inserted text when the clipboard uses Windows newlines (\r\n). Normalizing first (e.g., split on /\r?\n/ or replace \r\n with \n) will prevent carriage returns from appearing in code blocks.
Summary
return false), deferring to Lexical's default paste handler which creates paragraph nodes that break out of the code nodeTextNode/LineBreakNodepairs directly into the code block, keeping all pasted lines insideFizzy card #4979