-
Notifications
You must be signed in to change notification settings - Fork 11
Mention post-fixer now correctly handles undo. #17
Conversation
Three things:
|
AFAIR it's an edge-case when selection is after mention and you remove last character (ie |
Oh, ok, you meant "last" as "right-most" not "the only" :D. Yeah, I checked it and you are right. |
So, in this case, we also need a check for the "left-most" character and I'd like to see a more clear comment. Use "left-most" and "right-most" or "at the beginning" and "at the end" instead of "first" and "last" to make it non-ambiguous. Also, considering the other changes, I think that maybe structuring the post-fixer this way will be better: for ( ... ) {
if ( change.type == 'insert' ) {
// Logic for insertions of text and non-text inside the mention.
// And also looking for partial mentions in inserted content.
} else if ( change.type == 'remove' ) {
// Logic for removing of text inside, at the beginning and at the end of a mention.
}
} |
…ents and splitting parent.
…inside a mention.
…redundant condition check.
@scofalik: Ready to re-review.
|
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.
Some changes required :P
Other than that I miss two things in tests:
- In attribute post-fixer, I'd like to see if unbolding also works correctly (you can add it to the existing unit tests).
- I am missing clipboard test (pasting something like
<blockquote><p>Foo bar</p><p>@jod</p></blockquote>
.
src/mentionediting.js
Outdated
} | ||
|
||
// Check text nodes on inserted elements (might occur when splitting paragraph on enter key). | ||
if ( change.name != '$text' && change.type == 'insert' && schema.checkChild( change.name, '$text' ) ) { |
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.
schema.checkChild()
looks like a premature optimization. Is it needed? Does not having it produce any bugs?
Also, I think this might be incorrect, for example what if following is pasted:
<blockquote><p>@jod</p><p>Foo</p></blockquote>
blockQuote
cannot contain text but there is still text node somewhere inside.
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.
But this is fixed by upcast converter. This check is for splitting parent element with mention when split occurred inside text with mention. As such there is no need to check other cases then the elements that allow text inside.
if ( change.type == 'remove' ) { | ||
const nodeBefore = change.position.nodeBefore; | ||
// Inserted inline elements might break mention. | ||
if ( change.type == 'insert' && schema.isInline( change.name ) ) { |
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.
After resolving the above comment this might be the last usage of schema
in this postfixer. If this is for optimization purposes, I'd consider removing this and removing the need of passing schema
here. Keep it simple.
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.
checkAttributeMentionOnNode
already checks if the node is text so it will be safe.
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.
no it explicitly checks for inserted in-line elements like <softBreak>
which may break text (with mention.
src/mentionediting.js
Outdated
if ( change.name == '$text' && textNode && textNode.hasAttribute( 'mention' ) ) { | ||
writer.removeAttribute( 'mention', textNode ); | ||
wasChanged = true; | ||
for ( const child of insertedNode.getChildren() ) { |
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.
Same as above, if a deep structure was pasted, you need to go deeper.
for ( const item of writer.createRangeIn( insertedNode ) ) { ... }
src/mentionediting.js
Outdated
// Yield text nodes with broken mention from the range. | ||
for ( const textProxy of range.getItems() ) { | ||
if ( !checkMentionAttributeOnNode( textProxy ) ) { | ||
yield textProxy.textNode; |
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 understand that this is covered by unit tests but I wonder out of curiosity when this happens?
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 mean, if that happens for mentions that are not on the boundary of the range. Some something like [foo<mention />bar]
.
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.
So this is used in attribute post-fixer:
fo[o <$text mention="{...}">men]tion</$text>
So when the range on which bold is applied is ends inside a mention. Then I want to get that dangling mention node on the right(left) side of range.end(start).
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.
So isn't it that we are always interested in nodes next to range boundaries?
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.
When applying attribute on this range yes.
Co-Authored-By: jodator <jodator@gmail.com>
Co-Authored-By: jodator <jodator@gmail.com>
Co-Authored-By: jodator <jodator@gmail.com>
@scofalik I've resolved all the issues from your comments. I would leave those optimizations are they checks cases that might break mention during editing. The pasting part of nested nodes is done by upcast converter - it does not allow partial mentions. I've added tests for undo integration with attribute post-fixer. |
Typically, I prefer having general solutions that cover the whole problem instead of directly solving the predicted cases. This is safer (and, in my opinion, correct) way to go. For example, I just had to fix a bug in view writer because some behavior was predicted only for one case while it could simply be inside a This is why I have mixed feelings about it. I don't want to introduce redundant code that might never be used. But I also don't want to introduce potentially bad code. For now, if I didn't miss anything, the code is fine. However, if there is ever a feature (widget) that breaks blockquote, there will be a problem: <blockQuote><paragraph><$text mention="jodator">@jod[]ator</$text></paragraph></blockQuote> Paste the widget that can be only in the root at <blockQuote><paragraph><$text mention="jodator">@jod</$text></paragraph></blockQuote>
<hypotethicalWidget />
<blockQuote><paragraph><$text mention="jodator">ator</$text></paragraph></blockQuote> Or when a 3rd party plugin will simply split both Anyway, it is hypothetical for now. I tried to think about cases in OT and block lists (the thing we plan to introduce, where you will be able to, for example, add multiple paragraphs, headers, maybe tables to list items) but I don't know if anything there will break this behavior. So as I said, I have mixed feelings. I summon @Reinmar . |
…range boundaries.
OK valid TC. I've manged to do it with
and split:
I'll update the PR for deep check. |
Fix: Mention post-fixer now correctly handles partial mentions on insert, remove and paste. Closes ckeditor/ckeditor5#4629. Closes ckeditor/ckeditor5#4622.