Ver código fonte

Converted engine.view.Writer class to utilities functions.

Szymon Kupś 9 anos atrás
pai
commit
586a658b9f
24 arquivos alterados com 721 adições e 706 exclusões
  1. 4 3
      packages/ckeditor5-engine/src/conversion/model-selection-to-view-converters.js
  2. 8 7
      packages/ckeditor5-engine/src/conversion/model-to-view-converters.js
  3. 2 2
      packages/ckeditor5-engine/src/conversion/modelconsumable.js
  4. 2 2
      packages/ckeditor5-engine/src/conversion/modelconversiondispatcher.js
  5. 0 10
      packages/ckeditor5-engine/src/datacontroller.js
  6. 0 1
      packages/ckeditor5-engine/src/editingcontroller.js
  7. 0 9
      packages/ckeditor5-engine/src/view/document.js
  8. 512 498
      packages/ckeditor5-engine/src/view/writer.js
  9. 10 12
      packages/ckeditor5-engine/tests/conversion/advanced-converters.js
  10. 2 3
      packages/ckeditor5-engine/tests/conversion/model-converter-builder.js
  11. 4 4
      packages/ckeditor5-engine/tests/conversion/model-selection-to-view-converters.js
  12. 2 5
      packages/ckeditor5-engine/tests/conversion/model-to-view-converters.js
  13. 0 2
      packages/ckeditor5-engine/tests/view/document/document.js
  14. 3 9
      packages/ckeditor5-engine/tests/view/writer/breakattributes.js
  15. 4 10
      packages/ckeditor5-engine/tests/view/writer/breakrange.js
  16. 39 0
      packages/ckeditor5-engine/tests/view/writer/getparentcontainer.js
  17. 5 11
      packages/ckeditor5-engine/tests/view/writer/insert.js
  18. 4 10
      packages/ckeditor5-engine/tests/view/writer/mergeattributes.js
  19. 102 0
      packages/ckeditor5-engine/tests/view/writer/move.js
  20. 5 11
      packages/ckeditor5-engine/tests/view/writer/remove.js
  21. 5 11
      packages/ckeditor5-engine/tests/view/writer/unwrap.js
  22. 5 11
      packages/ckeditor5-engine/tests/view/writer/wrap.js
  23. 3 9
      packages/ckeditor5-engine/tests/view/writer/wrapposition.js
  24. 0 66
      packages/ckeditor5-engine/tests/view/writer/writer.js

+ 4 - 3
packages/ckeditor5-engine/src/conversion/model-selection-to-view-converters.js

@@ -7,6 +7,7 @@
 
 import ViewElement from '../view/element.js';
 import ViewRange from '../view/range.js';
