浏览代码

Fix: Attribute and remove change on intersecting ranges done in the same change block will be correctly saved in `Differ` and downcasted.

Szymon Cofalik 6 年之前
父节点
当前提交
001e4fabbb
共有 2 个文件被更改,包括 62 次插入2 次删除
  1. 21 1
      packages/ckeditor5-engine/src/model/differ.js
  2. 41 1
      packages/ckeditor5-engine/tests/model/differ.js

+ 21 - 1
packages/ckeditor5-engine/src/model/differ.js

@@ -821,6 +821,26 @@ export default class Differ {
 					}
 				}
 
+				if ( old.type == 'remove' ) {
+					// This is a case when attribute change "contains" remove change.
+					// The attribute change needs to be split into two because changes cannot intersect.
+					if ( inc.offset < old.offset && incEnd > old.offset ) {
+						const attributePart = {
+							type: 'attribute',
+							offset: old.offset,
+							howMany: incEnd - old.offset,
+							count: this._changeCount++
+						};
+
+						this._handleChange( attributePart, changes );
+
+						changes.push( attributePart );
+
+						inc.nodesToHandle = old.offset - inc.offset;
+						inc.howMany = inc.nodesToHandle;
+					}
+				}
+
 				if ( old.type == 'attribute' ) {
 					// There are only two conflicting scenarios possible here:
 					if ( inc.offset >= old.offset && incEnd <= oldEnd ) {
@@ -1087,7 +1107,7 @@ function _generateActionsFromChanges( oldChildrenLength, changes ) {
 		} else {
 			actions.push( ...'a'.repeat( change.howMany ).split( '' ) );
 
-			// The last handled offset isa at the position after the changed range.
+			// The last handled offset is at the position after the changed range.
 			offset = change.offset + change.howMany;
 			// We changed `howMany` old nodes, update `oldChildrenHandled`.
 			oldChildrenHandled += change.howMany;

+ 41 - 1
packages/ckeditor5-engine/tests/model/differ.js

@@ -998,7 +998,7 @@ describe( 'Differ', () => {
 			} );
 		} );
 
-		it( 'remove and add attribute on text', () => {
+		it( 'remove attribute and add attribute on text', () => {
 			const p = root.getChild( 1 );
 
 			p.getChild( 0 )._setAttribute( 'bold', true );
@@ -1283,6 +1283,46 @@ describe( 'Differ', () => {
 				] );
 			} );
 		} );
+
+		it( 'add attribute after some text was removed', () => {
+			const p = root.getChild( 0 );
+
+			const range = new Range( Position._createAt( p, 0 ), Position._createAt( p, 2 ) );
+			const position = Position._createAt( p, 1 );
+
+			model.change( () => {
+				remove( position, 1 );
+				attribute( range, 'a', null, true );
+
+				const type = 'attribute';
+				const attributeOldValue = null;
+				const attributeNewValue = true;
+
+				// Attribute change glueing does not work 100% correct.
+				expectChanges( [
+					{
+						type,
+						range: new Range( Position._createAt( p, 0 ), Position._createAt( p, 1 ) ),
+						attributeKey: 'a',
+						attributeOldValue,
+						attributeNewValue
+					},
+					{
+						type: 'remove',
+						position,
+						length: 1,
+						name: '$text'
+					},
+					{
+						type,
+						range: new Range( Position._createAt( p, 1 ), Position._createAt( p, 2 ) ),
+						attributeKey: 'a',
+						attributeOldValue,
+						attributeNewValue
+					}
+				] );
+			} );
+		} );
 	} );
 
 	describe( 'split', () => {