Explorar el Código

Model#deleteContent no longer including start of trailing block.

Kuba Niegowski hace 5 años
padre
commit
acd65f68d7
Se han modificado 1 ficheros con 50 adiciones y 13 borrados
  1. 50 13
      packages/ckeditor5-engine/src/model/utils/deletecontent.js

+ 50 - 13
packages/ckeditor5-engine/src/model/utils/deletecontent.js

@@ -71,6 +71,27 @@ export default function deleteContent( model, selection, options = {} ) {
 
 	const schema = model.schema;
 
+	let startPosition = selRange.start;
+	let endPosition = selRange.end;
+
+	// If the end of selection is at the start position of last block in the selection, then
+	// shrink it to not include that trailing block. Note that this should happen only for not empty selection.
+	if ( hasContent( selRange ) ) {
+		const endBlock = getParentBlock( endPosition );
+
+		if ( endBlock && endPosition.isTouching( model.createPositionAt( endBlock, 0 ) ) ) {
+			// Create forward selection from range.
+			const selection = model.createSelection( selRange );
+
+			// Modify the selection in backward direction to shrink it and remove first position of following block from it.
+			model.modifySelection( selection, { direction: 'backward' } );
+
+			endPosition = selection.getLastPosition();
+		}
+	}
+
+	endPosition = LivePosition.fromPosition( endPosition, 'toNext' );
+
 	model.change( writer => {
 		// 1. Replace the entire content with paragraph.
 		// See: https://github.com/ckeditor/ckeditor5-engine/issues/1012#issuecomment-315017594.
@@ -80,9 +101,6 @@ export default function deleteContent( model, selection, options = {} ) {
 			return;
 		}
 
-		let startPos = selRange.start;
-		const endPos = LivePosition.fromPosition( selRange.end, 'toNext' );
-
 		// 2. Remove the content if there is any.
 		if ( !selRange.start.isTouching( selRange.end ) ) {
 			writer.remove( selRange );
@@ -97,7 +115,7 @@ export default function deleteContent( model, selection, options = {} ) {
 		// as it's hard to imagine what should actually be the default behavior. Usually, specific features will
 		// want to override that behavior anyway.
 		if ( !options.leaveUnmerged ) {
-			startPos = mergeBranches( writer, startPos, endPos ) || startPos;
+			startPosition = mergeBranches( writer, startPosition, endPosition ) || startPosition;
 
 			// TMP this will be replaced with a postfixer.
 			// We need to check and strip disallowed attributes in all nested nodes because after merge
@@ -105,22 +123,40 @@ export default function deleteContent( model, selection, options = {} ) {
 			//
 			// e.g. bold is disallowed for <H1>
 			// <h1>Fo{o</h1><p>b}a<b>r</b><p> -> <h1>Fo{}a<b>r</b><h1> -> <h1>Fo{}ar<h1>.
-			schema.removeDisallowedAttributes( startPos.parent.getChildren(), writer );
+			schema.removeDisallowedAttributes( startPosition.parent.getChildren(), writer );
 		}
 
-		collapseSelectionAt( writer, selection, startPos );
+		collapseSelectionAt( writer, selection, startPosition );
 
 		// 4. Add a paragraph to set selection in it.
 		// Check if a text is allowed in the new container. If not, try to create a new paragraph (if it's allowed here).
 		// If autoparagraphing is off, we assume that you know what you do so we leave the selection wherever it was.
-		if ( !options.doNotAutoparagraph && shouldAutoparagraph( schema, startPos ) ) {
-			insertParagraph( writer, startPos, selection );
+		if ( !options.doNotAutoparagraph && shouldAutoparagraph( schema, startPosition ) ) {
+			insertParagraph( writer, startPosition, selection );
 		}
 
-		endPos.detach();
+		endPosition.detach();
 	} );
 }
 
+// Finds the lowest element in position's ancestors which is a block.
+// It will search until first ancestor that is a limit element.
+function getParentBlock( position ) {
+	const element = position.parent;
+	const schema = element.root.document.model.schema;
+	const ancestors = element.getAncestors( { parentFirst: true, includeSelf: true } );
+
+	for ( const element of ancestors ) {
+		if ( schema.isLimit( element ) ) {
+			return null;
+		}
+
+		if ( schema.isBlock( element ) ) {
+			return element;
+		}
+	}
+}
+
 // This function is a result of reaching the Ballmer's peak for just the right amount of time.
 // Even I had troubles documenting it after a while and after reading it again I couldn't believe that it really works.
 function mergeBranches( writer, startPosition, endPosition ) {
@@ -300,10 +336,11 @@ function getElementsNextToCommonAncestor( positionA, positionB ) {
 	return [ ancestorsA[ i ], ancestorsB[ i ] ];
 }
 
-// Returns true if element contains any text.
-function hasContent( element ) {
-	const schema = element.root.document.model.schema;
-	const range = Range._createIn( element );
+// Returns true if element or range contains any text.
+function hasContent( elementOrRange ) {
+	const model = elementOrRange.root.document.model;
+	const schema = model.schema;
+	const range = elementOrRange.is( 'range' ) ? elementOrRange : model.createRangeIn( elementOrRange );
 
 	for ( const item of range.getItems() ) {
 		if ( item.is( 'textProxy' ) || schema.isObject( item ) ) {