Skip to content

Commit

Permalink
Merge pull request #13171 from ckeditor/ck/12996
Browse files Browse the repository at this point in the history
Other (autoformat): Rewrite ckeditor5-autoformat to TypeScript. Closes #12996.
  • Loading branch information
arkflpc authored Jan 17, 2023
2 parents 3686a81 + de1c75c commit ac3dbbe
Show file tree
Hide file tree
Showing 14 changed files with 688 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ yalc.lock
# Ignore compiled TypeScript files.
packages/ckeditor5-adapter-ckfinder/src/**/*.js
packages/ckeditor5-alignment/src/**/*.js
packages/ckeditor5-autoformat/src/**/*.js
packages/ckeditor5-basic-styles/src/**/*.js
packages/ckeditor5-block-quote/src/**/*.js
packages/ckeditor5-clipboard/src/**/*.js
Expand Down
File renamed without changes.
10 changes: 7 additions & 3 deletions packages/ckeditor5-autoformat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"ckeditor5-plugin",
"ckeditor5-dll"
],
"main": "src/index.js",
"main": "src/index.ts",
"dependencies": {
"ckeditor5": "^35.4.0"
},
Expand All @@ -30,6 +30,7 @@
"@ckeditor/ckeditor5-theme-lark": "^35.4.0",
"@ckeditor/ckeditor5-typing": "^35.4.0",
"@ckeditor/ckeditor5-undo": "^35.4.0",
"typescript": "^4.8.4",
"webpack": "^5.58.1",
"webpack-cli": "^4.9.0"
},
Expand All @@ -48,7 +49,8 @@
},
"files": [
"lang",
"src",
"src/**/*.js",
"src/**/*.d.ts",
"theme",
"build",
"ckeditor5-metadata.json",
Expand All @@ -58,6 +60,8 @@
"eslint-plugin-ckeditor5-rules"
],
"scripts": {
"dll:build": "webpack"
"dll:build": "webpack",
"build": "tsc -p ./tsconfig.release.json",
"postversion": "npm run build"
}
}
233 changes: 233 additions & 0 deletions packages/ckeditor5-autoformat/src/autoformat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* @module autoformat/autoformat
*/
import type { HeadingCommand } from '@ckeditor/ckeditor5-heading';

import { Plugin, type PluginDependencies, type Editor } from 'ckeditor5/src/core';
import type { Range, Writer } from 'ckeditor5/src/engine';
import { Delete } from 'ckeditor5/src/typing';

import blockAutoformatEditing from './blockautoformatediting';
import inlineAutoformatEditing from './inlineautoformatediting';

/**
* Enables a set of predefined autoformatting actions.
*
* For a detailed overview, check the {@glink features/autoformat Autoformatting feature documentation}
* and the {@glink api/autoformat package page}.
*/
export default class Autoformat extends Plugin {
/**
* @inheritdoc
*/
public static get requires(): PluginDependencies {
return [ Delete ];
}

/**
* @inheritDoc
*/
public static get pluginName(): 'Autoformat' {
return 'Autoformat';
}

/**
* @inheritDoc
*/
public afterInit(): void {
this._addListAutoformats();
this._addBasicStylesAutoformats();
this._addHeadingAutoformats();
this._addBlockQuoteAutoformats();
this._addCodeBlockAutoformats();
this._addHorizontalLineAutoformats();
}

/**
* Adds autoformatting related to the {@link module:list/list~List}.
*
* When typed:
* - `* ` 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).
* - `[] ` or `[ ] ` – A paragraph will be changed to a to-do list.
* - `[x] ` or `[ x ] ` – A paragraph will be changed to a checked to-do list.
*/
private _addListAutoformats(): void {
const commands = this.editor.commands;

if ( commands.get( 'bulletedList' ) ) {
blockAutoformatEditing( this.editor, this, /^[*-]\s$/, 'bulletedList' );
}

if ( commands.get( 'numberedList' ) ) {
blockAutoformatEditing( this.editor, this, /^1[.|)]\s$/, 'numberedList' );
}

if ( commands.get( 'todoList' ) ) {
blockAutoformatEditing( this.editor, this, /^\[\s?\]\s$/, 'todoList' );
}

if ( commands.get( 'checkTodoList' ) ) {
blockAutoformatEditing( this.editor, this, /^\[\s?x\s?\]\s$/, () => {
this.editor.execute( 'todoList' );
this.editor.execute( 'checkTodoList' );
} );
}
}

