Explorar el Código

Merge pull request #7253 from ckeditor/i/6769-table-paste-repeat-pattern

Internal (table): Repeat pasted table content when pasting into a bigger table part is selected. Closes #6769.
Maciej hace 5 años
padre
commit
e3e9521486

+ 21 - 21
packages/ckeditor5-table/src/tableclipboard.js

@@ -22,7 +22,7 @@ import {
 	splitVertically
 } from './utils';
 import { findAncestor } from './commands/utils';
-import { cropTableToDimensions } from './tableselection/croptable';
+import { cropTableToDimensions, trimTableCellIfNeeded } from './tableselection/croptable';
 import TableUtils from './tableutils';
 
 /**
@@ -191,15 +191,6 @@ export default class TableClipboard extends Plugin {
 			const selectionHeight = lastRowOfSelection - firstRowOfSelection + 1;
 			const selectionWidth = lastColumnOfSelection - firstColumnOfSelection + 1;
 
-			// The if below is temporal and will be removed when handling this case.
-			// This if to be removed as handling of replicating cells should be done in replaceSelectedCellsWithPasted().
-			// See: https://github.com/ckeditor/ckeditor5/issues/6769.
-			if ( selectionHeight > pasteHeight || selectionWidth > pasteWidth ) {
-				// @if CK_DEBUG // console.log( 'NOT IMPLEMENTED YET: Pasted table is smaller than selection area.' );
-
-				return;
-			}
-
 			// Crop pasted table if:
 			// - Pasted table dimensions exceeds selection area.
 			// - Pasted table has broken layout (ie some cells sticks out by the table dimensions established by the first and last row).
@@ -216,16 +207,18 @@ export default class TableClipboard extends Plugin {
 
 			pastedTable = cropTableToDimensions( pastedTable, cropDimensions, writer, tableUtils );
 
+			const pastedDimensions = {
+				width: pasteWidth,
+				height: pasteHeight
+			};
 			const selectionDimensions = {
 				firstColumnOfSelection,
 				firstRowOfSelection,
 				lastColumnOfSelection,
-				lastRowOfSelection,
-				selectionHeight,
-				selectionWidth
+				lastRowOfSelection
 			};
 
-			replaceSelectedCellsWithPasted( pastedTable, selectedTable, selectionDimensions, writer );
+			replaceSelectedCellsWithPasted( pastedTable, pastedDimensions, selectedTable, selectionDimensions, writer );
 		} );
 	}
 }
@@ -233,24 +226,26 @@ export default class TableClipboard extends Plugin {
 // Replaces the part of selectedTable with pastedTable.
 //
 // @param {module:engine/model/element~Element} pastedTable
+// @param {Object} pastedDimensions
+// @param {Number} pastedDimensions.height
+// @param {Number} pastedDimensions.width
 // @param {module:engine/model/element~Element} selectedTable
 // @param {Object} selectionDimensions
 // @param {Number} selectionDimensions.firstColumnOfSelection
 // @param {Number} selectionDimensions.firstRowOfSelection
 // @param {Number} selectionDimensions.lastColumnOfSelection
 // @param {Number} selectionDimensions.lastRowOfSelection
-// @param {Number} selectionDimensions.selectionHeight
-// @param {Number} selectionDimensions.selectionWidth
 // @param {module:engine/model/writer~Writer} writer
-function replaceSelectedCellsWithPasted( pastedTable, selectedTable, selectionDimensions, writer ) {
+function replaceSelectedCellsWithPasted( pastedTable, pastedDimensions, selectedTable, selectionDimensions, writer ) {
 	const {
 		firstColumnOfSelection, lastColumnOfSelection,
-		firstRowOfSelection, lastRowOfSelection,
-		selectionWidth, selectionHeight
+		firstRowOfSelection, lastRowOfSelection
 	} = selectionDimensions;
 
+	const { width: pastedWidth, height: pastedHeight } = pastedDimensions;
+
 	// Holds two-dimensional array that is addressed by [ row ][ column ] that stores cells anchored at given location.
-	const pastedTableLocationMap = createLocationMap( pastedTable, selectionWidth, selectionHeight );
+	const pastedTableLocationMap = createLocationMap( pastedTable, pastedWidth, pastedHeight );
 
 	const selectedTableMap = [ ...new TableWalker( selectedTable, {
 		startRow: firstRowOfSelection,
@@ -294,7 +289,9 @@ function replaceSelectedCellsWithPasted( pastedTable, selectedTable, selectionDi
 		}
 
 		// Map current table slot location to an pasted table slot location.
-		const pastedCell = pastedTableLocationMap[ row - firstRowOfSelection ][ column - firstColumnOfSelection ];
+		const pastedRow = row - firstRowOfSelection;
+		const pastedColumn = column - firstColumnOfSelection;
+		const pastedCell = pastedTableLocationMap[ pastedRow % pastedHeight ][ pastedColumn % pastedWidth ];
 
 		// There is no cell to insert (might be spanned by other cell in a pasted table) - advance to the next content table slot.
 		if ( !pastedCell ) {
@@ -305,6 +302,9 @@ function replaceSelectedCellsWithPasted( pastedTable, selectedTable, selectionDi
 		// Cloning is required to support repeating pasted table content when inserting to a bigger selection.
 		const cellToInsert = pastedCell._clone( true );
 
+		// Trim the cell if it's row/col-spans would exceed selection area.
+		trimTableCellIfNeeded( cellToInsert, row, column, lastRowOfSelection, lastColumnOfSelection, writer );
+
 		let insertPosition;
 
 		if ( !previousCellInRow ) {

+ 14 - 5
packages/ckeditor5-table/src/tableselection/croptable.js

@@ -101,11 +101,20 @@ export function cropTableToDimensions( sourceTable, cropDimensions, writer, tabl
 	return croppedTable;
 }
 
-// Adjusts table cell dimensions to not exceed limit row and column.
-//
-// If table cell span to a column (or row) that is after a limit column (or row) trim colspan (or rowspan)
-// so the table cell will fit in a cropped area.
-function trimTableCellIfNeeded( tableCell, cellRow, cellColumn, limitRow, limitColumn, writer ) {
+/**
+ * Adjusts table cell dimensions to not exceed limit row and column.
+ *
+ * If table cell width (or height) covers a column (or row) that is after a limit column (or row)
+ * this method will trim "colspan" (or "rowspan") attribute so the table cell will fit in a defined limits.
+ *
+ * @param {module:engine/model/element~Element} tableCell
+ * @param {Number} cellRow
+ * @param {Number} cellColumn
+ * @param {Number} limitRow
+ * @param {Number} limitColumn
+ * @param {module:engine/model/writer~Writer} writer
+ */
+export function trimTableCellIfNeeded( tableCell, cellRow, cellColumn, limitRow, limitColumn, writer ) {
 	const colspan = parseInt( tableCell.getAttribute( 'colspan' ) || 1 );
 	const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );
 

