Procházet zdrojové kódy

Directly applying attributes instead of command execution.

Szymon Kupś před 9 roky
rodič
revize
eb5caad5a7

+ 49 - 50
packages/ckeditor5-autoformat/src/autoformat.js

@@ -51,6 +51,7 @@ export default class Autoformat extends Feature {
 	 * @private
 	 */
 	_addListAutoformats() {
+		// Add support for __foo__ and _foo_
 		new BlockAutoformatEngine( this.editor, /^[\*\-]\s$/, 'bulletedList' );
 		new BlockAutoformatEngine( this.editor, /^\d+[\.|)]?\s$/, 'numberedList' );
 	}
@@ -92,58 +93,56 @@ export default class Autoformat extends Feature {
 	_addInlineAutoformats() {
 		// Bold text between `**`, e.g. `**text to bold**`.
 		new InlineAutoformatEngine( this.editor, /(\*\*)(.+?)(\*\*)$/g, 'bold' );
-
+		new InlineAutoformatEngine( this.editor, /(?:[^\*])+(\*)([^\*]+)(\*)$/g, 'italic' );
 		// Italicize text between `*`, e.g. `*text to italicize*`.
 		// Slightly more complicated because of the clashing with the Bold autoformat.
 		// Won't work for text shorter than 3 characters.
-		new InlineAutoformatEngine(
-			this.editor,
-			( text ) => {
-				// For a text: 'Brown *fox* jumps over the lazy dog' the expression below will return following values:
-				//
-				// 	[0]: ' *fox* ',
-				// 	[1]: ' ',
-				// 	[2]: '*fox*',
-				// 	[index]: 5
-				//
-				// Value at index 1 is a "prefix". It can be empty, if the matched word is at the
-				// beginning of the line. Length of the prefix is used to calculate `start` index.
-				const pattern = /([^\*]|^)(\*[^\*].+?[^\*]\*)(?:[^\*]|$)/g;
-
-				let result;
-				let remove = [];
-				let format = [];
-
-				while ( ( result = pattern.exec( text ) ) !== null ) {
-					// Add "prefix" length.
-					const start = result.index + result[ 1 ].length;
-					const fullMatchLen = result[ 2 ].length;
-					const delimiterLen = 1; // Length of '*'.
-
-					const delStart = [
-						start,
-						start + delimiterLen
-					];
-					const delEnd = [
-						start + fullMatchLen - delimiterLen,
-						start + fullMatchLen
-					];
-
-					remove.push( delStart );
-					remove.push( delEnd );
-
-					// Calculation of offsets after deletion is not needed.
-					format.push( [ start + delimiterLen, start + fullMatchLen - delimiterLen ] );
-				}
-
-				return {
-					remove,
-					format
-				};
-			},
-			( editor, range, batch ) => {
-				this.editor.execute( 'italic', { batch, forceValue: true } );
-			}
-		);
+		// new InlineAutoformatEngine(
+		// 	this.editor,
+		// 	( text ) => {
+		// 		// For a text: 'Brown *fox* jumps over the lazy dog' the expression below will return following values:
+		// 		//
+		// 		// 	[0]: ' *fox* ',
+		// 		// 	[1]: ' ',
+		// 		// 	[2]: '*fox*',
+		// 		// 	[index]: 5
+		// 		//
+		// 		// Value at index 1 is a "prefix". It can be empty, if the matched word is at the
+		// 		// beginning of the line. Length of the prefix is used to calculate `start` index.
+		// 		const pattern = /(?:[^\*]|^)(\*[^\*].+?[^\*]\*)(?:[^\*]|$)/g;
+		//
+		// 		let result;
+		// 		let remove = [];
+		// 		let format = [];
+		//
+		// 		while ( ( result = pattern.exec( text ) ) !== null ) {
+		// 			// Add "prefix" length.
+		// 			const start = result.index + result[ 1 ].length;
+		// 			const fullMatchLen = result[ 2 ].length;
+		// 			const delimiterLen = 1; // Length of '*'.
+		//
+		// 			const delStart = [
+		// 				start,
+		// 				start + delimiterLen
+		// 			];
+		// 			const delEnd = [
+		// 				start + fullMatchLen - delimiterLen,
+		// 				start + fullMatchLen
+		// 			];
+		//
+		// 			remove.push( delStart );
+		// 			remove.push( delEnd );
+		//
+		// 			// Calculation of offsets after deletion is not needed.
+		// 			format.push( [ start + delimiterLen, start + fullMatchLen - delimiterLen ] );
+		// 		}
+		//
+		// 		return {
+		// 			remove,
+		// 			format
+		// 		};
+		// 	},
+		// 	'italic'
+		// );
 	}
 }

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

