Skip to content

Fix multi-line selection creating separate code blocks per line#912

Open
samuelpecher wants to merge 2 commits intomainfrom
fix-multiline-code-block
Open

Fix multi-line selection creating separate code blocks per line#912
samuelpecher wants to merge 2 commits intomainfrom
fix-multiline-code-block

Conversation

@samuelpecher
Copy link
Copy Markdown
Collaborator

Summary

  • Fixed selecting multiple lines and applying code block format — now creates a single multiline code block instead of separate blocks per line
  • Toggle-off splits code block content back into separate paragraphs
  • Added tests for both applying and toggling code blocks on multi-line selections

Fixes Fizzy card #5029: Making multiple lines a code block not working as expected

Selecting multiple lines and pressing the Code block button was creating
one code block per line instead of a single code block containing all
selected lines. The old implementation used $setBlocksType which wraps
each block individually.

Rewrite toggleCodeBlock() to collect all top-level elements in the
selection and merge them into a single CodeNode separated by line
breaks, mirroring how toggleBlockquote() already works. Toggle-off
splits the code block content at line breaks back into separate
paragraphs.
Copilot AI review requested due to automatic review settings March 23, 2026 16:16
@samuelpecher samuelpecher self-assigned this Mar 23, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes block-level code formatting so multi-line selections produce a single multiline code block (instead of one code block per line), and toggling the code block off restores separate paragraphs.

Changes:

  • Update toggleCodeBlock behavior to merge multiple selected blocks into a single CodeNode, and to unwrap multiline code blocks back into paragraphs.
  • Add browser tests covering multiline code-block apply + toggle-off behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/editor/contents.js Reworks code-block toggling to merge selected top-level blocks into one code block and unwrap back to paragraphs.
test/browser/tests/formatting/inline_formatting.test.js Adds Playwright coverage for multiline code-block creation and toggle-off restoration.

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.

editor,
"<p>Line one</p><p>Line two</p><p>Line three</p>",
)
})
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new multiline code-block tests cover plain paragraphs, but they don’t exercise selections inside nested block structures (e.g., multiple paragraphs within a blockquote, or multiple list items). Given the new code path builds the code block from topLevelElements, adding at least one regression test for a multiline selection inside a blockquote and/or list would help ensure line boundaries are preserved for those common structures.

Suggested change
})
})
test("selecting multiple paragraphs inside a blockquote and toggling code block creates a single code block", async ({ page, editor }) => {
await editor.setValue("<blockquote><p>Line one</p><p>Line two</p></blockquote>")
await editor.selectAll()
await page.getByRole("button", { name: "Code" }).click()
await assertEditorHtml(
editor,
'<pre data-language="plain" data-highlight-language="plain">Line one<br>Line two</pre>',
)
})

Copilot uses AI. Check for mistakes.
Comment on lines +309 to +314
children.forEach(child => {
if ($isTextNode(child)) {
codeNode.append($createTextNode(child.getTextContent()))
} else if ($isLineBreakNode(child)) {
codeNode.append($createLineBreakNode())
} else {
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#applyCodeBlockFormat assumes each entry in topLevelElements is a leaf block (e.g., a paragraph). If the selection is inside a container top-level element (e.g., a <blockquote> / QuoteNode or a list), element.getChildren() returns nested block nodes and the code currently appends each child’s getTextContent() back-to-back without inserting line breaks between them. That collapses multiple paragraphs/list items into a single line in the resulting code block. Consider flattening the selection to the actual block nodes you want to convert (paragraphs/list items/etc.) and inserting $createLineBreakNode() between those blocks, or apply $setBlocksType first and then merge adjacent CodeNodes so nested structures preserve line boundaries.

Suggested change
children.forEach(child => {
if ($isTextNode(child)) {
codeNode.append($createTextNode(child.getTextContent()))
} else if ($isLineBreakNode(child)) {
codeNode.append($createLineBreakNode())
} else {
children.forEach((child, childIndex) => {
if ($isTextNode(child)) {
codeNode.append($createTextNode(child.getTextContent()))
} else if ($isLineBreakNode(child)) {
codeNode.append($createLineBreakNode())
} else {
if (childIndex > 0) {
codeNode.append($createLineBreakNode())
}

Copilot uses AI. Check for mistakes.
When toggling a code block on a blockquote (or list) containing multiple
paragraphs, the content was collapsed into a single line because
#applyCodeBlockFormat only iterated top-level elements without descending
into containers.

Two fixes:
- In toggleCodeBlock, unwrap non-paragraph/non-code container elements
  (using the existing #unwrap method) and re-gather top-level elements
  before merging into a code block.
- In hasSelectedWordsInSingleLine, detect when anchor and focus are in
  different block-level children of the same top-level element (e.g. two
  paragraphs inside a blockquote) so the code button correctly triggers
  code block mode instead of inline code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants