Procházet zdrojové kódy

Merge branch 'master' into t/1210

Szymon Kupś před 7 roky
rodič
revize
89672ac473

+ 2 - 2
packages/ckeditor5-engine/src/controller/datacontroller.js

@@ -207,7 +207,7 @@ export default class DataController {
 	}
 
 	/**
-	 * Returns data parsed by the {@link #processor data processor} and then
+	 * Returns the data parsed by the {@link #processor data processor} and then
 	 * converted by the {@link #viewToModel view-to-model converters}.
 	 *
 	 * @see #set
@@ -229,7 +229,7 @@ export default class DataController {
 	 * {@link module:engine/view/documentfragment~DocumentFragment view document fragment} converted by the
 	 * {@link #viewToModel view-to-model converters}, wrapped by {module:engine/model/documentfragment~DocumentFragment}.
 	 *
-	 * When marker elements were converted during the conversion process, it will be set as a DocumentFragment's
+	 * When marker elements were converted during the conversion process, it will be set as a document fragment's
 	 * {@link module:engine/model/documentfragment~DocumentFragment#markers static markers map}.
 	 *
 	 * @param {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment} viewElementOrFragment

+ 8 - 8
packages/ckeditor5-engine/src/conversion/buildviewconverter.js

@@ -300,7 +300,7 @@ class ViewConverterBuilder {
 					// Find allowed parent for element that we are going to insert.
 					// If current parent does not allow to insert element but one of the ancestors does
 					// then split nodes to allowed parent.
-					const splitResult = conversionApi.splitToAllowedParent( modelElement, data.cursorPosition );
+					const splitResult = conversionApi.splitToAllowedParent( modelElement, data.modelCursor );
 
 					// When there is no split result it means that we can't insert element to model tree, so let's skip it.
 					if ( !splitResult ) {
@@ -324,20 +324,20 @@ class ViewConverterBuilder {
 						// element, so we need to move range after parent of the last converted child.
 						// before: <allowed>[]</allowed>
 						// after: <allowed>[<converted><child></child></converted><child></child><converted>]</converted></allowed>
-						Position.createAfter( childrenResult.cursorPosition.parent )
+						Position.createAfter( childrenResult.modelCursor.parent )
 					);
 
-					// Now we need to check where the cursorPosition should be.
+					// Now we need to check where the modelCursor should be.
 					// If we had to split parent to insert our element then we want to continue conversion inside split parent.
 					//
 					// before: <allowed><notAllowed>[]</notAllowed></allowed>
 					// after:  <allowed><notAllowed></notAllowed><converted></converted><notAllowed>[]</notAllowed></allowed>
 					if ( splitResult.cursorParent ) {
-						data.cursorPosition = Position.createAt( splitResult.cursorParent );
+						data.modelCursor = Position.createAt( splitResult.cursorParent );
 
 					// Otherwise just continue after inserted element.
 					} else {
-						data.cursorPosition = data.modelRange.end;
+						data.modelCursor = data.modelRange.end;
 					}
 
 					// Prevent multiple conversion if there are other correct matches.
@@ -389,7 +389,7 @@ class ViewConverterBuilder {
 					// If the range is not created yet, we will create it.
 					if ( !data.modelRange ) {
 						// Convert children and set conversion result as a current data.
-						data = Object.assign( data, conversionApi.convertChildren( data.viewItem, data.cursorPosition ) );
+						data = Object.assign( data, conversionApi.convertChildren( data.viewItem, data.modelCursor ) );
 					}
 
 					// Use attribute creator function, if provided.
@@ -501,9 +501,9 @@ class ViewConverterBuilder {
 						continue;
 					}
 
-					writer.insert( modelElement, data.cursorPosition );
+					writer.insert( modelElement, data.modelCursor );
 					data.modelRange = Range.createOn( modelElement );
-					data.cursorPosition = data.modelRange.end;
+					data.modelCursor = data.modelRange.end;
 
 					// Prevent multiple conversion if there are other correct matches.
 					break;

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

@@ -31,8 +31,8 @@ export function convertToModelFragment() {
 	return ( evt, data, conversionApi ) => {
 		// Second argument in `consumable.consume` is discarded for ViewDocumentFragment but is needed for ViewElement.
 		if ( !data.modelRange && conversionApi.consumable.consume( data.viewItem, { name: true } ) ) {
-			data = Object.assign( data, conversionApi.convertChildren( data.viewItem, data.cursorPosition ) );
-			data.cursorPosition = data.modelRange.end;
+			data = Object.assign( data, conversionApi.convertChildren( data.viewItem, data.modelCursor ) );
+			data.modelCursor = data.modelRange.end;
 		}
 	};
 }
@@ -44,14 +44,14 @@ export function convertToModelFragment() {
  */
 export function convertText() {
 	return ( evt, data, conversionApi ) => {
-		if ( conversionApi.schema.checkChild( data.cursorPosition, '$text' ) ) {
+		if ( conversionApi.schema.checkChild( data.modelCursor, '$text' ) ) {
 			if ( conversionApi.consumable.consume( data.viewItem ) ) {
 				const text = conversionApi.writer.createText( data.viewItem.data );
 
-				conversionApi.writer.insert( text, data.cursorPosition );
+				conversionApi.writer.insert( text, data.modelCursor );
 
-				data.modelRange = Range.createFromPositionAndShift( data.cursorPosition, text.offsetSize );
-				data.cursorPosition = data.modelRange.end;
+				data.modelRange = Range.createFromPositionAndShift( data.modelCursor, text.offsetSize );
+				data.modelCursor = data.modelRange.end;
 			}
 		}
 	};

+ 26 - 26
packages/ckeditor5-engine/src/conversion/viewconversiondispatcher.js

@@ -26,12 +26,12 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  * `ViewConversionDispatcher` fires corresponding events. Special callbacks called "converters" should listen to
  * `ViewConversionDispatcher` for those events.
  *
- * Each callback, as a first argument, is passed a special object `data` that has `viewItem`, `cursorPosition` and
+ * Each callback, as a first argument, is passed a special object `data` that has `viewItem`, `modelCursor` and
  * `modelRange` properties. `viewItem` property contains {@link module:engine/view/node~Node view node} or
  * {@link module:engine/view/documentfragment~DocumentFragment view document fragment}
  * that is converted at the moment and might be handled by the callback. `modelRange` property should be used to save the result
  * of conversion and is always a {@link module:engine/model/range~Range} when conversion result is correct.
- * `cursorPosition` property is a {@link module:engine/model/position~Position position} on which conversion result will be inserted
+ * `modelCursor` property is a {@link module:engine/model/position~Position position} on which conversion result will be inserted
  * and is a context according to {@link module:engine/model/schema~Schema schema} will be checked before the conversion.
  * See also {@link ~ViewConversionDispatcher#convert}. It is also shared by reference by all callbacks listening to given event.
  *
@@ -46,11 +46,11 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  *			const paragraph = conversionApi.createElement( 'paragraph' );
  *
  *			// Check if paragraph is allowed on current cursor position.
- *			if ( conversionApi.schema.checkChild( data.cursorPosition, paragraph ) ) {
+ *			if ( conversionApi.schema.checkChild( data.modelCursor, paragraph ) ) {
  *				// Try to consume value from consumable list.
  *				if ( !conversionApi.consumable.consume( data.viewItem, { name: true } ) ) {
  *					// Insert paragraph on cursor position.
- *					conversionApi.writer.insert( paragraph, data.cursorPosition );
+ *					conversionApi.writer.insert( paragraph, data.modelCursor );
  *
  *					// Convert paragraph children to paragraph element.
  *					conversionApi.convertChildren( data.viewItem, ModelPosition.createAt( paragraph ) );
@@ -59,7 +59,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  *					data.modelRange = ModelRange.createOn( paragraph );
  *
  *					// Continue conversion just after paragraph.
- *					data.cursorPosition = data.modelRange.end;
+ *					data.modelCursor = data.modelRange.end;
  *				}
  *			}
  *		} );
@@ -69,7 +69,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  *			if ( conversionApi.consumable.consume( data.viewItem, { name: true, attributes: [ 'href' ] } ) ) {
  *				// <a> element is inline and is represented by an attribute in the model.
  *				// This is why we need to convert only children.
- *				const { modelRange } = conversionApi.convertChildren( data.viewItem, data.cursorPosition );
+ *				const { modelRange } = conversionApi.convertChildren( data.viewItem, data.modelCursor );
  *
  *				for ( let item of modelRange.getItems() ) {
  *					if ( conversionApi.schema.checkAttribute( item, 'linkHref' ) ) {
@@ -221,8 +221,8 @@ export default class ViewConversionDispatcher {
 	 * @private
 	 * @see module:engine/conversion/viewconversiondispatcher~ViewConversionApi#convertItem
 	 */
-	_convertItem( viewItem, cursorPosition ) {
-		const data = Object.assign( { viewItem, cursorPosition, modelRange: null } );
+	_convertItem( viewItem, modelCursor ) {
+		const data = Object.assign( { viewItem, modelCursor, modelRange: null } );
 
 		if ( viewItem.is( 'element' ) ) {
 			this.fire( 'element:' + viewItem.name, data, this.conversionApi );
@@ -244,36 +244,36 @@ export default class ViewConversionDispatcher {
 			throw new CKEditorError( 'view-conversion-dispatcher-incorrect-result: Incorrect conversion result was dropped.' );
 		}
 
-		return { modelRange: data.modelRange, cursorPosition: data.cursorPosition };
+		return { modelRange: data.modelRange, modelCursor: data.modelCursor };
 	}
 
 	/**
 	 * @private
 	 * @see module:engine/conversion/viewconversiondispatcher~ViewConversionApi#convertChildren
 	 */
-	_convertChildren( viewItem, cursorPosition ) {
-		const modelRange = new ModelRange( cursorPosition );
-		let nextCursorPosition = cursorPosition;
+	_convertChildren( viewItem, modelCursor ) {
+		const modelRange = new ModelRange( modelCursor );
+		let nextModelCursor = modelCursor;
 
 		for ( const viewChild of Array.from( viewItem.getChildren() ) ) {
-			const result = this._convertItem( viewChild, nextCursorPosition );
+			const result = this._convertItem( viewChild, nextModelCursor );
 
 			if ( result.modelRange instanceof ModelRange ) {
 				modelRange.end = result.modelRange.end;
-				nextCursorPosition = result.cursorPosition;
+				nextModelCursor = result.modelCursor;
 			}
 		}
 
-		return { modelRange, cursorPosition: nextCursorPosition };
+		return { modelRange, modelCursor: nextModelCursor };
 	}
 
 	/**
 	 * @private
 	 * @see module:engine/conversion/viewconversiondispatcher~ViewConversionApi#splitToAllowedParent
 	 */
-	_splitToAllowedParent( node, cursorPosition ) {
+	_splitToAllowedParent( node, modelCursor ) {
 		// Try to find allowed parent.
-		const allowedParent = this.conversionApi.schema.findAllowedParent( node, cursorPosition );
+		const allowedParent = this.conversionApi.schema.findAllowedParent( node, modelCursor );
 
 		// When there is no parent that allows to insert node then return `null`.
 		if ( !allowedParent ) {
@@ -281,8 +281,8 @@ export default class ViewConversionDispatcher {
 		}
 
 		// When current position parent allows to insert node then return this position.
-		if ( allowedParent === cursorPosition.parent ) {
-			return { position: cursorPosition };
+		if ( allowedParent === modelCursor.parent ) {
+			return { position: modelCursor };
 		}
 
 		// When allowed parent is in context tree.
@@ -291,7 +291,7 @@ export default class ViewConversionDispatcher {
 		}
 
 		// Split element to allowed parent.
-		const splitResult = this.conversionApi.writer.split( cursorPosition, allowedParent );
+		const splitResult = this.conversionApi.writer.split( modelCursor, allowedParent );
 
 		// Remember all elements that are created as a result of split.
 		// This is important because at the end of conversion we want to remove all empty split elements.
@@ -350,12 +350,12 @@ export default class ViewConversionDispatcher {
 	 * all elements conversion or to conversion of specific elements.
 	 *
 	 * @event element
-	 * @param {Object} data Object containing viewItem to convert, cursorPosition as a conversion position and placeholder
+	 * @param {Object} data Object containing viewItem to convert, modelCursor as a conversion position and placeholder
 	 * for modelRange that is a conversion result. Keep in mind that this object is shared by reference between all
 	 * callbacks that will be called. This means that callbacks can override values if needed, and those values will
 	 * be available in other callbacks.
 	 * @param {module:engine/view/item~Item} data.viewItem Converted item.
-	 * @param {module:engine/model/position~Position} data.cursorPosition Target position for current item.
+	 * @param {module:engine/model/position~Position} data.modelCursor Target position for current item.
 	 * @param {module:engine/model/range~Range} data.modelRange The current state of conversion result. Every change to
 	 * converted element should be reflected by setting or modifying this property.
 	 * @param {ViewConversionApi} conversionApi Conversion interface to be used by callback, passed in
@@ -462,11 +462,11 @@ function createContextTree( contextDefinition, writer ) {
  * @fires module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher#event:text
  * @fires module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher#event:documentFragment
  * @param {module:engine/view/item~Item} viewItem Item to convert.
- * @param {module:engine/model/position~Position} cursorPosition Position of conversion.
+ * @param {module:engine/model/position~Position} modelCursor Position of conversion.
  * @returns {Object} result Conversion result.
  * @returns {module:engine/model/range~Range|null} result.modelRange Model range containing result of item conversion,
  * created and modified by callbacks attached to fired event, or `null` if the conversion result was incorrect.
- * @returns {module:engine/model/position~Position} result.cursorPosition Position where conversion should be continued.
+ * @returns {module:engine/model/position~Position} result.modelCursor Position where conversion should be continued.
  */
 
 /**
@@ -477,11 +477,11 @@ function createContextTree( contextDefinition, writer ) {
  * @fires module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher#event:text
  * @fires module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher#event:documentFragment
  * @param {module:engine/view/item~Item} viewItem Item to convert.
- * @param {module:engine/model/position~Position} cursorPosition Position of conversion.
+ * @param {module:engine/model/position~Position} modelCursor Position of conversion.
  * @returns {Object} result Conversion result.
  * @returns {module:engine/model/range~Range} result.modelRange Model range containing results of conversion of all children of given item.
  * When no children was converted then range is collapsed.
- * @returns {module:engine/model/position~Position} result.cursorPosition Position where conversion should be continued.
+ * @returns {module:engine/model/position~Position} result.modelCursor Position where conversion should be continued.
  */
 
 /**

+ 9 - 9
packages/ckeditor5-engine/src/dev-utils/model.js

@@ -323,9 +323,9 @@ export function parse( data, schema, options = {} ) {
 
 function convertToModelFragment() {
 	return ( evt, data, conversionApi ) => {
-		const childrenResult = conversionApi.convertChildren( data.viewItem, data.cursorPosition );
+		const childrenResult = conversionApi.convertChildren( data.viewItem, data.modelCursor );
 
-		conversionApi.mapper.bindElements( data.cursorPosition.parent, data.viewItem );
+		conversionApi.mapper.bindElements( data.modelCursor.parent, data.viewItem );
 
 		data = Object.assign( data, childrenResult );
 
@@ -337,7 +337,7 @@ function convertToModelElement() {
 	return ( evt, data, conversionApi ) => {
 		const elementName = data.viewItem.name;
 
-		if ( !conversionApi.schema.checkChild( data.cursorPosition, elementName ) ) {
+		if ( !conversionApi.schema.checkChild( data.modelCursor, elementName ) ) {
 			throw new Error( `Element '${ elementName }' was not allowed in given position.` );
 		}
 
@@ -346,14 +346,14 @@ function convertToModelElement() {
 		const attributes = convertAttributes( data.viewItem.getAttributes(), parseAttributeValue );
 		const element = conversionApi.writer.createElement( data.viewItem.name, attributes );
 
-		conversionApi.writer.insert( element, data.cursorPosition );
+		conversionApi.writer.insert( element, data.modelCursor );
 
 		conversionApi.mapper.bindElements( element, data.viewItem );
 
 		conversionApi.convertChildren( data.viewItem, ModelPosition.createAt( element ) );
 
 		data.modelRange = ModelRange.createOn( element );
-		data.cursorPosition = data.modelRange.end;
+		data.modelCursor = data.modelRange.end;
 
 		evt.stop();
 	};
@@ -361,7 +361,7 @@ function convertToModelElement() {
 
 function convertToModelText( withAttributes = false ) {
 	return ( evt, data, conversionApi ) => {
-		if ( !conversionApi.schema.checkChild( data.cursorPosition, '$text' ) ) {
+		if ( !conversionApi.schema.checkChild( data.modelCursor, '$text' ) ) {
 			throw new Error( 'Text was not allowed in given position.' );
 		}
 
@@ -377,10 +377,10 @@ function convertToModelText( withAttributes = false ) {
 			node = conversionApi.writer.createText( data.viewItem.data );
 		}
 
-		conversionApi.writer.insert( node, data.cursorPosition );
+		conversionApi.writer.insert( node, data.modelCursor );
 
-		data.modelRange = ModelRange.createFromPositionAndShift( data.cursorPosition, node.offsetSize );
-		data.cursorPosition = data.modelRange.end;
+		data.modelRange = ModelRange.createFromPositionAndShift( data.modelCursor, node.offsetSize );
+		data.modelCursor = data.modelRange.end;
 
 		evt.stop();
 	};

+ 44 - 2
packages/ckeditor5-engine/src/model/utils/insertcontent.js

@@ -165,6 +165,7 @@ class Insertion {
 	}
 
 	/**
+	 * @private
 	 * Handles insertion of a single node.
 	 *
 	 * @param {module:engine/model/node~Node} node
@@ -211,6 +212,7 @@ class Insertion {
 	}
 
 	/**
+	 * @private
 	 * @param {module:engine/model/element~Element} node The object element.
 	 * @param {Object} context
 	 */
@@ -226,6 +228,7 @@ class Insertion {
 	}
 
 	/**
+	 * @private
 	 * @param {module:engine/model/node~Node} node The disallowed node which needs to be handled.
 	 * @param {Object} context
 	 */
@@ -241,6 +244,7 @@ class Insertion {
 	}
 
 	/**
+	 * @private
 	 * @param {module:engine/model/node~Node} node The node to insert.
 	 */
 	_insert( node ) {
@@ -274,6 +278,7 @@ class Insertion {
 	}
 
 	/**
+	 * @private
 	 * @param {module:engine/model/node~Node} node The node which could potentially be merged.
 	 * @param {Object} context
 	 */
@@ -282,8 +287,8 @@ class Insertion {
 			return;
 		}
 
-		const mergeLeft = context.isFirst && ( node.previousSibling instanceof Element ) && this.canMergeWith.has( node.previousSibling );
-		const mergeRight = context.isLast && ( node.nextSibling instanceof Element ) && this.canMergeWith.has( node.nextSibling );
+		const mergeLeft = this._canMergeLeft( node, context );
+		const mergeRight = this._canMergeRight( node, context );
 		const mergePosLeft = LivePosition.createBefore( node );
 		const mergePosRight = LivePosition.createAfter( node );
 
@@ -330,8 +335,43 @@ class Insertion {
 	}
 
 	/**
+	 * Checks whether specified node can be merged with previous sibling element.
+	 *
+	 * @private
+	 * @param {module:engine/model/node~Node} node The node which could potentially be merged.
+	 * @param {Object} context
+	 * @returns {Boolean}
+	 */
+	_canMergeLeft( node, context ) {
+		const previousSibling = node.previousSibling;
+
+		return context.isFirst &&
+			( previousSibling instanceof Element ) &&
+			this.canMergeWith.has( previousSibling ) &&
+			this.model.schema.checkMerge( previousSibling, node );
+	}
+
+	/**
+	 * Checks whether specified node can be merged with next sibling element.
+	 *
+	 * @private
+	 * @param {module:engine/model/node~Node} node The node which could potentially be merged.
+	 * @param {Object} context
+	 * @returns {Boolean}
+	 */
+	_canMergeRight( node, context ) {
+		const nextSibling = node.nextSibling;
+
+		return context.isLast &&
+			( nextSibling instanceof Element ) &&
+			this.canMergeWith.has( nextSibling ) &&
+			this.model.schema.checkMerge( node, nextSibling );
+	}
+
+	/**
 	 * Tries wrapping the node in a new paragraph and inserting it this way.
 	 *
+	 * @private
 	 * @param {module:engine/model/node~Node} node The node which needs to be autoparagraphed.
 	 * @param {Object} context
 	 */
@@ -348,6 +388,7 @@ class Insertion {
 	}
 
 	/**
+	 * @private
 	 * @param {module:engine/model/node~Node} node
 	 * @returns {Boolean} Whether an allowed position was found.
 	 * `false` is returned if the node isn't allowed at any position up in the tree, `true` if was.
@@ -393,6 +434,7 @@ class Insertion {
 	/**
 	 * Gets the element in which the given node is allowed. It checks the passed element and all its ancestors.
 	 *
+	 * @private
 	 * @param {module:engine/model/node~Node} node The node to check.
 	 * @param {module:engine/model/element~Element} element The element in which the node's correctness should be checked.
 	 * @returns {module:engine/model/element~Element|null}

+ 7 - 16
packages/ckeditor5-engine/src/view/domconverter.js

@@ -22,6 +22,7 @@ import global from '@ckeditor/ckeditor5-utils/src/dom/global';
 import indexOf from '@ckeditor/ckeditor5-utils/src/dom/indexof';
 import getAncestors from '@ckeditor/ckeditor5-utils/src/dom/getancestors';
 import getCommonAncestor from '@ckeditor/ckeditor5-utils/src/dom/getcommonancestor';
+import isText from '@ckeditor/ckeditor5-utils/src/dom/istext';
 
 /**
  * DomConverter is a set of tools to do transformations between DOM nodes and view nodes. It also handles
@@ -339,7 +340,7 @@ export default class DomConverter {
 
 			// If there is an inline filler at position return position inside the filler. We should never return
 			// the position before the inline filler.
-			if ( this.isText( domAfter ) && startsWithFiller( domAfter ) ) {
+			if ( isText( domAfter ) && startsWithFiller( domAfter ) ) {
 				return { parent: domAfter, offset: INLINE_FILLER_LENGTH };
 			}
 
@@ -375,7 +376,7 @@ export default class DomConverter {
 			return uiElement;
 		}
 
-		if ( this.isText( domNode ) ) {
+		if ( isText( domNode ) ) {
 			if ( isInlineFiller( domNode ) ) {
 				return null;
 			} else {
@@ -460,7 +461,7 @@ export default class DomConverter {
 			let container = domSelection.getRangeAt( 0 ).startContainer;
 
 			// The DOM selection might be moved to the text node inside the fake selection container.
-			if ( this.isText( container ) ) {
+			if ( isText( container ) ) {
 				container = container.parentNode;
 			}
 
@@ -533,7 +534,7 @@ export default class DomConverter {
 			return ViewPosition.createBefore( viewElement );
 		}
 
-		if ( this.isText( domParent ) ) {
+		if ( isText( domParent ) ) {
 			if ( isInlineFiller( domParent ) ) {
 				return this.domPositionToView( domParent.parentNode, indexOf( domParent ) );
 			}
@@ -562,7 +563,7 @@ export default class DomConverter {
 				}
 			} else {
 				const domBefore = domParent.childNodes[ domOffset - 1 ];
-				const viewBefore = this.isText( domBefore ) ?
+				const viewBefore = isText( domBefore ) ?
 					this.findCorrespondingViewText( domBefore ) :
 					this.mapDomToView( domBefore );
 
@@ -750,16 +751,6 @@ export default class DomConverter {
 	}
 
 	/**
-	 * Returns `true` when `node.nodeType` equals `Node.TEXT_NODE`.
-	 *
-	 * @param {Node} node Node to check.
-	 * @returns {Boolean}
-	 */
-	isText( node ) {
-		return node && node.nodeType == Node.TEXT_NODE;
-	}
-
-	/**
 	 * Returns `true` when `node.nodeType` equals `Node.ELEMENT_NODE`.
 	 *
 	 * @param {Node} node Node to check.
@@ -864,7 +855,7 @@ export default class DomConverter {
 	 */
 	_isDomSelectionPositionCorrect( domParent, offset ) {
 		// If selection is before or in the middle of inline filler string, it is incorrect.
-		if ( this.isText( domParent ) && startsWithFiller( domParent ) && offset < INLINE_FILLER_LENGTH ) {
+		if ( isText( domParent ) && startsWithFiller( domParent ) && offset < INLINE_FILLER_LENGTH ) {
 			// Selection in a text node, at wrong position (before or in the middle of filler).
 			return false;
 		}

+ 4 - 3
packages/ckeditor5-engine/src/view/renderer.js

@@ -17,6 +17,7 @@ import insertAt from '@ckeditor/ckeditor5-utils/src/dom/insertat';
 import remove from '@ckeditor/ckeditor5-utils/src/dom/remove';
 import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+import isText from '@ckeditor/ckeditor5-utils/src/dom/istext';
 
 /**
  * Renderer updates DOM structure and selection, to make them a reflection of the view structure and selection.
@@ -259,7 +260,7 @@ export default class Renderer {
 		const childNodes = domParentOrArray instanceof Array ? domParentOrArray : domParentOrArray.childNodes;
 		const nodeAfterFiller = childNodes[ offset ];
 
-		if ( this.domConverter.isText( nodeAfterFiller ) ) {
+		if ( isText( nodeAfterFiller ) ) {
 			nodeAfterFiller.data = INLINE_FILLER + nodeAfterFiller.data;
 
 			return nodeAfterFiller;
@@ -324,7 +325,7 @@ export default class Renderer {
 		const selectionPosition = this.selection.getFirstPosition();
 		const position = this.domConverter.viewPositionToDom( selectionPosition );
 
-		if ( position && this.domConverter.isText( position.parent ) && startsWithFiller( position.parent ) ) {
+		if ( position && isText( position.parent ) && startsWithFiller( position.parent ) ) {
 			return true;
 		}
 
@@ -518,7 +519,7 @@ export default class Renderer {
 				return true;
 			}
 			// Texts.
-			else if ( domConverter.isText( actualDomChild ) && domConverter.isText( expectedDomChild ) ) {
+			else if ( isText( actualDomChild ) && isText( expectedDomChild ) ) {
 				return actualDomChild.data === expectedDomChild.data;
 			}
 			// Block fillers.

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

@@ -54,9 +54,9 @@ describe( 'view-to-model-converters', () => {
 			dispatcher.on( 'text', ( evt, data, conversionApi ) => {
 				if ( conversionApi.consumable.consume( data.viewItem ) ) {
 					const text = conversionApi.writer.createText( data.viewItem.data.replace( /fuck/gi, '****' ) );
-					conversionApi.writer.insert( text, data.cursorPosition );
-					data.modelRange = ModelRange.createFromPositionAndShift( data.cursorPosition, text.offsetSize );
-					data.cursorPosition = data.modelRange.end;
+					conversionApi.writer.insert( text, data.modelCursor );
+					data.modelRange = ModelRange.createFromPositionAndShift( data.modelCursor, text.offsetSize );
+					data.modelCursor = data.modelRange.end;
 				}
 			} );
 
@@ -135,11 +135,11 @@ describe( 'view-to-model-converters', () => {
 				if ( conversionApi.consumable.consume( data.viewItem, { name: true } ) ) {
 					const paragraph = conversionApi.writer.createElement( 'paragraph' );
 
-					conversionApi.writer.insert( paragraph, data.cursorPosition );
+					conversionApi.writer.insert( paragraph, data.modelCursor );
 					conversionApi.convertChildren( data.viewItem, ModelPosition.createAt( paragraph ) );
 
 					data.modelRange = ModelRange.createOn( paragraph );
-					data.cursorPosition = data.modelRange.end;
+					data.modelCursor = data.modelRange.end;
 				}
 			} );
 

+ 26 - 26
packages/ckeditor5-engine/tests/conversion/viewconversiondispatcher.js

@@ -82,7 +82,7 @@ describe( 'ViewConversionDispatcher', () => {
 				dispatcher._removeIfEmpty.add( conversionApi.writer.createElement( 'paragraph' ) );
 
 				// Convert children - this will call second converter.
-				conversionApi.convertChildren( data.viewItem, data.cursorPosition );
+				conversionApi.convertChildren( data.viewItem, data.modelCursor );
 
 				spy();
 			} );
@@ -147,7 +147,7 @@ describe( 'ViewConversionDispatcher', () => {
 				// Check correctness of passed parameters.
 				expect( evt.name ).to.equal( 'text' );
 				expect( data.viewItem ).to.equal( viewText );
-				expect( data.cursorPosition ).to.instanceof( ModelPosition );
+				expect( data.modelCursor ).to.instanceof( ModelPosition );
 
 				// Check whether consumable has appropriate value to consume.
 				expect( conversionApi.consumable.consume( data.viewItem ) ).to.be.true;
@@ -156,7 +156,7 @@ describe( 'ViewConversionDispatcher', () => {
 				expect( conversionApi ).to.equal( dispatcher.conversionApi );
 
 				const text = conversionApi.writer.createText( data.viewItem.data );
-				conversionApi.writer.insert( text, data.cursorPosition );
+				conversionApi.writer.insert( text, data.modelCursor );
 
 				// Set conversion result to `modelRange` property of `data`.
 				// Later we will check if it was returned by `convert` method.
@@ -183,7 +183,7 @@ describe( 'ViewConversionDispatcher', () => {
 				// Check correctness of passed parameters.
 				expect( evt.name ).to.equal( 'element:p' );
 				expect( data.viewItem ).to.equal( viewElement );
-				expect( data.cursorPosition ).to.instanceof( ModelPosition );
+				expect( data.modelCursor ).to.instanceof( ModelPosition );
 
 				// Check whether consumable has appropriate value to consume.
 				expect( conversionApi.consumable.consume( data.viewItem, { name: true } ) ).to.be.true;
@@ -193,7 +193,7 @@ describe( 'ViewConversionDispatcher', () => {
 				expect( conversionApi ).to.equal( dispatcher.conversionApi );
 
 				const paragraph = conversionApi.writer.createElement( 'paragraph' );
-				conversionApi.writer.insert( paragraph, data.cursorPosition );
+				conversionApi.writer.insert( paragraph, data.modelCursor );
 
 				// Set conversion result to `modelRange` property of `data`.
 				// Later we will check if it was returned by `convert` method.
@@ -221,7 +221,7 @@ describe( 'ViewConversionDispatcher', () => {
 				// Check correctness of passed parameters.
 				expect( evt.name ).to.equal( 'documentFragment' );
 				expect( data.viewItem ).to.equal( viewFragment );
-				expect( data.cursorPosition ).to.instanceof( ModelPosition );
+				expect( data.modelCursor ).to.instanceof( ModelPosition );
 
 				// Check whether consumable has appropriate value to consume.
 				expect( conversionApi.consumable.consume( data.viewItem ) ).to.be.true;
@@ -230,7 +230,7 @@ describe( 'ViewConversionDispatcher', () => {
 				expect( conversionApi ).to.equal( dispatcher.conversionApi );
 
 				const text = conversionApi.writer.createText( 'foo' );
-				conversionApi.writer.insert( text, data.cursorPosition );
+				conversionApi.writer.insert( text, data.modelCursor );
 
 				// Set conversion result to `modelRange` property of `data`.
 				// Later we will check if it was returned by `convert` method.
@@ -254,7 +254,7 @@ describe( 'ViewConversionDispatcher', () => {
 			dispatcher.on( 'element', ( evt, data, conversionApi ) => {
 				// First let's convert target element.
 				const paragraph = conversionApi.writer.createElement( 'paragraph' );
-				conversionApi.writer.insert( paragraph, data.cursorPosition );
+				conversionApi.writer.insert( paragraph, data.modelCursor );
 
 				// Then add some elements and mark as split.
 
@@ -279,7 +279,7 @@ describe( 'ViewConversionDispatcher', () => {
 				dispatcher._removeIfEmpty.add( innerSplit );
 
 				data.modelRange = ModelRange.createOn( paragraph );
-				data.cursorPosition = data.modelRange.end;
+				data.modelCursor = data.modelRange.end;
 
 				// We have the following result:
 				// <p><p></p></p>[<p></p>]<p></p><p>foo</p>
@@ -316,10 +316,10 @@ describe( 'ViewConversionDispatcher', () => {
 				] );
 
 				// Insert to conversion tree.
-				conversionApi.writer.insert( fragment, data.cursorPosition );
+				conversionApi.writer.insert( fragment, data.modelCursor );
 
 				// Create range on this fragment as a conversion result.
-				data.modelRange = ModelRange.createIn( data.cursorPosition.parent );
+				data.modelRange = ModelRange.createIn( data.modelCursor.parent );
 			} );
 
 			const conversionResult = dispatcher.convert( viewFragment );
@@ -354,7 +354,7 @@ describe( 'ViewConversionDispatcher', () => {
 
 			dispatcher.on( 'element:third', ( evt, data, conversionApi ) => {
 				spy();
-				checkChildResult = conversionApi.schema.checkChild( data.cursorPosition, 'third' );
+				checkChildResult = conversionApi.schema.checkChild( data.modelCursor, 'third' );
 			} );
 
 			// Default context $root.
@@ -403,14 +403,14 @@ describe( 'ViewConversionDispatcher', () => {
 				spyP();
 
 				data.modelRange = ModelRange.createOn( modelP );
-				data.cursorPosition = data.modelRange.end;
+				data.modelCursor = data.modelRange.end;
 			} );
 
 			dispatcher.on( 'text', ( evt, data ) => {
 				spyText();
 
 				data.modelRange = ModelRange.createOn( modelText );
-				data.cursorPosition = data.modelRange.end;
+				data.modelCursor = data.modelRange.end;
 			} );
 
 			spyNull = sinon.spy();
@@ -438,22 +438,22 @@ describe( 'ViewConversionDispatcher', () => {
 				dispatcher.on( 'documentFragment', ( evt, data, conversionApi ) => {
 					spy();
 
-					const pResult = conversionApi.convertItem( viewP, data.cursorPosition );
+					const pResult = conversionApi.convertItem( viewP, data.modelCursor );
 					expect( pResult.modelRange ).instanceof( ModelRange );
 					expect( pResult.modelRange.start.path ).to.deep.equal( [ 0 ] );
 					expect( pResult.modelRange.end.path ).to.deep.equal( [ 1 ] );
 					expect( first( pResult.modelRange.getItems() ) ).to.equal( modelP );
-					expect( pResult.cursorPosition ).to.instanceof( ModelPosition );
-					expect( pResult.cursorPosition.path ).to.deep.equal( [ 1 ] );
+					expect( pResult.modelCursor ).to.instanceof( ModelPosition );
+					expect( pResult.modelCursor.path ).to.deep.equal( [ 1 ] );
 
-					const textResult = conversionApi.convertItem( viewText, data.cursorPosition );
+					const textResult = conversionApi.convertItem( viewText, data.modelCursor );
 					expect( textResult.modelRange ).instanceof( ModelRange );
 					expect( textResult.modelRange.start.path ).to.deep.equal( [ 1 ] );
 					expect( textResult.modelRange.end.path ).to.deep.equal( [ 7 ] );
 					expect( first( textResult.modelRange.getItems() ) ).to.instanceof( ModelTextProxy );
 					expect( first( textResult.modelRange.getItems() ).data ).to.equal( 'foobar' );
-					expect( textResult.cursorPosition ).to.instanceof( ModelPosition );
-					expect( textResult.cursorPosition.path ).to.deep.equal( [ 7 ] );
+					expect( textResult.modelCursor ).to.instanceof( ModelPosition );
+					expect( textResult.modelCursor.path ).to.deep.equal( [ 7 ] );
 				} );
 
 				dispatcher.convert( new ViewDocumentFragment() );
@@ -467,8 +467,8 @@ describe( 'ViewConversionDispatcher', () => {
 				dispatcher.on( 'documentFragment', ( evt, data, conversionApi ) => {
 					spy();
 
-					expect( conversionApi.convertItem( viewDiv, data.cursorPosition ).modelRange ).to.equal( null );
-					expect( conversionApi.convertItem( viewNull, data.cursorPosition ).modelRange ).to.equal( null );
+					expect( conversionApi.convertItem( viewDiv, data.modelCursor ).modelRange ).to.equal( null );
+					expect( conversionApi.convertItem( viewNull, data.modelCursor ).modelRange ).to.equal( null );
 				} );
 
 				dispatcher.convert( new ViewDocumentFragment() );
@@ -481,7 +481,7 @@ describe( 'ViewConversionDispatcher', () => {
 				dispatcher.on( 'documentFragment', ( evt, data, conversionApi ) => {
 					spy();
 
-					conversionApi.convertItem( viewArray, data.cursorPosition );
+					conversionApi.convertItem( viewArray, data.modelCursor );
 				} );
 
 				expect( () => {
@@ -509,8 +509,8 @@ describe( 'ViewConversionDispatcher', () => {
 					expect( Array.from( result.modelRange.getItems() )[ 1 ] ).to.instanceof( ModelTextProxy );
 					expect( Array.from( result.modelRange.getItems() )[ 1 ].data ).to.equal( 'foobar' );
 
-					expect( result.cursorPosition ).instanceof( ModelPosition );
-					expect( result.cursorPosition.path ).to.deep.equal( [ 7 ] );
+					expect( result.modelCursor ).instanceof( ModelPosition );
+					expect( result.modelCursor.path ).to.deep.equal( [ 7 ] );
 				} );
 
 				dispatcher.convert( new ViewDocumentFragment( [ viewP, viewText ] ) );
@@ -616,7 +616,7 @@ describe( 'ViewConversionDispatcher', () => {
 
 				dispatcher.on( 'documentFragment', ( evt, data, conversionApi ) => {
 					const code = conversionApi.writer.createElement( 'div' );
-					const result = conversionApi.splitToAllowedParent( code, data.cursorPosition );
+					const result = conversionApi.splitToAllowedParent( code, data.modelCursor );
 
 					expect( result ).to.null;
 					spy();

+ 57 - 0
packages/ckeditor5-engine/tests/model/utils/insertcontent.js

@@ -407,6 +407,63 @@ describe( 'DataController utils', () => {
 						'<paragraph>b</paragraph>'
 					);
 				} );
+
+				it( 'should not merge a paragraph wrapped in blockQuote with list item (checking left merge)', () => {
+					model.schema.register( 'blockQuote', {
+						allowWhere: '$block',
+						allowContentOf: '$root',
+					} );
+
+					setData( model, '<listItem>fo[]o</listItem>' );
+
+					insertHelper( '<blockQuote><paragraph>xxx</paragraph></blockQuote><heading1>yyy</heading1>' );
+
+					expect( getData( model ) ).to.equal(
+						'<listItem>fo</listItem>' +
+						'<blockQuote>' +
+						'<paragraph>xxx</paragraph>' +
+						'</blockQuote>' +
+						'<heading1>yyy[]o</heading1>'
+					);
+				} );
+
+				it( 'should not merge a paragraph wrapped in blockQuote with list item (checking right merge)', () => {
+					model.schema.register( 'blockQuote', {
+						allowWhere: '$block',
+						allowContentOf: '$root',
+					} );
+
+					setData( model, '<listItem>fo[]o</listItem>' );
+
+					insertHelper( '<heading1>yyy</heading1><blockQuote><paragraph>xxx</paragraph></blockQuote>' );
+
+					expect( getData( model ) ).to.equal(
+						'<listItem>foyyy</listItem>' +
+						'<blockQuote>' +
+						'<paragraph>xxx</paragraph>' +
+						'</blockQuote>' +
+						'<listItem>[]o</listItem>'
+					);
+				} );
+
+				it( 'should not merge a paragraph wrapped in blockQuote with list item (checking both merges)', () => {
+					model.schema.register( 'blockQuote', {
+						allowWhere: '$block',
+						allowContentOf: '$root',
+					} );
+
+					setData( model, '<listItem>fo[]o</listItem>' );
+
+					insertHelper( '<blockQuote><paragraph>xxx</paragraph></blockQuote>' );
+
+					expect( getData( model ) ).to.equal(
+						'<listItem>fo</listItem>' +
+						'<blockQuote>' +
+						'<paragraph>xxx</paragraph>' +
+						'</blockQuote>' +
+						'<listItem>[]o</listItem>'
+					);
+				} );
 			} );
 
 			describe( 'mixed content to block', () => {

+ 0 - 13
packages/ckeditor5-engine/tests/view/domconverter/domconverter.js

@@ -142,19 +142,6 @@ describe( 'DomConverter', () => {
 			comment = document.createComment( 'a' );
 		} );
 
-		describe( 'isText()', () => {
-			it( 'should return true for Text nodes', () => {
-				expect( converter.isText( text ) ).to.be.true;
-			} );
-
-			it( 'should return false for other arguments', () => {
-				expect( converter.isText( element ) ).to.be.false;
-				expect( converter.isText( documentFragment ) ).to.be.false;
-				expect( converter.isText( comment ) ).to.be.false;
-				expect( converter.isText( {} ) ).to.be.false;
-			} );
-		} );
-
 		describe( 'isElement()', () => {
 			it( 'should return true for HTMLElement nodes', () => {
 				expect( converter.isElement( element ) ).to.be.true;