@ckeditor/ckeditor5-autoformat
Advanced tools
Comparing version 0.5.1 to 0.6.0
Changelog | ||
========= | ||
## [0.6.0](https://github.com/ckeditor/ckeditor5-autoformat/compare/v0.5.1...v0.6.0) (2017-09-03) | ||
### Bug fixes | ||
* The `Autoformat` plugin should not require other features. Closes [#5](https://github.com/ckeditor/ckeditor5-autoformat/issues/5) and [#17](https://github.com/ckeditor/ckeditor5-autoformat/issues/17). ([d22c5b6](https://github.com/ckeditor/ckeditor5-autoformat/commit/d22c5b6)) | ||
* Autoformatting will not be triggered if the batch with changes is `transparent` (e.g. it represents other user's changes). ([f1131bc](https://github.com/ckeditor/ckeditor5-autoformat/commit/f1131bc)) | ||
### Features | ||
* Added support for block quotes. Closes [#26](https://github.com/ckeditor/ckeditor5-autoformat/issues/26). ([4c1e83e](https://github.com/ckeditor/ckeditor5-autoformat/commit/4c1e83e)) | ||
### Other changes | ||
* Aligned the implementation to the new Command API (see https://github.com/ckeditor/ckeditor5-core/issues/88). ([f20ef7d](https://github.com/ckeditor/ckeditor5-autoformat/commit/f20ef7d)) | ||
* The autoformat feature will not depend on the configuration of the heading feature but it will use the available `heading*` commands. Closes [#29](https://github.com/ckeditor/ckeditor5-autoformat/issues/29). ([d0cee1f](https://github.com/ckeditor/ckeditor5-autoformat/commit/d0cee1f)) | ||
### BREAKING CHANGES | ||
* The command API has been changed. | ||
### NOTE | ||
* The Autoformat feature doesn't require Bold, Italic, Heading, etc. any longer. In order to make the most of the plugin, please make sure that relevant features are loaded in your editor. | ||
## [0.5.1](https://github.com/ckeditor/ckeditor5-autoformat/compare/v0.5.0...v0.5.1) (2017-05-07) | ||
@@ -5,0 +30,0 @@ |
@@ -6,3 +6,3 @@ /** | ||
/* jshint browser: false, node: true, strict: true */ | ||
/* eslint-env node */ | ||
@@ -12,3 +12,4 @@ 'use strict'; | ||
const gulp = require( 'gulp' ); | ||
const ckeditor5Lint = require( '@ckeditor/ckeditor5-dev-lint' )( { | ||
const ckeditor5Lint = require( '@ckeditor/ckeditor5-dev-lint' ); | ||
const options = { | ||
// Files ignored by `gulp lint` task. | ||
@@ -19,6 +20,6 @@ // Files from .gitignore will be added automatically during task execution. | ||
] | ||
} ); | ||
}; | ||
gulp.task( 'lint', ckeditor5Lint.lint ); | ||
gulp.task( 'lint-staged', ckeditor5Lint.lintStaged ); | ||
gulp.task( 'lint', () => ckeditor5Lint.lint( options ) ); | ||
gulp.task( 'lint-staged', () => ckeditor5Lint.lintStaged( options ) ); | ||
gulp.task( 'pre-commit', [ 'lint-staged' ] ); |
{ | ||
"name": "@ckeditor/ckeditor5-autoformat", | ||
"version": "0.5.1", | ||
"version": "0.6.0", | ||
"description": "Replaces predefined characters with corresponding structures.", | ||
"keywords": [], | ||
"dependencies": { | ||
"@ckeditor/ckeditor5-basic-styles": "^0.8.1", | ||
"@ckeditor/ckeditor5-core": "^0.8.1", | ||
"@ckeditor/ckeditor5-engine": "^0.10.0", | ||
"@ckeditor/ckeditor5-heading": "^0.9.1", | ||
"@ckeditor/ckeditor5-list": "^0.6.1" | ||
"@ckeditor/ckeditor5-core": "^0.9.0", | ||
"@ckeditor/ckeditor5-engine": "^0.11.0" | ||
}, | ||
"devDependencies": { | ||
"@ckeditor/ckeditor5-dev-lint": "^2.0.2", | ||
"@ckeditor/ckeditor5-editor-classic": "^0.7.3", | ||
"@ckeditor/ckeditor5-enter": "^0.9.1", | ||
"@ckeditor/ckeditor5-list": "*", | ||
"@ckeditor/ckeditor5-paragraph": "^0.8.0", | ||
"@ckeditor/ckeditor5-typing": "^0.9.1", | ||
"@ckeditor/ckeditor5-undo": "^0.8.1", | ||
"gulp": "^3.9.0", | ||
"@ckeditor/ckeditor5-basic-styles": "^0.9.0", | ||
"@ckeditor/ckeditor5-dev-lint": "^3.1.0", | ||
"@ckeditor/ckeditor5-block-quote": "^0.2.0", | ||
"@ckeditor/ckeditor5-editor-classic": "^0.8.0", | ||
"@ckeditor/ckeditor5-enter": "^0.10.0", | ||
"@ckeditor/ckeditor5-heading": "^0.10.0", | ||
"@ckeditor/ckeditor5-list": "^0.7.0", | ||
"@ckeditor/ckeditor5-paragraph": "^0.9.0", | ||
"@ckeditor/ckeditor5-typing": "^0.10.0", | ||
"@ckeditor/ckeditor5-undo": "^0.9.0", | ||
"eslint-config-ckeditor5": "^1.0.5", | ||
"gulp": "^3.9.1", | ||
"guppy-pre-commit": "^0.4.0" | ||
@@ -23,0 +24,0 @@ }, |
@@ -1,4 +0,5 @@ | ||
ckeditor5-autoformat | ||
CKEditor 5 autoformat feature | ||
======================================== | ||
[![Join the chat at https://gitter.im/ckeditor/ckeditor5](https://badges.gitter.im/ckeditor/ckeditor5.svg)](https://gitter.im/ckeditor/ckeditor5?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
[![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-autoformat.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-autoformat) | ||
@@ -5,0 +6,0 @@ [![Build Status](https://travis-ci.org/ckeditor/ckeditor5-autoformat.svg?branch=master)](https://travis-ci.org/ckeditor/ckeditor5-autoformat) |
@@ -13,39 +13,7 @@ /** | ||
import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; | ||
import HeadingEngine from '@ckeditor/ckeditor5-heading/src/headingengine'; | ||
import ListEngine from '@ckeditor/ckeditor5-list/src/listengine'; | ||
import BoldEngine from '@ckeditor/ckeditor5-basic-styles/src/boldengine'; | ||
import ItalicEngine from '@ckeditor/ckeditor5-basic-styles/src/italicengine'; | ||
/** | ||
* Includes a set of predefined autoformatting actions. | ||
* Includes a set of predefined autoformatting actions. For a detailed overview, check | ||
* the {@glink features/autoformat Autoformatting feature documentation}. | ||
* | ||
* ## Bulleted list | ||
* | ||
* You can create a bulleted list by staring a line with: | ||
* | ||
* * `* ` | ||
* * `- ` | ||
* | ||
* ## Numbered list | ||
* | ||
* You can create a numbered list by staring a line with: | ||
* | ||
* * `1. ` | ||
* * `1) ` | ||
* | ||
* ## Headings | ||
* | ||
* You can create a heading by starting a line with: | ||
* | ||
* `# ` – will create Heading 1, | ||
* `## ` – will create Heading 2, | ||
* `### ` – will create Heading 3. | ||
* | ||
* ## Bold and italic | ||
* | ||
* You can apply bold or italic to a text by typing Markdown formatting: | ||
* | ||
* * `**foo bar**` or `__foo bar__` – will bold the text, | ||
* * `*foo bar*` or `_foo bar_` – will italicize the text, | ||
* | ||
* @extends module:core/plugin~Plugin | ||
@@ -57,11 +25,4 @@ */ | ||
*/ | ||
static get requires() { | ||
return [ HeadingEngine, ListEngine, BoldEngine, ItalicEngine ]; | ||
} | ||
/** | ||
* @inheritDoc | ||
*/ | ||
static get pluginName() { | ||
return 'autoformat/autoformat'; | ||
return 'Autoformat'; | ||
} | ||
@@ -72,14 +33,15 @@ | ||
*/ | ||
init() { | ||
afterInit() { | ||
this._addListAutoformats(); | ||
this._addBasicStylesAutoformats(); | ||
this._addHeadingAutoformats(); | ||
this._addInlineAutoformats(); | ||
this._addBlockQuoteAutoformats(); | ||
} | ||
/** | ||
* Adds autoformatting related to ListEngine commands. | ||
* Adds autoformatting related to the {@link module:list/list~List}. | ||
* | ||
* When typed: | ||
* - `* ` or `- ` - paragraph will be changed to a bulleted list, | ||
* - `1. ` or `1) ` - paragraph will be changed to a numbered list (1 can be any digit or list of digits). | ||
* - `* ` or `- ` – A paragraph will be changed to a bulleted list. | ||
* - `1. ` or `1) ` – A paragraph will be changed to a numbered list ("1" can be any digit or a list of digits). | ||
* | ||
@@ -89,41 +51,89 @@ * @private | ||
_addListAutoformats() { | ||
new BlockAutoformatEngine( this.editor, /^[\*\-]\s$/, 'bulletedList' ); | ||
new BlockAutoformatEngine( this.editor, /^\d+[\.|)]?\s$/, 'numberedList' ); | ||
const commands = this.editor.commands; | ||
if ( commands.get( 'bulletedList' ) ) { | ||
// eslint-disable-next-line no-new | ||
new BlockAutoformatEngine( this.editor, /^[*-]\s$/, 'bulletedList' ); | ||
} | ||
if ( commands.get( 'numberedList' ) ) { | ||
// eslint-disable-next-line no-new | ||
new BlockAutoformatEngine( this.editor, /^\d+[.|)]?\s$/, 'numberedList' ); | ||
} | ||
} | ||
/** | ||
* Adds autoformatting related to HeadingEngine commands. | ||
* When typed `# ` or `## ` or `### ` paragraph will be changed to a corresponding heading level. | ||
* Adds autoformatting related to the {@link module:basic-styles/bold~Bold} and | ||
* {@link module:basic-styles/italic~Italic}. | ||
* | ||
* When typed: | ||
* - `**foobar**` – `**` characters are removed and `foobar` is set to bold. | ||
* - `__foobar__` – `__` characters are removed and `foobar` is set to bold. | ||
* - `*foobar*` – `*` characters are removed and `foobar` is set to italic. | ||
* - `_foobar_` – `_` characters are removed and `foobar` is set to italic. | ||
* | ||
* @private | ||
*/ | ||
_addBasicStylesAutoformats() { | ||
const commands = this.editor.commands; | ||
if ( commands.get( 'bold' ) ) { | ||
/* eslint-disable no-new */ | ||
new InlineAutoformatEngine( this.editor, /(\*\*)([^*]+)(\*\*)$/g, 'bold' ); | ||
new InlineAutoformatEngine( this.editor, /(__)([^_]+)(__)$/g, 'bold' ); | ||
/* eslint-enable no-new */ | ||
} | ||
if ( commands.get( 'italic' ) ) { | ||
// The italic autoformatter cannot be triggered by the bold markers, so we need to check the | ||
// text before the pattern (e.g. `(?:^|[^\*])`). | ||
/* eslint-disable no-new */ | ||
new InlineAutoformatEngine( this.editor, /(?:^|[^*])(\*)([^*_]+)(\*)$/g, 'italic' ); | ||
new InlineAutoformatEngine( this.editor, /(?:^|[^_])(_)([^_]+)(_)$/g, 'italic' ); | ||
/* eslint-enable no-new */ | ||
} | ||
} | ||
/** | ||
* Adds autoformatting related to {@link module:heading/heading~Heading}. | ||
* | ||
* It is using a number at the end of the command name to associate it with the proper trigger: | ||
* | ||
* * `heading1` will be executed when typing `#`, | ||
* * `heading2` will be executed when typing `##`, | ||
* * ... up to `heading6` and `######`. | ||
* | ||
* @private | ||
*/ | ||
_addHeadingAutoformats() { | ||
new BlockAutoformatEngine( this.editor, /^(#{1,3})\s$/, ( context ) => { | ||
const { batch, match } = context; | ||
const headingLevel = match[ 1 ].length; | ||
Array.from( this.editor.commands.names() ) | ||
.filter( name => name.match( /^heading[1-6]$/ ) ) | ||
.forEach( commandName => { | ||
const level = commandName[ 7 ]; | ||
const pattern = new RegExp( `^(#{${ level }})\\s$` ); | ||
this.editor.execute( `heading${ headingLevel }`, { batch } ); | ||
} ); | ||
// eslint-disable-next-line no-new | ||
new BlockAutoformatEngine( this.editor, pattern, context => { | ||
const { batch } = context; | ||
this.editor.execute( commandName, { batch } ); | ||
} ); | ||
} ); | ||
} | ||
/** | ||
* Adds inline autoformatting capabilities to the editor. | ||
* Adds autoformatting related to {@link module:block-quote/blockquote~BlockQuote}. | ||
* | ||
* When typed: | ||
* - `**foobar**`: `**` characters are removed, and `foobar` is set to bold, | ||
* - `__foobar__`: `__` characters are removed, and `foobar` is set to bold, | ||
* - `*foobar*`: `*` characters are removed, and `foobar` is set to italic, | ||
* - `_foobar_`: `_` characters are removed, and `foobar` is set to italic. | ||
* * `> ` – A paragraph will be changed to a block quote. | ||
* | ||
* @private | ||
*/ | ||
_addInlineAutoformats() { | ||
new InlineAutoformatEngine( this.editor, /(\*\*)([^\*]+)(\*\*)$/g, 'bold' ); | ||
new InlineAutoformatEngine( this.editor, /(__)([^_]+)(__)$/g, 'bold' ); | ||
// The italic autoformatter cannot be triggered by the bold markers, so we need to check the | ||
// text before the pattern (e.g. `(?:^|[^\*])`). | ||
new InlineAutoformatEngine( this.editor, /(?:^|[^\*])(\*)([^\*_]+)(\*)$/g, 'italic' ); | ||
new InlineAutoformatEngine( this.editor, /(?:^|[^_])(_)([^_]+)(_)$/g, 'italic' ); | ||
_addBlockQuoteAutoformats() { | ||
if ( this.editor.commands.get( 'blockQuote' ) ) { | ||
// eslint-disable-next-line no-new | ||
new BlockAutoformatEngine( this.editor, /^>\s$/, 'blockQuote' ); | ||
} | ||
} | ||
} |
@@ -14,7 +14,7 @@ /** | ||
/** | ||
* The block autoformatting engine. Allows to format various block patterns. For example, | ||
* it can be configured to make a paragraph starting with "* " a list item. | ||
* The block autoformatting engine. It allows to format various block patterns. For example, | ||
* it can be configured to turn a paragraph starting with `*` and followed by a space into a list item. | ||
* | ||
* The autoformatting operation is integrated with the undo manager, | ||
* so the autoformatting step can be undone, if the user's intention wasn't to format the text. | ||
* so the autoformatting step can be undone if the user's intention was not to format the text. | ||
* | ||
@@ -27,13 +27,13 @@ * See the constructors documentation to learn how to create custom inline autoformatters. You can also use | ||
/** | ||
* Creates listener triggered on `change` event in document. | ||
* Calls callback when inserted text matches regular expression or command name | ||
* if provided instead of callback. | ||
* 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 paragraph to heading1 when `- ` is typed, using just commmand name: | ||
* To convert a paragraph to heading 1 when `- ` is typed, using just the commmand name: | ||
* | ||
* new BlockAutoformatEngine( editor, /^\- $/, 'heading1' ); | ||
* | ||
* To convert paragraph to heading1 when `- ` is typed, using just callback: | ||
* To convert a paragraph to heading 1 when `- ` is typed, using just the callback: | ||
* | ||
@@ -50,8 +50,8 @@ * new BlockAutoformatEngine( editor, /^\- $/, ( context ) => { | ||
* | ||
* @param {module:core/editor/editor~Editor} editor Editor instance. | ||
* @param {RegExp} pattern Regular expression to exec on just inserted text. | ||
* @param {Function|String} callbackOrCommand Callback to execute or command to run when text is matched. | ||
* In case of providing callback it receives following parameters: | ||
* @param {module:core/editor/editor~Editor} editor The editor 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 parameters: | ||
* * {module:engine/model/batch~Batch} batch Newly created batch for autoformat changes. | ||
* * {Object} match RegExp.exec() result of matching pattern to inserted text. | ||
* * {Object} match RegExp.exec() result of matching the pattern to inserted text. | ||
*/ | ||
@@ -67,3 +67,3 @@ constructor( editor, pattern, callbackOrCommand ) { | ||
callback = ( context ) => { | ||
callback = context => { | ||
const { batch } = context; | ||
@@ -76,3 +76,7 @@ | ||
editor.document.on( 'change', ( event, type, changes ) => { | ||
editor.document.on( 'change', ( event, type, changes, batch ) => { | ||
if ( batch.type == 'transparent' ) { | ||
return; | ||
} | ||
if ( type != 'insert' ) { | ||
@@ -106,3 +110,3 @@ return; | ||
// Create new batch to separate typing batch from the Autoformat changes. | ||
const batch = editor.document.batch(); | ||
const fixBatch = editor.document.batch(); | ||
@@ -113,5 +117,5 @@ // Matched range. | ||
// Remove matched text. | ||
batch.remove( range ); | ||
fixBatch.remove( range ); | ||
callback( { batch, match } ); | ||
callback( { fixBatch, match } ); | ||
} ); | ||
@@ -118,0 +122,0 @@ } ); |
@@ -11,10 +11,9 @@ /** | ||
import LiveRange from '@ckeditor/ckeditor5-engine/src/model/liverange'; | ||
import getSchemaValidRanges from '@ckeditor/ckeditor5-core/src/command/helpers/getschemavalidranges'; | ||
/** | ||
* The inline autoformatting engine. Allows to format various inline patterns. For example, | ||
* 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 wasn't to format the text. | ||
* so the autoformatting step can be undone if the user's intention was not to format the text. | ||
* | ||
@@ -27,27 +26,27 @@ * See the constructors documentation to learn how to create custom inline autoformatters. You can also use | ||
/** | ||
* Enables autoformatting mechanism on a given {@link module:core/editor/editor~Editor}. | ||
* Enables autoformatting mechanism for a given {@link module:core/editor/editor~Editor}. | ||
* | ||
* It formats the matched text by applying given model attribute or by running the provided formatting callback. | ||
* Each time data model changes text from given node (from the beginning of the current node to the collapsed | ||
* selection location) will be tested. | ||
* 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 Editor instance. | ||
* @param {Function|RegExp} testRegexpOrCallback RegExp or callback to execute on text. | ||
* Provided RegExp *must* have three capture groups. First and third capture groups | ||
* should match opening/closing delimiters. Second capture group should match text to format. | ||
* @param {module:core/editor/editor~Editor} editor The editor 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 `**bold text**` pattern. | ||
* // Matches the `**bold text**` pattern. | ||
* // There are three capturing groups: | ||
* // - first to match starting `**` delimiter, | ||
* // - second to match text to format, | ||
* // - third to match ending `**` delimiter. | ||
* new InlineAutoformatEngine( this.editor, /(\*\*)([^\*]+?)(\*\*)$/g, 'bold' ); | ||
* // - The first to match the starting `**` delimiter. | ||
* // - The second to match the text to format. | ||
* // - The third to match the ending `**` delimiter. | ||
* new InlineAutoformatEngine( editor, /(\*\*)([^\*]+?)(\*\*)$/g, 'bold' ); | ||
* | ||
* When function is provided instead of RegExp, it will be executed with text to match as a parameter. Function | ||
* should return proper "ranges" to delete and format. | ||
* 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 first letter from the given text. | ||
* [ 5, 6 ] // Remove 6th letter from the given text. | ||
* [ 0, 1 ], // Remove the first letter from the given text. | ||
* [ 5, 6 ] // Remove the 6th letter from the given text. | ||
* ], | ||
@@ -59,10 +58,10 @@ * format: [ | ||
* | ||
* @param {Function|String} attributeOrCallback Name of attribute to apply on matching text or callback for manual | ||
* @param {Function|String} attributeOrCallback The name of attribute to apply on matching text or a callback for manual | ||
* formatting. | ||
* | ||
* // Use attribute name: | ||
* new InlineAutoformatEngine( this.editor, /(\*\*)([^\*]+?)(\*\*)$/g, 'bold' ); | ||
* new InlineAutoformatEngine( editor, /(\*\*)([^\*]+?)(\*\*)$/g, 'bold' ); | ||
* | ||
* // Use formatting callback: | ||
* new InlineAutoformatEngine( this.editor, /(\*\*)([^\*]+?)(\*\*)$/g, ( batch, validRanges ) => { | ||
* new InlineAutoformatEngine( editor, /(\*\*)([^\*]+?)(\*\*)$/g, ( batch, validRanges ) => { | ||
* for ( let range of validRanges ) { | ||
@@ -74,4 +73,2 @@ * batch.setAttribute( range, command, true ); | ||
constructor( editor, testRegexpOrCallback, attributeOrCallback ) { | ||
this.editor = editor; | ||
let regExp; | ||
@@ -95,6 +92,6 @@ let command; | ||
// A test callback run on changed text. | ||
testCallback = testCallback || ( ( text ) => { | ||
testCallback = testCallback || ( text => { | ||
let result; | ||
let remove = []; | ||
let format = []; | ||
const remove = []; | ||
const format = []; | ||
@@ -142,3 +139,3 @@ while ( ( result = regExp.exec( text ) ) !== null ) { | ||
formatCallback = formatCallback || ( ( batch, validRanges ) => { | ||
for ( let range of validRanges ) { | ||
for ( const range of validRanges ) { | ||
batch.setAttribute( range, command, true ); | ||
@@ -148,3 +145,7 @@ } | ||
editor.document.on( 'change', ( evt, type ) => { | ||
editor.document.on( 'change', ( evt, type, changes, batch ) => { | ||
if ( batch.type == 'transparent' ) { | ||
return; | ||
} | ||
if ( type !== 'insert' ) { | ||
@@ -154,3 +155,3 @@ return; | ||
const selection = this.editor.document.selection; | ||
const selection = editor.document.selection; | ||
@@ -167,3 +168,3 @@ if ( !selection.isCollapsed || !selection.focus || !selection.focus.parent ) { | ||
// Apply format before deleting text. | ||
ranges.format.forEach( ( range ) => { | ||
ranges.format.forEach( range => { | ||
if ( range[ 0 ] === undefined || range[ 1 ] === undefined ) { | ||
@@ -182,3 +183,3 @@ return; | ||
// Reverse order to not mix the offsets while removing. | ||
ranges.remove.slice().reverse().forEach( ( range ) => { | ||
ranges.remove.slice().reverse().forEach( range => { | ||
if ( range[ 0 ] === undefined || range[ 1 ] === undefined ) { | ||
@@ -198,13 +199,14 @@ return; | ||
const batch = editor.document.batch(); | ||
editor.document.enqueueChanges( () => { | ||
const validRanges = getSchemaValidRanges( command, rangesToFormat, editor.document.schema ); | ||
// Create new batch to separate typing batch from the Autoformat changes. | ||
const fixBatch = editor.document.batch(); | ||
const validRanges = editor.document.schema.getValidRanges( rangesToFormat, command ); | ||
// Apply format. | ||
formatCallback( batch, validRanges ); | ||
formatCallback( fixBatch, validRanges ); | ||
// Remove delimiters. | ||
for ( let range of rangesToRemove ) { | ||
batch.remove( range ); | ||
for ( const range of rangesToRemove ) { | ||
fixBatch.remove( range ); | ||
} | ||
@@ -211,0 +213,0 @@ } ); |
@@ -7,8 +7,18 @@ /** | ||
import Autoformat from '../src/autoformat'; | ||
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; | ||
import ListEngine from '@ckeditor/ckeditor5-list/src/listengine'; | ||
import HeadingEngine from '@ckeditor/ckeditor5-heading/src/headingengine'; | ||
import BoldEngine from '@ckeditor/ckeditor5-basic-styles/src/boldengine'; | ||
import ItalicEngine from '@ckeditor/ckeditor5-basic-styles/src/italicengine'; | ||
import BlockQuoteEngine from '@ckeditor/ckeditor5-block-quote/src/blockquoteengine'; | ||
import Enter from '@ckeditor/ckeditor5-enter/src/enter'; | ||
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'; | ||
import Command from '@ckeditor/ckeditor5-core/src/command'; | ||
testUtils.createSinonSandbox(); | ||
@@ -20,12 +30,17 @@ | ||
beforeEach( () => { | ||
return VirtualTestEditor.create( { | ||
plugins: [ Enter, Paragraph, Autoformat ] | ||
} ) | ||
.then( newEditor => { | ||
editor = newEditor; | ||
doc = editor.document; | ||
batch = doc.batch(); | ||
} ); | ||
return VirtualTestEditor | ||
.create( { | ||
plugins: [ Enter, Paragraph, Autoformat, ListEngine, HeadingEngine, BoldEngine, ItalicEngine, BlockQuoteEngine ] | ||
} ) | ||
.then( newEditor => { | ||
editor = newEditor; | ||
doc = editor.document; | ||
batch = doc.batch(); | ||
} ); | ||
} ); | ||
afterEach( () => { | ||
return editor.destroy(); | ||
} ); | ||
describe( 'Bulleted list', () => { | ||
@@ -99,3 +114,3 @@ it( 'should replace asterisk with bulleted list item', () => { | ||
it( 'should not replace minus character when inside heading', () => { | ||
it( 'should not replace hash character when inside heading', () => { | ||
setData( doc, '<heading1>#[]</heading1>' ); | ||
@@ -108,4 +123,89 @@ doc.enqueueChanges( () => { | ||
} ); | ||
it( 'should work with heading1-heading6 commands regardless of the config of the heading feature', () => { | ||
const spy1 = sinon.spy(); | ||
const spy6 = sinon.spy(); | ||
class Heading6 extends Command { | ||
execute() { | ||
spy6(); | ||
} | ||
} | ||
class Heading1 extends Command { | ||
execute() { | ||
spy1(); | ||
} | ||
} | ||
function HeadingPlugin( editor ) { | ||
editor.commands.add( 'heading1', new Heading1( editor ) ); | ||
editor.commands.add( 'heading6', new Heading6( editor ) ); | ||
} | ||
return VirtualTestEditor | ||
.create( { | ||
plugins: [ | ||
Paragraph, Autoformat, HeadingPlugin | ||
] | ||
} ) | ||
.then( editor => { | ||
const doc = editor.document; | ||
setData( doc, '<paragraph>#[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
doc.batch().insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
expect( spy1.calledOnce ).to.be.true; | ||
setData( doc, '<paragraph>######[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
doc.batch().insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
expect( spy6.calledOnce ).to.be.true; | ||
return editor.destroy(); | ||
} ); | ||
} ); | ||
} ); | ||
describe( 'Block quote', () => { | ||
it( 'should replace greater-than character with heading', () => { | ||
setData( doc, '<paragraph>>[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
batch.insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<blockQuote><paragraph>[]</paragraph></blockQuote>' ); | ||
} ); | ||
it( 'should not replace greater-than character when inside heading', () => { | ||
setData( doc, '<heading1>>[]</heading1>' ); | ||
doc.enqueueChanges( () => { | ||
batch.insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<heading1>> []</heading1>' ); | ||
} ); | ||
it( 'should not replace greater-than character when inside numbered list', () => { | ||
setData( doc, '<listItem indent="0" type="numbered">1. >[]</listItem>' ); | ||
doc.enqueueChanges( () => { | ||
batch.insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<listItem indent="0" type="numbered">1. > []</listItem>' ); | ||
} ); | ||
it( 'should not replace greater-than character when inside buletted list', () => { | ||
setData( doc, '<listItem indent="0" type="bulleted">1. >[]</listItem>' ); | ||
doc.enqueueChanges( () => { | ||
batch.insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<listItem indent="0" type="bulleted">1. > []</listItem>' ); | ||
} ); | ||
} ); | ||
describe( 'Inline autoformat', () => { | ||
@@ -148,2 +248,112 @@ it( 'should replace both `**` with bold', () => { | ||
} ); | ||
describe( 'without commands', () => { | ||
beforeEach( () => { | ||
return VirtualTestEditor | ||
.create( { | ||
plugins: [ Enter, Paragraph, Autoformat ] | ||
} ) | ||
.then( newEditor => { | ||
editor = newEditor; | ||
doc = editor.document; | ||
batch = doc.batch(); | ||
} ); | ||
} ); | ||
it( 'should not replace asterisk with bulleted list item', () => { | ||
setData( doc, '<paragraph>*[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
batch.insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<paragraph>* []</paragraph>' ); | ||
} ); | ||
it( 'should not replace minus character with bulleted list item', () => { | ||
setData( doc, '<paragraph>-[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
batch.insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<paragraph>- []</paragraph>' ); | ||
} ); | ||
it( 'should not replace digit with numbered list item', () => { | ||
setData( doc, '<paragraph>1.[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
batch.insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<paragraph>1. []</paragraph>' ); | ||
} ); | ||
it( 'should not replace hash character with heading', () => { | ||
setData( doc, '<paragraph>#[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
batch.insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<paragraph># []</paragraph>' ); | ||
} ); | ||
it( 'should not replace two hash characters with heading level 2', () => { | ||
setData( doc, '<paragraph>##[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
batch.insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<paragraph>## []</paragraph>' ); | ||
} ); | ||
it( 'should not replace both `**` with bold', () => { | ||
setData( doc, '<paragraph>**foobar*[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
batch.insert( doc.selection.getFirstPosition(), '*' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<paragraph>**foobar**[]</paragraph>' ); | ||
} ); | ||
it( 'should not replace both `*` with italic', () => { | ||
setData( doc, '<paragraph>*foobar[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
batch.insert( doc.selection.getFirstPosition(), '*' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<paragraph>*foobar*[]</paragraph>' ); | ||
} ); | ||
it( 'should not replace `>` with block quote', () => { | ||
setData( doc, '<paragraph>>[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
batch.insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<paragraph>> []</paragraph>' ); | ||
} ); | ||
it( 'should use only configured headings', () => { | ||
return VirtualTestEditor | ||
.create( { | ||
plugins: [ Enter, Paragraph, Autoformat, ListEngine, HeadingEngine ], | ||
heading: { | ||
options: [ | ||
{ modelElement: 'paragraph' }, | ||
{ modelElement: 'heading1', viewElement: 'h2' } | ||
] | ||
} | ||
} ) | ||
.then( editor => { | ||
doc = editor.document; | ||
batch = doc.batch(); | ||
setData( doc, '<paragraph>##[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
batch.insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<paragraph>## []</paragraph>' ); | ||
} ); | ||
} ); | ||
} ); | ||
} ); |
@@ -12,3 +12,3 @@ /** | ||
import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; | ||
import Command from '@ckeditor/ckeditor5-core/src/command/command'; | ||
import Command from '@ckeditor/ckeditor5-core/src/command'; | ||
@@ -21,10 +21,11 @@ testUtils.createSinonSandbox(); | ||
beforeEach( () => { | ||
return VirtualTestEditor.create( { | ||
plugins: [ Enter, Paragraph ] | ||
} ) | ||
.then( newEditor => { | ||
editor = newEditor; | ||
doc = editor.document; | ||
batch = doc.batch(); | ||
} ); | ||
return VirtualTestEditor | ||
.create( { | ||
plugins: [ Enter, Paragraph ] | ||
} ) | ||
.then( newEditor => { | ||
editor = newEditor; | ||
doc = editor.document; | ||
batch = doc.batch(); | ||
} ); | ||
} ); | ||
@@ -35,4 +36,4 @@ | ||
const spy = testUtils.sinon.spy(); | ||
editor.commands.set( 'testCommand', new TestCommand( editor, spy ) ); | ||
new BlockAutoformatEngine( editor, /^[\*]\s$/, 'testCommand' ); | ||
editor.commands.add( 'testCommand', new TestCommand( editor, spy ) ); | ||
new BlockAutoformatEngine( editor, /^[*]\s$/, 'testCommand' ); // eslint-disable-line no-new | ||
@@ -47,6 +48,19 @@ setData( doc, '<paragraph>*[]</paragraph>' ); | ||
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( doc, '<paragraph>*[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
doc.batch( 'transparent' ).insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
sinon.assert.notCalled( spy ); | ||
} ); | ||
it( 'should remove found pattern', () => { | ||
const spy = testUtils.sinon.spy(); | ||
editor.commands.set( 'testCommand', new TestCommand( editor, spy ) ); | ||
new BlockAutoformatEngine( editor, /^[\*]\s$/, 'testCommand' ); | ||
editor.commands.add( 'testCommand', new TestCommand( editor, spy ) ); | ||
new BlockAutoformatEngine( editor, /^[*]\s$/, 'testCommand' ); // eslint-disable-line no-new | ||
@@ -66,3 +80,3 @@ setData( doc, '<paragraph>*[]</paragraph>' ); | ||
const spy = testUtils.sinon.spy(); | ||
new BlockAutoformatEngine( editor, /^[\*]\s$/, spy ); | ||
new BlockAutoformatEngine( editor, /^[*]\s$/, spy ); // eslint-disable-line no-new | ||
@@ -77,5 +91,17 @@ setData( doc, '<paragraph>*[]</paragraph>' ); | ||
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( doc, '<paragraph>*[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
doc.batch( 'transparent' ).insert( doc.selection.getFirstPosition(), ' ' ); | ||
} ); | ||
sinon.assert.notCalled( spy ); | ||
} ); | ||
it( 'should ignore other delta operations', () => { | ||
const spy = testUtils.sinon.spy(); | ||
new BlockAutoformatEngine( editor, /^[\*]\s/, spy ); | ||
new BlockAutoformatEngine( editor, /^[*]\s/, spy ); // eslint-disable-line no-new | ||
@@ -92,3 +118,3 @@ setData( doc, '<paragraph>*[]</paragraph>' ); | ||
const spy = testUtils.sinon.spy(); | ||
new BlockAutoformatEngine( editor, /^[\*]\s/, spy ); | ||
new BlockAutoformatEngine( editor, /^[*]\s/, spy ); // eslint-disable-line no-new | ||
@@ -113,3 +139,3 @@ setData( doc, '<paragraph>[]</paragraph>' ); | ||
* @param {module:core/editor~Editor} editor Editor instance. | ||
* @param {Function} onExecuteCallback _doExecute call hook | ||
* @param {Function} onExecuteCallback execute call hook | ||
*/ | ||
@@ -127,5 +153,5 @@ constructor( editor, onExecuteCallback ) { | ||
*/ | ||
_doExecute() { | ||
execute() { | ||
this.onExecute(); | ||
} | ||
} |
@@ -19,11 +19,12 @@ /** | ||
beforeEach( () => { | ||
return VirtualTestEditor.create( { | ||
plugins: [ Enter, Paragraph ] | ||
} ) | ||
.then( newEditor => { | ||
editor = newEditor; | ||
doc = editor.document; | ||
batch = doc.batch(); | ||
doc.schema.allow( { name: '$inline', attributes: [ 'testAttribute' ] } ); | ||
} ); | ||
return VirtualTestEditor | ||
.create( { | ||
plugins: [ Enter, Paragraph ] | ||
} ) | ||
.then( newEditor => { | ||
editor = newEditor; | ||
doc = editor.document; | ||
batch = doc.batch(); | ||
doc.schema.allow( { name: '$inline', attributes: [ 'testAttribute' ] } ); | ||
} ); | ||
} ); | ||
@@ -33,3 +34,3 @@ | ||
it( 'should stop early if there are less than 3 capture groups', () => { | ||
new InlineAutoformatEngine( editor, /(\*)(.+?)\*/g, 'testAttribute' ); | ||
new InlineAutoformatEngine( editor, /(\*)(.+?)\*/g, 'testAttribute' ); // eslint-disable-line no-new | ||
@@ -45,3 +46,3 @@ setData( doc, '<paragraph>*foobar[]</paragraph>' ); | ||
it( 'should apply an attribute when the pattern is matched', () => { | ||
new InlineAutoformatEngine( editor, /(\*)(.+?)(\*)/g, 'testAttribute' ); | ||
new InlineAutoformatEngine( editor, /(\*)(.+?)(\*)/g, 'testAttribute' ); // eslint-disable-line no-new | ||
@@ -56,4 +57,15 @@ setData( doc, '<paragraph>*foobar[]</paragraph>' ); | ||
it( 'should not apply an attribute when changes are in transparent batch', () => { | ||
new InlineAutoformatEngine( editor, /(\*)(.+?)(\*)/g, 'testAttribute' ); // eslint-disable-line no-new | ||
setData( doc, '<paragraph>*foobar[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
doc.batch( 'transparent' ).insert( doc.selection.getFirstPosition(), '*' ); | ||
} ); | ||
expect( getData( doc ) ).to.equal( '<paragraph>*foobar*[]</paragraph>' ); | ||
} ); | ||
it( 'should stop early if selection is not collapsed', () => { | ||
new InlineAutoformatEngine( editor, /(\*)(.+?)\*/g, 'testAttribute' ); | ||
new InlineAutoformatEngine( editor, /(\*)(.+?)\*/g, 'testAttribute' ); // eslint-disable-line no-new | ||
@@ -70,2 +82,14 @@ setData( doc, '<paragraph>*foob[ar]</paragraph>' ); | ||
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( doc, '<paragraph>*foobar[]</paragraph>' ); | ||
doc.enqueueChanges( () => { | ||
doc.batch( 'transparent' ).insert( doc.selection.getFirstPosition(), '*' ); | ||
} ); | ||
sinon.assert.notCalled( spy ); | ||
} ); | ||
it( 'should stop when there are no format ranges returned from testCallback', () => { | ||
@@ -78,3 +102,3 @@ const formatSpy = testUtils.sinon.spy(); | ||
new InlineAutoformatEngine( editor, testStub, formatSpy ); | ||
new InlineAutoformatEngine( editor, testStub, formatSpy ); // eslint-disable-line no-new | ||
@@ -96,3 +120,3 @@ setData( doc, '<paragraph>*[]</paragraph>' ); | ||
new InlineAutoformatEngine( editor, testStub, formatSpy ); | ||
new InlineAutoformatEngine( editor, testStub, formatSpy ); // eslint-disable-line no-new | ||
@@ -114,3 +138,3 @@ setData( doc, '<paragraph>*[]</paragraph>' ); | ||
new InlineAutoformatEngine( editor, testStub, formatSpy ); | ||
new InlineAutoformatEngine( editor, testStub, formatSpy ); // eslint-disable-line no-new | ||
@@ -117,0 +141,0 @@ setData( doc, '<paragraph>[]</paragraph>' ); |
@@ -8,6 +8,7 @@ /** | ||
import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classic'; | ||
import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor'; | ||
import Autoformat from '../../src/autoformat'; | ||
import Enter from '@ckeditor/ckeditor5-enter/src/enter'; | ||
import List from '@ckeditor/ckeditor5-list/src/list'; | ||
import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote'; | ||
import Typing from '@ckeditor/ckeditor5-typing/src/typing'; | ||
@@ -20,11 +21,12 @@ import Heading from '@ckeditor/ckeditor5-heading/src/heading'; | ||
ClassicEditor.create( document.querySelector( '#editor' ), { | ||
plugins: [ Enter, Typing, Paragraph, Undo, Bold, Italic, Heading, List, Autoformat ], | ||
toolbar: [ 'headings', 'numberedList', 'bulletedList', 'bold', 'italic', 'undo', 'redo' ] | ||
} ) | ||
.then( editor => { | ||
window.editor = editor; | ||
} ) | ||
.catch( err => { | ||
console.error( err.stack ); | ||
} ); | ||
ClassicEditor | ||
.create( document.querySelector( '#editor' ), { | ||
plugins: [ Enter, Typing, Paragraph, Undo, Bold, Italic, Heading, List, Autoformat, BlockQuote ], | ||
toolbar: [ 'headings', 'numberedList', 'bulletedList', 'blockQuote', 'bold', 'italic', 'undo', 'redo' ] | ||
} ) | ||
.then( editor => { | ||
window.editor = editor; | ||
} ) | ||
.catch( err => { | ||
console.error( err.stack ); | ||
} ); |
## Autoformat | ||
1. Type `#` and press space in empty paragraph to replace it with the heading. | ||
1. Type `#` and press the space in an empty paragraph to replace it with a heading. | ||
2. Type `*` or `-` and press space in empty paragraph to replace it with list item. | ||
1. Type `*` or `-` and the press space in an empty paragraph to replace it with a list item. | ||
3. Type number from the range **1-3** to replace empty paragraph with numbered list item. | ||
1. Type `> ` and press the space in an empty paragraph to replace it with a block quote. | ||
4. Type `*foobar*`/`_foobar_` to italicize `foobar`. `*`/`_` should be removed. | ||
1. Type a number from the range **1-3** to replace an empty paragraph with a numbered list item. | ||
5. Type `**foobar**`/`__foobar__` to bold `foobar`. `**`/`__` should be removed. | ||
1. Type `*foobar*`/`_foobar_` to italicize `foobar`. `*`/`_` should be removed. | ||
6. For every autoformat pattern: Undo until you'll see just the pattern (e.g. `- `). Typing should be then possible without triggering autoformatting again. | ||
1. Type `**foobar**`/`__foobar__` to bold `foobar`. `**`/`__` should be removed. | ||
7. Typing a different pattern in already converted block **must not** trigger autoformatting. For example, typing `- ` in heading should not convert heading to list. | ||
1. For every autoformat pattern: Undo until you'll see just the pattern (e.g. `- `). Typing should be then possible without triggering the autoformatting again. | ||
1. Typing a different pattern in an already converted block **must not** trigger the autoformatting. For example, typing `- ` in a heading should not convert a heading to a list. | ||
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
49284
2
971
16
13
22
+ Added@ckeditor/ckeditor5-core@0.9.0(transitive)
+ Added@ckeditor/ckeditor5-engine@0.11.0(transitive)
+ Added@ckeditor/ckeditor5-utils@0.10.0(transitive)
- Removed@ckeditor/ckeditor5-heading@^0.9.1
- Removed@ckeditor/ckeditor5-list@^0.6.1
- Removed@ckeditor/ckeditor5-basic-styles@0.8.1(transitive)
- Removed@ckeditor/ckeditor5-core@0.8.1(transitive)
- Removed@ckeditor/ckeditor5-engine@0.10.0(transitive)
- Removed@ckeditor/ckeditor5-heading@0.9.1(transitive)
- Removed@ckeditor/ckeditor5-list@0.6.1(transitive)
- Removed@ckeditor/ckeditor5-paragraph@0.8.0(transitive)
- Removed@ckeditor/ckeditor5-theme-lark@0.8.0(transitive)
- Removed@ckeditor/ckeditor5-ui@0.9.0(transitive)
- Removed@ckeditor/ckeditor5-utils@0.9.1(transitive)