From cafe8f44a41eeebda4b414c414ee15ad1ed8eef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Wytr=C4=99bowicz?= Date: Fri, 29 May 2020 20:27:53 +0200 Subject: [PATCH] Replace *AutoformatEditing trivial classes with simple functions, as suggested at https://github.com/ckeditor/ckeditor5/issues/7290#issuecomment-633892533. --- .../docs/features/autoformat.md | 2 +- .../ckeditor5-autoformat/src/autoformat.js | 41 +-- .../src/blockautoformatediting.js | 169 +++++---- .../src/inlineautoformatediting.js | 332 +++++++++--------- .../tests/blockautoformatediting.js | 26 +- .../tests/inlineautoformatediting.js | 22 +- 6 files changed, 285 insertions(+), 307 deletions(-) diff --git a/packages/ckeditor5-autoformat/docs/features/autoformat.md b/packages/ckeditor5-autoformat/docs/features/autoformat.md index c61fbd7e7cc..5ea2b276186 100644 --- a/packages/ckeditor5-autoformat/docs/features/autoformat.md +++ b/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. diff --git a/packages/ckeditor5-autoformat/src/autoformat.js b/packages/ckeditor5-autoformat/src/autoformat.js index 6fd4a01636b..b66e4392f79 100644 --- a/packages/ckeditor5-autoformat/src/autoformat.js +++ b/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 diff --git a/packages/ckeditor5-autoformat/src/blockautoformatediting.js b/packages/ckeditor5-autoformat/src/blockautoformatediting.js index d14decedfd9..79fe176e905 100644 --- a/packages/ckeditor5-autoformat/src/blockautoformatediting.js +++ b/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(); } ); - } + } ); } diff --git a/packages/ckeditor5-autoformat/src/inlineautoformatediting.js b/packages/ckeditor5-autoformat/src/inlineautoformatediting.js index 5afd3f51218..aa86857f427 100644 --- a/packages/ckeditor5-autoformat/src/inlineautoformatediting.js +++ b/packages/ckeditor5-autoformat/src/inlineautoformatediting.js @@ -3,10 +3,6 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -/** - * @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). @@ -14,193 +10,193 @@ * 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 diff --git a/packages/ckeditor5-autoformat/tests/blockautoformatediting.js b/packages/ckeditor5-autoformat/tests/blockautoformatediting.js index 25d4afc060e..72b9c80f42c 100644 --- a/packages/ckeditor5-autoformat/tests/blockautoformatediting.js +++ b/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, '*[]' ); @@ -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, '*[]' ); @@ -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, '*[]' ); @@ -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, '*[]' ); 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, '*[]' ); 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, '[]' ); 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, '*[]' ); 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, '* []' ); @@ -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, '*[]' ); 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, '*[]' ); model.enqueueChange( 'transparent', writer => { diff --git a/packages/ckeditor5-autoformat/tests/inlineautoformatediting.js b/packages/ckeditor5-autoformat/tests/inlineautoformatediting.js index e06ac9e086c..5ee1839e715 100644 --- a/packages/ckeditor5-autoformat/tests/inlineautoformatediting.js +++ b/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, '*foobar[]' ); 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, '*foobar[]' ); 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, '*foob[ar]' ); 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, '*[]' ); 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, '*[]' ); 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, '[]' ); 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, '*foobar[]' ); model.enqueueChange( 'transparent', writer => {