瀏覽代碼

Update model selection data before the post fixers are called.

Oskar Wróbel 6 年之前
父節點
當前提交
2d6d872d74

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

@@ -305,6 +305,9 @@ export default class Document {
 	 */
 	_handleChangeBlock( writer ) {
 		if ( this._hasDocumentChangedFromTheLastChangeBlock() ) {
+			// Refresh the selection data as attributes or markers before the post-fixers are called.
+			this.selection._refresh( writer );
+
 			this._callPostFixers( writer );
 
 			if ( this.differ.hasDataChanges() ) {

+ 28 - 20
packages/ckeditor5-engine/src/model/documentselection.js

@@ -378,6 +378,16 @@ export default class DocumentSelection {
 	}
 
 	/**
+	 * Refreshes the selection data as attributes or markers.
+	 *
+	 * @protected
+	 * @param {module:engine/model/writer~Writer} writer
+	 */
+	_refresh( writer ) {
+		this._selection.refresh( writer );
+	}
+
+	/**
 	 * Moves {@link module:engine/model/documentselection~DocumentSelection#focus} to the specified location.
 	 * Should be used only within the {@link module:engine/model/writer~Writer#setSelectionFocus} method.
 	 *
@@ -617,17 +627,6 @@ class LiveSelection extends Selection {
 			}
 		} );
 
-		this.listenTo( this._document, 'change', ( evt, batch ) => {
-			// Update selection's markers.
-			this._updateMarkers();
-
-			// Update selection's attributes.
-			this._updateAttributes( false );
-
-			// Clear selection attributes from element if no longer empty.
-			clearAttributesStoredInElement( this._model, batch );
-		} );
-
 		this.listenTo( this._model, 'applyOperation', () => {
 			while ( this._fixGraveyardRangesData.length ) {
 				const { liveRange, sourcePosition } = this._fixGraveyardRangesData.shift();
@@ -730,6 +729,17 @@ class LiveSelection extends Selection {
 		}
 	}
 
+	refresh( writer ) {
+		// Update selection's markers.
+		this._updateMarkers();
+
+		// Update selection's attributes.
+		this._updateAttributes( false );
+
+		// Clear selection attributes from element if no longer empty.
+		clearAttributesStoredInElement( this._model, writer );
+	}
+
 	overrideGravity() {
 		const overrideUid = uid();
 
@@ -1136,8 +1146,8 @@ function getAttrsIfCharacter( node ) {
 //
 // @private
 // @param {module:engine/model/model~Model} model
-// @param {module:engine/model/batch~Batch} batch
-function clearAttributesStoredInElement( model, batch ) {
+// @param {module:engine/model/writer~Writer} writer
+function clearAttributesStoredInElement( model, writer ) {
 	const differ = model.document.differ;
 
 	for ( const entry of differ.getChanges() ) {
@@ -1149,14 +1159,12 @@ function clearAttributesStoredInElement( model, batch ) {
 		const isNoLongerEmpty = entry.length === changeParent.maxOffset;
 
 		if ( isNoLongerEmpty ) {
-			model.enqueueChange( batch, writer => {
-				const storedAttributes = Array.from( changeParent.getAttributeKeys() )
-					.filter( key => key.startsWith( storePrefix ) );
+			const storedAttributes = Array.from( changeParent.getAttributeKeys() )
+				.filter( key => key.startsWith( storePrefix ) );
 
-				for ( const key of storedAttributes ) {
-					writer.removeAttribute( key, changeParent );
-				}
-			} );
+			for ( const key of storedAttributes ) {
+				writer.removeAttribute( key, changeParent );
+			}
 		}
 	}
 }

+ 25 - 0
packages/ckeditor5-engine/tests/model/document.js

@@ -295,6 +295,31 @@ describe( 'Document', () => {
 			sinon.assert.calledTwice( callB );
 			sinon.assert.calledOnce( callC );
 		} );
+
+		// https://github.com/ckeditor/ckeditor5-engine/issues/1673
+		it( 'should be called when DocumentSelection data are up to date', done => {
+			doc.registerPostFixer( () => {
+				expect( model.document.selection.hasAttribute( 'foo' ) ).to.equal( true );
+				expect( Array.from( model.document.selection.markers, m => m.name ) ).to.deep.equal( [ 'marker' ] );
+				done();
+			} );
+
+			model.schema.extend( '$text', { allowAttributes: 'foo' } );
+
+			model.change( writer => {
+				writer.insertText( 'abcdef', { foo: 'bar' }, doc.getRoot(), 0 );
+
+				writer.addMarker( 'marker', {
+					range: writer.createRange(
+						writer.createPositionFromPath( doc.getRoot(), [ 1 ] ),
+						writer.createPositionFromPath( doc.getRoot(), [ 5 ] )
+					),
+					usingOperation: false
+				} );
+
+				writer.setSelection( writer.createPositionFromPath( doc.getRoot(), [ 3 ] ) );
+			} );
+		} );
 	} );
 
 	describe( 'event change', () => {

+ 41 - 0
packages/ckeditor5-engine/tests/model/documentselection.js

@@ -508,6 +508,47 @@ describe( 'DocumentSelection', () => {
 		} );
 	} );
 
+	describe( '_refresh()', () => {
+		it( 'should refresh selection markers', () => {
+			model.change( writer => {
+				writer.setSelection( writer.createRange(
+					writer.createPositionFromPath( root, [ 2, 3 ] )
+				) );
+			} );
+
+			const range = model.createRange(
+				model.createPositionFromPath( root, [ 2, 0 ] ),
+				model.createPositionFromPath( root, [ 2, 6 ] )
+			);
+
+			model.markers._set( 'marker', range, false, false );
+
+			expect( Array.from( selection.markers ) ).to.length( 0 );
+
+			selection._refresh();
+
+			expect( Array.from( selection.markers ) ).to.length( 1 );
+		} );
+
+		it( 'should refresh selection attributes', () => {
+			model.schema.extend( '$text', { allowAttributes: 'foo' } );
+
+			model.change( writer => {
+				writer.setSelection( writer.createRange(
+					writer.createPositionFromPath( root, [ 2, 3 ] )
+				) );
+			} );
+
+			root.getChild( 2 ).getChild( 0 )._setAttribute( 'foo', 'bar' );
+
+			expect( selection.hasAttribute( 'foo' ) ).to.equal( false );
+
+			selection._refresh();
+
+			expect( selection.hasAttribute( 'foo' ) ).to.equal( true );
+		} );
+	} );
+
 	describe( '_setTo() - set collapsed at', () => {
 		it( 'detaches all existing ranges', () => {
 			selection._setTo( [ range, liveRange ] );