+import viewWriter from '../view/writer.js';
 
 /**
  * Contains {@link engine.model.Selection model selection} to {@link engine.view.Selection view selection} converters for
@@ -85,7 +86,7 @@ export function convertCollapsedSelection() {
 
 		const modelPosition = selection.getFirstPosition();
 		const viewPosition = conversionApi.mapper.toViewPosition( modelPosition );
-		const brokenPosition = conversionApi.writer.breakAttributes( viewPosition );
+		const brokenPosition = viewWriter.breakAttributes( viewPosition );
 
 		conversionApi.viewSelection.removeAllRanges();
 		conversionApi.viewSelection.addRange( new ViewRange( brokenPosition, brokenPosition ) );
@@ -163,7 +164,7 @@ export function convertSelectionAttribute( elementCreator ) {
 				elementCreator.clone( true ) :
 				elementCreator( data.value, selection, consumable, conversionApi );
 
-		viewPosition = conversionApi.writer.wrapPosition( viewPosition, viewElement );
+		viewPosition = viewWriter.wrapPosition( viewPosition, viewElement );
 
 		conversionApi.viewSelection.addRange( new ViewRange( viewPosition, viewPosition ) );
 	};
@@ -202,7 +203,7 @@ export function clearAttributes() {
 			if ( range.isCollapsed ) {
 				// Position might be in the node removed by the Writer.
 				if ( range.end.parent.getDocument() ) {
-					conversionApi.writer.mergeAttributes( range.start );
+					viewWriter.mergeAttributes( range.start );
 				}
 			}
 		}

+ 8 - 7
packages/ckeditor5-engine/src/conversion/model-to-view-converters.js

@@ -10,6 +10,7 @@ import ModelRange from '../model/range.js';
 
 import ViewElement from '../view/element.js';
 import ViewText from '../view/text.js';
+import viewWriter from '../view/writer.js';
 
 /**
  * Contains {@link engine.model model} to {@link engine.view view} converters for
@@ -57,7 +58,7 @@ export function insertElement( elementCreator ) {
 			elementCreator( data, consumable, conversionApi );
 
 		conversionApi.mapper.bindElements( data.item, viewElement );
-		conversionApi.writer.insert( viewPosition, viewElement );
+		viewWriter.insert( viewPosition, viewElement );
 
 		evt.stop();
 	};
@@ -82,7 +83,7 @@ export function insertText() {
 		const viewPosition = conversionApi.mapper.toViewPosition( data.range.start );
 		const viewText = new ViewText( data.item.text );
 
-		conversionApi.writer.insert( viewPosition, viewText );
+		viewWriter.insert( viewPosition, viewText );
 
 		evt.stop();
 	};
@@ -223,10 +224,10 @@ export function wrap( elementCreator ) {
 		// view element basing on old value and unwrap it before wrapping with a newly created view element.
 		if ( data.attributeOldValue !== null && !( elementCreator instanceof ViewElement ) ) {
 			const oldViewElement = elementCreator( data.attributeOldValue, data, consumable, conversionApi );
-			viewRange = conversionApi.writer.unwrap( viewRange, oldViewElement, evt.priority );
+			viewRange = viewWriter.unwrap( viewRange, oldViewElement, evt.priority );
 		}
 
-		conversionApi.writer.wrap( viewRange, viewElement, evt.priority );
+		viewWriter.wrap( viewRange, viewElement, evt.priority );
 
 		evt.stop();
 	};
@@ -265,7 +266,7 @@ export function unwrap( elementCreator ) {
 			elementCreator.clone( true ) :
 			elementCreator( data.attributeOldValue, data, consumable, conversionApi );
 
-		conversionApi.writer.unwrap( viewRange, viewNode );
+		viewWriter.unwrap( viewRange, viewNode );
 
 		evt.stop();
 	};
@@ -295,7 +296,7 @@ export function move() {
 		const sourceViewRange = conversionApi.mapper.toViewRange( sourceModelRange );
 		const targetViewPosition = conversionApi.mapper.toViewPosition( data.range.start );
 
-		conversionApi.writer.move( sourceViewRange, targetViewPosition );
+		viewWriter.move( sourceViewRange, targetViewPosition );
 	};
 }
 
@@ -321,7 +322,7 @@ export function remove() {
 		const sourceModelRange = ModelRange.createFromPositionAndShift( data.sourcePosition, length );
 		const sourceViewRange = conversionApi.mapper.toViewRange( sourceModelRange );
 
-		conversionApi.writer.remove( sourceViewRange );
+		viewWriter.remove( sourceViewRange );
 	};
 }
 

+ 2 - 2
packages/ckeditor5-engine/src/conversion/modelconsumable.js

@@ -74,11 +74,11 @@ import TextProxy from '../model/textproxy.js';
  *
  *					conversionApi.mapper.bindElements( modelCaption, viewCaption );
  *					conversionApi.mapper.bindElements( data.item, viewImageHolder );
- *					conversionApi.writer.insert( insertPosition, viewImageHolder );
+ *					viewWriter.insert( insertPosition, viewImageHolder );
  *				}
  *			} else {
  *				conversionApi.mapper.bindElements( data.item, viewImage );
- *				conversionApi.writer.insert( insertPosition, viewImage );
+ *				viewWriter.insert( insertPosition, viewImage );
  *			}
  *
  *			evt.stop();

+ 2 - 2
packages/ckeditor5-engine/src/conversion/modelconversiondispatcher.js

@@ -62,7 +62,7 @@ import extend from '../../utils/lib/lodash/extend.js';
  *			conversionApi.mapper.bindElements( data.item, viewNode );
  *
  *			// Add the newly created view element to the view.
- *			conversionApi.writer.insert( viewPosition, viewElement );
+ *			viewWriter.insert( viewPosition, viewElement );
  *
  *			// Remember to stop the event propagation if the data.item was consumed.
  *			evt.stop();
@@ -96,7 +96,7 @@ import extend from '../../utils/lib/lodash/extend.js';
  *			// different source, `data.item` already existed. This means we are safe to get `viewQuote` from mapper.
  *			const viewQuote = conversionApi.mapper.toViewElement( data.item );
  *			const position = new ViewPosition( viewQuote, viewQuote.getChildCount() );
- *			conversionApi.writer.insert( position, viewSourceBtn );
+ *			viewWriter.insert( position, viewSourceBtn );
  *
  *			evt.stop();
  *		}, 1 );

+ 0 - 10
packages/ckeditor5-engine/src/datacontroller.js

@@ -13,7 +13,6 @@ import { insertText } from './conversion/model-to-view-converters.js';
 import ViewConversionDispatcher from './conversion/viewconversiondispatcher.js';
 import { convertText, convertToModelFragment } from './conversion/view-to-model-converters.js';
 
-import Writer from './view/writer.js';
 import ViewDocumentFragment from './view/documentfragment.js';
 import DomConverter from './view/domconverter.js';
 import { NBSP_FILLER } from './view/filler.js';
@@ -68,14 +67,6 @@ export default class DataController {
 		this._mapper = new Mapper();
 
 		/**
-		 * Writer used during the conversion.
-		 *
-		 * @private
-		 * @member {engine.view.Writer} engine.DataController#_writer
-		 */
-		this._writer = new Writer();
-
-		/**
 		 * DOM converter used during the conversion.
 		 *
 		 * @private
@@ -97,7 +88,6 @@ export default class DataController {
 		 * @member {engine.conversion.ModelConversionDispatcher} engine.DataController#modelToView
 		 */
 		this.modelToView = new ModelConversionDispatcher( {
-			writer: this._writer,
 			mapper: this._mapper
 		} );
 		this.modelToView.on( 'insert:$text', insertText() );

+ 0 - 1
packages/ckeditor5-engine/src/editingcontroller.js

