-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[lexical][lexical-overflow] Refactor: simplified removeText and insertText rewrite (part 1) #6456
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
size-limit report 📦
|
|
||
isInline(): boolean { | ||
return true; | ||
} | ||
|
||
static transform(): (node: LexicalNode) => void { | ||
return (node: LexicalNode) => { | ||
invariant($isOverflowNode(node), 'node is not a OverflowNode'); | ||
if (node.isEmpty()) { | ||
node.remove(); | ||
} | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's likely that insert/removeText
currently has some logic that prevents this. If it works in all cases, I don't know.
Ensuring that an overFlowNode cannot be empty in lexical-overflow is a more robust and less error-prone solution, and it frees up the load on other packages.
Due to this change, it can be understood why the eliminated unit test is no longer necessary.
// Now that "removeText" has been improved and does not depend on | ||
// insertText, insertText can be greatly simplified. The next | ||
// commented version is a WIP (about 5 tests fail). | ||
// | ||
// this.removeText(); | ||
// if (text === '') { | ||
// return; | ||
// } | ||
// const anchorNode = this.anchor.getNode(); | ||
// const textNode = $createTextNode(text); | ||
// textNode.setFormat(this.format); | ||
// textNode.setStyle(this.style); | ||
// if ($isTextNode(anchorNode)) { | ||
// const parent = anchorNode.getParentOrThrow(); | ||
// if (this.anchor.offset === 0) { | ||
// if (parent.isInline() && !anchorNode.__prev) { | ||
// parent.insertBefore(textNode); | ||
// } else { | ||
// anchorNode.insertBefore(textNode); | ||
// } | ||
// } else if (this.anchor.offset === anchorNode.getTextContentSize()) { | ||
// if (parent.isInline() && !anchorNode.__next) { | ||
// parent.insertAfter(textNode); | ||
// } else { | ||
// anchorNode.insertAfter(textNode); | ||
// } | ||
// } else { | ||
// const [before] = anchorNode.splitText(this.anchor.offset); | ||
// before.insertAfter(textNode); | ||
// } | ||
// } else { | ||
// anchorNode.splice(this.anchor.offset, 0, [textNode]); | ||
// } | ||
// const nodeToSelect = textNode.isAttached() ? textNode : anchorNode; | ||
// nodeToSelect.selectEnd(); | ||
// // When composing, we need to adjust the anchor offset so that | ||
// // we correctly replace that right range. | ||
// if ( | ||
// textNode.isComposing() && | ||
// this.anchor.type === 'text' && | ||
// anchorNode.getTextContent() !== '' | ||
// ) { | ||
// this.anchor.offset -= text.length; | ||
// } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once the failing tests are fixed and this implementation is activated, 364 lines of code can be removed ($transferStartingElementPointToTextPoint
is only used in the current implementation of insertText
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you expect to add that to this PR, or after this one lands?
done @etrepum |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could use another set of eyes on this one since it's such a fundamental change to removeText
Let's do 0.17.1 first and then do this one as a 0.18 with the defaults change. |
@@ -96,6 +96,18 @@ export class CollapsibleTitleNode extends ElementNode { | |||
return true; | |||
} | |||
|
|||
static transform(): (node: LexicalNode) => void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we adding this method to this specific node, when deletion of this one here happens on container node-level?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not always. If you remove this you will see that some tests fail because there may be empty CollapsibleTitleNodes.
Apparently, it wasn't necessary because it was one of the many things that insertText
did.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, let's update those tests and remove the transform part from the node, since when people copy-paste the Playground it would create more confusion, if it's purely there for tests to pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think both may be necessary for correct behavior, not just tests. The container won't be empty if it has an empty title since it's measured by children and not text, so the empty title is removed first and then the next pass removes the container
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's not just about the tests. I don't see anything wrong or confusing here. It's a pattern that other nodes use.
When I said "it wasn't necessary because it was one of the many things that insertText did", I meant that insertText
solved this for certain cases, but we don't know what other situations weren't covered. This solution is more robust and secure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the part that confuses me, the Layout plugin should be in the same boat by that rule, since it's near identical implementation to the Collapsible Plugin, unless it's purely because we are missing tests for it and the transform method should be added to the Layout plugin as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a general rule I think it's a good idea to use transforms to make sure that nodes that should be used together can't be orphaned.
I don't know what the layout plugin is but I think improving that would be out of scope of this PR.
PS: I updated the branch and there is a flaky test that failed. Can you please restart it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in general we are missing transforms for a handful of nodes to guarantee correct behavior, e.g. the merge-unmerge issue with TableCellNode not having paragraph children. The static transform method is experimental and new-ish, previously you had to have the correct editor and/or plugin configuration to have all of the required transforms. I would agree that adding other transforms is out of scope for this PR, unless the previous removeText implementation also handled them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving, trusting you both you have this planned out. Haven't spent much time getting the whole context here.
I am a bit late to the party but the changes here break removal of TextNodes in |
Do you mind creating a new issue so that this isn't lost? |
Isn't that supposedly the correct behavior, or am I missing something? Are you sure it worked differently before? There were a lot of those tests I had to pass. |
Chose an unfortunate example here, the mention node created in the GIF is in Anyway, our users can insert placeholder nodes, which are basically a custom text node in According to the docs:
Before these changes, For |
This is something I've been wanting to do since I did the insertNodes rewrite. These are also methods with long and very complex implementations.
Currently, removeText depends on insertText, since removeText is defined as inserting an empty string (
insertText("")
). This PR seeks to reverse the dependency of these methods, making insertText dependent on removeText.This can be achieved in 2 steps:
removeText
without making it dependent on insertText.insertText
with the new removeText.So far in this PR, I have managed to complete step 1 and 95% of step 2. I decided to leave step 2 for a later PR because there are about 4 tests that fail and, after having dedicated so much time to it, I want to do a pause on this.