瀏覽代碼

Introduced `Document#runPostFixersAndResetDiffer` and `Document#_hasDocumentChangedFromTheLastChangeBlock`.

Maciej Bukowski 6 年之前
父節點
當前提交
260ae418e0
共有 2 個文件被更改,包括 66 次插入40 次删除
  1. 53 34
      packages/ckeditor5-engine/src/model/document.js
  2. 13 6
      packages/ckeditor5-engine/src/model/model.js

+ 53 - 34
packages/ckeditor5-engine/src/model/document.js

@@ -46,7 +46,7 @@ export default class Document {
 		 * The {@link module:engine/model/model~Model model} that the document is a part of.
 		 *
 		 * @readonly
-		 * @member {module:engine/model/model~Model}
+		 * @type {module:engine/model/model~Model}
 		 */
 		this.model = model;
 
@@ -58,7 +58,7 @@ export default class Document {
 		 * a {@link module:utils/ckeditorerror~CKEditorError model-document-applyOperation-wrong-version} error is thrown.
 		 *
 		 * @readonly
-		 * @member {Number}
+		 * @type {Number}
 		 */
 		this.version = 0;
 
@@ -66,7 +66,7 @@ export default class Document {
 		 * The document's history.
 		 *
 		 * @readonly
-		 * @member {module:engine/model/history~History}
+		 * @type {module:engine/model/history~History}
 		 */
 		this.history = new History( this );
 
@@ -74,7 +74,7 @@ export default class Document {
 		 * The selection in this document.
 		 *
 		 * @readonly
-		 * @member {module:engine/model/documentselection~DocumentSelection}
+		 * @type {module:engine/model/documentselection~DocumentSelection}
 		 */
 		this.selection = new DocumentSelection( this );
 
@@ -83,7 +83,7 @@ export default class Document {
 		 * {@link #getRoot} to manipulate it.
 		 *
 		 * @readonly
-		 * @member {module:utils/collection~Collection}
+		 * @type {module:utils/collection~Collection}
 		 */
 		this.roots = new Collection( { idProperty: 'rootName' } );
 
@@ -91,7 +91,7 @@ export default class Document {
 		 * The model differ object. Its role is to buffer changes done on the model document and then calculate a diff of those changes.
 		 *
 		 * @readonly
-		 * @member {module:engine/model/differ~Differ}
+		 * @type {module:engine/model/differ~Differ}
 		 */
 		this.differ = new Differ( model.markers );
 
@@ -99,10 +99,18 @@ export default class Document {
 		 * Post-fixer callbacks registered to the model document.
 		 *
 		 * @private
-		 * @member {Set}
+		 * @type {Set.<Function>}
 		 */
 		this._postFixers = new Set();
 
+		/**
+		 * A boolean indicates whether the selection has changed until
+		 *
+		 * @private
+		 * @type {Boolean}
+		 */
+		this._hasSelectionChangedFromTheLastChangeBlock = false;
+
 		// Graveyard tree root. Document always have a graveyard root, which stores removed nodes.
 		this.createRoot( '$root', graveyardName );
 
@@ -143,34 +151,8 @@ export default class Document {
 			}
 		}, { priority: 'low' } );
 
-		// Listen to selection changes. If selection changed, mark it.
-		let hasSelectionChanged = false;
-
 		this.listenTo( this.selection, 'change', () => {
-			hasSelectionChanged = true;
-		} );
-
-		// Wait for `_change` event from model, which signalizes that outermost change block has finished.
-		// When this happens, check if there were any changes done on document, and if so, call post-fixers,
-		// fire `change` event for features and conversion and then reset the differ.
-		// Fire `change:data` event when at least one operation or buffered marker changes the data.
-		this.listenTo( model, '_change', ( evt, writer ) => {
-			const documentChanged = !this.differ.isEmpty || hasSelectionChanged;
-
-			evt.return = documentChanged;
-
-			if ( documentChanged ) {
-				this._callPostFixers( writer );
-
-				if ( this.differ.hasDataChanges() ) {
-					this.fire( 'change:data', writer.batch );
-				} else {
-					this.fire( 'change', writer.batch );
-				}
-
-				this.differ.reset();
-				hasSelectionChanged = false;
-			}
+			this._hasSelectionChangedFromTheLastChangeBlock = true;
 		} );
 
 		// Buffer marker changes.
@@ -296,6 +278,42 @@ export default class Document {
 	}
 
 	/**
+	 * Check if there were any changes done on document, and if so, call post-fixers,
+	 * fire `change` event for features and conversion and then reset the differ.
+	 * Fire `change:data` event when at least one operation or buffered marker changes the data.
+	 *
+	 * @fires change
+	 * @fires change:data
+	 * @param {module:engine/model/writer~Writer writer} writer The writer on which post-fixers will be called.
+	 */
+	runPostFixersAndResetDiffer( writer ) {
+		if ( this._hasDocumentChangedFromTheLastChangeBlock() ) {
+			this._callPostFixers( writer );
+
+			if ( this.differ.hasDataChanges() ) {
+				this.fire( 'change:data', writer.batch );
+			} else {
+				this.fire( 'change', writer.batch );
+			}
+
+			this.differ.reset();
+		}
+
+		this._hasSelectionChangedFromTheLastChangeBlock = false;
+	}
+
+	/**
+	 * Returns whether there is a buffered change or if the selection has changed from the last `{@link module:engine/model/model~Model#enqueueChange `enqueueChange()` block}
+	 * or {@link module:engine/model/model~Model#change `change()` block}.
+	 *
+	 * @protected
+	 * @returns {Boolean} Returns `true` when document has changed from the last change block.
+	 */
+	_hasDocumentChangedFromTheLastChangeBlock() {
+		return !this.differ.isEmpty || this._hasSelectionChangedFromTheLastChangeBlock;
+	}
+
+	/**
 	 * A custom `toJSON()` method to solve child-parent circular dependencies.
 	 *
 	 * @returns {Object} A clone of this object with the document property changed to a string.
@@ -363,6 +381,7 @@ export default class Document {
 	 * Performs post-fixer loops. Executes post-fixer callbacks as long as none of them has done any changes to the model.
 	 *
 	 * @private
+	 * @param {module:engine/model/writer~Writer writer} writer The writer on which post-fixer callbacks will be called.
 	 */
 	_callPostFixers( writer ) {
 		let wasFixed = false;

+ 13 - 6
packages/ckeditor5-engine/src/model/model.js

@@ -683,7 +683,7 @@ export default class Model {
 	 */
 	_runPendingChanges() {
 		const ret = [];
-		let modelChanged = false;
+		let hasDocumentChanged = false;
 
 		this.fire( '_beforeChanges' );
 
@@ -696,15 +696,21 @@ export default class Model {
 			const callbackReturnValue = this._pendingChanges[ 0 ].callback( this._currentWriter );
 			ret.push( callbackReturnValue );
 
-			// Fire internal `_change` event and collect the information whether the model is changed after the callback.
-			const modelChangedAfterCallback = this.fire( '_change', this._currentWriter );
-			modelChanged = modelChanged || modelChangedAfterCallback;
+			this.document.callPostFixers
+
+			// Collect an information whether the model document has changed during from the last pending change callback.
+			hasDocumentChanged = hasDocumentChanged || this.document._hasDocumentChangedFromTheLastChangeBlock();
+
+			this.document.runPostFixersAndResetDiffer( this._currentWriter );
+
+			// With <3 for @scofalic.
+			this.fire( '_change', this._currentWriter );
 
 			this._pendingChanges.shift();
 			this._currentWriter = null;
 		}
 
-		this.fire( '_afterChanges', { modelChanged } );
+		this.fire( '_afterChanges', { hasDocumentChanged } );
 
 		return ret;
 	}
@@ -715,6 +721,7 @@ export default class Model {
 	 *
 	 * **Note:** This is an internal event! Use {@link module:engine/model/document~Document#event:change} instead.
 	 *
+	 * @deprecated
 	 * @protected
 	 * @event _change
 	 * @param {module:engine/model/writer~Writer} writer `Writer` instance that has been used in the change block.
@@ -735,7 +742,7 @@ export default class Model {
 	 * @protected
 	 * @event _afterChanges
 	 * @param {Object} options
-	 * @param {Boolean} options.modelChanged A boolean indicates whether the model was changed during the {@link module:engine/model/model~Model#change} blocks.
+	 * @param {Boolean} options.hasDocumentChanged A boolean indicates whether the model document has changed during the {@link module:engine/model/model~Model#change} blocks.
 	 */
 
 	/**