浏览代码

Merge pull request #44 from ckeditor/t/ckeditor5-engine/1207

Other: Callbacks should listen to new `model.Document#event:change` and use `model.Document#differ`.
Piotr Jasiun 8 年之前
父节点
当前提交
451d837b92

+ 21 - 34
packages/ckeditor5-autoformat/src/blockautoformatengine.js

@@ -8,7 +8,6 @@
  */
 
 import Range from '@ckeditor/ckeditor5-engine/src/model/range';
-import TextProxy from '@ckeditor/ckeditor5-engine/src/model/textproxy';
 
 /**
  * The block autoformatting engine. It allows to format various block patterns. For example,
@@ -64,47 +63,35 @@ export default class BlockAutoformatEngine {
 			};
 		}
 
-		editor.model.document.on( 'change', ( event, type, changes, batch ) => {
-			if ( batch.type == 'transparent' ) {
-				return;
-			}
+		editor.model.document.on( 'change', () => {
+			const changes = editor.model.document.differ.getChanges();
 
-			if ( type != 'insert' ) {
-				return;
-			}
+			for ( const entry of changes ) {
+				if ( entry.type == 'insert' && entry.name == '$text' ) {
+					const item = entry.position.textNode || entry.position.nodeAfter;
 
-			// Take the first element. Typing shouldn't add more than one element at once.
-			// And if it is not typing (e.g. paste), Autoformat should not be fired.
-			const value = changes.range.getItems().next().value;
+					if ( !item.parent.is( 'paragraph' ) ) {
+						continue;
+					}
 
-			if ( !( value instanceof TextProxy ) ) {
-				return;
-			}
+					const match = pattern.exec( item.data );
 
-			const textNode = value.textNode;
-			const text = textNode.data;
+					if ( !match ) {
+						continue;
+					}
 
-			// Run matching only on non-empty paragraphs.
-			if ( textNode.parent.name !== 'paragraph' || !text ) {
-				return;
-			}
+					// Use enqueueChange to create new batch to separate typing batch from the auto-format changes.
+					editor.model.enqueueChange( writer => {
+						// Matched range.
+						const range = Range.createFromParentsAndOffsets( item.parent, 0, item.parent, match[ 0 ].length );
 
-			const match = pattern.exec( text );
+						// Remove matched text.
+						writer.remove( range );
 
-			if ( !match ) {
-				return;
+						callback( { match } );
+					} );
+				}
 			}
-
-			// Use enqueueChange to create new batch to separate typing batch from the autoformat changes.
-			editor.model.enqueueChange( writer => {
-				// Matched range.
-				const range = Range.createFromParentsAndOffsets( textNode.parent, 0, textNode.parent, match[ 0 ].length );
-
-				// Remove matched text.
-				writer.remove( range );
-
-				callback( { match } );
-			} );
 		} );
 	}
 }

+ 58 - 58
packages/ckeditor5-autoformat/src/inlineautoformatengine.js

@@ -136,75 +136,75 @@ export default class InlineAutoformatEngine {
 			}
 		} );
 
-		editor.model.document.on( 'change', ( evt, type, changes, batch ) => {
-			if ( batch.type == 'transparent' ) {
-				return;
-			}
-
-			if ( type !== 'insert' ) {
-				return;
-			}
-
-			const selection = editor.model.document.selection;
-
-			if ( !selection.isCollapsed || !selection.focus || !selection.focus.parent ) {
-				return;
-			}
-
-			const block = selection.focus.parent;
-			const text = getText( block ).slice( 0, selection.focus.offset );
-			const ranges = testCallback( text );
-			const rangesToFormat = [];
+		editor.model.document.on( 'change', () => {
+			const changes = editor.model.document.differ.getChanges();
 
-			// Apply format before deleting text.
-			ranges.format.forEach( range => {
-				if ( range[ 0 ] === undefined || range[ 1 ] === undefined ) {
-					return;
+			for ( const entry of changes ) {
+				if ( entry.type != 'insert' || entry.name != '$text' ) {
+					continue;
 				}
 
-				rangesToFormat.push( LiveRange.createFromParentsAndOffsets(
-					block, range[ 0 ],
-					block, range[ 1 ]
-				) );
-			} );
+				const selection = editor.model.document.selection;
 
-			const rangesToRemove = [];
-
-			// Reverse order to not mix the offsets while removing.
-			ranges.remove.slice().reverse().forEach( range => {
-				if ( range[ 0 ] === undefined || range[ 1 ] === undefined ) {
-					return;
+				if ( !selection.isCollapsed || !selection.focus || !selection.focus.parent ) {
+					continue;
 				}
 
-				rangesToRemove.push( LiveRange.createFromParentsAndOffsets(
-					block, range[ 0 ],
-					block, range[ 1 ]
-				) );
-			} );
-
-			if ( !( rangesToFormat.length && rangesToRemove.length ) ) {
-				return;
-			}
+				const block = selection.focus.parent;
+				const text = getText( block ).slice( 0, selection.focus.offset );
+				const ranges = testCallback( text );
+				const rangesToFormat = [];
+
+				// Apply format before deleting text.
+				ranges.format.forEach( range => {
+					if ( range[ 0 ] === undefined || range[ 1 ] === undefined ) {
+						return;
+					}
+
+					rangesToFormat.push( LiveRange.createFromParentsAndOffsets(
+						block, range[ 0 ],
+						block, range[ 1 ]
+					) );
+				} );
+
+				const rangesToRemove = [];
+
+				// Reverse order to not mix the offsets while removing.
+				ranges.remove.slice().reverse().forEach( range => {
+					if ( range[ 0 ] === undefined || range[ 1 ] === undefined ) {
+						return;
+					}
+
+					rangesToRemove.push( LiveRange.createFromParentsAndOffsets(
+						block, range[ 0 ],
+						block, range[ 1 ]
+					) );
+				} );
+
+				if ( !( rangesToFormat.length && rangesToRemove.length ) ) {
+					continue;
+				}
 
-			// Use enqueueChange to create new batch to separate typing batch from the autoformat changes.
-			editor.model.enqueueChange( writer => {
-				const validRanges = editor.model.schema.getValidRanges( rangesToFormat, command );
+				// Use enqueueChange to create new batch to separate typing batch from the auto-format changes.
+				editor.model.enqueueChange( writer => {
+					const validRanges = editor.model.schema.getValidRanges( rangesToFormat, command );
 
-				// Apply format.
-				formatCallback( writer, validRanges );
+					// Apply format.
+					formatCallback( writer, validRanges );
 
-				// Detach ranges used to apply Autoformat. Prevents memory leaks. #39
-				rangesToFormat.forEach( range => range.detach() );
+					// Detach ranges used to apply Autoformat. Prevents memory leaks. #39
+					rangesToFormat.forEach( range => range.detach() );
 
-				// Remove delimiters.
-				for ( const range of rangesToRemove ) {
-					writer.remove( range );
+					// Remove delimiters.
+					for ( const range of rangesToRemove ) {
+						writer.remove( range );
 
-					// Prevents memory leaks.
-					// https://github.com/ckeditor/ckeditor5-autoformat/issues/39
-					range.detach();
-				}
-			} );
+						// Prevents memory leaks.
+						// https://github.com/ckeditor/ckeditor5-autoformat/issues/39
+						range.detach();
+					}
+				} );
+			}
 		} );
 	}
 }

+ 0 - 25
packages/ckeditor5-autoformat/tests/blockautoformatengine.js

@@ -42,19 +42,6 @@ describe( 'BlockAutoformatEngine', () => {
 			sinon.assert.calledOnce( spy );
 		} );
 
-		it( 'should not run a command when changes are in transparent batch', () => {
-			const spy = testUtils.sinon.spy();
-			editor.commands.add( 'testCommand', new TestCommand( editor, spy ) );
-			new BlockAutoformatEngine( editor, /^[*]\s$/, 'testCommand' ); // eslint-disable-line no-new
-
-			setData( model, '<paragraph>*[]</paragraph>' );
-			model.enqueueChange( 'transparent', writer => {
-				writer.insertText( ' ', doc.selection.getFirstPosition() );
-			} );
-
-			sinon.assert.notCalled( spy );
-		} );
-
 		it( 'should remove found pattern', () => {
 			const spy = testUtils.sinon.spy();
 			editor.commands.add( 'testCommand', new TestCommand( editor, spy ) );
@@ -83,18 +70,6 @@ describe( 'BlockAutoformatEngine', () => {
 			sinon.assert.calledOnce( spy );
 		} );
 
-		it( 'should not run a callback when changes are in transparent batch', () => {
-			const spy = testUtils.sinon.spy();
-			new BlockAutoformatEngine( editor, /^[*]\s$/, spy ); // eslint-disable-line no-new
-
-			setData( model, '<paragraph>*[]</paragraph>' );
-			model.enqueueChange( 'transparent', writer => {
-				writer.insertText( ' ', doc.selection.getFirstPosition() );
-			} );
-
-			sinon.assert.notCalled( spy );
-		} );
-
 		it( 'should ignore other delta operations', () => {
 			const spy = testUtils.sinon.spy();
 			new BlockAutoformatEngine( editor, /^[*]\s/, spy ); // eslint-disable-line no-new

+ 0 - 23
packages/ckeditor5-autoformat/tests/inlineautoformatengine.js

@@ -52,17 +52,6 @@ describe( 'InlineAutoformatEngine', () => {
 			expect( getData( model ) ).to.equal( '<paragraph><$text testAttribute="true">foobar</$text>[]</paragraph>' );
 		} );
 
-		it( 'should not apply an attribute when changes are in transparent batch', () => {
-			new InlineAutoformatEngine( editor, /(\*)(.+?)(\*)/g, 'testAttribute' ); // eslint-disable-line no-new
-
-			setData( model, '<paragraph>*foobar[]</paragraph>' );
-			model.enqueueChange( 'transparent', writer => {
-				writer.insertText( '*', doc.selection.getFirstPosition() );
-			} );
-
-			expect( getData( model ) ).to.equal( '<paragraph>*foobar*[]</paragraph>' );
-		} );
-
 		it( 'should stop early if selection is not collapsed', () => {
 			new InlineAutoformatEngine( editor, /(\*)(.+?)\*/g, 'testAttribute' ); // eslint-disable-line no-new
 
@@ -76,18 +65,6 @@ describe( 'InlineAutoformatEngine', () => {
 	} );
 
 	describe( 'Callback', () => {
-		it( 'should not run a callback when changes are in transparent batch', () => {
-			const spy = testUtils.sinon.spy();
-			new InlineAutoformatEngine( editor, /(\*)(.+?)(\*)/g, spy ); // eslint-disable-line no-new
-
-			setData( model, '<paragraph>*foobar[]</paragraph>' );
-			model.enqueueChange( 'transparent', writer => {
-				writer.insertText( '*', doc.selection.getFirstPosition() );
-			} );
-
-			sinon.assert.notCalled( spy );
-		} );
-
 		it( 'should stop when there are no format ranges returned from testCallback', () => {
 			const formatSpy = testUtils.sinon.spy();
 			const testStub = testUtils.sinon.stub().returns( {