Skip to content

Commit

Permalink
Autoformat only the text after the last code element,
Browse files Browse the repository at this point in the history
as agreed at #1239 (comment).
  • Loading branch information
tomalec committed May 29, 2020
1 parent 29feae4 commit 87261af
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 4 deletions.
28 changes: 25 additions & 3 deletions packages/ckeditor5-autoformat/src/inlineautoformatediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
* @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 @@ -174,7 +172,7 @@ export default class InlineAutoformatEditing {

const focus = selection.focus;
const block = focus.parent;
const { text, range } = getLastTextLine( model.createRange( model.createPositionAt( block, 0 ), focus ), model );
const { text, range } = getTextAfterCode( model.createRange( model.createPositionAt( block, 0 ), focus ), model );
const testOutput = testCallback( text );
const rangesToFormat = testOutputToRanges( range.start, testOutput.format, model );
const rangesToRemove = testOutputToRanges( range.start, testOutput.remove, model );
Expand Down Expand Up @@ -216,3 +214,27 @@ function testOutputToRanges( start, arrays, model ) {
return model.createRange( start.getShiftedBy( array[ 0 ] ), start.getShiftedBy( array[ 1 ] ) );
} );
}

// Returns the last text line after the last code element from the given range.
// It is similar to {@link module:typing/utils/getlasttextline.getLastTextLine `getLastTextLine()`},
// but it ignores any text before the last `code`.
//
// @param {module:engine/model/range~Range} range
// @param {module:engine/model/model~Model} model
// @returns {module:typing/utils/getlasttextline~LastTextLineData}
function getTextAfterCode( 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' ) ) || node.getAttribute( 'code' ) ) {
start = model.createPositionAfter( node );

return '';
}

return rangeText + node.data;
}, '' );

return { text, range: model.createRange( start, range.end ) };
}
128 changes: 128 additions & 0 deletions packages/ckeditor5-autoformat/tests/autoformat.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,134 @@ describe( 'Autoformat', () => {
expect( getData( model ) ).to.equal( '<paragraph>**foobar**[]</paragraph>' );
} );

describe( 'with code element', () => {
it( 'should not format inside', () => {
// Test *.
setData( model, '<paragraph><$text code="true">fo*obar[]</$text></paragraph>' );

model.change( writer => {
writer.insertText( '*', { code: true }, doc.selection.getFirstPosition() );
} );

expect( getData( model ) ).to
.equal( '<paragraph><$text code="true">fo*obar*[]</$text></paragraph>' );

// Test __.
setData( model, '<paragraph><$text code="true">fo__obar_[]</$text></paragraph>' );

model.change( writer => {
writer.insertText( '_', { code: true }, doc.selection.getFirstPosition() );
} );

expect( getData( model ) ).to
.equal( '<paragraph><$text code="true">fo__obar__[]</$text></paragraph>' );

// Test ~~.
setData( model, '<paragraph><$text code="true">fo~~obar~[]</$text></paragraph>' );

model.change( writer => {
writer.insertText( '~', { code: true }, doc.selection.getFirstPosition() );
} );

expect( getData( model ) ).to
.equal( '<paragraph><$text code="true">fo~~obar~~[]</$text></paragraph>' );

// Test `.
setData( model, '<paragraph><$text code="true">fo`obar[]</$text></paragraph>' );

model.change( writer => {
writer.insertText( '`', { code: true }, doc.selection.getFirstPosition() );
} );

expect( getData( model ) ).to
.equal( '<paragraph><$text code="true">fo`obar`[]</$text></paragraph>' );
} );

it( 'should not format across', () => {
// Test *.
setData( model, '<paragraph><$text code="true">fo*o</$text>bar[]</paragraph>' );

model.change( writer => {
writer.insertText( '*', doc.selection.getFirstPosition() );
} );

expect( getData( model ) ).to
.equal( '<paragraph><$text code="true">fo*o</$text>bar*[]</paragraph>' );

// Test __.
setData( model, '<paragraph><$text code="true">fo__o</$text>bar_[]</paragraph>' );

model.change( writer => {
writer.insertText( '_', doc.selection.getFirstPosition() );
} );

expect( getData( model ) ).to
.equal( '<paragraph><$text code="true">fo__o</$text>bar__[]</paragraph>' );

// Test ~~.
setData( model, '<paragraph><$text code="true">fo~~o</$text>bar~[]</paragraph>' );

model.change( writer => {
writer.insertText( '~', doc.selection.getFirstPosition() );
} );

expect( getData( model ) ).to
.equal( '<paragraph><$text code="true">fo~~o</$text>bar~~[]</paragraph>' );

// Test `.
setData( model, '<paragraph><$text code="true">fo`o</$text>bar[]</paragraph>' );

model.change( writer => {
writer.insertText( '`', doc.selection.getFirstPosition() );
} );

expect( getData( model ) ).to
.equal( '<paragraph><$text code="true">fo`o</$text>bar`[]</paragraph>' );
} );

it( 'should format after', () => {
// Test *.
setData( model, '<paragraph><$text code="true">fo*o</$text>b*ar[]</paragraph>' );

model.change( writer => {
writer.insertText( '*', doc.selection.getFirstPosition() );
} );

expect( getData( model ) ).to
.equal( '<paragraph><$text code="true">fo*o</$text>b<$text italic="true">ar</$text>[]</paragraph>' );

// Test __.
setData( model, '<paragraph><$text code="true">fo__o</$text>b__ar_[]</paragraph>' );

model.change( writer => {
writer.insertText( '_', doc.selection.getFirstPosition() );
} );

expect( getData( model ) ).to
.equal( '<paragraph><$text code="true">fo__o</$text>b<$text bold="true">ar</$text>[]</paragraph>' );

// Test ~~.
setData( model, '<paragraph><$text code="true">fo~~o</$text>b~~ar~[]</paragraph>' );

model.change( writer => {
writer.insertText( '~', doc.selection.getFirstPosition() );
} );

expect( getData( model ) ).to
.equal( '<paragraph><$text code="true">fo~~o</$text>b<$text strikethrough="true">ar</$text>[]</paragraph>' );

// Test `.
setData( model, '<paragraph><$text code="true">fo`o</$text>b`ar[]</paragraph>' );

model.change( writer => {
writer.insertText( '`', doc.selection.getFirstPosition() );
} );

expect( getData( model ) ).to
.equal( '<paragraph><$text code="true">fo`o</$text>b<$text code="true">ar</$text>[]</paragraph>' );
} );
} );

it( 'should work with <softBreak>s in paragraph', () => {
setData( model, '<paragraph>foo<softBreak></softBreak>**barbaz*[]</paragraph>' );
model.change( writer => {
Expand Down
2 changes: 1 addition & 1 deletion packages/ckeditor5-autoformat/tests/manual/autoformat.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<div id="editor">
<p>This is the editor instance.</p>
<p>This is the editor instance, with a <code>code_element*2;</code> inside.</p>
</div>

0 comments on commit 87261af

Please sign in to comment.