[lexical-code-core][lexical-code-prism][lexical-code-shiki] Bug Fix: code blocks keep the caret and their own attributes - #9056
Open
LeSingh1 wants to merge 1 commit into
Conversation
…code blocks keep the caret and their own attributes ## Description Code blocks mishandle their own line and selection bookkeeping: the highlighter restores a selection point that the re-highlight has already invalidated, the line-shift and outdent helpers mis-measure a line that is blank or whose caret sits at column 0, and the importer never reads back the `data-theme` that the exporter writes. The user-visible result is a caret that gets dragged into (or thrown out of) a code block whenever it re-highlights or re-indents, and a block that loses its own attributes on an HTML round trip. This consolidates five fixes: - **facebook#8947** — `$updateAndRetainSelection` in `CodeHighlighterPrism.ts` and `CodeHighlighterShiki.ts` walked the code node's children with `.some()` and only decremented the running offset for text nodes, so a `LineBreakNode` at the start of a code line drove `textOffset` negative and the next text node was selected at an out-of-range offset. The walk is now an explicit loop: a `LineBreakNode` consumes one unit of offset, and when the offset lands on one an element point on the code node is used instead of a negative text point. - **facebook#8972** — the same `$updateAndRetainSelection` retained the selection even when the anchor was not inside the code node being highlighted, dragging the caret into a code block after e.g. a markdown import. It now falls back to a plain `updateFn()` unless the anchor is the code node or a descendant of it. - **facebook#8986** — `$handleShiftLines` in `CodeIndentation.ts` accepted a `LineBreakNode` sibling as an insertion-point candidate, but that linebreak belongs to a *different* line, so Alt+Arrow across a blank line spliced the moving line into the line beyond it and merged the two. A blank adjacent line is now detected up front and the range is re-inserted directly after the sibling linebreak, in both directions. - **facebook#8988** — `$convertPreElement` in `CodeNode.ts` and the `pre` / multiline-`code` import rules in `CodeImportExtension.ts` read `data-language` but not `data-theme`, so `exportDOM` wrote a theme that `importDOM` silently dropped. Both now pass `data-theme` through to `$createCodeNode`. - **facebook#8992** — `$getCodeLines` drops a trailing line when the selection ends exactly at its start, which for a collapsed caret is always true, so Shift+Tab at column 0 never reached the outdent loop and silently did nothing. A new `$outdentLineAtCaret` in `CodeIndentation.ts` resolves the line from the anchor and applies the same rule as that loop: strip a leading `TabNode`, or `tabSize` leading spaces when the extension is configured for them. ## Test plan New unit tests per fix: selection-offset retention for both Prism and Shiki, caret placement after a markdown import that ends in a code block, Alt+Arrow across a blank line, `data-theme` import plus an `exportDOM`/`importDOM` round trip, and Shift+Tab at column 0 (tab indent, space indent, a non-zero column, and an unindented no-op control). ### Before Source fixes reverted, new tests kept: ``` $ npx vitest run --project unit packages/lexical-code-core packages/lexical-code-prism packages/lexical-code-shiki FAIL |unit| packages/lexical-code-prism/src/__tests__/unit/CodeHighlighterPrismRetainSelection.test.ts > CodeHighlighterPrism $updateAndRetainSelection > retains a non-negative offset for code content "\nfoo" (facebook#8943) FAIL |unit| packages/lexical-code-prism/src/__tests__/unit/CodeHighlighterPrismRetainSelection.test.ts > CodeHighlighterPrism $updateAndRetainSelection > retains a non-negative offset for code content "\n\nfoo" (facebook#8943) FAIL |unit| packages/lexical-code-prism/src/__tests__/unit/CodePrismRetainSelection.test.ts > Prism highlighting only retains a selection it owns (facebook#6305) > importing markdown that ends in a code block leaves the caret at the document start FAIL |unit| packages/lexical-code-prism/src/__tests__/unit/CodePrismRetainSelection.test.ts > Prism highlighting only retains a selection it owns (facebook#6305) > the caret is not dragged into the last of several imported code blocks FAIL |unit| packages/lexical-code-shiki/src/__tests__/unit/CodeShikiRetainSelection.test.ts > CodeHighlighterShiki $updateAndRetainSelection > retains a non-negative offset for code content "\nfoo" (facebook#8943) FAIL |unit| packages/lexical-code-shiki/src/__tests__/unit/CodeShikiRetainSelection.test.ts > CodeHighlighterShiki $updateAndRetainSelection > retains a non-negative offset for code content "\n\nfoo" (facebook#8943) FAIL |unit| packages/lexical-code-shiki/src/__tests__/unit/CodeShikiRetainSelection.test.ts > Shiki highlighting only retains a selection it owns (facebook#6305) > importing markdown that ends in a code block leaves the caret at the document start FAIL |unit| packages/lexical-code-shiki/src/__tests__/unit/CodeShikiRetainSelection.test.ts > Shiki highlighting only retains a selection it owns (facebook#6305) > the caret is not dragged into the last of several imported code blocks FAIL |unit| packages/lexical-code-core/src/__tests__/unit/CodeImportExtension.test.ts > CodeImportExtension > <pre data-theme="poimandres"> restores the theme FAIL |unit| packages/lexical-code-core/src/__tests__/unit/CodeImportExtension.test.ts > CodeImportExtension > multi-line <code data-theme> restores the theme FAIL |unit| packages/lexical-code-core/src/__tests__/unit/CodeIndentation.test.ts > CodeIndentExtension > shiftLines > moves a line up past a blank line without merging it into the line above FAIL |unit| packages/lexical-code-core/src/__tests__/unit/CodeIndentation.test.ts > CodeIndentExtension > shiftLines > moves a line down past a blank line without merging it into the line below FAIL |unit| packages/lexical-code-core/src/__tests__/unit/CodeNode.test.ts > CodeNode > round-trips the theme through exportDOM/importDOM FAIL |unit| packages/lexical-code-core/src/__tests__/unit/CodeOutdentCollapsedAtLineStart.test.ts > OUTDENT_CONTENT_COMMAND at the start of a code line > outdents when the caret is collapsed at column 0 (on the TabNode) FAIL |unit| packages/lexical-code-core/src/__tests__/unit/CodeOutdentCollapsedAtLineStart.test.ts > OUTDENT_CONTENT_COMMAND at the start of a code line > outdents when the caret is collapsed at column 0 of the code text FAIL |unit| packages/lexical-code-core/src/__tests__/unit/CodeOutdentCollapsedAtLineStart.test.ts > OUTDENT_CONTENT_COMMAND at the start of a code line > strips a space indent from column 0 when tabSize is configured Test Files 7 failed | 7 passed (14) Tests 16 failed | 218 passed (234) ``` ### After ``` $ npx vitest run --project unit packages/lexical-code-core packages/lexical-code-prism packages/lexical-code-shiki Test Files 14 passed (14) Tests 234 passed (234) $ npx tsc --noEmit -p . (clean) ``` Supersedes facebook#8947, facebook#8972, facebook#8986, facebook#8988, facebook#8992, consolidated per the review feedback on facebook#9027 and facebook#9035.
LeSingh1
requested review from
acywatson,
etrepum,
fantactuka,
ivailop7,
potatowagon and
zurfyx
as code owners
August 10, 2026 04:13
|
@LeSingh1 is attempting to deploy a commit to the Meta Open Source Team on Vercel. A member of the Team first needs to authorize it. |
This was referenced Aug 10, 2026
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.
Description
Code blocks mishandle their own line and selection bookkeeping: the highlighter
restores a selection point that the re-highlight has already invalidated, the
line-shift and outdent helpers mis-measure a line that is blank or whose caret
sits at column 0, and the importer never reads back the
data-themethat theexporter writes. The user-visible result is a caret that gets dragged into (or
thrown out of) a code block whenever it re-highlights or re-indents, and a block
that loses its own attributes on an HTML round trip.
This consolidates five fixes:
[lexical-code-prism][lexical-code-shiki] Bug Fix: don't restore a negative selection offset when a code line starts with a line break #8947 —
$updateAndRetainSelectioninCodeHighlighterPrism.tsandCodeHighlighterShiki.tswalked the code node's children with.some()andonly decremented the running offset for text nodes, so a
LineBreakNodeatthe start of a code line drove
textOffsetnegative and the next text nodewas selected at an out-of-range offset. The walk is now an explicit loop: a
LineBreakNodeconsumes one unit of offset, and when the offset lands on onean element point on the code node is used instead of a negative text point.
[lexical-code-prism][lexical-code-shiki] Bug Fix: don't drag the caret into a code block while highlighting #8972 — the same
$updateAndRetainSelectionretained the selection evenwhen the anchor was not inside the code node being highlighted, dragging the
caret into a code block after e.g. a markdown import. It now falls back to a
plain
updateFn()unless the anchor is the code node or a descendant of it.[lexical-code-core] Bug Fix: Alt+Arrow no longer merges two lines when a code block line is blank #8986 —
$handleShiftLinesinCodeIndentation.tsaccepted aLineBreakNodesibling as an insertion-point candidate, but that linebreakbelongs to a different line, so Alt+Arrow across a blank line spliced the
moving line into the line beyond it and merged the two. A blank adjacent line
is now detected up front and the range is re-inserted directly after the
sibling linebreak, in both directions.
[lexical-code-core] Bug Fix: restore the code block theme from data-theme on import #8988 —
$convertPreElementinCodeNode.tsand thepre/multiline-
codeimport rules inCodeImportExtension.tsreaddata-languagebut not
data-theme, soexportDOMwrote a theme thatimportDOMsilentlydropped. Both now pass
data-themethrough to$createCodeNode.[lexical-code-core] Bug Fix: Shift+Tab outdents a code line when the caret is at column 0 #8992 —
$getCodeLinesdrops a trailing line when the selection endsexactly at its start, which for a collapsed caret is always true, so Shift+Tab
at column 0 never reached the outdent loop and silently did nothing. A new
$outdentLineAtCaretinCodeIndentation.tsresolves the line from theanchor and applies the same rule as that loop: strip a leading
TabNode, ortabSizeleading spaces when the extension is configured for them.Test plan
New unit tests per fix: selection-offset retention for both Prism and Shiki,
caret placement after a markdown import that ends in a code block, Alt+Arrow
across a blank line,
data-themeimport plus anexportDOM/importDOMroundtrip, and Shift+Tab at column 0 (tab indent, space indent, a non-zero column,
and an unindented no-op control).
Before
Source fixes reverted, new tests kept:
After
Supersedes #8947, #8972, #8986, #8988, #8992, consolidated per the review
feedback on #9027 and #9035.