Skip to content

[lexical] Bug Fix: keep the text mode on every part of a split TextNode - #8924

Open
LeSingh1 wants to merge 1 commit into
facebook:mainfrom
LeSingh1:fix/splittext-preserve-mode
Open

[lexical] Bug Fix: keep the text mode on every part of a split TextNode#8924
LeSingh1 wants to merge 1 commit into
facebook:mainfrom
LeSingh1:fix/splittext-preserve-mode

Conversation

@LeSingh1

@LeSingh1 LeSingh1 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Description

splitText() copies format, style, detail and NodeState onto the parts it creates, but not __mode. The first part reuses the node being split so it keeps its mode; every later part is created as a normal-mode TextNode. Splitting a token node therefore yields a token head followed by editable tails, which are isSimpleText() and can be merged into adjacent text by normalization.

The siblings now inherit the mode of the first part. Segmented nodes are still downgraded to normal, because the first part is already replaced with a plain TextNode in that branch.

Related but distinct: #4167 asks that RangeSelection.formatText not call splitText on token nodes. That's a guard at one caller; this is splitText's own contract losing the mode, which affects every caller ($splitTextPointCaret, drop handling, $insertNodeToNearestRootAtCaret, markdown and autolink transformers). You may want to consider them together.

Test plan

Before

New unit test splitText keeps the token mode on every part fails on main:

[ "token", -   "token" +   "normal" ]

After

Passes; Tests 102 passed (102) in that file. The existing convert segmented node into plain text test is unaffected — the fix reads the mode off the already-created first part, so the segmented branch is unchanged. pnpm run test-unit green (227 files, 4939 passed).

splitText() copies format, style, detail and NodeState onto the parts it
creates but not __mode. Splitting a token TextNode therefore left a token
head followed by normal-mode tails, which are editable and can be merged
into neighbouring text by normalization. Segmented nodes keep their
existing behaviour of being downgraded to normal.
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lexical Ready Ready Preview Aug 4, 2026 8:12pm
lexical-playground Ready Ready Preview Aug 4, 2026 8:12pm

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 4, 2026

@etrepum etrepum left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

AFAIK downgrade of mode here is intentional, special modes are lost when the text is manipulated. I don't think this should be merged without careful consideration of the consequences.

@LeSingh1

LeSingh1 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

That's a fair reading for segmented — the segmented branch replaces the node with a fresh $createTextNode, so the downgrade there is explicit and clearly deliberate.

Token is different, and the reason I filed this is that the current behaviour isn't a downgrade, it's an inconsistency. The non-segmented branch does writableNode = self.setTextContent(firstPart), reusing the node, so part 0 keeps its mode while parts 1..n are fresh normal-mode nodes. Splitting a token node on main gives:

['token', 'normal']

So the invariant "special modes are lost when the text is manipulated" isn't what the code does — it's lost on every part except the first. That leaves a token head glued to editable tails, and the tails are isSimpleText() so normalization can merge them into neighbouring text.

Either consistent answer seems defensible to me and I don't have a stake in which:

  1. Preserve on all parts (this PR).
  2. Downgrade on all parts, matching segmented — writableNode.setMode('normal') in the else branch, one line in the other direction.

Happy to switch this to option 2 if that's the intent. It'd be worth deciding either way, since the callers most affected are internal (formatText, autolink, the markdown transformers, $splitTextPointCaret) rather than a user deliberately breaking a token apart.

@etrepum

etrepum commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

IIRC “lost on every part but the first” is consistent with the expected behavior. I will only be convinced one way or the other if a full audit of every splitText call site when the subject is a TextNode not in normal mode to see if any expectations are violated. Even better evidence would be a demonstration of unexpected behavior in the playground due to the current semantics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants