Kaynağa Gözat

Replace *AutoformatEditing trivial classes with simple functions,

as suggested at https://github.com/ckeditor/ckeditor5/issues/7290#issuecomment-633892533.
Tomek Wytrębowicz 5 yıl önce
ebeveyn
işleme
cafe8f44a4

+ 1 - 1
packages/ckeditor5-autoformat/docs/features/autoformat.md

@@ -81,7 +81,7 @@ ClassicEditor
 
 ## Creating custom autoformatters
 
-The {@link module:autoformat/autoformat~Autoformat} feature bases on {@link module:autoformat/blockautoformatediting~BlockAutoformatEditing} and {@link module:autoformat/inlineautoformatediting~InlineAutoformatEditing} tools to create the autoformatters mentioned above.
+The {@link module:autoformat/autoformat~Autoformat} feature bases on {@link module:autoformat/blockautoformatediting~blockAutoformatEditing} and {@link module:autoformat/inlineautoformatediting~inlineAutoformatEditing} tools to create the autoformatters mentioned above.
 
 You can use these tools to create your own autoformatters. Check the [`Autoformat` feature's code](https://github.com/ckeditor/ckeditor5-autoformat/blob/master/src/autoformat.js) as an example.
 

+ 14 - 27
packages/ckeditor5-autoformat/src/autoformat.js

@@ -7,8 +7,8 @@
  * @module autoformat/autoformat
  */
 
-import BlockAutoformatEditing from './blockautoformatediting';
-import InlineAutoformatEditing from './inlineautoformatediting';
+import blockAutoformatEditing from './blockautoformatediting';
+import inlineAutoformatEditing from './inlineautoformatediting';
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 
 /**
@@ -51,13 +51,11 @@ export default class Autoformat extends Plugin {
 		const commands = this.editor.commands;
 
 		if ( commands.get( 'bulletedList' ) ) {
-			// eslint-disable-next-line no-new
-			new BlockAutoformatEditing( this.editor, this, /^[*-]\s$/, 'bulletedList' );
+			blockAutoformatEditing( this.editor, this, /^[*-]\s$/, 'bulletedList' );
 		}
 
 		if ( commands.get( 'numberedList' ) ) {
-			// eslint-disable-next-line no-new
-			new BlockAutoformatEditing( this.editor, this, /^1[.|)]\s$/, 'numberedList' );
+			blockAutoformatEditing( this.editor, this, /^1[.|)]\s$/, 'numberedList' );
 		}
 	}
 
@@ -80,39 +78,31 @@ export default class Autoformat extends Plugin {
 		const commands = this.editor.commands;
 
 		if ( commands.get( 'bold' ) ) {
-			/* eslint-disable no-new */
 			const boldCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'bold' );
 
-			new InlineAutoformatEditing( this.editor, this, /(\*\*)([^*]+)(\*\*)$/g, boldCallback );
-			new InlineAutoformatEditing( this.editor, this, /(__)([^_]+)(__)$/g, boldCallback );
-			/* eslint-enable no-new */
+			inlineAutoformatEditing( this.editor, this, /(\*\*)([^*]+)(\*\*)$/g, boldCallback );
+			inlineAutoformatEditing( this.editor, this, /(__)([^_]+)(__)$/g, boldCallback );
 		}
 
 		if ( commands.get( 'italic' ) ) {
-			/* eslint-disable no-new */
 			const italicCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'italic' );
 
 			// The italic autoformatter cannot be triggered by the bold markers, so we need to check the
 			// text before the pattern (e.g. `(?:^|[^\*])`).
-			new InlineAutoformatEditing( this.editor, this, /(?:^|[^*])(\*)([^*_]+)(\*)$/g, italicCallback );
-			new InlineAutoformatEditing( this.editor, this, /(?:^|[^_])(_)([^_]+)(_)$/g, italicCallback );
-			/* eslint-enable no-new */
+			inlineAutoformatEditing( this.editor, this, /(?:^|[^*])(\*)([^*_]+)(\*)$/g, italicCallback );
+			inlineAutoformatEditing( this.editor, this, /(?:^|[^_])(_)([^_]+)(_)$/g, italicCallback );
 		}
 
 		if ( commands.get( 'code' ) ) {
-			/* eslint-disable no-new */
 			const codeCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'code' );
 
-			new InlineAutoformatEditing( this.editor, this, /(`)([^`]+)(`)$/g, codeCallback );
-			/* eslint-enable no-new */
+			inlineAutoformatEditing( this.editor, this, /(`)([^`]+)(`)$/g, codeCallback );
 		}
 
 		if ( commands.get( 'strikethrough' ) ) {
-			/* eslint-disable no-new */
 			const strikethroughCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'strikethrough' );
 
-			new InlineAutoformatEditing( this.editor, this, /(~~)([^~]+)(~~)$/g, strikethroughCallback );
-			/* eslint-enable no-new */
+			inlineAutoformatEditing( this.editor, this, /(~~)([^~]+)(~~)$/g, strikethroughCallback );
 		}
 	}
 
@@ -137,8 +127,7 @@ export default class Autoformat extends Plugin {
 					const level = commandValue[ 7 ];
 					const pattern = new RegExp( `^(#{${ level }})\\s$` );
 
-					// eslint-disable-next-line no-new
-					new BlockAutoformatEditing( this.editor, this, pattern, () => {
+					blockAutoformatEditing( this.editor, this, pattern, () => {
 						if ( !command.isEnabled ) {
 							return false;
 						}
@@ -159,8 +148,7 @@ export default class Autoformat extends Plugin {
 	 */
 	_addBlockQuoteAutoformats() {
 		if ( this.editor.commands.get( 'blockQuote' ) ) {
-			// eslint-disable-next-line no-new
-			new BlockAutoformatEditing( this.editor, this, /^>\s$/, 'blockQuote' );
+			blockAutoformatEditing( this.editor, this, /^>\s$/, 'blockQuote' );
 		}
 	}
 
@@ -174,13 +162,12 @@ export default class Autoformat extends Plugin {
 	 */
 	_addCodeBlockAutoformats() {
 		if ( this.editor.commands.get( 'codeBlock' ) ) {
-			// eslint-disable-next-line no-new
-			new BlockAutoformatEditing( this.editor, this, /^```$/, 'codeBlock' );
+			blockAutoformatEditing( this.editor, this, /^```$/, 'codeBlock' );
 		}
 	}
 }
 
-// Helper function for getting `InlineAutoformatEditing` callbacks that checks if command is enabled.
+// Helper function for getting `inlineAutoformatEditing` callbacks that checks if command is enabled.
 //
 // @param {module:core/editor/editor~Editor} editor
 // @param {String} attributeKey

+ 82 - 87
packages/ckeditor5-autoformat/src/blockautoformatediting.js

@@ -2,11 +2,6 @@
  * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
-
-/**
- * @module autoformat/blockautoformatediting
- */
-
 import LiveRange from '@ckeditor/ckeditor5-engine/src/model/liverange';
 
 /**
@@ -16,102 +11,102 @@ import LiveRange from '@ckeditor/ckeditor5-engine/src/model/liverange';
  * The autoformatting operation is integrated with the undo manager,
  * so the autoformatting step can be undone if the user's intention was not to format the text.
  *
- * See the constructors documentation to learn how to create custom inline autoformatters. You can also use
+ * See the functions documentation to learn how to create custom inline autoformatters. You can also use
  * the {@link module:autoformat/autoformat~Autoformat} feature which enables a set of default autoformatters
  * (lists, headings, bold and italic).
+ * @module autoformat/blockautoformatediting
  */
-export default class BlockAutoformatEditing {
-	/**
-	 * Creates a listener triggered on `change` event in the document.
-	 * Calls the callback when inserted text matches the regular expression or the command name
-	 * if provided instead of the callback.
-	 *
-	 * Examples of usage:
-	 *
-	 * To convert a paragraph to heading 1 when `- ` is typed, using just the command name:
-	 *
-	 *		new BlockAutoformatEditing( editor, plugin, /^\- $/, 'heading1' );
-	 *
-	 * To convert a paragraph to heading 1 when `- ` is typed, using just the callback:
-	 *
-	 *		new BlockAutoformatEditing( editor, plugin, /^\- $/, ( context ) => {
-	 *			const { match } = context;
-	 *			const headingLevel = match[ 1 ].length;
-	 *
-	 *			editor.execute( 'heading', {
-	 *				formatId: `heading${ headingLevel }`
-	 *			} );
-	 * 		} );
-	 *
-	 * @param {module:core/editor/editor~Editor} editor The editor instance.
-	 * @param {module:autoformat/autoformat~Autoformat} plugin The autoformat plugin instance.
-	 * @param {RegExp} pattern The regular expression to execute on just inserted text.
-	 * @param {Function|String} callbackOrCommand The callback to execute or the command to run when the text is matched.
-	 * In case of providing the callback, it receives the following parameter:
-	 * * {Object} match RegExp.exec() result of matching the pattern to inserted text.
-	 */
-	constructor( editor, plugin, pattern, callbackOrCommand ) {
-		let callback;
-		let command = null;
-
-		if ( typeof callbackOrCommand == 'function' ) {
-			callback = callbackOrCommand;
-		} else {
-			// We assume that the actual command name was provided.
-			command = editor.commands.get( callbackOrCommand );
-
-			callback = () => {
-				editor.execute( callbackOrCommand );
-			};
-		}
 
-		editor.model.document.on( 'change:data', ( evt, batch ) => {
-			if ( command && !command.isEnabled || !plugin.isEnabled ) {
-				return;
-			}
+/**
+ * Creates a listener triggered on `change` event in the document.
+ * Calls the callback when inserted text matches the regular expression or the command name
+ * if provided instead of the callback.
+ *
+ * Examples of usage:
+ *
+ * To convert a paragraph to heading 1 when `- ` is typed, using just the command name:
+ *
+ *		blockAutoformatEditing( editor, plugin, /^\- $/, 'heading1' );
+ *
+ * To convert a paragraph to heading 1 when `- ` is typed, using just the callback:
+ *
+ *		blockAutoformatEditing( editor, plugin, /^\- $/, ( context ) => {
+ *			const { match } = context;
+ *			const headingLevel = match[ 1 ].length;
+ *
+ *			editor.execute( 'heading', {
+ *				formatId: `heading${ headingLevel }`
+ *			} );
+ * 		} );
+ *
+ * @param {module:core/editor/editor~Editor} editor The editor instance.
+ * @param {module:autoformat/autoformat~Autoformat} plugin The autoformat plugin instance.
+ * @param {RegExp} pattern The regular expression to execute on just inserted text.
+ * @param {Function|String} callbackOrCommand The callback to execute or the command to run when the text is matched.
+ * In case of providing the callback, it receives the following parameter:
+ * * {Object} match RegExp.exec() result of matching the pattern to inserted text.
+ */
+export default function blockAutoformatEditing( editor, plugin, pattern, callbackOrCommand ) {
+	let callback;
+	let command = null;
+
+	if ( typeof callbackOrCommand == 'function' ) {
+		callback = callbackOrCommand;
+	} else {
+		// We assume that the actual command name was provided.
+		command = editor.commands.get( callbackOrCommand );
+
+		callback = () => {
+			editor.execute( callbackOrCommand );
+		};
+	}
 
-			if ( batch.type == 'transparent' ) {
-				return;
-			}
+	editor.model.document.on( 'change:data', ( evt, batch ) => {
+		if ( command && !command.isEnabled || !plugin.isEnabled ) {
+			return;
+		}
 
-			const changes = Array.from( editor.model.document.differ.getChanges() );
-			const entry = changes[ 0 ];
+		if ( batch.type == 'transparent' ) {
+			return;
+		}
 
-			// Typing is represented by only a single change.
-			if ( changes.length != 1 || entry.type !== 'insert' || entry.name != '$text' || entry.length != 1 ) {
-				return;
-			}
+		const changes = Array.from( editor.model.document.differ.getChanges() );
+		const entry = changes[ 0 ];
 
-			const blockToFormat = entry.position.parent;
+		// Typing is represented by only a single change.
+		if ( changes.length != 1 || entry.type !== 'insert' || entry.name != '$text' || entry.length != 1 ) {
+			return;
+		}
 
-			// Block formatting should trigger only if the entire content of a paragraph is a single text node... (see ckeditor5#5671).
-			if ( !blockToFormat.is( 'paragraph' ) || blockToFormat.childCount !== 1 ) {
-				return;
-			}
+		const blockToFormat = entry.position.parent;
 
-			const match = pattern.exec( blockToFormat.getChild( 0 ).data );
+		// Block formatting should trigger only if the entire content of a paragraph is a single text node... (see ckeditor5#5671).
+		if ( !blockToFormat.is( 'paragraph' ) || blockToFormat.childCount !== 1 ) {
+			return;
+		}
 
-			// ...and this text node's data match the pattern.
-			if ( !match ) {
-				return;
-			}
+		const match = pattern.exec( blockToFormat.getChild( 0 ).data );
 
-			// Use enqueueChange to create new batch to separate typing batch from the auto-format changes.
-			editor.model.enqueueChange( writer => {
-				// Matched range.
-				const start = writer.createPositionAt( blockToFormat, 0 );
-				const end = writer.createPositionAt( blockToFormat, match[ 0 ].length );
-				const range = new LiveRange( start, end );
+		// ...and this text node's data match the pattern.
+		if ( !match ) {
+			return;
+		}
 
-				const wasChanged = callback( { match } );
+		// Use enqueueChange to create new batch to separate typing batch from the auto-format changes.
+		editor.model.enqueueChange( writer => {
+			// Matched range.
+			const start = writer.createPositionAt( blockToFormat, 0 );
+			const end = writer.createPositionAt( blockToFormat, match[ 0 ].length );
+			const range = new LiveRange( start, end );
 
-				// Remove matched text.
-				if ( wasChanged !== false ) {
-					writer.remove( range );
-				}
+			const wasChanged = callback( { match } );
 
-				range.detach();
-			} );
+			// Remove matched text.
+			if ( wasChanged !== false ) {
+				writer.remove( range );
+			}
+
+			range.detach();
 		} );
-	}
+	} );
 }

+ 164 - 168
packages/ckeditor5-autoformat/src/inlineautoformatediting.js

@@ -4,203 +4,199 @@
  */
 
 /**
- * @module autoformat/inlineautoformatediting
- */
-
-/**
  * The inline autoformatting engine. It allows to format various inline patterns. For example,
  * it can be configured to make "foo" bold when typed `**foo**` (the `**` markers will be removed).
  *
  * The autoformatting operation is integrated with the undo manager,
  * so the autoformatting step can be undone if the user's intention was not to format the text.
  *
- * See the constructors documentation to learn how to create custom inline autoformatters. You can also use
+ * See the functions documentation to learn how to create custom inline autoformatters. You can also use
  * the {@link module:autoformat/autoformat~Autoformat} feature which enables a set of default autoformatters
  * (lists, headings, bold and italic).
+ * @module autoformat/inlineautoformatediting
  */
-export default class InlineAutoformatEditing {
-	/**
-	 * Enables autoformatting mechanism for a given {@link module:core/editor/editor~Editor}.
-	 *
-	 * It formats the matched text by applying the given model attribute or by running the provided formatting callback.
-	 * On every change applied to the model the autoformatting engine checks the text on the left of the selection
-	 * and executes the provided action if the text matches given criteria (regular expression or callback).
-	 *
-	 * @param {module:core/editor/editor~Editor} editor The editor instance.
-	 * @param {module:autoformat/autoformat~Autoformat} plugin The autoformat plugin instance.
-	 * @param {Function|RegExp} testRegexpOrCallback The regular expression or callback to execute on text.
-	 * Provided regular expression *must* have three capture groups. The first and the third capture group
-	 * should match opening and closing delimiters. The second capture group should match the text to format.
-	 *
-	 *		// Matches the `**bold text**` pattern.
-	 *		// There are three capturing groups:
-	 *		// - The first to match the starting `**` delimiter.
-	 *		// - The second to match the text to format.
-	 *		// - The third to match the ending `**` delimiter.
-	 *		new InlineAutoformatEditing( editor, plugin, /(\*\*)([^\*]+?)(\*\*)$/g, 'bold' );
-	 *
-	 * When a function is provided instead of the regular expression, it will be executed with the text to match as a parameter.
-	 * The function should return proper "ranges" to delete and format.
-	 *
-	 *		{
-	 *			remove: [
-	 *				[ 0, 1 ],	// Remove the first letter from the given text.
-	 *				[ 5, 6 ]	// Remove the 6th letter from the given text.
-	 *			],
-	 *			format: [
-	 *				[ 1, 5 ]	// Format all letters from 2nd to 5th.
-	 *			]
-	 *		}
-	 *
-	 * @param {Function|String} attributeOrCallback The name of attribute to apply on matching text or a callback for manual
-	 * formatting. If callback is passed it should return `false` if changes should not be applied (e.g. if a command is disabled).
-	 *
-	 *		// Use attribute name:
-	 *		new InlineAutoformatEditing( editor, plugin, /(\*\*)([^\*]+?)(\*\*)$/g, 'bold' );
-	 *
-	 *		// Use formatting callback:
-	 *		new InlineAutoformatEditing( editor, plugin, /(\*\*)([^\*]+?)(\*\*)$/g, ( writer, rangesToFormat ) => {
-	 *			const command = editor.commands.get( 'bold' );
-	 *
-	 *			if ( !command.isEnabled ) {
-	 *				return false;
-	 *			}
-	 *
-	 *			const validRanges = editor.model.schema.getValidRanges( rangesToFormat, 'bold' );
-	 *
-	 *			for ( let range of validRanges ) {
-	 *				writer.setAttribute( 'bold', true, range );
-	 *			}
-	 *		} );
-	 */
-	constructor( editor, plugin, testRegexpOrCallback, attributeOrCallback ) {
-		let regExp;
-		let attributeKey;
-		let testCallback;
-		let formatCallback;
-
-		if ( testRegexpOrCallback instanceof RegExp ) {
-			regExp = testRegexpOrCallback;
-		} else {
-			testCallback = testRegexpOrCallback;
-		}
 
-		if ( typeof attributeOrCallback == 'string' ) {
-			attributeKey = attributeOrCallback;
-		} else {
-			formatCallback = attributeOrCallback;
-		}
+/**
+ * Enables autoformatting mechanism for a given {@link module:core/editor/editor~Editor}.
+ *
+ * It formats the matched text by applying the given model attribute or by running the provided formatting callback.
+ * On every change applied to the model the autoformatting engine checks the text on the left of the selection
+ * and executes the provided action if the text matches given criteria (regular expression or callback).
+ *
+ * @param {module:core/editor/editor~Editor} editor The editor instance.
+ * @param {module:autoformat/autoformat~Autoformat} plugin The autoformat plugin instance.
+ * @param {Function|RegExp} testRegexpOrCallback The regular expression or callback to execute on text.
+ * Provided regular expression *must* have three capture groups. The first and the third capture group
+ * should match opening and closing delimiters. The second capture group should match the text to format.
+ *
+ *		// Matches the `**bold text**` pattern.
+ *		// There are three capturing groups:
+ *		// - The first to match the starting `**` delimiter.
+ *		// - The second to match the text to format.
+ *		// - The third to match the ending `**` delimiter.
+ *		inlineAutoformatEditing( editor, plugin, /(\*\*)([^\*]+?)(\*\*)$/g, 'bold' );
+ *
+ * When a function is provided instead of the regular expression, it will be executed with the text to match as a parameter.
+ * The function should return proper "ranges" to delete and format.
+ *
+ *		{
+ *			remove: [
+ *				[ 0, 1 ],	// Remove the first letter from the given text.
+ *				[ 5, 6 ]	// Remove the 6th letter from the given text.
+ *			],
+ *			format: [
+ *				[ 1, 5 ]	// Format all letters from 2nd to 5th.
+ *			]
+ *		}
+ *
+ * @param {Function|String} attributeOrCallback The name of attribute to apply on matching text or a callback for manual
+ * formatting. If callback is passed it should return `false` if changes should not be applied (e.g. if a command is disabled).
+ *
+ *		// Use attribute name:
+ *		inlineAutoformatEditing( editor, plugin, /(\*\*)([^\*]+?)(\*\*)$/g, 'bold' );
+ *
+ *		// Use formatting callback:
+ *		inlineAutoformatEditing( editor, plugin, /(\*\*)([^\*]+?)(\*\*)$/g, ( writer, rangesToFormat ) => {
+ *			const command = editor.commands.get( 'bold' );
+ *
+ *			if ( !command.isEnabled ) {
+ *				return false;
+ *			}
+ *
+ *			const validRanges = editor.model.schema.getValidRanges( rangesToFormat, 'bold' );
+ *
+ *			for ( let range of validRanges ) {
+ *				writer.setAttribute( 'bold', true, range );
+ *			}
+ *		} );
+ */
+export default function inlineAutoformatEditing( editor, plugin, testRegexpOrCallback, attributeOrCallback ) {
+	let regExp;
+	let attributeKey;
+	let testCallback;
+	let formatCallback;
+
+	if ( testRegexpOrCallback instanceof RegExp ) {
+		regExp = testRegexpOrCallback;
+	} else {
+		testCallback = testRegexpOrCallback;
+	}
+
+	if ( typeof attributeOrCallback == 'string' ) {
+		attributeKey = attributeOrCallback;
+	} else {
+		formatCallback = attributeOrCallback;
+	}
+
+	// A test callback run on changed text.
+	testCallback = testCallback || ( text => {
+		let result;
+		const remove = [];
+		const format = [];
 
-		// A test callback run on changed text.
-		testCallback = testCallback || ( text => {
-			let result;
-			const remove = [];
-			const format = [];
-
-			while ( ( result = regExp.exec( text ) ) !== null ) {
-				// There should be full match and 3 capture groups.
-				if ( result && result.length < 4 ) {
-					break;
-				}
-
-				let {
-					index,
-					'1': leftDel,
-					'2': content,
-					'3': rightDel
-				} = result;
-
-				// Real matched string - there might be some non-capturing groups so we need to recalculate starting index.
-				const found = leftDel + content + rightDel;
-				index += result[ 0 ].length - found.length;
-
-				// Start and End offsets of delimiters to remove.
-				const delStart = [
-					index,
-					index + leftDel.length
-				];
-				const delEnd = [
-					index + leftDel.length + content.length,
-					index + leftDel.length + content.length + rightDel.length
-				];
-
-				remove.push( delStart );
-				remove.push( delEnd );
-
-				format.push( [ index + leftDel.length, index + leftDel.length + content.length ] );
+		while ( ( result = regExp.exec( text ) ) !== null ) {
+			// There should be full match and 3 capture groups.
+			if ( result && result.length < 4 ) {
+				break;
 			}
 
-			return {
-				remove,
-				format
-			};
-		} );
+			let {
+				index,
+				'1': leftDel,
+				'2': content,
+				'3': rightDel
+			} = result;
+
+			// Real matched string - there might be some non-capturing groups so we need to recalculate starting index.
+			const found = leftDel + content + rightDel;
+			index += result[ 0 ].length - found.length;
+
+			// Start and End offsets of delimiters to remove.
+			const delStart = [
+				index,
+				index + leftDel.length
+			];
+			const delEnd = [
+				index + leftDel.length + content.length,
+				index + leftDel.length + content.length + rightDel.length
+			];
+
+			remove.push( delStart );
+			remove.push( delEnd );
+
+			format.push( [ index + leftDel.length, index + leftDel.length + content.length ] );
+		}
 
-		// A format callback run on matched text.
-		formatCallback = formatCallback || ( ( writer, rangesToFormat ) => {
-			const validRanges = editor.model.schema.getValidRanges( rangesToFormat, attributeKey );
+		return {
+			remove,
+			format
+		};
+	} );
 
-			for ( const range of validRanges ) {
-				writer.setAttribute( attributeKey, true, range );
-			}
+	// A format callback run on matched text.
+	formatCallback = formatCallback || ( ( writer, rangesToFormat ) => {
+		const validRanges = editor.model.schema.getValidRanges( rangesToFormat, attributeKey );
 
-			// After applying attribute to the text, remove given attribute from the selection.
-			// This way user is able to type a text without attribute used by auto formatter.
-			writer.removeSelectionAttribute( attributeKey );
-		} );
+		for ( const range of validRanges ) {
+			writer.setAttribute( attributeKey, true, range );
+		}
 
-		editor.model.document.on( 'change:data', ( evt, batch ) => {
-			if ( batch.type == 'transparent' || !plugin.isEnabled ) {
-				return;
-			}
+		// After applying attribute to the text, remove given attribute from the selection.
+		// This way user is able to type a text without attribute used by auto formatter.
+		writer.removeSelectionAttribute( attributeKey );
+	} );
 
-			const model = editor.model;
-			const selection = model.document.selection;
+	editor.model.document.on( 'change:data', ( evt, batch ) => {
+		if ( batch.type == 'transparent' || !plugin.isEnabled ) {
+			return;
+		}
 
-			// Do nothing if selection is not collapsed.
-			if ( !selection.isCollapsed ) {
-				return;
-			}
+		const model = editor.model;
+		const selection = model.document.selection;
 
-			const changes = Array.from( model.document.differ.getChanges() );
-			const entry = changes[ 0 ];
+		// Do nothing if selection is not collapsed.
+		if ( !selection.isCollapsed ) {
+			return;
+		}
 
-			// Typing is represented by only a single change.
-			if ( changes.length != 1 || entry.type !== 'insert' || entry.name != '$text' || entry.length != 1 ) {
-				return;
-			}
+		const changes = Array.from( model.document.differ.getChanges() );
+		const entry = changes[ 0 ];
+
+		// Typing is represented by only a single change.
+		if ( changes.length != 1 || entry.type !== 'insert' || entry.name != '$text' || entry.length != 1 ) {
+			return;
+		}
 
-			const focus = selection.focus;
-			const block = focus.parent;
-			const { text, range } = getTextAfterCode( model.createRange( model.createPositionAt( block, 0 ), focus ), model );
-			const testOutput = testCallback( text );
-			const rangesToFormat = testOutputToRanges( range.start, testOutput.format, model );
-			const rangesToRemove = testOutputToRanges( range.start, testOutput.remove, model );
+		const focus = selection.focus;
+		const block = focus.parent;
+		const { text, range } = getTextAfterCode( model.createRange( model.createPositionAt( block, 0 ), focus ), model );
+		const testOutput = testCallback( text );
+		const rangesToFormat = testOutputToRanges( range.start, testOutput.format, model );
+		const rangesToRemove = testOutputToRanges( range.start, testOutput.remove, model );
 
-			if ( !( rangesToFormat.length && rangesToRemove.length ) ) {
+		if ( !( rangesToFormat.length && rangesToRemove.length ) ) {
+			return;
+		}
+
+		// Use enqueueChange to create new batch to separate typing batch from the auto-format changes.
+		model.enqueueChange( writer => {
+			// Apply format.
+			const hasChanged = formatCallback( writer, rangesToFormat );
+
+			// Strict check on `false` to have backward compatibility (when callbacks were returning `undefined`).
+			if ( hasChanged === false ) {
 				return;
 			}
 
-			// Use enqueueChange to create new batch to separate typing batch from the auto-format changes.
-			model.enqueueChange( writer => {
-				// Apply format.
-				const hasChanged = formatCallback( writer, rangesToFormat );
-
-				// Strict check on `false` to have backward compatibility (when callbacks were returning `undefined`).
-				if ( hasChanged === false ) {
-					return;
-				}
-
-				// Remove delimiters - use reversed order to not mix the offsets while removing.
-				for ( const range of rangesToRemove.reverse() ) {
-					writer.remove( range );
-				}
-			} );
+			// Remove delimiters - use reversed order to not mix the offsets while removing.
+			for ( const range of rangesToRemove.reverse() ) {
+				writer.remove( range );
+			}
 		} );
-	}
+	} );
 }
 
-// Converts output of the test function provided to the InlineAutoformatEditing and converts it to the model ranges
+// Converts output of the test function provided to the inlineAutoformatEditing and converts it to the model ranges
 // inside provided block.
 //
 // @private

+ 13 - 13
packages/ckeditor5-autoformat/tests/blockautoformatediting.js

@@ -4,7 +4,7 @@
  */
 
 import Autoformat from '../src/autoformat';
-import BlockAutoformatEditing from '../src/blockautoformatediting';
+import blockAutoformatEditing from '../src/blockautoformatediting';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import Enter from '@ckeditor/ckeditor5-enter/src/enter';
@@ -12,7 +12,7 @@ import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 import Command from '@ckeditor/ckeditor5-core/src/command';
 
-describe( 'BlockAutoformatEditing', () => {
+describe( 'blockAutoformatEditing', () => {
 	let editor, model, doc, plugin;
 
 	testUtils.createSinonSandbox();
@@ -37,7 +37,7 @@ describe( 'BlockAutoformatEditing', () => {
 
 			editor.commands.add( 'testCommand', testCommand );
 
-			new BlockAutoformatEditing( editor, plugin, /^[*]\s$/, 'testCommand' ); // eslint-disable-line no-new
+			blockAutoformatEditing( editor, plugin, /^[*]\s$/, 'testCommand' );
 
 			setData( model, '<paragraph>*[]</paragraph>' );
 
@@ -54,7 +54,7 @@ describe( 'BlockAutoformatEditing', () => {
 
 			editor.commands.add( 'testCommand', testCommand );
 
-			new BlockAutoformatEditing( editor, plugin, /^[*]\s$/, 'testCommand' ); // eslint-disable-line no-new
+			blockAutoformatEditing( editor, plugin, /^[*]\s$/, 'testCommand' );
 
 			setData( model, '<paragraph>*[]</paragraph>' );
 
@@ -76,7 +76,7 @@ describe( 'BlockAutoformatEditing', () => {
 
 			editor.commands.add( 'testCommand', testCommand );
 
-			new BlockAutoformatEditing( editor, plugin, /^[*]\s$/, 'testCommand' ); // eslint-disable-line no-new
+			blockAutoformatEditing( editor, plugin, /^[*]\s$/, 'testCommand' );
 
 			setData( model, '<paragraph>*[]</paragraph>' );
 
@@ -91,7 +91,7 @@ describe( 'BlockAutoformatEditing', () => {
 	describe( 'callback', () => {
 		it( 'should run callback when the pattern is matched', () => {
 			const spy = testUtils.sinon.spy();
-			new BlockAutoformatEditing( editor, plugin, /^[*]\s$/, spy ); // eslint-disable-line no-new
+			blockAutoformatEditing( editor, plugin, /^[*]\s$/, spy );
 
 			setData( model, '<paragraph>*[]</paragraph>' );
 			model.change( writer => {
@@ -103,7 +103,7 @@ describe( 'BlockAutoformatEditing', () => {
 
 		it( 'should not call callback when the pattern is matched and plugin is disabled', () => {
 			const callbackSpy = testUtils.sinon.spy().named( 'callback' );
-			new BlockAutoformatEditing( editor, plugin, /^[*]\s$/, callbackSpy ); // eslint-disable-line no-new
+			blockAutoformatEditing( editor, plugin, /^[*]\s$/, callbackSpy );
 
 			plugin.isEnabled = false;
 
@@ -117,7 +117,7 @@ describe( 'BlockAutoformatEditing', () => {
 
 		it( 'should ignore other delta operations', () => {
 			const spy = testUtils.sinon.spy();
-			new BlockAutoformatEditing( editor, plugin, /^[*]\s/, spy ); // eslint-disable-line no-new
+			blockAutoformatEditing( editor, plugin, /^[*]\s/, spy );
 
 			setData( model, '<paragraph>*[]</paragraph>' );
 			model.change( writer => {
@@ -129,7 +129,7 @@ describe( 'BlockAutoformatEditing', () => {
 
 		it( 'should stop if there is no text to run matching on', () => {
 			const spy = testUtils.sinon.spy();
-			new BlockAutoformatEditing( editor, plugin, /^[*]\s/, spy ); // eslint-disable-line no-new
+			blockAutoformatEditing( editor, plugin, /^[*]\s/, spy );
 
 			setData( model, '<paragraph>[]</paragraph>' );
 			model.change( writer => {
@@ -157,7 +157,7 @@ describe( 'BlockAutoformatEditing', () => {
 				} );
 
 			const spy = testUtils.sinon.spy();
-			new BlockAutoformatEditing( editor, plugin, /^[*]\s/, spy ); // eslint-disable-line no-new
+			blockAutoformatEditing( editor, plugin, /^[*]\s/, spy );
 
 			setData( model, '<paragraph>*<softBreak></softBreak>[]</paragraph>' );
 			model.change( writer => {
@@ -185,7 +185,7 @@ describe( 'BlockAutoformatEditing', () => {
 				} );
 
 			const spy = testUtils.sinon.spy();
-			new BlockAutoformatEditing( editor, plugin, /^[*]\s/, spy ); // eslint-disable-line no-new
+			blockAutoformatEditing( editor, plugin, /^[*]\s/, spy );
 
 			setData( model, '<paragraph>* <softBreak></softBreak>[]</paragraph>' );
 
@@ -197,7 +197,7 @@ describe( 'BlockAutoformatEditing', () => {
 		} );
 
 		it( 'should stop if callback returned false', () => {
-			new BlockAutoformatEditing( editor, plugin, /^[*]\s$/, () => false ); // eslint-disable-line no-new
+			blockAutoformatEditing( editor, plugin, /^[*]\s$/, () => false );
 
 			setData( model, '<paragraph>*[]</paragraph>' );
 			model.change( writer => {
@@ -210,7 +210,7 @@ describe( 'BlockAutoformatEditing', () => {
 
 	it( 'should ignore transparent batches', () => {
 		const spy = testUtils.sinon.spy();
-		new BlockAutoformatEditing( editor, plugin, /^[*]\s$/, spy ); // eslint-disable-line no-new
+		blockAutoformatEditing( editor, plugin, /^[*]\s$/, spy );
 
 		setData( model, '<paragraph>*[]</paragraph>' );
 		model.enqueueChange( 'transparent', writer => {

+ 11 - 11
packages/ckeditor5-autoformat/tests/inlineautoformatediting.js

@@ -4,14 +4,14 @@
  */
 
 import Autoformat from '../src/autoformat';
-import InlineAutoformatEditing from '../src/inlineautoformatediting';
+import inlineAutoformatEditing from '../src/inlineautoformatediting';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import Enter from '@ckeditor/ckeditor5-enter/src/enter';
 import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
-describe( 'InlineAutoformatEditing', () => {
+describe( 'inlineAutoformatEditing', () => {
 	let editor, model, doc, plugin;
 
 	testUtils.createSinonSandbox();
@@ -33,7 +33,7 @@ describe( 'InlineAutoformatEditing', () => {
 
 	describe( 'attribute', () => {
 		it( 'should stop early if there are less than 3 capture groups', () => {
-			new InlineAutoformatEditing( editor, plugin, /(\*)(.+?)\*/g, 'testAttribute' ); // eslint-disable-line no-new
+			inlineAutoformatEditing( editor, plugin, /(\*)(.+?)\*/g, 'testAttribute' );
 
 			setData( model, '<paragraph>*foobar[]</paragraph>' );
 			model.change( writer => {
@@ -44,7 +44,7 @@ describe( 'InlineAutoformatEditing', () => {
 		} );
 
 		it( 'should apply an attribute when the pattern is matched', () => {
-			new InlineAutoformatEditing( editor, plugin, /(\*)(.+?)(\*)/g, 'testAttribute' ); // eslint-disable-line no-new
+			inlineAutoformatEditing( editor, plugin, /(\*)(.+?)(\*)/g, 'testAttribute' );
 
 			setData( model, '<paragraph>*foobar[]</paragraph>' );
 			model.change( writer => {
@@ -55,7 +55,7 @@ describe( 'InlineAutoformatEditing', () => {
 		} );
 
 		it( 'should stop early if selection is not collapsed', () => {
-			new InlineAutoformatEditing( editor, plugin, /(\*)(.+?)\*/g, 'testAttribute' ); // eslint-disable-line no-new
+			inlineAutoformatEditing( editor, plugin, /(\*)(.+?)\*/g, 'testAttribute' );
 
 			setData( model, '<paragraph>*foob[ar]</paragraph>' );
 			model.change( writer => {
@@ -74,7 +74,7 @@ describe( 'InlineAutoformatEditing', () => {
 				remove: []
 			} );
 
-			new InlineAutoformatEditing( editor, plugin, testStub, formatSpy ); // eslint-disable-line no-new
+			inlineAutoformatEditing( editor, plugin, testStub, formatSpy );
 
 			setData( model, '<paragraph>*[]</paragraph>' );
 			model.change( writer => {
@@ -91,7 +91,7 @@ describe( 'InlineAutoformatEditing', () => {
 				remove: [ [] ]
 			} );
 
-			new InlineAutoformatEditing( editor, plugin, testStub, formatSpy ); // eslint-disable-line no-new
+			inlineAutoformatEditing( editor, plugin, testStub, formatSpy );
 
 			setData( model, '<paragraph>*[]</paragraph>' );
 			model.change( writer => {
@@ -108,7 +108,7 @@ describe( 'InlineAutoformatEditing', () => {
 				remove: [ [] ]
 			} );
 
-			new InlineAutoformatEditing( editor, plugin, testStub, formatSpy ); // eslint-disable-line no-new
+			inlineAutoformatEditing( editor, plugin, testStub, formatSpy );
 
 			setData( model, '<paragraph>[]</paragraph>' );
 			model.change( writer => {
@@ -120,7 +120,7 @@ describe( 'InlineAutoformatEditing', () => {
 
 		it( 'should not run callback when the pattern is matched and plugin is disabled', () => {
 			const callbackSpy = testUtils.sinon.spy().named( 'callback' );
-			new InlineAutoformatEditing( editor, plugin, /(\*)(.+?)(\*)/g, callbackSpy ); // eslint-disable-line no-new
+			inlineAutoformatEditing( editor, plugin, /(\*)(.+?)(\*)/g, callbackSpy );
 
 			plugin.isEnabled = false;
 
@@ -144,7 +144,7 @@ describe( 'InlineAutoformatEditing', () => {
 
 			const formatCallback = () => false;
 
-			new InlineAutoformatEditing( editor, plugin, testCallback, formatCallback ); // eslint-disable-line no-new
+			inlineAutoformatEditing( editor, plugin, testCallback, formatCallback );
 
 			model.change( writer => {
 				writer.insertText( ' ', doc.selection.getFirstPosition() );
@@ -155,7 +155,7 @@ describe( 'InlineAutoformatEditing', () => {
 	} );
 
 	it( 'should ignore transparent batches', () => {
-		new InlineAutoformatEditing( editor, plugin, /(\*)(.+?)(\*)/g, 'testAttribute' ); // eslint-disable-line no-new
+		inlineAutoformatEditing( editor, plugin, /(\*)(.+?)(\*)/g, 'testAttribute' );
 
 		setData( model, '<paragraph>*foobar[]</paragraph>' );
 		model.enqueueChange( 'transparent', writer => {