+ 57 - 11
packages/ckeditor5-table/tests/manual/tablemocking.js

@@ -14,6 +14,8 @@ import { diffString } from 'json-diff';
 import { debounce } from 'lodash-es';
 import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
 import TableWalker from '../../src/tablewalker';
+import { getSelectionAffectedTableCells } from '../../src/utils';
+import { findAncestor } from '../../src/commands/utils';
 
 ClassicEditor
 	.create( document.querySelector( '#editor' ), {
@@ -38,21 +40,32 @@ ClassicEditor
 		document.getElementById( 'set-model-data' ).addEventListener( 'click', () => {
 			updateInputStatus();
 
+			const table = findTable( editor );
 			const inputModelData = parseModelData( modelData.value );
-			setModelData( editor.model, inputModelData ? modelTable( inputModelData ) : '' );
+
+			if ( inputModelData ) {
+				const element = setModelData._parse( modelTable( inputModelData ), editor.model.schema );
+
+				editor.model.change( writer => {
+					editor.model.insertContent( element, table ? editor.model.createRangeOn( table ) : null );
+					writer.setSelection( element, 'on' );
+				} );
+
+				editor.editing.view.focus();
+			}
 		} );
 
 		document.getElementById( 'get-model-data' ).addEventListener( 'click', () => {
 			updateInputStatus();
 
-			const table = findTable( editor );
+			const table = findTable( editor, true );
 			modelData.value = table ? prettyFormatModelTableInput( prepareModelTableInput( editor.model, table ) ) : '';
 
 			updateAsciiAndDiff();
 		} );
 
 		document.getElementById( 'renumber-cells' ).addEventListener( 'click', () => {
-			const table = findTable( editor );
+			const table = findTable( editor, true );
 
 			if ( !table ) {
 				return;
@@ -75,15 +88,15 @@ ClassicEditor
 		updateAsciiAndDiff();
 
 		function updateAsciiAndDiff() {
-			const table = findTable( editor );
+			const tables = getAllTables( editor );
 
-			if ( !table ) {
+			if ( !tables.length ) {
 				asciiOut.innerText = '-- table not found --';
 				return;
 			}
 
 			const inputModelData = parseModelData( modelData.value );
-			const currentModelData = prepareModelTableInput( editor.model, table );
+			const currentModelData = prepareModelTableInput( editor.model, tables[ 0 ] );
 
 			const diffOutput = inputModelData ? diffString( inputModelData, currentModelData, {
 				theme: {
@@ -93,20 +106,53 @@ ClassicEditor
 				}
 			} ) : '-- no input --';
 
-			asciiOut.innerHTML = createTableAsciiArt( editor.model, table ) + '\n\n' +
-				'Diff: input vs post-fixed model:\n' + ( diffOutput ? diffOutput : '-- no differences --' );
+			const asciiArt = tables
+				.map( table => createTableAsciiArt( editor.model, table ) )
+				.join( '\n\n' );
+
+			asciiOut.innerHTML = asciiArt + '\n\n' +
+				'Diff: input vs post-fixed model (only first table):\n' + ( diffOutput ? diffOutput : '-- no differences --' );
 		}
 
-		function findTable( editor ) {
+		function findTable( editor, useAnyTable = false ) {
+			const selection = editor.model.document.selection;
+
+			const tableCells = getSelectionAffectedTableCells( selection );
+
+			if ( tableCells.length ) {
+				return findAncestor( 'table', tableCells[ 0 ] );
+			}
+
+			const element = selection.getSelectedElement();
+
+			if ( element && element.is( 'table' ) ) {
+				return element;
+			}
+
+			if ( useAnyTable ) {
+				const range = editor.model.createRangeIn( editor.model.document.getRoot() );
+
+				for ( const element of range.getItems() ) {
+					if ( element.is( 'table' ) ) {
+						return element;
+					}
+				}
+			}
+
+			return null;
+		}
+
+		function getAllTables( editor ) {
 			const range = editor.model.createRangeIn( editor.model.document.getRoot() );
+			const tables = [];
 
 			for ( const element of range.getItems() ) {
 				if ( element.is( 'table' ) ) {
-					return element;
+					tables.push( element );
 				}
 			}
 
-			return null;
+			return tables;
 		}
 
 		function parseModelData( string ) {

+ 306 - 24
packages/ckeditor5-table/tests/tableclipboard-paste.js

@@ -3,7 +3,7 @@
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
-/* globals document console */
+/* globals document */
 
 import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
 import BlockQuoteEditing from '@ckeditor/ckeditor5-block-quote/src/blockquoteediting';
@@ -2580,33 +2580,315 @@ describe( 'table clipboard', () => {
 		} );
 
 		describe( 'pasted table is smaller than the selected area', () => {
-			it( 'blocks this case', () => {
-				tableSelection.setCellSelection(
-					modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
-					modelRoot.getNodeByPath( [ 0, 3, 3 ] )
-				);
+			describe( 'no spans', () => {
+				beforeEach( () => {
+					setModelData( model, modelTable( [
+						[ '00', '01', '02', '03', '04', '05' ],
+						[ '10', '11', '12', '13', '14', '15' ],
+						[ '20', '21', '22', '23', '24', '25' ],
+						[ '30', '31', '32', '33', '34', '35' ],
+						[ '40', '41', '42', '43', '44', '45' ],
+						[ '50', '51', '52', '53', '54', '55' ]
+					] ) );
+				} );
 
-				// Catches the temporary console log in the CK_DEBUG mode.
-				sinon.stub( console, 'log' );
+				it( 'should repeat pasted cells horizontally', () => {
+					tableSelection.setCellSelection(
+						modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
+						modelRoot.getNodeByPath( [ 0, 1, 4 ] )
+					);
 
-				pasteTable( [
-					[ 'aa', 'ab' ],
-					[ 'ba', 'bb' ]
-				] );
+					pasteTable( [
+						[ 'aa', 'ab', 'ac' ],
+						[ 'ba', 'bb', 'bc' ],
+						[ 'ca', 'cb', 'cc' ]
+					] );
 
-				assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
-					[ '00', '01', '02', '03' ],
-					[ '10', '11', '12', '13' ],
-					[ '20', '21', '22', '23' ],
-					[ '30', '31', '32', '33' ]
-				] ) );
+					assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
+						[ 'aa', 'ab', 'ac', 'aa', 'ab', '05' ],
+						[ 'ba', 'bb', 'bc', 'ba', 'bb', '15' ],
+						[ '20', '21', '22', '23', '24', '25' ],
+						[ '30', '31', '32', '33', '34', '35' ],
+						[ '40', '41', '42', '43', '44', '45' ],
+						[ '50', '51', '52', '53', '54', '55' ]
+					] ) );
 
-				assertSelectedCells( model, [
-					[ 1, 1, 1, 1 ],
-					[ 1, 1, 1, 1 ],
-					[ 1, 1, 1, 1 ],
-					[ 1, 1, 1, 1 ]
-				] );
+					assertSelectedCells( model, [
+						[ 1, 1, 1, 1, 1, 0 ],
+						[ 1, 1, 1, 1, 1, 0 ],
+						[ 0, 0, 0, 0, 0, 0 ],
+						[ 0, 0, 0, 0, 0, 0 ]
+					] );
+				} );
+
+				it( 'should repeat pasted cells vertically', () => {
+					tableSelection.setCellSelection(
+						modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
+						modelRoot.getNodeByPath( [ 0, 4, 1 ] )
+					);
+
+					pasteTable( [
+						[ 'aa', 'ab', 'ac' ],
+						[ 'ba', 'bb', 'bc' ],
+						[ 'ca', 'cb', 'cc' ]
+					] );
+
+					assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
+						[ 'aa', 'ab', '02', '03', '04', '05' ],
+						[ 'ba', 'bb', '12', '13', '14', '15' ],
+						[ 'ca', 'cb', '22', '23', '24', '25' ],
+						[ 'aa', 'ab', '32', '33', '34', '35' ],
+						[ 'ba', 'bb', '42', '43', '44', '45' ],
+						[ '50', '51', '52', '53', '54', '55' ]
+					] ) );
+
+					assertSelectedCells( model, [
+						[ 1, 1, 0, 0, 0, 0 ],
+						[ 1, 1, 0, 0, 0, 0 ],
+						[ 1, 1, 0, 0, 0, 0 ],
+						[ 1, 1, 0, 0, 0, 0 ],
+						[ 1, 1, 0, 0, 0, 0 ],
+						[ 0, 0, 0, 0, 0, 0 ]
+					] );
+				} );
+
+				it( 'should repeat pasted cells in both directions', () => {
+					tableSelection.setCellSelection(
+						modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
+						modelRoot.getNodeByPath( [ 0, 4, 4 ] )
+					);
+
+					pasteTable( [
+						[ 'aa', 'ab', 'ac' ],
+						[ 'ba', 'bb', 'bc' ],
+						[ 'ca', 'cb', 'cc' ]
+					] );
+
+					assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
+						[ 'aa', 'ab', 'ac', 'aa', 'ab', '05' ],
+						[ 'ba', 'bb', 'bc', 'ba', 'bb', '15' ],
+						[ 'ca', 'cb', 'cc', 'ca', 'cb', '25' ],
+						[ 'aa', 'ab', 'ac', 'aa', 'ab', '35' ],
+						[ 'ba', 'bb', 'bc', 'ba', 'bb', '45' ],
+						[ '50', '51', '52', '53', '54', '55' ]
+					] ) );
+
+					assertSelectedCells( model, [
+						[ 1, 1, 1, 1, 1, 0 ],
+						[ 1, 1, 1, 1, 1, 0 ],
+						[ 1, 1, 1, 1, 1, 0 ],
+						[ 1, 1, 1, 1, 1, 0 ],
+						[ 1, 1, 1, 1, 1, 0 ],
+						[ 0, 0, 0, 0, 0, 0 ]
+					] );
+				} );
+
+				it( 'should repeat pasted cells in the both directions when pasted on table end', () => {
+					tableSelection.setCellSelection(
+						modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
+						modelRoot.getNodeByPath( [ 0, 5, 5 ] )
+					);
+
+					pasteTable( [
+						[ 'aa', 'ab', 'ac' ],
+						[ 'ba', 'bb', 'bc' ],
+						[ 'ca', 'cb', 'cc' ]
+					] );
+
+					assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
+						[ '00', '01', '02', '03', '04', '05' ],
+						[ '10', 'aa', 'ab', 'ac', 'aa', 'ab' ],
+						[ '20', 'ba', 'bb', 'bc', 'ba', 'bb' ],
+						[ '30', 'ca', 'cb', 'cc', 'ca', 'cb' ],
+						[ '40', 'aa', 'ab', 'ac', 'aa', 'ab' ],
+						[ '50', 'ba', 'bb', 'bc', 'ba', 'bb' ]
+					] ) );
+
+					assertSelectedCells( model, [
+						[ 0, 0, 0, 0, 0, 0 ],
+						[ 0, 1, 1, 1, 1, 1 ],
+						[ 0, 1, 1, 1, 1, 1 ],
+						[ 0, 1, 1, 1, 1, 1 ],
+						[ 0, 1, 1, 1, 1, 1 ],
+						[ 0, 1, 1, 1, 1, 1 ]
+					] );
+				} );
+			} );
+
+			describe( 'content table has spans', () => {
+				beforeEach( () => {
+					// +----+----+----+----+----+----+
+					// | 00 | 01 | 02 | 03 | 04 | 05 |
+					// +----+----+----+    +----+----+
+					// | 10 | 11 | 12 |    | 14 | 15 |
+					// +----+----+    +    +----+----+
+					// | 20 | 21 |    |    | 24 | 25 |
+					// +----+----+----+    +    +----+
+					// | 30 | 31      |    |    | 35 |
+					// +----+----+----+----+----+----+
+					// | 40                | 44      |
+					// +----+----+----+----+         +
+					// | 50 | 51 | 52      |         |
+					// +----+----+----+----+----+----+
+					// | 60 | 61 | 62 | 63 | 64 | 65 |
+					// +----+----+----+----+----+----+
+					setModelData( model, modelTable( [
+						[ '00', '01', '02', { contents: '03', rowspan: 4 }, '04', '05' ],
+						[ '10', '11', { contents: '12', rowspan: 2 }, '14', '15' ],
+						[ '20', '21', { contents: '24', rowspan: 2 }, '25' ],
+						[ '30', { contents: '31', colspan: 2 }, '35' ],
+						[ { contents: '40', colspan: 4 }, { contents: '44', colspan: 2, rowspan: 2 } ],
+						[ '50', '51', { contents: '52', colspan: 2 } ],
+						[ '60', '61', '62', '63', '64', '65' ]
+					] ) );
+				} );
+
+				it( 'should split spanned cells on the selection edges (vertical spans)', () => {
+					tableSelection.setCellSelection(
+						modelRoot.getNodeByPath( [ 0, 2, 0 ] ),
+						modelRoot.getNodeByPath( [ 0, 4, 1 ] ) // Cell 44.
+					);
+
+					pasteTable( [
+						[ 'aa', 'ab' ],
+						[ 'ba', 'bb' ]
+					] );
+
+					// +----+----+----+----+----+----+
+					// | 00 | 01 | 02 | 03 | 04 | 05 |
+					// +----+----+----+    +----+----+
+					// | 10 | 11 | 12 |    | 14 | 15 |
+					// +----+----+----+----+----+----+
+					// | aa | ab | aa | ab | aa | 25 |
+					// +----+----+----+----+----+----+
+					// | ba | bb | ba | bb | ba | 35 |
+					// +----+----+----+----+----+----+
+					// | aa | ab | aa | ab | aa |    |
+					// +----+----+----+----+----+    +
+					// | 50 | 51 | 52      |    |    |
+					// +----+----+----+----+----+----+
+					// | 60 | 61 | 62 | 63 | 64 | 65 |
+					// +----+----+----+----+----+----+
+					assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
+						[ '00', '01', '02', { contents: '03', rowspan: 2 }, '04', '05' ],
+						[ '10', '11', '12', '14', '15' ],
+						[ 'aa', 'ab', 'aa', 'ab', 'aa', '25' ],
+						[ 'ba', 'bb', 'ba', 'bb', 'ba', '35' ],
+						[ 'aa', 'ab', 'aa', 'ab', 'aa', { contents: '', rowspan: 2 } ],
+						[ '50', '51', { contents: '52', colspan: 2 }, '' ],
+						[ '60', '61', '62', '63', '64', '65' ]
+					] ) );
+				} );
+
+				it( 'should split spanned cells on the selection edges (horizontal spans)', () => {
+					tableSelection.setCellSelection(
+						modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
+						modelRoot.getNodeByPath( [ 0, 4, 1 ] ) // Cell 44.
+					);
+
+					pasteTable( [
+						[ 'aa', 'ab' ],
+						[ 'ba', 'bb' ]
+					] );
+
+					// +----+----+----+----+----+----+
+					// | 00 | 01 | aa | ab | aa | 05 |
+					// +----+----+----+----+----+----+
+					// | 10 | 11 | ba | bb | ba | 15 |
+					// +----+----+----+----+----+----+
+					// | 20 | 21 | aa | ab | aa | 25 |
+					// +----+----+----+----+----+----+
+					// | 30 | 31 | ba | bb | ba | 35 |
+					// +----+----+----+----+----+----+
+					// | 40      | aa | ab | aa |    |
+					// +----+----+----+----+----+    +
+					// | 50 | 51 | 52      |    |    |
+					// +----+----+----+----+----+----+
+					// | 60 | 61 | 62 | 63 | 64 | 65 |
+					// +----+----+----+----+----+----+
+					assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
+						[ '00', '01', 'aa', 'ab', 'aa', '05' ],
+						[ '10', '11', 'ba', 'bb', 'ba', '15' ],
+						[ '20', '21', 'aa', 'ab', 'aa', '25' ],
+						[ '30', '31', 'ba', 'bb', 'ba', '35' ],
+						[ { contents: '40', colspan: 2 }, 'aa', 'ab', 'aa', { contents: '', rowspan: 2 } ],
+						[ '50', '51', { contents: '52', colspan: 2 }, '' ],
+						[ '60', '61', '62', '63', '64', '65' ]
+					] ) );
+				} );
+			} );
+
+			describe( 'pasted table has spans', () => {
+				beforeEach( () => {
+					setModelData( model, modelTable( [
+						[ '00', '01', '02', '03', '04', '05', '06' ],
+						[ '10', '11', '12', '13', '14', '15', '16' ],
+						[ '20', '21', '22', '23', '24', '25', '26' ],
+						[ '30', '31', '32', '33', '34', '35', '36' ],
+						[ '40', '41', '42', '43', '44', '45', '46' ],
+						[ '50', '51', '52', '53', '54', '55', '56' ],
+						[ '60', '61', '62', '63', '64', '65', '66' ],
+						[ '70', '71', '72', '73', '74', '75', '76' ],
+						[ '80', '81', '82', '83', '84', '85', '86' ]
+					] ) );
+				} );
+
+				it( 'should trim overlapping cells', () => {
+					tableSelection.setCellSelection(
+						modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
+						modelRoot.getNodeByPath( [ 0, 8, 6 ] )
+					);
+
+					// +----+----+----+----+
+					// | aa                |
+					// +----+----+----+----+
+					// | ba | bb           |
+					// +    +----+----+----+
+					// |    | cb | cc      |
+					// +    +    +----+----+
+					// |    |    | dc | dd |
+					// +    +    +    +----+
+					// |    |    |    | ed |
+					// +----+----+----+----+
+					pasteTable( [
+						[ { contents: 'aa', colspan: 4 } ],
+						[ { contents: 'ba', rowspan: 4 }, { contents: 'bb', colspan: 3 } ],
+						[ { contents: 'cb', rowspan: 3 }, { contents: 'cc', colspan: 2 } ],
+						[ { contents: 'dc', rowspan: 2 }, 'dd' ],
+						[ 'ed' ]
+					] );
+
+					// +----+----+----+----+----+----+----+
+					// | 00 | 01 | 02 | 03 | 04 | 05 | 06 |
+					// +----+----+----+----+----+----+----+
+					// | 10 | aa                | aa      |
+					// +----+----+----+----+----+----+----+
+					// | 20 | ba | bb           | ba | bb |
+					// +----+    +----+----+----+    +----+
+					// | 30 |    | cb | cc      |    | cb |
+					// +----+    +    +----+----+    +    +
+					// | 40 |    |    | dc | dd |    |    |
+					// +----+    +    +    +----+    +    +
+					// | 50 |    |    |    | ed |    |    |
+					// +----+----+----+----+----+----+----+
+					// | 60 | aa                | aa      |
+					// +----+----+----+----+----+----+----+
+					// | 70 | ba | bb           | ba | bb |
+					// +----+    +----+----+----+    +----+
+					// | 80 |    | cb | cc      |    | cb |
+					// +----+----+----+----+----+----+----+
+					assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
+						[ '00', '01', '02', '03', '04', '05', '06' ],
+						[ '10', { contents: 'aa', colspan: 4 }, { contents: 'aa', colspan: 2 } ],
+						[ '20', { contents: 'ba', rowspan: 4 }, { contents: 'bb', colspan: 3 }, { contents: 'ba', rowspan: 4 }, 'bb' ],
+						[ '30', { contents: 'cb', rowspan: 3 }, { contents: 'cc', colspan: 2 }, { contents: 'cb', rowspan: 3 } ],
+						[ '40', { contents: 'dc', rowspan: 2 }, 'dd' ],
+						[ '50', 'ed' ],
+						[ '60', { contents: 'aa', colspan: 4 }, { contents: 'aa', colspan: 2 } ],
+						[ '70', { contents: 'ba', rowspan: 2 }, { contents: 'bb', colspan: 3 }, { contents: 'ba', rowspan: 2 }, 'bb' ],
+						[ '80', 'cb', { contents: 'cc', colspan: 2 }, 'cb' ]
+					] ) );
+				} );
 			} );
 		} );