瀏覽代碼

ChangeBuffer locking.

Krzysztof Krztoń 8 年之前
父節點
當前提交
a0c54d4be5
共有 2 個文件被更改,包括 143 次插入6 次删除
  1. 48 6
      packages/ckeditor5-typing/src/changebuffer.js
  2. 95 0
      packages/ckeditor5-typing/tests/changebuffer.js

+ 48 - 6
packages/ckeditor5-typing/src/changebuffer.js

@@ -60,12 +60,28 @@ export default class ChangeBuffer {
 		 */
 		this.limit = limit;
 
+		/**
+		 * Whether the buffer is locked. The locked buffer cannot be reset unless it gets unlocked.
+		 *
+		 * @readonly
+		 * @member {Boolean} #isLocked
+		 */
+		this.isLocked = false;
+
 		this._changeCallback = ( evt, type, changes, batch ) => {
 			this._onBatch( batch );
 		};
 
+		this._resetOnChangeCallback = () => {
+			this._reset();
+		};
+
 		doc.on( 'change', this._changeCallback );
 
+		doc.selection.on( 'change:range', this._resetOnChangeCallback );
+
+		doc.selection.on( 'change:attribute', this._resetOnChangeCallback );
+
 		/**
 		 * The current batch instance.
 		 *
@@ -79,13 +95,20 @@ export default class ChangeBuffer {
 		 * @private
 		 * @member #_changeCallback
 		 */
+
+		/**
+		 * The callback to document selection change:attribute and change:range events which resets the buffer.
+		 *
+		 * @private
+		 * @member #_resetOnChangeCallback
+		 */
 	}
 
 	/**
 	 * The current batch to which a feature should add its deltas. Once the {@link #size}
 	 * is reached or exceeds the {@link #limit}, the batch is set to a new instance and the size is reset.
 	 *
-	 * @type {engine.treeModel.batch.Batch}
+	 * @type {module:engine/model/batch~Batch}
 	 */
 	get batch() {
 		if ( !this._batch ) {
@@ -105,15 +128,31 @@ export default class ChangeBuffer {
 		this.size += changeCount;
 
 		if ( this.size >= this.limit ) {
-			this._reset();
+			this._reset( true );
 		}
 	}
 
 	/**
+	 * Locks the buffer.
+	 */
+	lock() {
+		this.isLocked = true;
+	}
+
+	/**
+	 * Unlocks the buffer.
+	 */
+	unlock() {
+		this.isLocked = false;
+	}
+
+	/**
 	 * Destroys the buffer.
 	 */
 	destroy() {
 		this.document.off( 'change', this._changeCallback );
+		this.document.selection.off( 'change:range', this._resetOnChangeCallback );
+		this.document.selection.off( 'change:attribute', this._resetOnChangeCallback );
 	}
 
 	/**
@@ -130,7 +169,7 @@ export default class ChangeBuffer {
 	_onBatch( batch ) {
 		// One operation means a newly created batch.
 		if ( batch.type != 'transparent' && batch !== this._batch && count( batch.getOperations() ) <= 1 ) {
-			this._reset();
+			this._reset( true );
 		}
 	}
 
@@ -138,9 +177,12 @@ export default class ChangeBuffer {
 	 * Resets the change buffer.
 	 *
 	 * @private
+	 * @param {Boolean} [ignoreLock] Whether internal lock {@link #isLocked} should be ignored.
 	 */
-	_reset() {
-		this._batch = null;
-		this.size = 0;
+	_reset( ignoreLock ) {
+		if ( !this.isLocked || ignoreLock ) {
+			this._batch = null;
+			this.size = 0;
+		}
 	}
 }

+ 95 - 0
packages/ckeditor5-typing/tests/changebuffer.js

@@ -23,6 +23,7 @@ describe( 'ChangeBuffer', () => {
 			expect( buffer ).to.have.property( 'document', doc );
 			expect( buffer ).to.have.property( 'limit', CHANGE_LIMIT );
 			expect( buffer ).to.have.property( 'size', 0 );
+			expect( buffer ).to.have.property( 'isLocked', false );
 		} );
 
 		it( 'sets limit property according to default value', () => {
@@ -32,6 +33,26 @@ describe( 'ChangeBuffer', () => {
 		} );
 	} );
 
+	describe( 'locking', () => {
+		it( 'is unlocked by default', () => {
+			expect( buffer.isLocked ).to.be.false;
+		} );
+
+		it( 'is locked by lock method', () => {
+			buffer.lock();
+
+			expect( buffer.isLocked ).to.be.true;
+		} );
+
+		it( 'is unlocked by unlock method', () => {
+			buffer.isLocked = true;
+
+			buffer.unlock();
+
+			expect( buffer.isLocked ).to.be.false;
+		} );
+	} );
+
 	describe( 'batch', () => {
 		it( 'it is set initially', () => {
 			expect( buffer ).to.have.property( 'batch' );
@@ -107,6 +128,60 @@ describe( 'ChangeBuffer', () => {
 
 			expect( buffer.batch ).to.equal( bufferBatch );
 		} );
+
+		it( 'is not reset while locked', () => {
+			const initialBatch = buffer.batch;
+
+			buffer.lock();
+
+			buffer.input( 1 );
+			buffer._reset();
+
+			buffer.unlock();
+
+			expect( buffer.batch ).to.be.equal( initialBatch );
+			expect( buffer.size ).to.equal( 1 );
+		} );
+
+		it( 'is reset while locked with ignoreLock used', () => {
+			const initialBatch = buffer.batch;
+
+			buffer.lock();
+
+			buffer.input( 1 );
+			buffer._reset( true );
+
+			buffer.unlock();
+
+			expect( buffer.batch ).to.not.equal( initialBatch );
+			expect( buffer.size ).to.equal( 0 );
+		} );
+
+		it( 'is reset while locked and limit exceeded', () => {
+			const initialBatch = buffer.batch;
+
+			buffer.lock();
+
+			buffer.input( CHANGE_LIMIT + 1 );
+
+			buffer.unlock();
+
+			expect( buffer.batch ).to.not.equal( initialBatch );
+			expect( buffer.size ).to.equal( 0 );
+		} );
+
+		it( 'is reset while locked and new batch is applied', () => {
+			const initialBatch = buffer.batch;
+
+			buffer.lock();
+
+			doc.batch().insert( Position.createAt( root, 0 ), 'a' );
+
+			buffer.unlock();
+
+			expect( buffer.batch ).to.not.equal( initialBatch );
+			expect( buffer.size ).to.equal( 0 );
+		} );
 	} );
 
 	describe( 'destroy', () => {
@@ -119,5 +194,25 @@ describe( 'ChangeBuffer', () => {
 
 			expect( buffer.batch ).to.equal( batch1 );
 		} );
+
+		it( 'offs the buffer from the selection change:range', () => {
+			const batch1 = buffer.batch;
+
+			buffer.destroy();
+
+			doc.selection.fire( 'change:attribute' );
+
+			expect( buffer.batch ).to.equal( batch1 );
+		} );
+
+		it( 'offs the buffer from the selection change:attribute', () => {
+			const batch1 = buffer.batch;
+
+			buffer.destroy();
+
+			doc.selection.fire( 'change:range' );
+
+			expect( buffer.batch ).to.equal( batch1 );
+		} );
 	} );
 } );