@@ -3,7 +3,8 @@
  * For licensing, see LICENSE.md.
  */
 
-import Range from '../engine/model/range.js';
+import Range from '../engine/model/liverange.js';
+import getSchemaValidRanges from '../core/command/helpers/getschemavalidranges.js';
 
 /**
  * A paragraph feature for editor.
@@ -13,13 +14,12 @@ import Range from '../engine/model/range.js';
  * @extends ckeditor5.Feature
  */
 export default class InlineAutoformatEngine {
-
 	/**
 	 * Assigns to `editor` to watch for pattern (either by executing that pattern or passing the text to `testCallbackOrPattern` callback).
 	 * It formats found text by executing command `formatCallbackOrCommand` or by running `formatCallbackOrCommand` format callback.
 	 *
 	 * @param {core.editor.Editor} editor Editor instance.
-	 * @param {Function|RegExp} testCallbackOrPattern RegExp literal to execute on text or test callback returning Object with offsets to
+	 * @param {Function|RegExp} testRegexpOrCallback RegExp to execute on text or test callback returning Object with offsets to
 	 * remove and offsets to format.
 	 *  * Format is applied before deletion,
 	 *	* RegExp literal *must* have 3 capture groups.
@@ -42,7 +42,7 @@ export default class InlineAutoformatEngine {
 	 *  2. {engine.model.Range} Range of matched text to format,
 	 *  3. {engine.model.Batch} Batch to group format operations.
 	 */
-	constructor( editor, testCallbackOrPattern, formatCallbackOrCommand ) {
+	constructor( editor, testRegexpOrCallback, formatCallbackOrCommand ) {
 		this.editor = editor;
 
 		let pattern;
@@ -50,12 +50,10 @@ export default class InlineAutoformatEngine {
 		let testCallback;
 		let formatCallback;
 
-		if ( typeof testCallbackOrPattern == 'string' ) {
-			pattern = new RegExp( testCallbackOrPattern, 'g' );
-		} else if ( testCallbackOrPattern instanceof RegExp ) {
-			pattern = testCallbackOrPattern;
+		if ( testRegexpOrCallback instanceof RegExp ) {
+			pattern = testRegexpOrCallback;
 		} else {
-			testCallback = testCallbackOrPattern;
+			testCallback = testRegexpOrCallback;
 		}
 
 		if ( typeof formatCallbackOrCommand == 'string' ) {
@@ -76,6 +74,7 @@ export default class InlineAutoformatEngine {
 					break;
 				}
 
+				console.log( result );
 				const {
 					index,
 					'1': leftDel,
@@ -106,8 +105,10 @@ export default class InlineAutoformatEngine {
 		} );
 
 		// A format callback run on matched text.
-		formatCallback = formatCallback || ( ( editor, options ) => {
-			editor.execute( command, options );
+		formatCallback = formatCallback || ( ( batch, validRanges ) => {
+			for ( let range of validRanges ) {
+				batch.setAttribute( range, command, true );
+			}
 		} );
 
 		editor.document.on( 'change', ( evt, type ) => {
@@ -139,35 +140,34 @@ export default class InlineAutoformatEngine {
 				) );
 			} );
 
-			if ( rangesToFormat.length === 0 ) {
-				return;
-			}
-
-			const batch = editor.document.batch();
-			editor.document.enqueueChanges( () => {
-				selection.setRanges( rangesToFormat );
-			} );
-
-			formatCallback( this.editor, { batch } );
-
-			editor.document.enqueueChanges( () => {
-				selection.collapseToEnd();
-			} );
-
+			const rangesToRemove = [];
 			// Reverse order to not mix the offsets while removing.
 			ranges.remove.slice().reverse().forEach( ( range ) => {
 				if ( range[ 0 ] === undefined || range[ 1 ] === undefined ) {
 					return;
 				}
 
-				const rangeToDelete = Range.createFromParentsAndOffsets(
+				rangesToRemove.push( Range.createFromParentsAndOffsets(
 					block, range[ 0 ],
 					block, range[ 1 ]
-				);
+				) );
+			} );
+
+			if ( !( rangesToFormat.length && rangesToRemove.length ) ) {
+				return;
+			}
 
-				editor.document.enqueueChanges( () => {
-					batch.remove( rangeToDelete );
-				} );
+			const batch = editor.document.batch();
+			editor.document.enqueueChanges( () => {
+				const validRanges = getSchemaValidRanges( command, rangesToFormat, editor.document.schema );
+
+				// Apply format.
+				formatCallback( batch, validRanges );
+
+				// Remove delimiters.
+				for ( let range of rangesToRemove ) {
+					batch.remove( range );
+				}
 			} );
 		} );
 	}