Ver código fonte

PoC: Initial 'refresh' downcast action.

Maciej Gołaszewski 5 anos atrás
pai
commit
7d85e32ea7

+ 14 - 3
packages/ckeditor5-engine/src/conversion/downcastdispatcher.js

@@ -131,15 +131,26 @@ export default class DowncastDispatcher {
 			this.convertMarkerRemove( change.name, change.range, writer );
 		}
 
+		const changes = differ.getChanges();
+
+		if ( changes.length ) {
+			// @if CK_DEBUG // console.log( `convertChanges() size: ${ changes.length }` );
+		}
+
 		// Convert changes that happened on model tree.
-		for ( const entry of differ.getChanges() ) {
+		for ( const entry of changes ) {
+			// @if CK_DEBUG // console.log( `differ: ${ entry.type }` );
+
 			if ( entry.type == 'insert' ) {
 				this.convertInsert( Range._createFromPositionAndShift( entry.position, entry.length ), writer );
 			} else if ( entry.type == 'remove' ) {
 				this.convertRemove( entry.position, entry.length, entry.name, writer );
-			} else {
-				// entry.type == 'attribute'.
+			} else if ( entry.type == 'refresh' ) {
+				// @if CK_DEBUG // console.warn( 'convertRemove' );
+			} else if ( entry.type == 'attribute' ) {
 				this.convertAttribute( entry.range, entry.attributeKey, entry.attributeOldValue, entry.attributeNewValue, writer );
+			} else {
+				// todo warning
 			}
 		}
 

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

@@ -136,6 +136,26 @@ export default class Differ {
 		this._cachedChanges = null;
 	}
 
+	_pocRefreshItem( item ) {
+		if ( this._isInInsertedElement( item.parent ) ) {
+			return;
+		}
+
+		this._markRefresh( item.parent, item.startOffset, item.offsetSize );
+
+		// @todo: Probably makes sense - check later.
+		const range = Range._createOn( item );
+
+		for ( const marker of this._markerCollection.getMarkersIntersectingRange( range ) ) {
+			const markerRange = marker.getRange();
+
+			this.bufferMarkerChange( marker.name, markerRange, markerRange, marker.affectsData );
+		}
+
+		// Clear cache after each buffered operation as it is no longer valid.
+		this._cachedChanges = null;
+	}
+
 	/**
 	 * Buffers the given operation. An operation has to be buffered before it is executed.
 	 *
@@ -453,6 +473,12 @@ export default class Differ {
 					// there is a single diff for each of them) and insert them into the diff set.
 					diffSet.push( ...this._getAttributesDiff( range, snapshotAttributes, elementAttributes ) );
 
+					i++;
+					j++;
+				} else if ( action === 'x' ) {
+					// Swap action - similar to 'equal'
+					diffSet.push( this._getRefreshDiff( element, i, elementChildren[ i ].name ) );
+
 					i++;
 					j++;
 				} else {
@@ -585,6 +611,16 @@ export default class Differ {
 		this._removeAllNestedChanges( parent, offset, howMany );
 	}
 
+	_markRefresh( parent, offset, howMany ) {
+		const changeItem = { type: 'refresh', offset, howMany, count: this._changeCount++ };
+
+		this._markChange( parent, changeItem );
+
+		// Needed to remove "attribute" change or other.
+		// @todo: might need to retain "slot" changes.
+		this._removeAllNestedAttributeChanges( parent, offset, howMany );
+	}
+
 	/**
 	 * Saves and handles an attribute change.
 	 *
@@ -982,6 +1018,16 @@ export default class Differ {
 		return diffs;
 	}
 
+	_getRefreshDiff( parent, offset, name ) {
+		return {
+			type: 'refresh',
+			position: Position._createAt( parent, offset ),
+			name,
+			length: 1,
+			changeCount: this._changeCount++
+		};
+	}
+
 	/**
 	 * Checks whether given element or any of its parents is an element that is buffered as an inserted element.
 	 *
@@ -1031,6 +1077,16 @@ export default class Differ {
 			}
 		}
 	}
+
+	_removeAllNestedAttributeChanges( parent, offset, howMany ) {
+		const parentChanges = this._changesInElement.get( parent );
+
+		const notAttributeChange = change => change.type !== 'attribute' || change.offset !== offset || change.howMany !== howMany;
+
+		if ( parentChanges ) {
+			this._changesInElement.set( parent, parentChanges.filter( notAttributeChange ) );
+		}
+	}
 }
 
 // Returns an array that is a copy of passed child list with the exception that text nodes are split to one or more
@@ -1136,13 +1192,18 @@ function _generateActionsFromChanges( oldChildrenLength, changes ) {
 			offset = change.offset;
 			// We removed `howMany` old nodes, update `oldChildrenHandled`.
 			oldChildrenHandled += change.howMany;
-		} else {
+		} else if ( change.type == 'attribute' ) {
 			actions.push( ...'a'.repeat( change.howMany ).split( '' ) );
 
 			// 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;
+		} else {
+			actions.push( 'x' );
+
+			// The last handled offset is after inserted range.
+			offset = change.offset + change.howMany;
 		}
 	}
 

+ 4 - 0
packages/ckeditor5-engine/src/model/document.js

@@ -307,6 +307,8 @@ export default class Document {
 	 * @param {module:engine/model/writer~Writer} writer The writer on which post-fixers will be called.
 	 */
 	_handleChangeBlock( writer ) {
+		// @if CK_DEBUG // console.group( 'changeBlock' );
+
 		if ( this._hasDocumentChangedFromTheLastChangeBlock() ) {
 			this._callPostFixers( writer );
 
@@ -326,6 +328,8 @@ export default class Document {
 			this.differ.reset();
 		}
 
+		// @if CK_DEBUG // console.groupEnd();
+
 		this._hasSelectionChangedFromTheLastChangeBlock = false;
 	}
 

+ 2 - 1
packages/ckeditor5-table/src/converters/table-cell-refresh-post-fixer.js

@@ -32,7 +32,8 @@ function tableCellRefreshPostFixer( model ) {
 	let insertCount = 0;
 
 	for ( const change of differ.getChanges() ) {
-		const parent = change.type == 'insert' || change.type == 'remove' ? change.position.parent : change.range.start.parent;
+		// @todo port to main
+		const parent = change.type == 'attribute' ? change.range.start.parent : change.position.parent;
 
 		if ( !parent.is( 'element', 'tableCell' ) ) {
 			continue;

+ 1 - 1
tests/manual/article.js

@@ -57,7 +57,7 @@ function boxRefresh( model ) {
 	const boxToRefresh = new Set( boxElements );
 
 	console.group( 'refreshItem' );
-	[ ...boxToRefresh.values() ].forEach( box => differ.refreshItem( box ) );
+	[ ...boxToRefresh.values() ].forEach( box => differ._pocRefreshItem( box ) );
 	console.groupEnd();
 
 	return boxToRefresh.size > 1;