8
0
Quellcode durchsuchen

Other: Move selection post fixer to model/utils and inject it in model's constructor.

Maciej Gołaszewski vor 7 Jahren
Ursprung
Commit
f9c07db70c

+ 0 - 4
packages/ckeditor5-engine/src/controller/editingcontroller.js

@@ -14,7 +14,6 @@ import DowncastDispatcher from '../conversion/downcastdispatcher';
 import { insertText, remove } from '../conversion/downcast-converters';
 import { convertSelectionChange } from '../conversion/upcast-selection-converters';
 import { clearAttributes, convertCollapsedSelection, convertRangeSelection } from '../conversion/downcast-selection-converters';
-import selectionPostFixer from './selectionpostfixer';
 
 import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
@@ -93,9 +92,6 @@ export default class EditingController {
 		this.downcastDispatcher.on( 'selection', convertRangeSelection(), { priority: 'low' } );
 		this.downcastDispatcher.on( 'selection', convertCollapsedSelection(), { priority: 'low' } );
 
-		// Add selection post fixer.
-		doc.registerPostFixer( writer => selectionPostFixer( writer, model ) );
-
 		// Binds {@link module:engine/view/document~Document#roots view roots collection} to
 		// {@link module:engine/model/document~Document#roots model roots collection} so creating
 		// model root automatically creates corresponding view root.

+ 3 - 0
packages/ckeditor5-engine/src/model/model.js

@@ -26,6 +26,7 @@ import insertContent from './utils/insertcontent';
 import deleteContent from './utils/deletecontent';
 import modifySelection from './utils/modifyselection';
 import getSelectedContent from './utils/getselectedcontent';
+import { injectSelectionPostFixer } from './utils/selection-postfixer';
 
 /**
  * Editor's data model. Read about the model in the
@@ -111,6 +112,8 @@ export default class Model {
 		this.schema.register( '$marker', {
 			allowIn: [ '$root', '$block' ]
 		} );
+
+		injectSelectionPostFixer( this );
 	}
 
 	/**

+ 15 - 6
packages/ckeditor5-engine/src/controller/selectionpostfixer.js

@@ -4,14 +4,16 @@
  */
 
 /**
- * @module engine/controller/selectionpostfixer
+ * @module engine/mode/utils/selection-postfixer
  */
 
-import Range from '../model/range';
-import Position from '../model/position';
+import Range from '../range';
+import Position from '../position';
 
 /**
- * The selection post fixer which check if nodes with `isLimit` property in schema are properly selected.
+ * Injects selection post fixer to the model.
+ *
+ * The selection post fixer checks if nodes with `isLimit` property in schema are properly selected.
  *
  * See as an example selection that starts in P1 element and ends inside text of TD element
  * (`[` and `]` are range boundaries and `(l)` denotes element defines as `isLimit=true`):
@@ -44,10 +46,17 @@ import Position from '../model/position';
  *
  * See {@link module:engine/model/schema~Schema#isLimit}.
  *
- * @param {module:engine/model/writer~Writer} writer
  * @param {module:engine/model/model~Model} model
  */
-export default function selectionPostFixer( writer, model ) {
+export function injectSelectionPostFixer( model ) {
+	model.document.registerPostFixer( writer => selectionPostFixer( writer, model ) );
+}
+
+// The selection post fixer.
+//
+// @param {module:engine/model/writer~Writer} writer
+// @param {module:engine/model/model~Model} model
+function selectionPostFixer( writer, model ) {
 	const selection = model.document.selection;
 	const schema = model.schema;
 

+ 1 - 302
packages/ckeditor5-engine/tests/controller/editingcontroller.js

@@ -21,7 +21,7 @@ import ModelPosition from '../../src/model/position';
 import ModelRange from '../../src/model/range';
 import ModelDocumentFragment from '../../src/model/documentfragment';
 
-import { getData as getModelData, setData as modelSetData, parse } from '../../src/dev-utils/model';
+import { getData as getModelData, parse } from '../../src/dev-utils/model';
 import { getData as getViewData } from '../../src/dev-utils/view';
 
 describe( 'EditingController', () => {
@@ -341,307 +341,6 @@ describe( 'EditingController', () => {
 		} );
 	} );
 
-	describe( 'post fixers', () => {
-		describe( 'selection post fixer', () => {
-			let model, modelRoot, viewRoot, domRoot, editing;
-
-			beforeEach( () => {
-				model = new Model();
-				modelRoot = model.document.createRoot();
-
-				editing = new EditingController( model );
-
-				domRoot = document.createElement( 'div' );
-				domRoot.contentEditable = true;
-
-				document.body.appendChild( domRoot );
-
-				viewRoot = editing.view.document.getRoot();
-				editing.view.attachDomRoot( domRoot );
-
-				model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
-				model.schema.register( 'table', {
-					allowWhere: '$block',
-					isObject: true,
-					isLimit: true
-				} );
-
-				model.schema.register( 'tableRow', {
-					allowIn: 'table',
-					isLimit: true
-				} );
-
-				model.schema.register( 'tableCell', {
-					allowIn: 'tableRow',
-					allowContentOf: '$block',
-					isLimit: true
-				} );
-
-				downcastElementToElement( { model: 'paragraph', view: 'p' } )( editing.downcastDispatcher );
-				downcastElementToElement( { model: 'table', view: 'table' } )( editing.downcastDispatcher );
-				downcastElementToElement( { model: 'tableRow', view: 'tr' } )( editing.downcastDispatcher );
-				downcastElementToElement( { model: 'tableCell', view: 'td' } )( editing.downcastDispatcher );
-
-				// Note: The below code is highly overcomplicated due to #455.
-				model.change( writer => {
-					writer.setSelection( null );
-				} );
-
-				modelRoot._removeChildren( 0, modelRoot.childCount );
-
-				viewRoot._removeChildren( 0, viewRoot.childCount );
-
-				const modelData = new ModelDocumentFragment( parse(
-					'<paragraph>foo</paragraph>' +
-					'<table><tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow></table>' +
-					'<paragraph>bar</paragraph>',
-					model.schema
-				)._children );
-
-				model.change( writer => {
-					writer.insert( modelData, model.document.getRoot() );
-
-					writer.setSelection( ModelRange.createFromParentsAndOffsets(
-						modelRoot.getChild( 0 ), 1, modelRoot.getChild( 0 ), 1 )
-					);
-				} );
-			} );
-
-			it( 'should not crash if there is no correct position for model selection', () => {
-				modelSetData( model, '' );
-
-				expect( getModelData( model ) ).to.equal( '[]' );
-			} );
-
-			describe( 'not collapsed selection', () => {
-				it( 'should fix #1', () => {
-					model.change( writer => {
-						writer.setSelection( ModelRange.createFromParentsAndOffsets(
-							modelRoot.getChild( 0 ), 1,
-							modelRoot.getChild( 1 ).getChild( 0 ), 1
-						) );
-					} );
-
-					expect( getViewData( editing.view ) ).to.equal(
-						'<p>f{oo</p>' +
-						'<table><tr><td>aaa</td><td>bbb</td></tr></table>]' +
-						'<p>bar</p>'
-					);
-				} );
-
-				it( 'should fix #2', () => {
-					model.change( writer => {
-						writer.setSelection( ModelRange.createFromParentsAndOffsets(
-							modelRoot.getChild( 1 ).getChild( 0 ), 1,
-							modelRoot.getChild( 2 ), 1
-						) );
-					} );
-
-					expect( getViewData( editing.view ) ).to.equal(
-						'<p>foo</p>' +
-						'[<table><tr><td>aaa</td><td>bbb</td></tr></table>' +
-						'<p>b}ar</p>'
-					);
-				} );
-
-				it( 'should fix #3', () => {
-					model.change( writer => {
-						writer.setSelection( ModelRange.createFromParentsAndOffsets(
-							modelRoot.getChild( 0 ), 1,
-							modelRoot.getChild( 1 ), 0
-						) );
-					} );
-
-					expect( getViewData( editing.view ) ).to.equal(
-						'<p>f{oo</p>' +
-						'<table><tr><td>aaa</td><td>bbb</td></tr></table>]' +
-						'<p>bar</p>'
-					);
-				} );
-
-				it( 'should fix #4', () => {
-					model.change( writer => {
-						writer.setSelection( ModelRange.createFromParentsAndOffsets(
-							modelRoot.getChild( 1 ).getChild( 0 ).getChild( 0 ), 1,
-							modelRoot.getChild( 1 ).getChild( 0 ).getChild( 1 ), 2
-						) );
-					} );
-
-					expect( getViewData( editing.view ) ).to.equal(
-						'<p>foo</p>' +
-						'[<table><tr><td>aaa</td><td>bbb</td></tr></table>]' +
-						'<p>bar</p>'
-					);
-				} );
-
-				it( 'should fix #5', () => {
-					modelSetData( model, '' );
-
-					const modelData = new ModelDocumentFragment( parse(
-						'<paragraph>foo</paragraph>' +
-						'<table><tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow></table>' +
-						'<table><tableRow><tableCell>xxx</tableCell><tableCell>yyy</tableCell></tableRow></table>' +
-						'<paragraph>baz</paragraph>',
-						model.schema
-					)._children );
-
-					model.change( writer => {
-						writer.insert( modelData, model.document.getRoot() );
-
-						writer.setSelection( ModelRange.createFromParentsAndOffsets(
-							modelRoot.getChild( 2 ), 0,
-							modelRoot.getChild( 2 ), 0
-						) );
-					} );
-
-					expect( getViewData( editing.view ) ).to.equal(
-						'<p>foo</p>' +
-						'[<table><tr><td>aaa</td><td>bbb</td></tr></table>]' +
-						'<table><tr><td>xxx</td><td>yyy</td></tr></table>' +
-						'<p>baz</p>'
-					);
-				} );
-
-				it( 'should not fix #1', () => {
-					modelSetData( model, '' );
-
-					const modelData = new ModelDocumentFragment( parse(
-						'<paragraph>foo</paragraph>' +
-						'<table><tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow></table>' +
-						'<paragraph>bar</paragraph>' +
-						'<paragraph>baz</paragraph>',
-						model.schema
-					)._children );
-
-					model.change( writer => {
-						writer.insert( modelData, model.document.getRoot() );
-
-						writer.setSelection( ModelRange.createFromParentsAndOffsets(
-							modelRoot.getChild( 2 ), 1,
-							modelRoot.getChild( 3 ), 2
-						) );
-					} );
-
-					expect( getViewData( editing.view ) ).to.equal(
-						'<p>foo</p>' +
-						'<table><tr><td>aaa</td><td>bbb</td></tr></table>' +
-						'<p>b{ar</p>' +
-						'<p>ba}z</p>'
-					);
-				} );
-
-				it( 'should fix multiple ranges #1', () => {
-					model.change( writer => {
-						const ranges = [
-							new ModelRange( new ModelPosition( modelRoot, [ 0, 1 ] ), new ModelPosition( modelRoot, [ 1, 0 ] ) ),
-							new ModelRange( new ModelPosition( modelRoot, [ 1, 0, 0, 0 ] ), new ModelPosition( modelRoot, [ 1, 1 ] ) )
-						];
-						writer.setSelection( ranges );
-					} );
-
-					expect( getViewData( editing.view ) ).to.equal(
-						'<p>f{oo</p>' +
-						'<table><tr><td>aaa</td><td>bbb</td></tr></table>]' +
-						'<p>bar</p>'
-					);
-				} );
-
-				it( 'should fix multiple ranges #2', () => {
-					model.change( writer => {
-						const ranges = [
-							new ModelRange( new ModelPosition( modelRoot, [ 0, 1 ] ), new ModelPosition( modelRoot, [ 1, 0 ] ) ),
-							new ModelRange( new ModelPosition( modelRoot, [ 1, 0, 0, 0 ] ), new ModelPosition( modelRoot, [ 2, 2 ] ) )
-						];
-
-						writer.setSelection( ranges );
-					} );
-
-					expect( getViewData( editing.view ) ).to.equal(
-						'<p>f{oo</p>' +
-						'<table><tr><td>aaa</td><td>bbb</td></tr></table>' +
-						'<p>ba}r</p>'
-					);
-				} );
-
-				it( 'should fix multiple ranges #3', () => {
-					modelSetData( model, '' );
-
-					const modelData = new ModelDocumentFragment( parse(
-						'<paragraph>foo</paragraph>' +
-						'<table>' +
-						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
-						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
-						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
-						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
-						'</table>' +
-						'<paragraph>baz</paragraph>',
-						model.schema
-					)._children );
-
-					model.change( writer => {
-						writer.insert( modelData, model.document.getRoot() );
-
-						const ranges = [
-							new ModelRange( new ModelPosition( modelRoot, [ 1, 0, 0, 0 ] ), new ModelPosition( modelRoot, [ 1, 1 ] ) ),
-							new ModelRange( new ModelPosition( modelRoot, [ 1, 1, 0, 0 ] ), new ModelPosition( modelRoot, [ 1, 2 ] ) ),
-							new ModelRange( new ModelPosition( modelRoot, [ 1, 2, 0, 0 ] ), new ModelPosition( modelRoot, [ 1, 3 ] ) ),
-							new ModelRange( new ModelPosition( modelRoot, [ 1, 3, 0, 0 ] ), new ModelPosition( modelRoot, [ 2, 1 ] ) )
-						];
-
-						writer.setSelection( ranges );
-					} );
-
-					expect( getViewData( editing.view ) ).to.equal(
-						'<p>foo</p>' +
-						'[<table>' +
-						'<tr><td>aaa</td><td>bbb</td></tr>' +
-						'<tr><td>aaa</td><td>bbb</td></tr>' +
-						'<tr><td>aaa</td><td>bbb</td></tr>' +
-						'<tr><td>aaa</td><td>bbb</td></tr>' +
-						'</table>' +
-						'<p>b}az</p>'
-					);
-				} );
-
-				it( 'should fix multiple ranges #4', () => {
-					model.change( writer => {
-						const ranges = [
-							new ModelRange( new ModelPosition( modelRoot, [ 0, 1 ] ), new ModelPosition( modelRoot, [ 1, 0 ] ) ),
-							new ModelRange( new ModelPosition( modelRoot, [ 1, 0, 0, 0 ] ), new ModelPosition( modelRoot, [ 2, 1 ] ) ),
-							new ModelRange( new ModelPosition( modelRoot, [ 2, 2 ] ), new ModelPosition( modelRoot, [ 2, 3 ] ) )
-						];
-
-						writer.setSelection( ranges );
-					} );
-
-					expect( getViewData( editing.view ) ).to.equal(
-						'<p>f{oo</p>' +
-						'<table><tr><td>aaa</td><td>bbb</td></tr></table>' +
-						'<p>b}a{r}</p>'
-					);
-				} );
-			} );
-
-			describe( 'collapsed selection', () => {
-				it( 'should fix #1', () => {
-					model.change( writer => {
-						writer.setSelection(
-							ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 1 ), 0, modelRoot.getChild( 1 ), 0 )
-						);
-					} );
-
-					expect( getViewData( editing.view ) ).to.equal(
-						'<p>foo{}</p><table><tr><td>aaa</td><td>bbb</td></tr></table><p>bar</p>'
-					);
-				} );
-			} );
-
-			afterEach( () => {
-				editing.destroy();
-			} );
-		} );
-	} );
-
 	describe( 'destroy()', () => {
 		it( 'should remove listenters', () => {
 			const model = new Model();

+ 11 - 5
packages/ckeditor5-engine/tests/model/schema.js

@@ -886,11 +886,13 @@ describe( 'Schema', () => {
 			schema.extend( 'article', { isLimit: true } );
 			schema.extend( 'section', { isLimit: true } );
 
-			setData( model, '<div><section><article>[foo</article><article>bar]</article></section></div>' );
+			model.enqueueChange( 'transparent', () => {
+				setData( model, '<div><section><article>[foo</article><article>bar]</article></section></div>' );
 
-			const section = root.getNodeByPath( [ 0, 0 ] );
+				const section = root.getNodeByPath( [ 0, 0 ] );
 
-			expect( schema.getLimitElement( doc.selection ) ).to.equal( section );
+				expect( schema.getLimitElement( doc.selection ) ).to.equal( section );
+			} );
 		} );
 
 		it( 'works fine with multi-range selections', () => {
@@ -1389,8 +1391,12 @@ describe( 'Schema', () => {
 
 		function test( testName, data, direction, expected ) {
 			it( testName, () => {
-				setData( model, data );
-				const range = schema.getNearestSelectionRange( selection.anchor, direction );
+				let range;
+
+				model.enqueueChange( 'transparent', () => {
+					setData( model, data );
+					range = schema.getNearestSelectionRange( selection.anchor, direction );
+				} );
 
 				if ( expected === null ) {
 					expect( range ).to.be.null;

+ 4 - 2
packages/ckeditor5-engine/tests/model/utils/deletecontent.js

@@ -900,9 +900,11 @@ describe( 'DataController utils', () => {
 
 		function test( title, input, output, options ) {
 			it( title, () => {
-				setData( model, input );
+				model.enqueueChange( 'transparent', () => {
+					setData( model, input );
 
-				deleteContent( model, doc.selection, options );
+					deleteContent( model, doc.selection, options );
+				} );
 
 				expect( getData( model ) ).to.equal( output );
 			} );

+ 5 - 3
packages/ckeditor5-engine/tests/model/utils/insertcontent.js

@@ -813,9 +813,11 @@ describe( 'DataController utils', () => {
 			expect( getData( model ) ).to.equal( '<limit>foo bar[]</limit>' );
 		} );
 
-		it( 'should insert text into limit element', () => {
-			setData( model, '<limit>foo[</limit><limit>]bar</limit>' );
-			insertHelper( 'baz' );
+		it( 'should insert text into limit element when selection spans over many limit elements', () => {
+			model.enqueueChange( 'transparent', () => {
+				setData( model, '<limit>foo[</limit><limit>]bar</limit>' );
+				insertHelper( 'baz' );
+			} );
 
 			expect( getData( model ) ).to.equal( '<limit>foobaz[]</limit><limit>bar</limit>' );
 		} );

+ 266 - 0
packages/ckeditor5-engine/tests/model/utils/selection-postfixer.js

@@ -0,0 +1,266 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import Model from '../../../src/model/model';
+import ModelPosition from '../../../src/model/position';
+import ModelRange from '../../../src/model/range';
+
+import { getData as getModelData, setData as setModelData } from '../../../src/dev-utils/model';
+
+describe( 'Selection post fixer', () => {
+	describe( 'selectionPostFixer()', () => {
+		let model, modelRoot;
+
+		beforeEach( () => {
+			model = new Model();
+			modelRoot = model.document.createRoot();
+
+			model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
+			model.schema.register( 'table', {
+				allowWhere: '$block',
+				isObject: true,
+				isLimit: true
+			} );
+
+			model.schema.register( 'tableRow', {
+				allowIn: 'table',
+				isLimit: true
+			} );
+
+			model.schema.register( 'tableCell', {
+				allowIn: 'tableRow',
+				allowContentOf: '$block',
+				isLimit: true
+			} );
+
+			setModelData( model,
+				'<paragraph>[]foo</paragraph>' +
+				'<table>' +
+					'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+				'</table>' +
+				'<paragraph>bar</paragraph>'
+			);
+		} );
+
+		it( 'should not crash if there is no correct position for model selection', () => {
+			setModelData( model, '' );
+
+			expect( getModelData( model ) ).to.equal( '[]' );
+		} );
+
+		describe( 'not collapsed selection', () => {
+			it( 'should fix #1', () => {
+				model.change( writer => {
+					writer.setSelection( ModelRange.createFromParentsAndOffsets(
+						modelRoot.getChild( 0 ), 1,
+						modelRoot.getChild( 1 ).getChild( 0 ), 1
+					) );
+				} );
+
+				expect( getModelData( model ) ).to.equal(
+					'<paragraph>f[oo</paragraph>' +
+					'<table>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>]' +
+					'<paragraph>bar</paragraph>'
+				);
+			} );
+
+			it( 'should fix #2', () => {
+				model.change( writer => {
+					writer.setSelection( ModelRange.createFromParentsAndOffsets(
+						modelRoot.getChild( 1 ).getChild( 0 ), 1,
+						modelRoot.getChild( 2 ), 1
+					) );
+				} );
+
+				expect( getModelData( model ) ).to.equal(
+					'<paragraph>foo</paragraph>' +
+					'[<table>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>' +
+					'<paragraph>b]ar</paragraph>'
+				);
+			} );
+
+			it( 'should fix #3', () => {
+				model.change( writer => {
+					writer.setSelection( ModelRange.createFromParentsAndOffsets(
+						modelRoot.getChild( 0 ), 1,
+						modelRoot.getChild( 1 ), 0
+					) );
+				} );
+
+				expect( getModelData( model ) ).to.equal(
+					'<paragraph>f[oo</paragraph>' +
+					'<table>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>]' +
+					'<paragraph>bar</paragraph>'
+				);
+			} );
+
+			it( 'should fix #4', () => {
+				model.change( writer => {
+					writer.setSelection( ModelRange.createFromParentsAndOffsets(
+						modelRoot.getChild( 1 ).getChild( 0 ).getChild( 0 ), 1,
+						modelRoot.getChild( 1 ).getChild( 0 ).getChild( 1 ), 2
+					) );
+				} );
+
+				expect( getModelData( model ) ).to.equal(
+					'<paragraph>foo</paragraph>' +
+					'[<table>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>]' +
+					'<paragraph>bar</paragraph>'
+				);
+			} );
+
+			it( 'should fix #5', () => {
+				setModelData( model,
+					'<paragraph>foo</paragraph>' +
+					'<table>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>' +
+					'[]<table>' +
+						'<tableRow><tableCell>xxx</tableCell><tableCell>yyy</tableCell></tableRow>' +
+					'</table>' +
+					'<paragraph>baz</paragraph>'
+				);
+
+				expect( getModelData( model ) ).to.equal(
+					'<paragraph>foo</paragraph>' +
+					'[<table>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>]' +
+					'<table>' +
+						'<tableRow><tableCell>xxx</tableCell><tableCell>yyy</tableCell></tableRow>' +
+					'</table>' +
+					'<paragraph>baz</paragraph>'
+				);
+			} );
+
+			it( 'should not fix #1', () => {
+				setModelData( model,
+					'<paragraph>foo</paragraph>' +
+					'<table>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>' +
+					'<paragraph>b[ar</paragraph>' +
+					'<paragraph>ba]z</paragraph>'
+				);
+
+				expect( getModelData( model ) ).to.equal(
+					'<paragraph>foo</paragraph>' +
+					'<table>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>' +
+					'<paragraph>b[ar</paragraph>' +
+					'<paragraph>ba]z</paragraph>'
+				);
+			} );
+
+			it( 'should fix multiple ranges #1', () => {
+				model.change( writer => {
+					const ranges = [
+						new ModelRange( new ModelPosition( modelRoot, [ 0, 1 ] ), new ModelPosition( modelRoot, [ 1, 0 ] ) ),
+						new ModelRange( new ModelPosition( modelRoot, [ 1, 0, 0, 0 ] ), new ModelPosition( modelRoot, [ 1, 1 ] ) )
+					];
+					writer.setSelection( ranges );
+				} );
+
+				expect( getModelData( model ) ).to.equal(
+					'<paragraph>f[oo</paragraph>' +
+					'<table>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>]' +
+					'<paragraph>bar</paragraph>'
+				);
+			} );
+
+			it( 'should fix multiple ranges #2', () => {
+				model.change( writer => {
+					const ranges = [
+						new ModelRange( new ModelPosition( modelRoot, [ 0, 1 ] ), new ModelPosition( modelRoot, [ 1, 0 ] ) ),
+						new ModelRange( new ModelPosition( modelRoot, [ 1, 0, 0, 0 ] ), new ModelPosition( modelRoot, [ 2, 2 ] ) )
+					];
+
+					writer.setSelection( ranges );
+				} );
+
+				expect( getModelData( model ) ).to.equal(
+					'<paragraph>f[oo</paragraph>' +
+					'<table>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>' +
+					'<paragraph>ba]r</paragraph>'
+				);
+			} );
+
+			it( 'should fix multiple ranges #3', () => {
+				setModelData( model,
+					'<paragraph>foo</paragraph>' +
+					'<table>' +
+						'<tableRow><tableCell>[aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+						'<tableRow>]<tableCell>[aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+						'<tableRow>]<tableCell>[aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+						'<tableRow>]<tableCell>[aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>' +
+					'<paragraph>b]az</paragraph>'
+				);
+
+				expect( getModelData( model ) ).to.equal(
+					'<paragraph>foo</paragraph>' +
+					'[<table>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>' +
+					'<paragraph>b]az</paragraph>'
+				);
+			} );
+
+			it( 'should fix multiple ranges #4', () => {
+				model.change( writer => {
+					const ranges = [
+						new ModelRange( new ModelPosition( modelRoot, [ 0, 1 ] ), new ModelPosition( modelRoot, [ 1, 0 ] ) ),
+						new ModelRange( new ModelPosition( modelRoot, [ 1, 0, 0, 0 ] ), new ModelPosition( modelRoot, [ 2, 1 ] ) ),
+						new ModelRange( new ModelPosition( modelRoot, [ 2, 2 ] ), new ModelPosition( modelRoot, [ 2, 3 ] ) )
+					];
+
+					writer.setSelection( ranges );
+				} );
+
+				expect( getModelData( model ) ).to.equal(
+					'<paragraph>f[oo</paragraph>' +
+					'<table>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>' +
+					'<paragraph>b]a[r]</paragraph>'
+				);
+			} );
+		} );
+
+		describe( 'collapsed selection', () => {
+			it( 'should fix #1', () => {
+				model.change( writer => {
+					writer.setSelection(
+						ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 1 ), 0, modelRoot.getChild( 1 ), 0 )
+					);
+				} );
+
+				expect( getModelData( model ) ).to.equal(
+					'<paragraph>foo[]</paragraph>' +
+					'<table>' +
+						'<tableRow><tableCell>aaa</tableCell><tableCell>bbb</tableCell></tableRow>' +
+					'</table>' +
+					'<paragraph>bar</paragraph>'
+				);
+			} );
+		} );
+	} );
+} );

+ 2 - 2
packages/ckeditor5-engine/tests/model/writer.js

@@ -2337,11 +2337,11 @@ describe( 'Writer', () => {
 		} );
 
 		it( 'should change document selection ranges', () => {
-			setSelection( new Position( root, [ 1 ] ) );
+			setSelection( new Position( root, [ 0, 0 ] ) );
 			setSelectionFocus( new Position( root, [ 2, 2 ] ) );
 
 			expect( model.document.selection._ranges.length ).to.equal( 1 );
-			expect( model.document.selection._ranges[ 0 ].start.path ).to.deep.equal( [ 1 ] );
+			expect( model.document.selection._ranges[ 0 ].start.path ).to.deep.equal( [ 0, 0 ] );
 			expect( model.document.selection._ranges[ 0 ].end.path ).to.deep.equal( [ 2, 2 ] );
 		} );
 	} );