Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #76 from ckeditor/t/ckeditor5/1955
Browse files Browse the repository at this point in the history
Fix: Autoformat transformations in blocks containing inline elements. Closes ckeditor/ckeditor5#1955.
  • Loading branch information
scofalik committed Sep 12, 2019
2 parents 515bc56 + 5cf2324 commit 133c647
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 23 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
33 changes: 14 additions & 19 deletions src/inlineautoformatediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -153,33 +155,35 @@ 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.
if ( changes.length != 1 || entry.type !== 'insert' || entry.name != '$text' || entry.length != 1 ) {
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 );

Expand All @@ -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 ] ) );
} );
}
13 changes: 12 additions & 1 deletion tests/autoformat.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -38,7 +39,8 @@ describe( 'Autoformat', () => {
BoldEditing,
ItalicEditing,
CodeEditing,
BlockQuoteEditing
BlockQuoteEditing,
ShiftEnter
]
} )
.then( newEditor => {
Expand Down Expand Up @@ -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', () => {
Expand Down
3 changes: 2 additions & 1 deletion tests/manual/autoformat.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
1 change: 1 addition & 0 deletions tests/manual/autoformat.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>).

0 comments on commit 133c647

Please sign in to comment.