Skip to content
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

Fix delete behaviour when selection ends in void element #3990

Merged
merged 3 commits into from
Mar 31, 2021

Conversation

songeunyou
Copy link
Contributor

@songeunyou songeunyou commented Nov 20, 2020

Is this adding or improving a feature or fixing a bug?

Fixing a bug

What's the new behavior?

Using Cmd+A -> Delete properly deletes the selection when the last element in the editor is a void element or a void element followed by a newline.

Slate Issue 3456

Intuitive cursor position after delete (Addressing [#3868]) This fix allows void nodes to be deleted correctly and places the cursor on a newline after deletion.

void-deletion

How does this change work?

Previously, when using Cmd+A -> Delete, selections ending with a void element had their endpoint at offset: 0 (before the void element), causing Editor.unhangRange to trigger and omit the last elements when executing delete.

Now, Transforms.delete checks that the last element is not a void element before calling Editor.unhangRange. If it is a void element, Editor.unhangRange is skipped, allowing the selection to be deleted properly.

Additionally, voids is passed as true to Editor.nodes within Editor.unhangRange so that void elements will be iterated through while unhanging. This resolves the issue of buggy deletion on void elements followed by a newline and does not affect selections without void elements.

Two new tests were added to address these changes. The output of the test delete-void-and-newline-at-end has an extra <block/> than would be expected; this is another small bug that occurs when deleting selections ending in a newline. However, that is an issue that should be addressed in a separate PR.

Have you checked that...?

  • The new code matches the existing patterns and styles.
  • The tests pass with yarn test.
  • The linter passes with yarn lint. (Fix errors with yarn fix.)
  • The relevant examples still work. (Run examples with yarn start.)

Does this fix any issues or need any specific reviewers?

Fixes two sub-issues mentioned in: [#3456]
Fixes: [#4005]
Fixes adjacent issue: [#3868]

- Include void node in range
- Add delete-void-at-end test
- Add delete-void-and-newline-at-end test
@@ -1265,6 +1265,13 @@ export const Editor = {
at: end,
match: n => Editor.isBlock(editor, n),
})

// If last element is a void node, unhang range by including void node in range
if (Editor.isVoid(editor, endBlock[0])) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am not sure this the best solution. Should we just simply not unhang the range in Transforms.delete if the selection end is inside a void node? I think maybe we should handle this in Transforms.delete vs. changing the default behavior of unhangRange? Let's pair on it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought there were some path issues before, but I didn't realize that they're handled further down in Transforms.delete. I've moved the end void node handling to Transforms.delete so that it now skips unhangRange. If you think there are still changes that could be made I'm happy to pair. Thanks for the feedback!

- remove end void node handling from Editor.unhangRange
@@ -1274,7 +1275,7 @@ export const Editor = {
at: before,
match: Text.isText,
reverse: true,
voids,
voids: true,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we might want to leave this as a variable and perhaps change how Transforms.delete is called in slate-react instead.

Comment on lines +73 to +81
const [, end] = Range.edges(at)
const endBlock = Editor.above(editor, {
at: end,
match: n => Editor.isBlock(editor, n),
})

if (endBlock && !Editor.isVoid(editor, endBlock[0])) {
at = Editor.unhangRange(editor, at, { voids })
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this might result in another set of bugs that we don't really intend to introduce. What if the endBlock is a void but it's not at the end of the document? Then, the void node would be deleted because the range is not "unuhung". Are there test cases for hanging ranges with voids in the middle of a document? If not, we should likely add a test case(s). And, maybe we consider doing something like the below:

Suggested change
const [, end] = Range.edges(at)
const endBlock = Editor.above(editor, {
at: end,
match: n => Editor.isBlock(editor, n),
})
if (endBlock && !Editor.isVoid(editor, endBlock[0])) {
at = Editor.unhangRange(editor, at, { voids })
}
const [, end] = Range.edges(at)
const endDoc = Editor.end(editor, []);
if (!Point.equals(end, endDoc)) {
at = Editor.unhangRange(editor, at, { voids })
}

Need to think about this behavior a bit more. The above might not be exactly what we want. We should pair.

@lukesmurray
Copy link
Contributor

Potentially related to this. In next, if the last element is a void element, then you click away, and refocus, the void element gets mouse focus and you can't edit or do anything until you use the arrow keys to navigate away from the void element.

@songeunyou
Copy link
Contributor Author

@lukesmurray what is "next" in reference to? Also, will take a look into this + finish up this fix this weekend, thanks for the comment.

@lukesmurray
Copy link
Contributor

slate@next published on npm. Version 0.60.2.

@songeunyou
Copy link
Contributor Author

@BrentFarese, I looked into the default voids behavior in Transforms.delete that was raised during our pairing session, and the current implementation is correct (voids: false denotes that text within void elements can be deleted). Changing it introduces further issues.

The current changes in this PR still fixes all of the issues, so I would appreciate a final review + merge!

@ulion
Copy link
Contributor

ulion commented Mar 3, 2021

I encountered similar but a little different issues. here is the case:
<text>|abc</text><inline void/><text>|</text>
If select bc+inline void, it can be deleted, but if select all (abc+inline void), the inline void is not deleted due to the unhangRange method skipped the last text, then skipped the inline void since it does not match Text.isText, finally after unhang it got only 'abc'. That is definitely incorrect unhang result. despite start.offset check in unhangRange is no reason. the inline void should not be removed from selection, nor the inline void be counted as anything hanging.
So, what's proper fix for this?

@ianstormtaylor
Copy link
Owner

/rebase

@ianstormtaylor ianstormtaylor merged commit 17b0bc5 into ianstormtaylor:main Mar 31, 2021
e1himself added a commit to prezly/slate that referenced this pull request Oct 20, 2021
There's a slight change in behavior, but we accept it.

Also there was a regression in how slate handles void nodes in 0.63,
but then it was fixed somewhere between 0.63 and 0.66.
See ianstormtaylor/slate#3990
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.

5 participants