This repository has been archived by the owner on Jun 26, 2020. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #219 from ckeditor/t/ckeditor5/1955
Fix: Autoformat transformations in blocks containing inline elements. See ckeditor/ckeditor5#1955.
- Loading branch information
Showing
5 changed files
with
178 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/** | ||
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license | ||
*/ | ||
|
||
/** | ||
* @module typing/utils/getlasttextline | ||
*/ | ||
|
||
/** | ||
* Returns the last text line from the given range. | ||
* | ||
* "The last text line" is understood as text (from one or more text nodes) which is limited either by a parent block | ||
* or by inline elements (e.g. `<softBreak>`). | ||
* | ||
* const rangeToCheck = model.createRange( | ||
* model.createPositionAt( paragraph, 0 ), | ||
* model.createPositionAt( paragraph, 'end' ) | ||
* ); | ||
* | ||
* const { text, range } = getLastTextLine( rangeToCheck, model ); | ||
* | ||
* For model below, the returned `text` will be "Foo bar baz" and `range` will be set on whole `<paragraph>` content: | ||
* | ||
* <paragraph>Foo bar baz<paragraph> | ||
* | ||
* However, in below case, `text` will be set to "baz" and `range` will be set only on "baz". | ||
* | ||
* <paragraph>Foo<softBreak></softBreak>bar<softBreak></softBreak>baz<paragraph> | ||
* | ||
* @protected | ||
* @param {module:engine/model/range~Range} range | ||
* @param {module:engine/model/model~Model} model | ||
* @returns {module:typing/utils/getlasttextline~LastTextLineData} | ||
*/ | ||
export default function getLastTextLine( range, model ) { | ||
let start = range.start; | ||
|
||
const text = Array.from( range.getItems() ).reduce( ( rangeText, node ) => { | ||
// Trim text to a last occurrence of an inline element and update range start. | ||
if ( !( node.is( 'text' ) || node.is( 'textProxy' ) ) ) { | ||
start = model.createPositionAfter( node ); | ||
|
||
return ''; | ||
} | ||
|
||
return rangeText + node.data; | ||
}, '' ); | ||
|
||
return { text, range: model.createRange( start, range.end ) }; | ||
} | ||
|
||
/** | ||
* The value returned by {@link module:typing/utils/getlasttextline~getLastTextLine}. | ||
* | ||
* @typedef {Object} module:typing/utils/getlasttextline~LastTextLineData | ||
* | ||
* @property {String} text The text from the text nodes in the last text line. | ||
* @property {module:engine/model/range~Range} range The range set on the text nodes in the last text line. | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/** | ||
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license | ||
*/ | ||
|
||
import Model from '@ckeditor/ckeditor5-engine/src/model/model'; | ||
import { setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; | ||
import getText from '../../src/utils/getlasttextline'; | ||
|
||
describe( 'utils', () => { | ||
let model, doc, root; | ||
|
||
beforeEach( () => { | ||
model = new Model(); | ||
doc = model.document; | ||
root = doc.createRoot(); | ||
|
||
model.schema.register( 'paragraph', { inheritAllFrom: '$block' } ); | ||
model.schema.register( 'softBreak', { allowWhere: '$text', isInline: true } ); | ||
model.schema.extend( '$text', { allowAttributes: [ 'bold', 'italic' ] } ); | ||
} ); | ||
|
||
describe( 'getText()', () => { | ||
it( 'should return all text from passed range', () => { | ||
setModelData( model, '<paragraph>foobar[]baz</paragraph>' ); | ||
|
||
testOutput( | ||
model.createRangeIn( root.getChild( 0 ) ), | ||
'foobarbaz', | ||
[ 0, 0 ], | ||
[ 0, 9 ] ); | ||
} ); | ||
|
||
it( 'should limit the output text to the given range', () => { | ||
setModelData( model, '<paragraph>foobar[]baz</paragraph>' ); | ||
|
||
const testRange = model.createRange( | ||
model.createPositionAt( root.getChild( 0 ), 1 ), | ||
model.document.selection.focus | ||
); | ||
|
||
testOutput( | ||
testRange, | ||
'oobar', | ||
[ 0, 1 ], | ||
[ 0, 6 ] ); | ||
} ); | ||
|
||
it( 'should limit the output to the last inline element text constrain in given range', () => { | ||
setModelData( model, '<paragraph>foo<softBreak></softBreak>bar<softBreak></softBreak>baz[]</paragraph>' ); | ||
|
||
const testRange = model.createRange( | ||
model.createPositionAt( root.getChild( 0 ), 0 ), | ||
model.document.selection.focus | ||
); | ||
|
||
testOutput( | ||
testRange, | ||
'baz', | ||
[ 0, 8 ], | ||
[ 0, 11 ] ); | ||
} ); | ||
|
||
it( 'should return text from text nodes with attributes', () => { | ||
setModelData( model, | ||
'<paragraph>' + | ||
'<$text bold="true">foo</$text>' + | ||
'<$text bold="true" italic="true">bar</$text>' + | ||
'<$text italic="true">baz</$text>[]' + | ||
'</paragraph>' | ||
); | ||
|
||
testOutput( | ||
model.createRangeIn( root.getChild( 0 ) ), | ||
'foobarbaz', | ||
[ 0, 0 ], | ||
[ 0, 9 ] ); | ||
} ); | ||
} ); | ||
|
||
function testOutput( range1, expectedText, startPath, endPath ) { | ||
const { text, range } = getText( range1, model ); | ||
|
||
expect( text ).to.equal( expectedText ); | ||
expect( range.start.path ).to.deep.equal( startPath ); | ||
expect( range.end.path ).to.deep.equal( endPath ); | ||
} | ||
} ); |