/**
* Adds autoformatting related to the {@link module:basic-styles/bold~Bold},
* {@link module:basic-styles/italic~Italic}, {@link module:basic-styles/code~Code}
* and {@link module:basic-styles/strikethrough~Strikethrough}
*
* 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,
* - ``` `foobar` – ``` ` ``` characters are removed and `foobar` is set to code,
* - `~~foobar~~` – `~~` characters are removed and `foobar` is set to strikethrough.
*/
private _addBasicStylesAutoformats(): void {
const commands = this.editor.commands;

if ( commands.get( 'bold' ) ) {
const boldCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'bold' );

inlineAutoformatEditing( this.editor, this, /(?:^|\s)(\*\*)([^*]+)(\*\*)$/g, boldCallback );
inlineAutoformatEditing( this.editor, this, /(?:^|\s)(__)([^_]+)(__)$/g, boldCallback );
}

if ( commands.get( 'italic' ) ) {
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. `(?:^|[^\*])`).
inlineAutoformatEditing( this.editor, this, /(?:^|\s)(\*)([^*_]+)(\*)$/g, italicCallback );
inlineAutoformatEditing( this.editor, this, /(?:^|\s)(_)([^_]+)(_)$/g, italicCallback );
}

if ( commands.get( 'code' ) ) {
const codeCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'code' );

inlineAutoformatEditing( this.editor, this, /(`)([^`]+)(`)$/g, codeCallback );
}

if ( commands.get( 'strikethrough' ) ) {
const strikethroughCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'strikethrough' );

inlineAutoformatEditing( this.editor, this, /(~~)([^~]+)(~~)$/g, strikethroughCallback );
}
}

/**
* 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:
*
* * `heading` with value `heading1` will be executed when typing `#`,
* * `heading` with value `heading2` will be executed when typing `##`,
* * ... up to `heading6` and `######`.
*/
private _addHeadingAutoformats(): void {
const command: HeadingCommand | undefined = this.editor.commands.get( 'heading' );

if ( command ) {
command.modelElements
.filter( name => name.match( /^heading[1-6]$/ ) )
.forEach( modelName => {
const level = modelName[ 7 ];
const pattern = new RegExp( `^(#{${ level }})\\s$` );

blockAutoformatEditing( this.editor, this, pattern, () => {
// Should only be active if command is enabled and heading style associated with pattern is inactive.
if ( !command.isEnabled || command.value === modelName ) {
return false;
}

this.editor.execute( 'heading', { value: modelName } );
} );
} );
}
}

/**
* Adds autoformatting related to {@link module:block-quote/blockquote~BlockQuote}.
*
* When typed:
* * `> ` – A paragraph will be changed to a block quote.
*/
private _addBlockQuoteAutoformats(): void {
if ( this.editor.commands.get( 'blockQuote' ) ) {
blockAutoformatEditing( this.editor, this, /^>\s$/, 'blockQuote' );
}
}

/**
* Adds autoformatting related to {@link module:code-block/codeblock~CodeBlock}.
*
* When typed:
* - `` ``` `` – A paragraph will be changed to a code block.
*/
private _addCodeBlockAutoformats(): void {
const editor = this.editor;
const selection = editor.model.document.selection;

if ( editor.commands.get( 'codeBlock' ) ) {
blockAutoformatEditing( editor, this, /^```$/, () => {
if ( selection.getFirstPosition()!.parent.is( 'element', 'listItem' ) ) {
return false;
}
this.editor.execute( 'codeBlock', {
usePreviousLanguageChoice: true
} );
} );
}
}

/**
* Adds autoformatting related to {@link module:horizontal-line/horizontalline~HorizontalLine}.
*
* When typed:
* - `` --- `` – Will be replaced with a horizontal line.
*/
private _addHorizontalLineAutoformats(): void {
if ( this.editor.commands.get( 'horizontalLine' ) ) {
blockAutoformatEditing( this.editor, this, /^---$/, 'horizontalLine' );
}
}
}

/**
* Helper function for getting `inlineAutoformatEditing` callbacks that checks if command is enabled.
*/
function getCallbackFunctionForInlineAutoformat( editor: Editor, attributeKey: string ) {
return ( writer: Writer, rangesToFormat: Array<Range> ): boolean | undefined => {
const command = editor.commands.get( attributeKey )!;

if ( !command.isEnabled ) {
return false;
}

const validRanges = editor.model.schema.getValidRanges( rangesToFormat, attributeKey );

for ( const range of validRanges ) {
writer.setAttribute( attributeKey, true, range );
}

// 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 );
};
}

declare module '@ckeditor/ckeditor5-core' {
interface PluginsMap {
[ Autoformat.pluginName ]: Autoformat;
}
}
Loading

0 comments on commit ac3dbbe

Please sign in to comment.