@@ -91,7 +91,6 @@ export default class EditingController {
 		 * @member {engine.conversion.ModelConversionDispatcher} engine.EditingController#modelToView
 		 */
 		this.modelToView = new ModelConversionDispatcher( {
-			writer: this.view.writer,
 			mapper: this.mapper,
 			viewSelection: this.view.selection
 		} );

+ 0 - 9
packages/ckeditor5-engine/src/view/document.js

@@ -7,7 +7,6 @@
 
 import Selection from './selection.js';
 import Renderer from './renderer.js';
-import Writer from './writer.js';
 import DomConverter from './domconverter.js';
 import RootEditableElement from './rooteditableelement.js';
 import { injectQuirksHandling } from './filler.js';
@@ -50,14 +49,6 @@ export default class Document {
 		this.selection = new Selection();
 
 		/**
-		 * Tree View writer.
-		 *
-		 * @readonly
-		 * @member {engine.view.Writer} engine.view.Document#writer
-		 */
-		this.writer = new Writer();
-
-		/**
 		 * Instance of the {@link engine.view.DomConverter domConverter} use by
 		 * {@link engine.view.Document#renderer renderer} and {@link engine.view.observer.Observer observers}.
 		 *

Diferenças do arquivo suprimidas por serem muito extensas
+ 512 - 498
packages/ckeditor5-engine/src/view/writer.js


+ 10 - 12
packages/ckeditor5-engine/tests/conversion/advanced-converters.js

@@ -20,7 +20,7 @@ import ViewElement from '/ckeditor5/engine/view/element.js';
 import ViewContainerElement from '/ckeditor5/engine/view/containerelement.js';
 import ViewAttributeElement from '/ckeditor5/engine/view/attributeelement.js';
 import ViewText from '/ckeditor5/engine/view/text.js';
-import ViewWriter from  '/ckeditor5/engine/view/writer.js';
+import viewWriter from  '/ckeditor5/engine/view/writer.js';
 import ViewPosition from '/ckeditor5/engine/view/position.js';
 import ViewRange from '/ckeditor5/engine/view/range.js';
 
@@ -41,7 +41,7 @@ import {
 } from '/ckeditor5/engine/conversion/model-to-view-converters.js';
 import { convertToModelFragment, convertText } from '/ckeditor5/engine/conversion/view-to-model-converters.js';
 
-let modelDoc, modelRoot, viewRoot, mapper, writer, modelDispatcher, viewDispatcher;
+let modelDoc, modelRoot, viewRoot, mapper, modelDispatcher, viewDispatcher;
 
 beforeEach( () => {
 	modelDoc = new ModelDocument();
@@ -51,9 +51,7 @@ beforeEach( () => {
 	mapper = new Mapper();
 	mapper.bindElements( modelRoot, viewRoot );
 
-	writer = new ViewWriter();
-
-	modelDispatcher = new ModelConversionDispatcher( { mapper, writer } );
+	modelDispatcher = new ModelConversionDispatcher( { mapper } );
 	// Schema is mocked up because we don't care about it in those tests.
 	viewDispatcher = new ViewConversionDispatcher( { schema: { check: () => true } } );
 
@@ -177,11 +175,11 @@ describe( 'image with caption converters', () => {
 
 					conversionApi.mapper.bindElements( modelCaption, viewCaption );
 					conversionApi.mapper.bindElements( data.item, viewImageHolder );
-					conversionApi.writer.insert( insertPosition, viewImageHolder );
+					viewWriter.insert( insertPosition, viewImageHolder );
 				}
 			} else {
 				conversionApi.mapper.bindElements( data.item, viewImage );
-				conversionApi.writer.insert( insertPosition, viewImage );
+				viewWriter.insert( insertPosition, viewImage );
 			}
 
 			evt.stop();
@@ -344,8 +342,8 @@ describe( 'custom attribute handling for given element', () => {
 				const viewOldA = elementCreator( data.attributeOldValue );
 				const viewNewA = elementCreator( data.attributeNewValue );
 
-				conversionApi.writer.unwrap( viewRange, viewOldA, evt.priority );
-				conversionApi.writer.wrap( viewRange, viewNewA, evt.priority );
+				viewWriter.unwrap( viewRange, viewOldA, evt.priority );
+				viewWriter.wrap( viewRange, viewNewA, evt.priority );
 
 				evt.stop();
 			};
@@ -412,7 +410,7 @@ describe( 'custom attribute handling for given element', () => {
 			const viewElement = new ViewContainerElement( 'blockquote' );
 
 			conversionApi.mapper.bindElements( data.item, viewElement );
-			conversionApi.writer.insert( viewPosition, viewElement );
+			viewWriter.insert( viewPosition, viewElement );
 
 			if ( consumable.consume( data.item, 'addAttribute:linkHref' ) ) {
 				const viewA = new ViewAttributeElement( 'a', { href: data.item.getAttribute( 'linkHref' ) }, new ViewText( 'see source' ) );
@@ -421,7 +419,7 @@ describe( 'custom attribute handling for given element', () => {
 					viewA.setAttribute( 'title', data.item.getAttribute( 'linkTitle' ) );
 				}
 
-				conversionApi.writer.insert( new ViewPosition( viewElement, viewElement.getChildCount() ), viewA );
+				viewWriter.insert( new ViewPosition( viewElement, viewElement.getChildCount() ), viewA );
 			}
 
 			evt.stop();
@@ -454,7 +452,7 @@ describe( 'custom attribute handling for given element', () => {
 			const viewA = viewElement.getChild( viewElement.getChildCount() - 1 );
 			const aIndex = viewA.getIndex();
 
-			conversionApi.writer.remove( ViewRange.createFromParentsAndOffsets( viewElement, aIndex, viewElement, aIndex + 1 ) );
+			viewWriter.remove( ViewRange.createFromParentsAndOffsets( viewElement, aIndex, viewElement, aIndex + 1 ) );
 
 			evt.stop();
 		} );

+ 2 - 3
packages/ckeditor5-engine/tests/conversion/model-converter-builder.js

@@ -71,7 +71,7 @@ function viewToString( item ) {
 describe( 'Model converter builder', () => {
 	let dispatcher, mapper;
 	let modelDoc, modelRoot;
-	let viewDoc, viewRoot, writer, viewSelection;
+	let viewDoc, viewRoot, viewSelection;
 
 	beforeEach( () => {
 		modelDoc = new ModelDocument();
@@ -79,13 +79,12 @@ describe( 'Model converter builder', () => {
 
 		viewDoc = new ViewDocument();
 		viewRoot = viewDoc.createRoot( 'div' );
-		writer = viewDoc.writer;
 		viewSelection = viewDoc.selection;
 
 		mapper = new Mapper();
 		mapper.bindElements( modelRoot, viewRoot );
 
-		dispatcher = new ModelConversionDispatcher( { writer, mapper, viewSelection } );
+		dispatcher = new ModelConversionDispatcher( { mapper, viewSelection } );
 
 		dispatcher.on( 'insert:$text', insertText() );
 		dispatcher.on( 'move', move() );

+ 4 - 4
packages/ckeditor5-engine/tests/conversion/model-selection-to-view-converters.js

@@ -15,6 +15,7 @@ import ModelPosition from '/ckeditor5/engine/model/position.js';
 import ViewDocument from '/ckeditor5/engine/view/document.js';
 import ViewContainerElement from '/ckeditor5/engine/view/containerelement.js';
 import ViewAttributeElement from '/ckeditor5/engine/view/attributeelement.js';
+import { mergeAttributes } from '/ckeditor5/engine/view/writer.js';
 
 import Mapper from '/ckeditor5/engine/conversion/mapper.js';
 import ModelConversionDispatcher from '/ckeditor5/engine/conversion/modelconversiondispatcher.js';
@@ -36,7 +37,7 @@ import { setData as setModelData } from '/tests/engine/_utils/model.js';
 
 let dispatcher, mapper;
 let modelDoc, modelRoot, modelSelection;
-let viewDoc, viewRoot, writer, viewSelection;
+let viewDoc, viewRoot, viewSelection;
 
 beforeEach( () => {
 	modelDoc = new ModelDocument();
@@ -45,13 +46,12 @@ beforeEach( () => {
 
 	viewDoc = new ViewDocument();
 	viewRoot = viewDoc.createRoot( 'div' );
-	writer = viewDoc.writer;
 	viewSelection = viewDoc.selection;
 
 	mapper = new Mapper();
 	mapper.bindElements( modelRoot, viewRoot );
 
-	dispatcher = new ModelConversionDispatcher( { mapper, writer, viewSelection } );
+	dispatcher = new ModelConversionDispatcher( { mapper, viewSelection } );
 
 	dispatcher.on( 'insert:$text', insertText() );
 	dispatcher.on( 'addAttribute:bold', wrap( new ViewAttributeElement( 'strong' ) ) );
@@ -288,7 +288,7 @@ describe( 'clean-up', () => {
 			);
 
 			// Remove <b></b> manually.
-			writer.mergeAttributes( viewSelection.getFirstPosition() );
+			mergeAttributes( viewSelection.getFirstPosition() );
 
 			const modelRange = ModelRange.createFromParentsAndOffsets( modelRoot, 1, modelRoot, 1 );
 			modelDoc.selection.setRanges( [ modelRange ] );

+ 2 - 5
packages/ckeditor5-engine/tests/conversion/model-to-view-converters.js

@@ -17,7 +17,6 @@ import ViewElement from '/ckeditor5/engine/view/element.js';
 import ViewContainerElement from '/ckeditor5/engine/view/containerelement.js';
 import ViewAttributeElement from '/ckeditor5/engine/view/attributeelement.js';
 import ViewText from '/ckeditor5/engine/view/text.js';
-import ViewWriter from  '/ckeditor5/engine/view/writer.js';
 
 import Mapper from '/ckeditor5/engine/conversion/mapper.js';
 import ModelConversionDispatcher from '/ckeditor5/engine/conversion/modelconversiondispatcher.js';
@@ -32,7 +31,7 @@ import {
 	remove
 } from '/ckeditor5/engine/conversion/model-to-view-converters.js';
 
-let dispatcher, modelDoc, modelRoot, mapper, viewRoot, writer;
+let dispatcher, modelDoc, modelRoot, mapper, viewRoot;
 
 beforeEach( () => {
 	modelDoc = new ModelDocument();
@@ -42,9 +41,7 @@ beforeEach( () => {
 	mapper = new Mapper();
 	mapper.bindElements( modelRoot, viewRoot );
 
-	writer = new ViewWriter();
-
-	dispatcher = new ModelConversionDispatcher( { mapper, writer } );
+	dispatcher = new ModelConversionDispatcher( { mapper } );
 } );
 
 function viewAttributesToString( item ) {

+ 0 - 2
packages/ckeditor5-engine/tests/view/document/document.js

@@ -11,7 +11,6 @@ import Document from '/ckeditor5/engine/view/document.js';
 import Observer from '/ckeditor5/engine/view/observer/observer.js';
 import Renderer from '/ckeditor5/engine/view/renderer.js';
 import ViewRange from '/ckeditor5/engine/view/range.js';
-import Writer from '/ckeditor5/engine/view/writer.js';
 import DomConverter from '/ckeditor5/engine/view/domconverter.js';
 import testUtils from '/tests/ckeditor5/_utils/utils.js';
 import count from '/ckeditor5/utils/count.js';
@@ -58,7 +57,6 @@ describe( 'Document', () => {
 			expect( count( viewDocument.roots ) ).to.equal( 0 );
 			expect( count( viewDocument._observers ) ).to.equal( 0 );
 			expect( viewDocument ).to.have.property( 'renderer' ).that.is.instanceOf( Renderer );
-			expect( viewDocument ).to.have.property( 'writer' ).that.is.instanceOf( Writer );
 			expect( viewDocument ).to.have.property( 'domConverter' ).that.is.instanceOf( DomConverter );
 			expect( viewDocument ).to.have.property( 'isFocused' ).that.is.false;
 		} );

+ 3 - 9
packages/ckeditor5-engine/tests/view/writer/breakattributes.js

@@ -7,15 +7,13 @@
 
 'use strict';
 
-import Writer from '/ckeditor5/engine/view/writer.js';
+import { breakAttributes } from '/ckeditor5/engine/view/writer.js';
 import DocumentFragment from '/ckeditor5/engine/view/documentfragment.js';
 import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
 import Text from '/ckeditor5/engine/view/text.js';
 import { stringify, parse } from '/tests/engine/_utils/view.js';
 
-describe( 'Writer', () => {
-	let writer;
-
+describe( 'writer', () => {
 	/**
 	 * Executes test using `parse` and `stringify` utils functions. Uses range delimiters `[]{}` to create and
 	 * test break position.
@@ -31,14 +29,10 @@ describe( 'Writer', () => {
 			view = new DocumentFragment( view );
 		}
 
-		const newPosition = writer.breakAttributes( selection.getFirstPosition() );
+		const newPosition = breakAttributes( selection.getFirstPosition() );
 		expect( stringify( view, newPosition, { showType: true, showPriority: true } ) ).to.equal( expected );
 	}
 
-	beforeEach( () => {
-		writer = new Writer();
-	} );
-
 	describe( 'breakAttributes', () => {
 		it( 'should not break text nodes if they are not in attribute elements - middle', () => {
 			test(

+ 4 - 10
packages/ckeditor5-engine/tests/view/writer/breakrange.js

@@ -7,7 +7,7 @@
 
 'use strict';
 
-import Writer from '/ckeditor5/engine/view/writer.js';
+import { breakRange } from '/ckeditor5/engine/view/writer.js';
 import DocumentFragment from '/ckeditor5/engine/view/documentfragment.js';
 import ContainerElement from '/ckeditor5/engine/view/containerelement.js';
 import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
@@ -15,9 +15,7 @@ import Text from '/ckeditor5/engine/view/text.js';
 import Range from '/ckeditor5/engine/view/range.js';
 import { stringify, parse } from '/tests/engine/_utils/view.js';
 
-describe( 'Writer', () => {
-	let writer;
-
+describe( 'writer', () => {
 	/**
 	 * Executes test using `parse` and `stringify` utils functions.
 	 *
@@ -31,21 +29,17 @@ describe( 'Writer', () => {
 			view = new DocumentFragment( view );
 		}
 
-		const newRange = writer.breakRange( selection.getFirstRange() );
+		const newRange = breakRange( selection.getFirstRange() );
 		expect( stringify( view, newRange, { showType: true } ) ).to.equal( expected );
 	}
 
-	beforeEach( () => {
-		writer = new Writer();
-	} );
-
 	describe( 'breakRange', () => {
 		it( 'should throw when range placed in two containers', () => {
 			const p1 = new ContainerElement( 'p' );
 			const p2 = new ContainerElement( 'p' );
 
 			expect( () => {
-				writer.breakRange( Range.createFromParentsAndOffsets( p1, 0, p2, 0 ) );
+				breakRange( Range.createFromParentsAndOffsets( p1, 0, p2, 0 ) );
 			} ).to.throw( 'view-writer-invalid-range-container' );
 		} );
 

+ 39 - 0
packages/ckeditor5-engine/tests/view/writer/getparentcontainer.js

@@ -0,0 +1,39 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: view, browser-only */
+
+'use strict';
+
+import { getParentContainer } from '/ckeditor5/engine/view/writer.js';
+import ContainerElement from '/ckeditor5/engine/view/containerelement.js';
+import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
+import Text from '/ckeditor5/engine/view/text.js';
+import Position from '/ckeditor5/engine/view/position.js';
+
+describe( 'writer', () => {
+	describe( 'getParentContainer', () => {
+		it( 'should return parent container of the node', () => {
+			const text = new Text( 'foobar' );
+			const b = new AttributeElement( 'b', null, [ text ] );
+			const parent = new ContainerElement( 'p', null, [ b ] );
+
+			b.priority = 1;
+			const container = getParentContainer( new Position( text, 0 ) );
+
+			expect( container ).to.equal( parent );
+		} );
+
+		it( 'should return undefined if no parent container', () => {
+			const text = new Text( 'foobar' );
+			const b = new AttributeElement( 'b', null, [ text ] );
+
+			b.priority = 1;
+			const container = getParentContainer( new Position( text, 0 ) );
+
+			expect( container ).to.be.undefined;
+		} );
+	} );
+} );

+ 5 - 11
packages/ckeditor5-engine/tests/view/writer/insert.js

@@ -7,7 +7,7 @@
 
 'use strict';
 
-import Writer from '/ckeditor5/engine/view/writer.js';
+import { insert } from '/ckeditor5/engine/view/writer.js';
 import DocumentFragment from '/ckeditor5/engine/view/documentfragment.js';
 import ContainerElement from '/ckeditor5/engine/view/containerelement.js';
 import Element from '/ckeditor5/engine/view/element.js';
@@ -17,9 +17,7 @@ import { stringify, parse } from '/tests/engine/_utils/view.js';
 import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
 import Text from '/ckeditor5/engine/view/text.js';
 
-describe( 'Writer', () => {
-	let writer;
-
+describe( 'writer', () => {
 	/**
 	 * Executes test using `parse` and `stringify` utils functions.
 	 *
@@ -35,14 +33,10 @@ describe( 'Writer', () => {
 			view = new DocumentFragment( view );
 		}
 
-		const newRange = writer.insert( selection.getFirstPosition(), nodesToInsert );
+		const newRange = insert( selection.getFirstPosition(), nodesToInsert );
 		expect( stringify( view, newRange, { showType: true, showPriority: true } ) ).to.equal( expected );
 	}
 
-	beforeEach( () => {
-		writer = new Writer();
-	} );
-
 	describe( 'insert', () => {
 		it( 'should return collapsed range in insertion position when using empty array', () => {
 			test(
@@ -157,7 +151,7 @@ describe( 'Writer', () => {
 			const container = new ContainerElement( 'p' );
 			const position = new Position( container, 0 );
 			expect( () => {
-				writer.insert( position, element );
+				insert( position, element );
 			} ).to.throw( CKEditorError, 'view-writer-insert-invalid-node' );
 		} );
 
@@ -168,7 +162,7 @@ describe( 'Writer', () => {
 			const position = new Position( container, 0 );
 
 			expect( () => {
-				writer.insert( position, root );
+				insert( position, root );
 			} ).to.throw( CKEditorError, 'view-writer-insert-invalid-node' );
 		} );
 	} );

+ 4 - 10
packages/ckeditor5-engine/tests/view/writer/mergeattributes.js

@@ -7,15 +7,13 @@
 
 'use strict';
 
-import Writer from '/ckeditor5/engine/view/writer.js';
+import { mergeAttributes } from '/ckeditor5/engine/view/writer.js';
 import ContainerElement from '/ckeditor5/engine/view/containerelement.js';
 import Text from '/ckeditor5/engine/view/text.js';
 import Position from '/ckeditor5/engine/view/position.js';
 import { stringify, parse } from '/tests/engine/_utils/view.js';
 
-describe( 'Writer', () => {
-	let writer;
-
+describe( 'writer', () => {
 	/**
 	 * Executes test using `parse` and `stringify` utils functions. Uses range delimiters `[]{}` to create and
 	 * test merge position.
@@ -25,14 +23,10 @@ describe( 'Writer', () => {
 	 */
 	function test( input, expected ) {
 		const { view, selection } = parse( input );
-		const newPosition = writer.mergeAttributes( selection.getFirstPosition() );
+		const newPosition = mergeAttributes( selection.getFirstPosition() );
 		expect( stringify( view, newPosition, { showType: true, showPriority: true } ) ).to.equal( expected );
 	}
 
-	beforeEach( () => {
-		writer = new Writer();
-	} );
-
 	describe( 'mergeAttributes', () => {
 		it( 'should not merge if inside text node', () => {
 			test( '<container:p>fo{}bar</container:p>', '<container:p>fo{}bar</container:p>' );
@@ -73,7 +67,7 @@ describe( 'Writer', () => {
 			const p = new ContainerElement( 'p', null, [ t1, t2 ] );
 			const position = new Position( p, 1 );
 
-			const newPosition = writer.mergeAttributes( position );
+			const newPosition = mergeAttributes( position );
 			expect( stringify( p, newPosition ) ).to.equal( '<p>foo{}bar</p>' );
 		} );
 

+ 102 - 0
packages/ckeditor5-engine/tests/view/writer/move.js

@@ -0,0 +1,102 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: view, browser-only */
+
+'use strict';
+
+import { move } from '/ckeditor5/engine/view/writer.js';
+import DocumentFragment from '/ckeditor5/engine/view/documentfragment.js';
+import { stringify, parse } from '/tests/engine/_utils/view.js';
+import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
+import Text from '/ckeditor5/engine/view/text.js';
+
+describe( 'writer', () => {
+	/**
+	 * Executes test using `parse` and `stringify` utils functions. Uses range delimiters `[]{}` to create and
+	 * test ranges.
+	 *
+	 * @param {String} input
+	 * @param {String} expectedResult
+	 * @param {String} expectedRemoved
+	 */
+	function test( source, destination, sourceAfterMove, destinationAfterMove ) {
+		let { view: srcView, selection: srcSelection } = parse( source );
+		let { view: dstView, selection: dstSelection } = parse( destination );
+
+		if ( srcView instanceof AttributeElement || srcView instanceof Text ) {
+			srcView = new DocumentFragment( srcView );
+		}
+
+		if ( dstView instanceof AttributeElement || dstView instanceof Text ) {
+			dstView = new DocumentFragment( dstView );
+		}
+
+		const newRange = move( srcSelection.getFirstRange(), dstSelection.getFirstPosition() );
+
+		expect( stringify( dstView, newRange, { showType: true, showPriority: true } ) ).to.equal( destinationAfterMove );
+		expect( stringify( srcView, null, { showType: true, showPriority: true } ) ).to.equal( sourceAfterMove );
+	}
+
+	describe( 'move', () => {
+		it( 'should move single text node', () => {
+			test(
+				'<container:p>[foobar]</container:p>',
+				'<container:p>[]</container:p>',
+				'<container:p></container:p>',
+				'<container:p>[foobar]</container:p>'
+			);
+		} );
+
+		it( 'should not leave empty text nodes', () => {
+			test(
+				'<container:p>{foobar}</container:p>',
+				'<container:p>[]</container:p>',
+				'<container:p></container:p>',
+				'<container:p>[foobar]</container:p>'
+			);
+		} );
+
+		it( 'should move part of the text node', () => {
+			test(
+				'<container:p>f{oob}ar</container:p>',
+				'<container:p>[]</container:p>',
+				'<container:p>far</container:p>',
+				'<container:p>[oob]</container:p>'
+			);
+		} );
+
+		it( 'should move parts of nodes', () => {
+			test(
+				'<container:p>f{oo<attribute:b:10>ba}r</attribute:b:10></container:p>',
+				'<container:p>[]<attribute:b:10>qux</attribute:b:10></container:p>',
+				'<container:p>f<attribute:b:10>r</attribute:b:10></container:p>',
+				'<container:p>[oo<attribute:b:10>ba}qux</attribute:b:10></container:p>'
+			);
+		} );
+
+		it( 'should merge after moving #1', () => {
+			test(
+				'<container:p><attribute:b:1>foo</attribute:b:1>[bar]<attribute:b:1>bazqux</attribute:b:1></container:p>',
+				'<container:p><attribute:b:1>foo{}bazqux</attribute:b:1></container:p>',
+				'<container:p><attribute:b:1>foobazqux</attribute:b:1></container:p>',
+				'<container:p><attribute:b:1>foo</attribute:b:1>[bar]<attribute:b:1>bazqux</attribute:b:1></container:p>'
+			);
+		} );
+
+		it( 'should merge after moving #2', () => {
+			test(
+				'<container:p><attribute:b:1>fo{o</attribute:b:1>bar<attribute:b:1>ba}zqux</attribute:b:1></container:p>',
+				'<container:p><attribute:b:1>fo{}zqux</attribute:b:1></container:p>',
+				'<container:p><attribute:b:1>fozqux</attribute:b:1></container:p>',
+				'<container:p><attribute:b:1>fo{o</attribute:b:1>bar<attribute:b:1>ba}zqux</attribute:b:1></container:p>'
+			);
+		} );
+
+		it( 'should move part of the text node in document fragment', () => {
+			test( 'fo{ob}ar', 'fo{}ar', 'foar', 'fo{ob}ar' );
+		} );
+	} );
+} );

+ 5 - 11
packages/ckeditor5-engine/tests/view/writer/remove.js

@@ -7,7 +7,7 @@
 
 'use strict';
 
-import Writer from '/ckeditor5/engine/view/writer.js';
+import { remove } from '/ckeditor5/engine/view/writer.js';
 import ContainerElement from '/ckeditor5/engine/view/containerelement.js';
 import Range from '/ckeditor5/engine/view/range.js';
 import DocumentFragment from '/ckeditor5/engine/view/documentfragment.js';
@@ -15,9 +15,7 @@ import { stringify, parse } from '/tests/engine/_utils/view.js';
 import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
 import Text from '/ckeditor5/engine/view/text.js';
 
-describe( 'Writer', () => {
-	let writer;
-
+describe( 'writer', () => {
 	/**
 	 * Executes test using `parse` and `stringify` utils functions. Uses range delimiters `[]{}` to create and
 	 * test ranges.
@@ -34,29 +32,25 @@ describe( 'Writer', () => {
 		}
 
 		const range = selection.getFirstRange();
-		const removed = writer.remove( range );
+		const removed = remove( range );
 		expect( stringify( view, range, { showType: true, showPriority: true } ) ).to.equal( expectedResult );
 		expect( stringify( removed, null, { showType: true, showPriority: true } ) ).to.equal( expectedRemoved );
 	}
 
-	beforeEach( () => {
-		writer = new Writer();
-	} );
-
 	describe( 'remove', () => {
 		it( 'should throw when range placed in two containers', () => {
 			const p1 = new ContainerElement( 'p' );
 			const p2 = new ContainerElement( 'p' );
 
 			expect( () => {
-				writer.remove( Range.createFromParentsAndOffsets( p1, 0, p2, 0 ) );
+				remove( Range.createFromParentsAndOffsets( p1, 0, p2, 0 ) );
 			} ).to.throw( 'view-writer-invalid-range-container' );
 		} );
 
 		it( 'should return empty DocumentFragment when range is collapsed', () => {
 			const p = new ContainerElement( 'p' );
 			const range = Range.createFromParentsAndOffsets( p, 0, p, 0 );
-			const fragment = writer.remove( range );
+			const fragment = remove( range );
 
 			expect( fragment ).to.be.instanceof( DocumentFragment );
 			expect( fragment.getChildCount() ).to.equal( 0 );

+ 5 - 11
packages/ckeditor5-engine/tests/view/writer/unwrap.js

@@ -7,7 +7,7 @@
 
 'use strict';
 
-import Writer from '/ckeditor5/engine/view/writer.js';
+import { unwrap } from '/ckeditor5/engine/view/writer.js';
 import Element from '/ckeditor5/engine/view/element.js';
 import ContainerElement from '/ckeditor5/engine/view/containerelement.js';
 import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
@@ -18,9 +18,7 @@ import Text from '/ckeditor5/engine/view/text.js';
 import CKEditorError from '/ckeditor5/utils/ckeditorerror.js';
 import { stringify, parse } from '/tests/engine/_utils/view.js';
 
-describe( 'Writer', () => {
-	let writer;
-
+describe( 'writer', () => {
 	/**
 	 * Executes test using `parse` and `stringify` utils functions.
 	 *
@@ -35,14 +33,10 @@ describe( 'Writer', () => {
 			view = new DocumentFragment( view );
 		}
 
-		const newRange = writer.unwrap( selection.getFirstRange(), parse( unwrapAttribute ) );
+		const newRange = unwrap( selection.getFirstRange(), parse( unwrapAttribute ) );
 		expect( stringify( view, newRange, { showType: true, showPriority: true } ) ).to.equal( expected );
 	}
 
-	beforeEach( () => {
-		writer = new Writer();
-	} );
-
 	describe( 'unwrap', () => {
 		it( 'should do nothing on collapsed ranges', () => {
 			test(
@@ -69,7 +63,7 @@ describe( 'Writer', () => {
 			const b = new Element( 'b' );
 
 			expect( () => {
-				writer.unwrap( range, b );
+				unwrap( range, b );
 			} ).to.throw( CKEditorError, 'view-writer-unwrap-invalid-attribute' );
 		} );
 
@@ -83,7 +77,7 @@ describe( 'Writer', () => {
 			const b = new AttributeElement( 'b' );
 
 			expect( () => {
-				writer.unwrap( range, b, 1 );
+				unwrap( range, b, 1 );
 			} ).to.throw( CKEditorError, 'view-writer-invalid-range-container' );
 		} );
 

+ 5 - 11
packages/ckeditor5-engine/tests/view/writer/wrap.js

@@ -7,7 +7,7 @@
 
 'use strict';
 
-import Writer from '/ckeditor5/engine/view/writer.js';
+import { wrap } from '/ckeditor5/engine/view/writer.js';
 import Element from '/ckeditor5/engine/view/element.js';
 import ContainerElement from '/ckeditor5/engine/view/containerelement.js';
 import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
@@ -18,9 +18,7 @@ import Text from '/ckeditor5/engine/view/text.js';
 import CKEditorError from '/ckeditor5/utils/ckeditorerror.js';
 import { stringify, parse } from '/tests/engine/_utils/view.js';
 
-describe( 'Writer', () => {
-	let writer;
-
+describe( 'writer', () => {
 	/**
 	 * Executes test using `parse` and `stringify` utils functions.
 	 *
@@ -35,14 +33,10 @@ describe( 'Writer', () => {
 			view = new DocumentFragment( view );
 		}
 
-		const newRange = writer.wrap( selection.getFirstRange(), parse( unwrapAttribute ) );
+		const newRange = wrap( selection.getFirstRange(), parse( unwrapAttribute ) );
 		expect( stringify( view, newRange, { showType: true, showPriority: true } ) ).to.equal( expected );
 	}
 
-	beforeEach( () => {
-		writer = new Writer();
-	} );
-
 	describe( 'wrap', () => {
 		it( 'should do nothing on collapsed ranges', () => {
 			test(
@@ -77,7 +71,7 @@ describe( 'Writer', () => {
 			const b = new Element( 'b' );
 
 			expect( () => {
-				writer.wrap( range, b );
+				wrap( range, b );
 			} ).to.throw( CKEditorError, 'view-writer-wrap-invalid-attribute' );
 		} );
 
@@ -91,7 +85,7 @@ describe( 'Writer', () => {
 			const b = new AttributeElement( 'b' );
 
 			expect( () => {
-				writer.wrap( range, b, 1 );
+				wrap( range, b, 1 );
 			} ).to.throw( CKEditorError, 'view-writer-invalid-range-container' );
 		} );
 

+ 3 - 9
packages/ckeditor5-engine/tests/view/writer/wrapposition.js

@@ -7,7 +7,7 @@
 
 'use strict';
 
-import Writer from '/ckeditor5/engine/view/writer.js';
+import { wrapPosition } from '/ckeditor5/engine/view/writer.js';
 import Text from '/ckeditor5/engine/view/text.js';
 import Element from '/ckeditor5/engine/view/element.js';
 import ContainerElement from '/ckeditor5/engine/view/containerelement.js';
@@ -18,8 +18,6 @@ import { stringify, parse } from '/tests/engine/_utils/view.js';
 import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
 
 describe( 'wrapPosition', () => {
-	let writer;
-
 	/**
 	 * Executes test using `parse` and `stringify` utils functions.
 	 *
@@ -34,21 +32,17 @@ describe( 'wrapPosition', () => {
 			view = new DocumentFragment( view );
 		}
 
-		const newPosition = writer.wrapPosition( selection.getFirstPosition(), parse( unwrapAttribute ) );
+		const newPosition = wrapPosition( selection.getFirstPosition(), parse( unwrapAttribute ) );
 		expect( stringify( view, newPosition, { showType: true, showPriority: true } ) ).to.equal( expected );
 	}
 
-	beforeEach( () => {
-		writer = new Writer();
-	} );
-
 	it( 'should throw error when element is not instance of AttributeElement', () => {
 		const container = new ContainerElement( 'p', null, new Text( 'foo' ) );
 		const position = new Position( container, 0 );
 		const b = new Element( 'b' );
 
 		expect( () => {
-			writer.wrapPosition( position, b );
+			wrapPosition( position, b );
 		} ).to.throw( CKEditorError, 'view-writer-wrap-invalid-attribute' );
 	} );
 

+ 0 - 66
packages/ckeditor5-engine/tests/view/writer/writer.js

@@ -1,66 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: view, browser-only */
-
-'use strict';
-
-import Writer from '/ckeditor5/engine/view/writer.js';
-import ContainerElement from '/ckeditor5/engine/view/containerelement.js';
-import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
-import Text from '/ckeditor5/engine/view/text.js';
-import Position from '/ckeditor5/engine/view/position.js';
-import { parse } from '/tests/engine/_utils/view.js';
-
-describe( 'Writer', () => {
-	let writer;
-
-	beforeEach( () => {
-		writer = new Writer();
-	} );
-
-	describe( 'getParentContainer', () => {
-		it( 'should return parent container of the node', () => {
-			const text = new Text( 'foobar' );
-			const b = new AttributeElement( 'b', null, [ text ] );
-			const parent = new ContainerElement( 'p', null, [ b ] );
-
-			b.priority = 1;
-			const container = writer.getParentContainer( new Position( text, 0 ) );
-
-			expect( container ).to.equal( parent );
-		} );
-
-		it( 'should return undefined if no parent container', () => {
-			const text = new Text( 'foobar' );
-			const b = new AttributeElement( 'b', null, [ text ] );
-
-			b.priority = 1;
-			const container = writer.getParentContainer( new Position( text, 0 ) );
-
-			expect( container ).to.be.undefined;
-		} );
-	} );
-
-	describe( 'move', () => {
-		it( 'should move nodes using remove and insert methods', () => {
-			const { selection: sourceSelection } = parse( '<container:p>[foobar]</container:p>' );
-			const { selection: targetSelection } = parse( '<container:div>[]</container:div>' );
-
-			const removeSpy = sinon.spy( writer, 'remove' );
-			const insertSpy = sinon.spy( writer, 'insert' );
-			const sourceRange = sourceSelection.getFirstRange();
-			const targetPosition = targetSelection.getFirstPosition();
-
-			const newRange = writer.move( sourceRange, targetPosition );
-
-			sinon.assert.calledOnce( removeSpy );
-			sinon.assert.calledWithExactly( removeSpy, sourceRange );
-			sinon.assert.calledOnce( insertSpy );
-			sinon.assert.calledWithExactly( insertSpy, targetPosition, removeSpy.firstCall.returnValue );
-			expect( newRange ).to.equal( insertSpy.firstCall.returnValue );
-		} );
-	} );
-} );