Selaa lähdekoodia

Changed: view.Writer renamed methods and updated docs.

Szymon Cofalik 9 vuotta sitten
vanhempi
commit
7fdb10cd06

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

@@ -84,7 +84,7 @@ export function convertCollapsedSelection() {
 
 		const modelPosition = selection.getFirstPosition();
 		const viewPosition = conversionApi.mapper.toViewPosition( modelPosition );
-		const brokenPosition = viewWriter.breakAt( viewPosition );
+		const brokenPosition = viewWriter.breakAttributes( viewPosition );
 
 		conversionApi.viewSelection.removeAllRanges();
 		conversionApi.viewSelection.addRange( new ViewRange( brokenPosition, brokenPosition ) );
@@ -202,7 +202,7 @@ export function clearAttributes() {
 			if ( range.isCollapsed ) {
 				// Position might be in the node removed by the view writer.
 				if ( range.end.parent.document ) {
-					viewWriter.mergeAt( range.start );
+					viewWriter.mergeAttributes( range.start );
 				}
 			}
 		}

+ 2 - 2
packages/ckeditor5-engine/src/view/containerelement.js

@@ -20,14 +20,14 @@ import Element from './element.js';
  * to put caret inside it, even if the container is empty.
  *
  * Secondly, {@link engine.view.writer view writer} uses this information.
- * Nodes {@link engine.view.writer.breakAt breaking} and {@link engine.view.writer.mergeAt merging}
+ * Nodes {@link engine.view.writer.breakAttributes breaking} and {@link engine.view.writer.mergeAttributes merging}
  * is performed only in a bounds of a container nodes.
  *
  * For instance if `<p>` is an container and `<b>` is attribute:
  *
  *		<p><b>fo^o</b></p>
  *
- * {@link engine.view.writer.breakAt breakAt} will create:
+ * {@link engine.view.writer.breakAttributes breakAttributes} will create:
  *
  *		<p><b>fo</b><b>o</b></p>
  *

+ 87 - 66
packages/ckeditor5-engine/src/view/writer.js

@@ -19,10 +19,9 @@ import isIterable from '../../utils/isiterable.js';
  */
 
 export default {
-	breakAt,
-	breakRange,
+	breakAttributes,
 	breakContainer,
-	mergeAt,
+	mergeAttributes,
 	mergeContainers,
 	insert,
 	remove,
@@ -33,44 +32,40 @@ export default {
 };
 
 /**
- * Breaks attribute nodes at provided position. It breaks `attribute` nodes inside `container` node.
+ * Breaks attribute nodes at provided position or at boundaries of provided range. It breaks attribute elements inside
+ * up to a container element.
  *
  * In following examples `<p>` is a container, `<b>` and `<u>` are attribute nodes:
  *
  *		<p>foo<b><u>bar{}</u></b></p> -> <p>foo<b><u>bar</u></b>[]</p>
  *		<p>foo<b><u>{}bar</u></b></p> -> <p>foo{}<b><u>bar</u></b></p>
  *		<p>foo<b><u>b{}ar</u></b></p> -> <p>foo<b><u>b</u></b>[]<b><u>ar</u></b></p>
+ *		<p><b>fo{o</b><u>ba}r</u></p> -> <p><b>fo</b><b>o</b><u>ba</u><u>r</u></b></p>
  *
- * Note that {@link engine.view.DocumentFragment DocumentFragment} is treated like a container.
+ * **Note:** {@link engine.view.DocumentFragment DocumentFragment} is treated like a container.
  *
- * @see engine.view.AttributeElement
- * @see engine.view.ContainerElement
- * @function engine.view.writer.breakAt
- * @param {engine.view.Position} position Position where to break attributes.
- * @returns {engine.view.Position} New position after breaking the attributes.
- */
-export function breakAt( position ) {
-	return _breakAt( position, false );
-}
-
-/**
- * Uses {@link engine.view.writer.breakAt breakAt} method to break attributes on
- * {@link engine.view.Range#start start} and {@link engine.view.Range#end end} positions of
- * provided {@link engine.view.Range Range}.
+ * **Note:** Difference between {@link engine.view.writer.breakAttributes breakAttributes} and
+ * {@link engine.view.writer.breakContainer breakContainer} is that `breakAttributes` breaks all
+ * {@link engine.view.AttributeElement attribute elements} that are ancestors of given `position`, up to the first
+ * encountered {@link engine.view.ContainerElement container element}. `breakContainer` assumes that given `position`
+ * is directly in container element and breaks that container element.
  *
- * Throws {@link utils.CKEditorError CKEditorError} `view-writer-invalid-range-container` when
- * {@link engine.view.Range#start start} and {@link engine.view.Range#end end} positions are not placed inside
- * same parent container.
+ * Throws {@link utils.CKEditorError CKEditorError} `view-writer-invalid-range-container` when {@link engine.view.Range#start start}
+ * and {@link engine.view.Range#end end} positions of a passed range are not placed inside same parent container.
  *
- * Note that {@link engine.view.DocumentFragment DocumentFragment} is treated like a container.
- *
- * @see engine.view.writer.breakAt
- * @function engine.view.writer.breakRange
- * @param {engine.view.Range} range Range which `start` and `end` positions will be used to break attributes.
- * @returns {engine.view.Range} New range with boundaries located at break positions.
+ * @see engine.view.AttributeElement
+ * @see engine.view.ContainerElement
+ * @see engine.view.writer.breakContainer
+ * @function engine.view.writer.breakAttributes
+ * @param {engine.view.Position|engine.view.Range} positionOrRange Position where to break attribute elements.
+ * @returns {engine.view.Position|engine.view.Range} New position or range, after breaking the attribute elements.
  */
-export function breakRange( range ) {
-	return _breakRange( range );
+export function breakAttributes( positionOrRange ) {
+	if ( positionOrRange instanceof Position ) {
+		return _breakAttributes( positionOrRange );
+	} else {
+		return _breakAttributesRange( positionOrRange );
+	}
 }
 
 /**
@@ -83,6 +78,16 @@ export function breakRange( range ) {
  *		<p>^foobar</p> -> ^<p>foobar</p>
  *		<p>foobar^</p> -> <p>foobar</p>^
  *
+ * **Note:** Difference between {@link engine.view.writer.breakAttributes breakAttributes} and
+ * {@link engine.view.writer.breakContainer breakContainer} is that `breakAttributes` breaks all
+ * {@link engine.view.AttributeElement attribute elements} that are ancestors of given `position`, up to the first
+ * encountered {@link engine.view.ContainerElement container element}. `breakContainer` assumes that given `position`
+ * is directly in container element and breaks that container element.
+ *
+ * @see engine.view.AttributeElement
+ * @see engine.view.ContainerElement
+ * @see engine.view.writer.breakAttributes
+ * @function engine.view.writer.breakContainer
  * @param {engine.view.Position} position Position where to break element.
  * @returns {engine.view.Position} Position between broken elements. If element has not been broken, the returned position
  * is placed either before it or after it.
@@ -125,10 +130,10 @@ export function breakContainer( position ) {
 }
 
 /**
- * Merges attribute nodes. It also merges text nodes if needed.
- * Only {@link engine.view.AttributeElement#isSimilar similar} `attribute` nodes can be merged.
+ * Merges {@link engine.view.AttributeElement attribute elements}. It also merges text nodes if needed.
+ * Only {@link engine.view.AttributeElement#isSimilar similar} attribute elements can be merged.
  *
- * In following examples `<p>` is a container and `<b>` is an attribute node:
+ * In following examples `<p>` is a container and `<b>` is an attribute element:
  *
  *		<p>foo[]bar</p> -> <p>foo{}bar</p>
  *		<p><b>foo</b>[]<b>bar</b> -> <p><b>foo{}bar</b></b>
@@ -139,13 +144,19 @@ export function breakContainer( position ) {
  *		<p><b>[]</b></p> -> <p>[]</p>
  *		<p><b>foo</b><i>[]</i><b>bar</b></p> -> <p><b>foo{}bar</b></p>
  *
+ * **Note:** Difference between {@link engine.view.writer.mergeAttributes mergeAttributes} and
+ * {@link engine.view.writer.mergeContainers mergeContainers} is that `mergeAttributes` merges two
+ * {@link engine.view.AttributeElement attribute elements} or {@link engine.view.Text text nodes}
+ * while `mergeContainer` merges two {@link engine.view.ContainerElement container elements}.
+ *
  * @see engine.view.AttributeElement
  * @see engine.view.ContainerElement
- * @function engine.view.writer.mergeAt
+ * @see engine.view.writer.mergeContainers
+ * @function engine.view.writer.mergeAttributes
  * @param {engine.view.Position} position Merge position.
  * @returns {engine.view.Position} Position after merge.
  */
-export function mergeAt( position ) {
+export function mergeAttributes( position ) {
 	const positionOffset = position.offset;
 	const positionParent = position.parent;
 
@@ -160,7 +171,7 @@ export function mergeAt( position ) {
 		const offset = positionParent.index;
 		positionParent.remove();
 
-		return mergeAt( new Position( parent, offset ) );
+		return mergeAttributes( new Position( parent, offset ) );
 	}
 
 	const nodeBefore = positionParent.getChild( positionOffset - 1 );
@@ -190,21 +201,30 @@ export function mergeAt( position ) {
 
 		// New position is located inside the first node, before new nodes.
 		// Call this method recursively to merge again if needed.
-		return mergeAt( new Position( nodeBefore, count ) );
+		return mergeAttributes( new Position( nodeBefore, count ) );
 	}
 
 	return position;
 }
 
 /**
- * Merges two {@link engine.view.ContainerElement container view elements} that are before and after given position.
- * Precisly, the element after the position is removed and it's contents are moved to element before the position.
+ * Merges two {@link engine.view.ContainerElement container elements} that are before and after given position.
+ * Precisely, the element after the position is removed and it's contents are moved to element before the position.
  *
  *		<p>foo</p>^<p>bar</p> -> <p>foo^bar</p>
  *		<div>foo</div>^<p>bar</p> -> <div>foo^bar</div>
  *
- * @param position
- * @returns {Position}
+ * **Note:** Difference between {@link engine.view.writer.mergeAttributes mergeAttributes} and
+ * {@link engine.view.writer.mergeContainers mergeContainers} is that `mergeAttributes` merges two
+ * {@link engine.view.AttributeElement attribute elements} or {@link engine.view.Text text nodes}
+ * while `mergeContainer` merges two {@link engine.view.ContainerElement container elements}.
+ *
+ * @see engine.view.AttributeElement
+ * @see engine.view.ContainerElement
+ * @see engine.view.writer.mergeAttributes
+ * @function engine.view.writer.mergeContainers
+ * @param {engine.view.Position} position Merge position.
+ * @returns {engine.view.Position} Position after merge.
  */
 export function mergeContainers( position ) {
 	const prev = position.nodeBefore;
@@ -216,7 +236,8 @@ export function mergeContainers( position ) {
 		 *
 		 * @error view-writer-merge-containers-invalid-position
 		 */
-		throw new CKEditorError( 'view-writer-merge-containers-invalid-position: Element before and after given position cannot be merged.' );
+		throw new CKEditorError( 'view-writer-merge-containers-invalid-position: ' +
+			'Element before and after given position cannot be merged.' );
 	}
 
 	const lastChild = prev.getChild( prev.childCount - 1 );
@@ -261,11 +282,11 @@ export function insert( position, nodes ) {
 		throw new CKEditorError( 'view-writer-invalid-position-container' );
 	}
 
-	const insertionPosition = _breakAt( position, true );
+	const insertionPosition = _breakAttributes( position, true );
 
 	const length = container.insertChildren( insertionPosition.offset, nodes );
 	const endPosition = insertionPosition.getShiftedBy( length );
-	const start = mergeAt( insertionPosition );
+	const start = mergeAttributes( insertionPosition );
 
 	// When no nodes were inserted - return collapsed range.
 	if ( length === 0 ) {
@@ -276,7 +297,7 @@ export function insert( position, nodes ) {
 			endPosition.offset--;
 		}
 
-		const end = mergeAt( endPosition );
+		const end = mergeAttributes( endPosition );
 
 		return new Range( start, end );
 	}
@@ -303,7 +324,7 @@ export function remove( range ) {
 	}
 
 	// Break attributes at range start and end.
-	const { start: breakStart, end: breakEnd } = _breakRange( range, true );
+	const { start: breakStart, end: breakEnd } = _breakAttributesRange( range, true );
 	const parentContainer = breakStart.parent;
 
 	const count = breakEnd.offset - breakStart.offset;
@@ -312,7 +333,7 @@ export function remove( range ) {
 	const removed = parentContainer.removeChildren( breakStart.offset, count );
 
 	// Merge after removing.
-	const mergePosition = mergeAt( breakStart );
+	const mergePosition = mergeAttributes( breakStart );
 	range.start = mergePosition;
 	range.end = Position.createFromPosition( mergePosition );
 
@@ -386,7 +407,7 @@ export function wrap( range, attribute ) {
 	}
 
 	// Break attributes at range start and end.
-	const { start: breakStart, end: breakEnd } = _breakRange( range, true );
+	const { start: breakStart, end: breakEnd } = _breakAttributesRange( range, true );
 	const parentContainer = breakStart.parent;
 
 	// Unwrap children located between break points.
@@ -396,13 +417,13 @@ export function wrap( range, attribute ) {
 	const newRange = wrapChildren( parentContainer, unwrappedRange.start.offset, unwrappedRange.end.offset, attribute );
 
 	// Merge attributes at the both ends and return a new range.
-	const start = mergeAt( newRange.start );
+	const start = mergeAttributes( newRange.start );
 
 	// If start position was merged - move end position back.
 	if ( !start.isEqual( newRange.start ) ) {
 		newRange.end.offset--;
 	}
-	const end = mergeAt( newRange.end );
+	const end = mergeAttributes( newRange.end );
 
 	return new Range( start, end );
 }
@@ -510,20 +531,20 @@ export function unwrap( range, attribute ) {
 	}
 
 	// Break attributes at range start and end.
-	const { start: breakStart, end: breakEnd } = _breakRange( range, true );
+	const { start: breakStart, end: breakEnd } = _breakAttributesRange( range, true );
 	const parentContainer = breakStart.parent;
 
 	// Unwrap children located between break points.
 	const newRange = unwrapChildren( parentContainer, breakStart.offset, breakEnd.offset, attribute );
 
 	// Merge attributes at the both ends and return a new range.
-	const start = mergeAt( newRange.start );
+	const start = mergeAttributes( newRange.start );
 
 	// If start position was merged - move end position back.
 	if ( !start.isEqual( newRange.start ) ) {
 		newRange.end.offset--;
 	}
-	const end = mergeAt( newRange.end );
+	const end = mergeAttributes( newRange.end );
 
 	return new Range( start, end );
 }
@@ -548,7 +569,7 @@ function getParentContainer( position ) {
 	return parent;
 }
 
-// Function used by both public breakRange (without splitting text nodes) and by other methods (with
+// Function used by both public breakAttributes (without splitting text nodes) and by other methods (with
 // splitting text nodes).
 //
 // @param {engine.view.Range} range Range which `start` and `end` positions will be used to break attributes.
@@ -556,7 +577,7 @@ function getParentContainer( position ) {
 // container element. This behavior will result in incorrect view state, but is needed by other view writing methods
 // which then fixes view state. Defaults to `false`.
 // @returns {engine.view.Range} New range with located at break positions.
-function _breakRange( range, forceSplitText = false ) {
+function _breakAttributesRange( range, forceSplitText = false ) {
 	const rangeStart = range.start;
 	const rangeEnd = range.end;
 
@@ -564,14 +585,14 @@ function _breakRange( range, forceSplitText = false ) {
 
 	// Break at the collapsed position. Return new collapsed range.
 	if ( range.isCollapsed ) {
-		const position = _breakAt( range.start, forceSplitText );
+		const position = _breakAttributes( range.start, forceSplitText );
 
 		return new Range( position, position );
 	}
 
-	const breakEnd = _breakAt( rangeEnd, forceSplitText );
+	const breakEnd = _breakAttributes( rangeEnd, forceSplitText );
 	const count = breakEnd.parent.childCount;
-	const breakStart = _breakAt( rangeStart, forceSplitText );
+	const breakStart = _breakAttributes( rangeStart, forceSplitText );
 
 	// Calculate new break end offset.
 	breakEnd.offset += breakEnd.parent.childCount - count;
@@ -579,7 +600,7 @@ function _breakRange( range, forceSplitText = false ) {
 	return new Range( breakStart, breakEnd );
 }
 
-// Function used by public breakAt (without splitting text nodes) and by other methods (with
+// Function used by public breakAttributes (without splitting text nodes) and by other methods (with
 // splitting text nodes).
 //
 // @param {engine.view.Position} position Position where to break attributes.
@@ -587,7 +608,7 @@ function _breakRange( range, forceSplitText = false ) {
 // container element. This behavior will result in incorrect view state, but is needed by other view writing methods
 // which then fixes view state. Defaults to `false`.
 // @returns {engine.view.Position} New position after breaking the attributes.
-function _breakAt( position, forceSplitText = false ) {
+function _breakAttributes( position, forceSplitText = false ) {
 	const positionOffset = position.offset;
 	const positionParent = position.parent;
 
@@ -603,7 +624,7 @@ function _breakAt( position, forceSplitText = false ) {
 
 	// Break text and start again in new position.
 	if ( positionParent instanceof Text ) {
-		return _breakAt( breakTextNode( position ), forceSplitText );
+		return _breakAttributes( breakTextNode( position ), forceSplitText );
 	}
 
 	const length = positionParent.childCount;
@@ -614,7 +635,7 @@ function _breakAt( position, forceSplitText = false ) {
 	if ( positionOffset == length ) {
 		const newPosition = new Position( positionParent.parent, positionParent.index + 1 );
 
-		return _breakAt( newPosition, forceSplitText );
+		return _breakAttributes( newPosition, forceSplitText );
 	} else
 	// <p>foo<b><u>{}bar</u></b></p>
 	// <p>foo<b>[]<u>bar</u></b></p>
@@ -622,7 +643,7 @@ function _breakAt( position, forceSplitText = false ) {
 	if ( positionOffset === 0 ) {
 		const newPosition = new Position( positionParent.parent, positionParent.index );
 
-		return _breakAt( newPosition, forceSplitText );
+		return _breakAttributes( newPosition, forceSplitText );
 	}
 	// <p>foo<b><u>b{}ar</u></b></p>
 	// <p>foo<b><u>b[]ar</u></b></p>
@@ -647,7 +668,7 @@ function _breakAt( position, forceSplitText = false ) {
 		// Create new position to work on.
 		const newPosition = new Position( positionParent.parent, offsetAfter );
 
-		return _breakAt( newPosition, forceSplitText );
+		return _breakAttributes( newPosition, forceSplitText );
 	}
 }
 
@@ -706,7 +727,7 @@ function unwrapChildren( parent, startOffset, endOffset, attribute ) {
 			continue;
 		}
 
-		const newPosition = mergeAt( position );
+		const newPosition = mergeAttributes( position );
 
 		// If nodes were merged - other merge offsets will change.
 		if ( !newPosition.isEqual( position ) ) {
@@ -764,7 +785,7 @@ function wrapChildren( parent, startOffset, endOffset, attribute ) {
 			continue;
 		}
 
-		const newPosition = mergeAt( position );
+		const newPosition = mergeAttributes( position );
 
 		// If nodes were merged - other merge offsets will change.
 		if ( !newPosition.isEqual( position ) ) {

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

@@ -13,7 +13,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 { mergeAt } from '/ckeditor5/engine/view/writer.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';
@@ -296,7 +296,7 @@ describe( 'clean-up', () => {
 			);
 
 			// Remove <b></b> manually.
-			mergeAt( viewSelection.getFirstPosition() );
+			mergeAttributes( viewSelection.getFirstPosition() );
 
 			const modelRange = ModelRange.createFromParentsAndOffsets( modelRoot, 1, modelRoot, 1 );
 			modelDoc.selection.setRanges( [ modelRange ] );

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

@@ -1,121 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: view, browser-only */
-
-import { breakAt } from '/ckeditor5/engine/view/writer.js';
-import { stringify, parse } from '/tests/engine/_utils/view.js';
-
-describe( 'writer', () => {
-	/**
-	 * Executes test using `parse` and `stringify` utils functions. Uses range delimiters `[]{}` to create and
-	 * test break position.
-	 *
-	 * @param {String} input
-	 * @param {String} expected
-	 */
-	function test( input, expected ) {
-		let { view, selection } = parse( input );
-
-		const newPosition = breakAt( selection.getFirstPosition() );
-		expect( stringify( view.root, newPosition, { showType: true, showPriority: true } ) ).to.equal( expected );
-	}
-
-	describe( 'breakAt', () => {
-		it( 'should not break text nodes if they are not in attribute elements - middle', () => {
-			test(
-				'<container:p>foo{}bar</container:p>',
-				'<container:p>foo{}bar</container:p>'
-			);
-		} );
-
-		it( 'should not break text nodes if they are not in attribute elements - beginning', () => {
-			test(
-				'<container:p>{}foobar</container:p>',
-				'<container:p>{}foobar</container:p>'
-			);
-		} );
-
-		it( 'should not break text nodes if they are not in attribute elements #2 - end', () => {
-			test(
-				'<container:p>foobar{}</container:p>',
-				'<container:p>foobar{}</container:p>'
-			);
-		} );
-
-		it( 'should split attribute element', () => {
-			test(
-				'<container:p><attribute:b view-priority="1">foo{}bar</attribute:b></container:p>',
-				'<container:p>' +
-					'<attribute:b view-priority="1">foo</attribute:b>[]<attribute:b view-priority="1">bar</attribute:b>' +
-				'</container:p>'
-			);
-		} );
-
-		it( 'should move from beginning of the nested text node to the container', () => {
-			test(
-				'<container:p>' +
-					'<attribute:b view-priority="1"><attribute:u view-priority="1">{}foobar</attribute:u></attribute:b>' +
-				'</container:p>',
-				'<container:p>' +
-					'[]<attribute:b view-priority="1"><attribute:u view-priority="1">foobar</attribute:u></attribute:b>' +
-				'</container:p>'
-			);
-		} );
-
-		it( 'should stick selection in text node if it is in container', () => {
-			test(
-				'<container:p>foo{}<attribute:b view-priority="1">bar</attribute:b></container:p>',
-				'<container:p>foo{}<attribute:b view-priority="1">bar</attribute:b></container:p>'
-			);
-		} );
-
-		it( 'should split nested attributes', () => {
-			test(
-				'<container:p>' +
-					'<attribute:b view-priority="1"><attribute:u view-priority="1">foo{}bar</attribute:u></attribute:b>' +
-				'</container:p>',
-				'<container:p>' +
-					'<attribute:b view-priority="1">' +
-						'<attribute:u view-priority="1">' +
-							'foo' +
-						'</attribute:u>' +
-					'</attribute:b>' +
-					'[]' +
-					'<attribute:b view-priority="1">' +
-						'<attribute:u view-priority="1">' +
-							'bar' +
-						'</attribute:u>' +
-					'</attribute:b>' +
-				'</container:p>'
-			);
-		} );
-
-		it( 'should move from end of the nested text node to the container', () => {
-			test(
-				'<container:p>' +
-					'<attribute:b view-priority="1"><attribute:u view-priority="1">foobar{}</attribute:u></attribute:b>' +
-				'</container:p>',
-				'<container:p>' +
-					'<attribute:b view-priority="1"><attribute:u view-priority="1">foobar</attribute:u></attribute:b>[]' +
-				'</container:p>'
-			);
-		} );
-
-		it( 'should split attribute element directly in document fragment', () => {
-			test(
-				'<attribute:b view-priority="1">foo{}bar</attribute:b>',
-				'<attribute:b view-priority="1">foo</attribute:b>[]<attribute:b view-priority="1">bar</attribute:b>'
-			);
-		} );
-
-		it( 'should not split text directly in document fragment', () => {
-			test(
-				'foo{}bar',
-				'foo{}bar'
-			);
-		} );
-	} );
-} );

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

@@ -0,0 +1,232 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: view, browser-only */
+
+import { breakAttributes } from '/ckeditor5/engine/view/writer.js';
+import { stringify, parse } from '/tests/engine/_utils/view.js';
+import ContainerElement from '/ckeditor5/engine/view/containerelement.js';
+import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
+import Range from '/ckeditor5/engine/view/range.js';
+import CKEditorError from '/ckeditor5/utils/ckeditorerror.js';
+
+describe( 'writer', () => {
+	describe( 'breakAttributes', () => {
+		describe( 'break position', () => {
+			/**
+			 * Executes test using `parse` and `stringify` utils functions. Uses range delimiters `[]{}` to create and
+			 * test break position.
+			 *
+			 * @param {String} input
+			 * @param {String} expected
+			 */
+			function test( input, expected ) {
+				let { view, selection } = parse( input );
+
+				const newPosition = breakAttributes( selection.getFirstPosition() );
+				expect( stringify( view.root, newPosition, {
+					showType: true,
+					showPriority: true
+				} ) ).to.equal( expected );
+			}
+
+			it( 'should not break text nodes if they are not in attribute elements - middle', () => {
+				test(
+					'<container:p>foo{}bar</container:p>',
+					'<container:p>foo{}bar</container:p>'
+				);
+			} );
+
+			it( 'should not break text nodes if they are not in attribute elements - beginning', () => {
+				test(
+					'<container:p>{}foobar</container:p>',
+					'<container:p>{}foobar</container:p>'
+				);
+			} );
+
+			it( 'should not break text nodes if they are not in attribute elements #2 - end', () => {
+				test(
+					'<container:p>foobar{}</container:p>',
+					'<container:p>foobar{}</container:p>'
+				);
+			} );
+
+			it( 'should split attribute element', () => {
+				test(
+					'<container:p><attribute:b view-priority="1">foo{}bar</attribute:b></container:p>',
+					'<container:p>' +
+					'<attribute:b view-priority="1">foo</attribute:b>[]<attribute:b view-priority="1">bar</attribute:b>' +
+					'</container:p>'
+				);
+			} );
+
+			it( 'should move from beginning of the nested text node to the container', () => {
+				test(
+					'<container:p>' +
+					'<attribute:b view-priority="1"><attribute:u view-priority="1">{}foobar</attribute:u></attribute:b>' +
+					'</container:p>',
+					'<container:p>' +
+					'[]<attribute:b view-priority="1"><attribute:u view-priority="1">foobar</attribute:u></attribute:b>' +
+					'</container:p>'
+				);
+			} );
+
+			it( 'should stick selection in text node if it is in container', () => {
+				test(
+					'<container:p>foo{}<attribute:b view-priority="1">bar</attribute:b></container:p>',
+					'<container:p>foo{}<attribute:b view-priority="1">bar</attribute:b></container:p>'
+				);
+			} );
+
+			it( 'should split nested attributes', () => {
+				test(
+					'<container:p>' +
+					'<attribute:b view-priority="1"><attribute:u view-priority="1">foo{}bar</attribute:u></attribute:b>' +
+					'</container:p>',
+					'<container:p>' +
+					'<attribute:b view-priority="1">' +
+					'<attribute:u view-priority="1">' +
+					'foo' +
+					'</attribute:u>' +
+					'</attribute:b>' +
+					'[]' +
+					'<attribute:b view-priority="1">' +
+					'<attribute:u view-priority="1">' +
+					'bar' +
+					'</attribute:u>' +
+					'</attribute:b>' +
+					'</container:p>'
+				);
+			} );
+
+			it( 'should move from end of the nested text node to the container', () => {
+				test(
+					'<container:p>' +
+					'<attribute:b view-priority="1"><attribute:u view-priority="1">foobar{}</attribute:u></attribute:b>' +
+					'</container:p>',
+					'<container:p>' +
+					'<attribute:b view-priority="1"><attribute:u view-priority="1">foobar</attribute:u></attribute:b>[]' +
+					'</container:p>'
+				);
+			} );
+
+			it( 'should split attribute element directly in document fragment', () => {
+				test(
+					'<attribute:b view-priority="1">foo{}bar</attribute:b>',
+					'<attribute:b view-priority="1">foo</attribute:b>[]<attribute:b view-priority="1">bar</attribute:b>'
+				);
+			} );
+
+			it( 'should not split text directly in document fragment', () => {
+				test(
+					'foo{}bar',
+					'foo{}bar'
+				);
+			} );
+		} );
+
+		describe( 'break range', () => {
+			/**
+			 * Executes test using `parse` and `stringify` utils functions.
+			 *
+			 * @param {String} input
+			 * @param {String} expected
+			 */
+			function test( input, expected ) {
+				let { view, selection } = parse( input );
+
+				const newRange = breakAttributes( selection.getFirstRange() );
+				expect( stringify( view.root, newRange, { showType: true } ) ).to.equal( expected );
+			}
+
+			it( 'should throw when range placed in two containers', () => {
+				const p1 = new ContainerElement( 'p' );
+				const p2 = new ContainerElement( 'p' );
+
+				expect( () => {
+					breakAttributes( Range.createFromParentsAndOffsets( p1, 0, p2, 0 ) );
+				} ).to.throw( CKEditorError, 'view-writer-invalid-range-container' );
+			} );
+
+			it( 'should throw when range has no parent container', () => {
+				const el = new AttributeElement( 'b' );
+
+				expect( () => {
+					breakAttributes( Range.createFromParentsAndOffsets( el, 0, el, 0 ) );
+				} ).to.throw( CKEditorError, 'view-writer-invalid-range-container' );
+			} );
+
+			it( 'should not break text nodes if they are not in attribute elements', () => {
+				test(
+					'<container:p>foo{}bar</container:p>',
+					'<container:p>foo{}bar</container:p>'
+				);
+			} );
+
+			it( 'should break at collapsed range and return collapsed one', () => {
+				test(
+					'<container:p><attribute:b>foo{}bar</attribute:b></container:p>',
+					'<container:p><attribute:b>foo</attribute:b>[]<attribute:b>bar</attribute:b></container:p>'
+				);
+			} );
+
+			it( 'should break inside text node #1', () => {
+				test(
+					'<container:p><attribute:b>foo{bar}baz</attribute:b></container:p>',
+					'<container:p><attribute:b>foo</attribute:b>[<attribute:b>bar</attribute:b>]<attribute:b>baz</attribute:b></container:p>'
+				);
+			} );
+
+			it( 'should break inside text node #2', () => {
+				test(
+					'<container:p><attribute:b>foo{barbaz}</attribute:b></container:p>',
+					'<container:p><attribute:b>foo</attribute:b>[<attribute:b>barbaz</attribute:b>]</container:p>'
+				);
+			} );
+
+			it( 'should break inside text node #3', () => {
+				test(
+					'<container:p><attribute:b>foo{barbaz]</attribute:b></container:p>',
+					'<container:p><attribute:b>foo</attribute:b>[<attribute:b>barbaz</attribute:b>]</container:p>'
+				);
+			} );
+
+			it( 'should break inside text node #4', () => {
+				test(
+					'<container:p><attribute:b>{foo}barbaz</attribute:b></container:p>',
+					'<container:p>[<attribute:b>foo</attribute:b>]<attribute:b>barbaz</attribute:b></container:p>'
+				);
+			} );
+
+			it( 'should break inside text node #5', () => {
+				test(
+					'<container:p><attribute:b>[foo}barbaz</attribute:b></container:p>',
+					'<container:p>[<attribute:b>foo</attribute:b>]<attribute:b>barbaz</attribute:b></container:p>'
+				);
+			} );
+
+			it( 'should break placed inside different nodes', () => {
+				test(
+					'<container:p>foo{bar<attribute:b>baz}qux</attribute:b></container:p>',
+					'<container:p>foo{bar<attribute:b>baz</attribute:b>]<attribute:b>qux</attribute:b></container:p>'
+				);
+			} );
+
+			it( 'should split attribute element directly in document fragment', () => {
+				test(
+					'<attribute:b>fo{ob}ar</attribute:b>',
+					'<attribute:b>fo</attribute:b>[<attribute:b>ob</attribute:b>]<attribute:b>ar</attribute:b>'
+				);
+			} );
+
+			it( 'should not split text directly in document fragment', () => {
+				test(
+					'foo{}bar',
+					'foo{}bar'
+				);
+			} );
+		} );
+	} );
+} );

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

@@ -1,117 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: view, browser-only */
-
-import { breakRange } from '/ckeditor5/engine/view/writer.js';
-import ContainerElement from '/ckeditor5/engine/view/containerelement.js';
-import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
-import Range from '/ckeditor5/engine/view/range.js';
-import { stringify, parse } from '/tests/engine/_utils/view.js';
-import CKEditorError from '/ckeditor5/utils/ckeditorerror.js';
-
-describe( 'writer', () => {
-	/**
-	 * Executes test using `parse` and `stringify` utils functions.
-	 *
-	 * @param {String} input
-	 * @param {String} expected
-	 */
-	function test( input, expected ) {
-		let { view, selection } = parse( input );
-
-		const newRange = breakRange( selection.getFirstRange() );
-		expect( stringify( view.root, newRange, { showType: true } ) ).to.equal( expected );
-	}
-
-	describe( 'breakRange', () => {
-		it( 'should throw when range placed in two containers', () => {
-			const p1 = new ContainerElement( 'p' );
-			const p2 = new ContainerElement( 'p' );
-
-			expect( () => {
-				breakRange( Range.createFromParentsAndOffsets( p1, 0, p2, 0 ) );
-			} ).to.throw( CKEditorError, 'view-writer-invalid-range-container' );
-		} );
-
-		it( 'should throw when range has no parent container', () => {
-			const el = new AttributeElement( 'b' );
-
-			expect( () => {
-				breakRange( Range.createFromParentsAndOffsets( el, 0, el, 0 ) );
-			} ).to.throw( CKEditorError, 'view-writer-invalid-range-container' );
-		} );
-
-		it( 'should not break text nodes if they are not in attribute elements', () => {
-			test(
-				'<container:p>foo{}bar</container:p>',
-				'<container:p>foo{}bar</container:p>'
-			);
-		} );
-
-		it( 'should break at collapsed range and return collapsed one', () => {
-			test(
-				'<container:p><attribute:b>foo{}bar</attribute:b></container:p>',
-				'<container:p><attribute:b>foo</attribute:b>[]<attribute:b>bar</attribute:b></container:p>'
-			);
-		} );
-
-		it( 'should break inside text node #1', () => {
-			test(
-				'<container:p><attribute:b>foo{bar}baz</attribute:b></container:p>',
-				'<container:p><attribute:b>foo</attribute:b>[<attribute:b>bar</attribute:b>]<attribute:b>baz</attribute:b></container:p>'
-			);
-		} );
-
-		it( 'should break inside text node #2', () => {
-			test(
-				'<container:p><attribute:b>foo{barbaz}</attribute:b></container:p>',
-				'<container:p><attribute:b>foo</attribute:b>[<attribute:b>barbaz</attribute:b>]</container:p>'
-			);
-		} );
-
-		it( 'should break inside text node #3', () => {
-			test(
-				'<container:p><attribute:b>foo{barbaz]</attribute:b></container:p>',
-				'<container:p><attribute:b>foo</attribute:b>[<attribute:b>barbaz</attribute:b>]</container:p>'
-			);
-		} );
-
-		it( 'should break inside text node #4', () => {
-			test(
-				'<container:p><attribute:b>{foo}barbaz</attribute:b></container:p>',
-				'<container:p>[<attribute:b>foo</attribute:b>]<attribute:b>barbaz</attribute:b></container:p>'
-			);
-		} );
-
-		it( 'should break inside text node #5', () => {
-			test(
-				'<container:p><attribute:b>[foo}barbaz</attribute:b></container:p>',
-				'<container:p>[<attribute:b>foo</attribute:b>]<attribute:b>barbaz</attribute:b></container:p>'
-			);
-		} );
-
-		it( 'should break placed inside different nodes', () => {
-			test(
-				'<container:p>foo{bar<attribute:b>baz}qux</attribute:b></container:p>',
-				'<container:p>foo{bar<attribute:b>baz</attribute:b>]<attribute:b>qux</attribute:b></container:p>'
-			);
-		} );
-
-		it( 'should split attribute element directly in document fragment', () => {
-			test(
-				'<attribute:b>fo{ob}ar</attribute:b>',
-				'<attribute:b>fo</attribute:b>[<attribute:b>ob</attribute:b>]<attribute:b>ar</attribute:b>'
-			);
-		} );
-
-		it( 'should not split text directly in document fragment', () => {
-			test(
-				'foo{}bar',
-				'foo{}bar'
-			);
-		} );
-	} );
-} );

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

@@ -5,7 +5,7 @@
 
 /* bender-tags: view, browser-only */
 
-import { mergeAt } 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';
@@ -21,11 +21,11 @@ describe( 'writer', () => {
 	 */
 	function test( input, expected ) {
 		const { view, selection } = parse( input );
-		const newPosition = mergeAt( selection.getFirstPosition() );
+		const newPosition = mergeAttributes( selection.getFirstPosition() );
 		expect( stringify( view, newPosition, { showType: true, showPriority: true } ) ).to.equal( expected );
 	}
 
-	describe( 'mergeAt', () => {
+	describe( 'mergeAttributes', () => {
 		it( 'should not merge if inside text node', () => {
 			test( '<container:p>fo{}bar</container:p>', '<container:p>fo{}bar</container:p>' );
 		} );
@@ -65,7 +65,7 @@ describe( 'writer', () => {
 			const p = new ContainerElement( 'p', null, [ t1, t2 ] );
 			const position = new Position( p, 1 );
 
-			const newPosition = mergeAt( position );
+			const newPosition = mergeAttributes( position );
 			expect( stringify( p, newPosition ) ).to.equal( '<p>foo{}bar</p>' );
 		} );