Przeglądaj źródła

Merge pull request #76 from ckeditor/t/ckeditor5/1955

Fix: Autoformat transformations in blocks containing inline elements. Closes ckeditor/ckeditor5#1955.
Szymon Cofalik 6 lat temu
rodzic
commit
1cac654c29

+ 2 - 2
packages/ckeditor5-autoformat/package.json

@@ -10,7 +10,8 @@
     "ckeditor5-plugin"
   ],
   "dependencies": {
-    "@ckeditor/ckeditor5-core": "^12.3.0"
+    "@ckeditor/ckeditor5-core": "^12.3.0",
+    "@ckeditor/ckeditor5-typing": "^12.2.0"
   },
   "devDependencies": {
     "@ckeditor/ckeditor5-basic-styles": "^11.1.4",
@@ -21,7 +22,6 @@
     "@ckeditor/ckeditor5-heading": "^11.0.5",
     "@ckeditor/ckeditor5-list": "^12.1.0",
     "@ckeditor/ckeditor5-paragraph": "^11.0.5",
-    "@ckeditor/ckeditor5-typing": "^12.2.0",
     "@ckeditor/ckeditor5-undo": "^11.0.5",
     "eslint": "^5.5.0",
     "eslint-config-ckeditor5": "^2.0.0",

+ 14 - 19
packages/ckeditor5-autoformat/src/inlineautoformatediting.js

@@ -7,6 +7,8 @@
  * @module autoformat/inlineautoformatediting
  */
 
+import getLastTextLine from '@ckeditor/ckeditor5-typing/src/utils/getlasttextline';
+
 /**
  * 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).
@@ -153,14 +155,15 @@ export default class InlineAutoformatEditing {
 				return;
 			}
 
-			const selection = editor.model.document.selection;
+			const model = editor.model;
+			const selection = model.document.selection;
 
 			// Do nothing if selection is not collapsed.
 			if ( !selection.isCollapsed ) {
 				return;
 			}
 
-			const changes = Array.from( editor.model.document.differ.getChanges() );
+			const changes = Array.from( model.document.differ.getChanges() );
 			const entry = changes[ 0 ];
 
 			// Typing is represented by only a single change.
@@ -168,18 +171,19 @@ export default class InlineAutoformatEditing {
 				return;
 			}
 
-			const block = selection.focus.parent;
-			const text = getText( block ).slice( 0, selection.focus.offset );
+			const focus = selection.focus;
+			const block = focus.parent;
+			const { text, range } = getLastTextLine( model.createRange( model.createPositionAt( block, 0 ), focus ), model );
 			const testOutput = testCallback( text );
-			const rangesToFormat = testOutputToRanges( block, testOutput.format, editor.model );
-			const rangesToRemove = testOutputToRanges( block, testOutput.remove, editor.model );
+			const rangesToFormat = testOutputToRanges( range.start, testOutput.format, model );
+			const rangesToRemove = testOutputToRanges( range.start, testOutput.remove, model );
 
 			if ( !( rangesToFormat.length && rangesToRemove.length ) ) {
 				return;
 			}
 
 			// Use enqueueChange to create new batch to separate typing batch from the auto-format changes.
-			editor.model.enqueueChange( writer => {
+			model.enqueueChange( writer => {
 				// Apply format.
 				const hasChanged = formatCallback( writer, rangesToFormat );
 
@@ -197,26 +201,17 @@ export default class InlineAutoformatEditing {
 	}
 }
 
-// Returns whole text from parent element by adding all data from text nodes together.
-//
-// @private
-// @param {module:engine/model/element~Element} element
-// @returns {String}
-function getText( element ) {
-	return Array.from( element.getChildren() ).reduce( ( a, b ) => a + b.data, '' );
-}
-
 // Converts output of the test function provided to the InlineAutoformatEditing and converts it to the model ranges
 // inside provided block.
 //
 // @private
-// @param {module:engine/model/element~Element} block
+// @param {module:engine/model/position~Position} start
 // @param {Array.<Array>} arrays
 // @param {module:engine/model/model~Model} model
-function testOutputToRanges( block, arrays, model ) {
+function testOutputToRanges( start, arrays, model ) {
 	return arrays
 		.filter( array => ( array[ 0 ] !== undefined && array[ 1 ] !== undefined ) )
 		.map( array => {
-			return model.createRange( model.createPositionAt( block, array[ 0 ] ), model.createPositionAt( block, array[ 1 ] ) );
+			return model.createRange( start.getShiftedBy( array[ 0 ] ), start.getShiftedBy( array[ 1 ] ) );
 		} );
 }

+ 12 - 1
packages/ckeditor5-autoformat/tests/autoformat.js

@@ -13,6 +13,7 @@ import CodeEditing from '@ckeditor/ckeditor5-basic-styles/src/code/codeediting';
 import ItalicEditing from '@ckeditor/ckeditor5-basic-styles/src/italic/italicediting';
 import BlockQuoteEditing from '@ckeditor/ckeditor5-block-quote/src/blockquoteediting';
 import Enter from '@ckeditor/ckeditor5-enter/src/enter';
+import ShiftEnter from '@ckeditor/ckeditor5-enter/src/shiftenter';
 
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 
@@ -38,7 +39,8 @@ describe( 'Autoformat', () => {
 					BoldEditing,
 					ItalicEditing,
 					CodeEditing,
-					BlockQuoteEditing
+					BlockQuoteEditing,
+					ShiftEnter
 				]
 			} )
 			.then( newEditor => {
@@ -311,6 +313,15 @@ describe( 'Autoformat', () => {
 
 			expect( getData( model ) ).to.equal( '<paragraph>**foobar**[]</paragraph>' );
 		} );
+
+		it( 'should work with <softBreak>s in paragraph', () => {
+			setData( model, '<paragraph>foo<softBreak></softBreak>**barbaz*[]</paragraph>' );
+			model.change( writer => {
+				writer.insertText( '*', doc.selection.getFirstPosition() );
+			} );
+
+			expect( getData( model ) ).to.equal( '<paragraph>foo<softBreak></softBreak><$text bold="true">barbaz</$text>[]</paragraph>' );
+		} );
 	} );
 
 	describe( 'without commands', () => {

+ 2 - 1
packages/ckeditor5-autoformat/tests/manual/autoformat.js

@@ -17,10 +17,11 @@ import Undo from '@ckeditor/ckeditor5-undo/src/undo';
 import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
 import Code from '@ckeditor/ckeditor5-basic-styles/src/code';
 import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
+import ShiftEnter from '@ckeditor/ckeditor5-enter/src/shiftenter';
 
 ClassicEditor
 	.create( document.querySelector( '#editor' ), {
-		plugins: [ Enter, Typing, Paragraph, Undo, Bold, Italic, Code, Heading, List, Autoformat, BlockQuote ],
+		plugins: [ Enter, Typing, Paragraph, Undo, Bold, Italic, Code, Heading, List, Autoformat, BlockQuote, ShiftEnter ],
 		toolbar: [ 'heading', '|', 'numberedList', 'bulletedList', 'blockQuote', 'bold', 'italic', 'code', 'undo', 'redo' ]
 	} )
 	.then( editor => {

+ 1 - 0
packages/ckeditor5-autoformat/tests/manual/autoformat.md

@@ -20,3 +20,4 @@
 
 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.
 
+1. Type inline formatting (bold, italic, code) after a soft break (<kbd>Shift</kbd>+<kbd>Enter</kbd>).