-
Notifications
You must be signed in to change notification settings - Fork 18
I/6123: The MergeTableCells command #290
Conversation
# Conflicts: # src/tableediting.js
Yo, @ckeditor/qa-team ! This needs some deep checks. So far I found (and fixed) some issues related to the direction of a selection (ie I had a bug when selecting from bottom right to top left). You should not be able to merge cells from heading section (rows) with other cells. Other than that everything should be possible, clean table should be preserved (no extra empty cells). Content (images, paragraphs) should be preserved, undo should work, etc. The button is available for now in the merge cells menu. |
We've tested it and everything looks good 👌 |
Oh, forgot about this. I'll create a follow for this case: ckeditor/ckeditor5#6521. The second thing (merging more then selected) - is this a bug or different screenshot? |
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.
Mostly questions.
for ( const tableCell of selectedTableCells ) { | ||
const tableRow = tableCell.parent; | ||
mergeTableCells( tableCell, firstTableCell, writer ); | ||
removeRowIfEmpty( tableRow, writer ); | ||
} |
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.
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'll look into it for a quick win. If I don't manage to find any let's move it to a follow-up.
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.
Quick wins are in API and it doesn't look good. TL;DR: follow-up and performance research.
Fiddling with the merge cells algorithm didn't get me any noticeable performance boost (and AFAICS was adding small time). I've tried to move thing in the first step and them remove other things (cells, rows) in the next step. That didn't helped.
What did help was checking what took the most time and try to optimize those.
Original time: _undo
650ms:
The is
time is from RootElement.is()
- there's a .replace
called on a string. I've removed that:
After removing this: _undo
took 566 ms:
And self time of is
dropped by ~100ms. The second one was in Position
constructor. Here we do .concat()
of arrays. DevTools marked that line as one that took long time. I've changed that to using spraed operator (wild guess): path = [ ...root.getPath(), ...path ];
and again perf boost, _undo
took 465 ms:
and self-time of Position
constructor dropped by ~100ms.
This is something in line with our performance research but I worry that here we just do a lot of stuff. Many operations that requires re-calculating positions, ranges and doing many transformations.
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.
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.
Congrats! Great findings. Completely valid changes to me. Both Position
and is()
appear in many cases that I've been investigating so those changes are totally worth doing. I'd be also interested in verifying how they affect some of our performance tests (see the manual tests in ckeditor5). Could you do the comparison?
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'll create a separate ticket for them as I did not implement this yet.
src/commands/mergecellscommand.js
Outdated
// Collapsed selection or selection only one range can't contain mergeable table cells. | ||
if ( selection.isCollapsed || selection.rangeCount < 2 ) { | ||
return false; | ||
} | ||
|
||
const selectedTableCells = getSelectionAffectedTableCells( selection ); | ||
|
||
if ( !areCellInTheSameTableSection( selectedTableCells ) ) { | ||
return false; | ||
} |
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.
Wouldn't simple
const selectedTableCells = getSelectedTableCells( ... );
if ( selectedTableCells.length < 2 ) {
return;
}
be enough?
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.
Would be enough but checking selection without calculating stuff is IMO a bit faster. The getSelectedTableCells()
will do all sort of things and here I didn't wanted to calculate all those things.
Probably.
This code is old and I've done some refactoring here already. So WDYT about leaving this as is?
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.
getSelectionAffectedTableCells()
executesgetSelectedTableCells()
anyway (see the code).- But
getSelectionAffectedTableCells()
also returns cells that have a selection inside of them (seegetTableCellsContainingSelection()
), which, if not for the initial condition (selection.isCollapsed || selection.rangeCount < 2)
, would yield invalid cells from the perspective of multi-cell merge. - So considering ☝️ , there's no point in using
getSelectionAffectedTableCells
.getSelectedTableCells
is enough.
- As for the initial condition: I understand that it spares us some unnecessary execution of
getSelectedTableCells()
. - But the condition is weak. For instance, you can have a selection with
selection.rangeCount === 3
in FF, each range spanning over some text in a cell, and that condition will pass.
2.1. It will also pass if anyone create such selection from code.
2.2. (referring to the previous section of this comment): in conjunction withgetSelectionAffectedTableCells()
(instead ofgetSelectedTableCells()
) this will cause bugs - I think it would be better if this condition was moved to
getSelectedTableCells()
as an initial filter to speed things up everywhere, not only here.
To sum up:
- the condition should land at the beginning of
getSelectedTableCells()
(return an empty array iffalse
) as a speed benefit for all the sub-features, - then it's enough to check if
getSelectedTableCells()
has length greater than 1.
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.
Sure fair enough. I've forgot about Firefox.
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've updated the code.
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've updated the code.
Co-Authored-By: Aleksander Nowodzinski <a.nowodzinski@cksource.com>
Suggested merge commit message (convention)
Feature: Introduce
MergeTableCellsCommand
. Closes ckeditor/ckeditor5#6123.Additional information
This should work OK now :)