From 3e62945b8a18237dfe0828a945df04630349d892 Mon Sep 17 00:00:00 2001 From: Mateusz Samsel Date: Mon, 27 May 2019 12:19:38 +0200 Subject: [PATCH 01/10] Add support for preserving text attributes on enter. --- src/entercommand.js | 11 +++++++++++ tests/entercommand.js | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/entercommand.js b/src/entercommand.js index db5478b..5de5f78 100644 --- a/src/entercommand.js +++ b/src/entercommand.js @@ -56,7 +56,10 @@ function enterBlock( model, writer, selection, schema ) { } if ( isSelectionEmpty ) { + // List of text attributes copied to new line/block. + const filteredAttr = getAllowedAttributes( writer.model.schema, selection.getAttributes() ); splitBlock( writer, range.start ); + writer.setSelectionAttribute( filteredAttr ); } else { const leaveUnmerged = !( range.start.isAtStart && range.end.isAtEnd ); const isContainedWithinOneElement = ( startElement == endElement ); @@ -84,3 +87,11 @@ function splitBlock( writer, splitPos ) { writer.split( splitPos ); writer.setSelection( splitPos.parent.nextSibling, 0 ); } + +function* getAllowedAttributes( schema, allAttributes ) { + for ( const attr of allAttributes ) { + if ( attr && schema.getAttributeProperties( attr[ 0 ] ).copyOnEnter ) { + yield attr; + } + } +} diff --git a/tests/entercommand.js b/tests/entercommand.js index d8e7217..dedd381 100644 --- a/tests/entercommand.js +++ b/tests/entercommand.js @@ -87,6 +87,31 @@ describe( 'EnterCommand', () => { '

x

[]

y

', '

x

[]

y

' ); + + describe( 'copyOnEnter', () => { + beforeEach( () => { + schema.extend( '$text', { allowAttributes: 'foo' } ); + schema.setAttributeProperties( 'foo', { copyOnEnter: true } ); + } ); + + test( + 'allowed attributes are copied', + '

<$text foo="true">test[]

', + '

<$text foo="true">test

<$text foo="true">[]

' + ); + + test( + 'unknown attributes are disabled', + '

<$text bar="true">test[]

', + '

<$text bar="true">test

[]

' + ); + + test( + 'only allowed attributes are copied from mix set', + '

<$text bar="true" foo="true">test[]

', + '

<$text bar="true" foo="true">test

<$text foo="true">[]

' + ); + } ); } ); describe( 'non-collapsed selection', () => { From 66d211f2e2b69cd85d5d4925d7a247524e601362 Mon Sep 17 00:00:00 2001 From: Mateusz Samsel Date: Mon, 1 Jul 2019 10:46:55 +0200 Subject: [PATCH 02/10] Apply suggestions from code review Co-Authored-By: Maciej --- tests/entercommand.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/entercommand.js b/tests/entercommand.js index dedd381..092b33d 100644 --- a/tests/entercommand.js +++ b/tests/entercommand.js @@ -101,7 +101,7 @@ describe( 'EnterCommand', () => { ); test( - 'unknown attributes are disabled', + 'unknown attributes are not copied', '

<$text bar="true">test[]

', '

<$text bar="true">test

[]

' ); From 753ea20108eb5d991734d5cce07112784447ca7f Mon Sep 17 00:00:00 2001 From: Mateusz Samsel Date: Mon, 1 Jul 2019 11:02:09 +0200 Subject: [PATCH 03/10] Improve name of variables used in code. --- src/entercommand.js | 13 ++++++------- tests/entercommand.js | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/entercommand.js b/src/entercommand.js index 5de5f78..0be8303 100644 --- a/src/entercommand.js +++ b/src/entercommand.js @@ -56,10 +56,9 @@ function enterBlock( model, writer, selection, schema ) { } if ( isSelectionEmpty ) { - // List of text attributes copied to new line/block. - const filteredAttr = getAllowedAttributes( writer.model.schema, selection.getAttributes() ); + const attributesToCopy = getCopyOnEnterAttributes( writer.model.schema, selection.getAttributes() ); splitBlock( writer, range.start ); - writer.setSelectionAttribute( filteredAttr ); + writer.setSelectionAttribute( attributesToCopy ); } else { const leaveUnmerged = !( range.start.isAtStart && range.end.isAtEnd ); const isContainedWithinOneElement = ( startElement == endElement ); @@ -88,10 +87,10 @@ function splitBlock( writer, splitPos ) { writer.setSelection( splitPos.parent.nextSibling, 0 ); } -function* getAllowedAttributes( schema, allAttributes ) { - for ( const attr of allAttributes ) { - if ( attr && schema.getAttributeProperties( attr[ 0 ] ).copyOnEnter ) { - yield attr; +function* getCopyOnEnterAttributes( schema, allAttributes ) { + for ( const attribute of allAttributes ) { + if ( attribute && schema.getAttributeProperties( attribute[ 0 ] ).copyOnEnter ) { + yield attribute; } } } diff --git a/tests/entercommand.js b/tests/entercommand.js index 092b33d..3d5f52c 100644 --- a/tests/entercommand.js +++ b/tests/entercommand.js @@ -90,7 +90,7 @@ describe( 'EnterCommand', () => { describe( 'copyOnEnter', () => { beforeEach( () => { - schema.extend( '$text', { allowAttributes: 'foo' } ); + schema.extend( '$text', { allowAttributes: [ 'foo', 'bar' ] } ); schema.setAttributeProperties( 'foo', { copyOnEnter: true } ); } ); From 13f994546efdb360adf94034346e9118b2c718b1 Mon Sep 17 00:00:00 2001 From: Mateusz Samsel Date: Thu, 11 Jul 2019 16:32:42 +0200 Subject: [PATCH 04/10] Add support for copyOnEnter for shiftenter command. --- src/entercommand.js | 9 +-------- src/shiftentercommand.js | 3 +++ src/utils.js | 25 +++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 src/utils.js diff --git a/src/entercommand.js b/src/entercommand.js index 0be8303..b267717 100644 --- a/src/entercommand.js +++ b/src/entercommand.js @@ -8,6 +8,7 @@ */ import Command from '@ckeditor/ckeditor5-core/src/command'; +import { getCopyOnEnterAttributes } from './utils'; /** * Enter command. It is used by the {@link module:enter/enter~Enter Enter feature} to handle the Enter key. @@ -86,11 +87,3 @@ function splitBlock( writer, splitPos ) { writer.split( splitPos ); writer.setSelection( splitPos.parent.nextSibling, 0 ); } - -function* getCopyOnEnterAttributes( schema, allAttributes ) { - for ( const attribute of allAttributes ) { - if ( attribute && schema.getAttributeProperties( attribute[ 0 ] ).copyOnEnter ) { - yield attribute; - } - } -} diff --git a/src/shiftentercommand.js b/src/shiftentercommand.js index 5944238..0dd83c5 100644 --- a/src/shiftentercommand.js +++ b/src/shiftentercommand.js @@ -8,6 +8,7 @@ */ import Command from '@ckeditor/ckeditor5-core/src/command'; +import { getCopyOnEnterAttributes } from './utils'; /** * ShiftEnter command. It is used by the {@link module:enter/shiftenter~ShiftEnter ShiftEnter feature} to handle @@ -81,7 +82,9 @@ function softBreakAction( model, writer, selection ) { const isContainedWithinOneElement = ( startElement == endElement ); if ( isSelectionEmpty ) { + const attributesToCopy = getCopyOnEnterAttributes( model.schema, selection.getAttributes() ); insertBreak( writer, range.end ); + writer.setSelectionAttribute( attributesToCopy ); } else { const leaveUnmerged = !( range.start.isAtStart && range.end.isAtEnd ); model.deleteContent( selection, { leaveUnmerged } ); diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..f428c7c --- /dev/null +++ b/src/utils.js @@ -0,0 +1,25 @@ +/** + * @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 enter/utils + */ + +/** + * Returns iterator which has filtered attributes read from {@link module:engine/model/selection~Selection#getAttributes selection}. + * Filtering is realized based on `copyOnEnter` attribute property. Read more about attribute properties + * {@link module:engine/model/schema~Schema#setAttributeProperties here}. + * + * @param {module:engine/model/schema~Schema} schema + * @param {Iterable.<*>} allAttributes iterator with attributes of current selection. + * @returns {Iterable.<*>} filtered attributes which has `copyOnEnter` property. + */ +export function* getCopyOnEnterAttributes( schema, allAttributes ) { + for ( const attribute of allAttributes ) { + if ( attribute && schema.getAttributeProperties( attribute[ 0 ] ).copyOnEnter ) { + yield attribute; + } + } +} From c0c23c2fae98edf3536b0bb2345b7e58d7dbc908 Mon Sep 17 00:00:00 2001 From: Mateusz Samsel Date: Thu, 11 Jul 2019 17:06:43 +0200 Subject: [PATCH 05/10] Add unit test for util generator. --- tests/utils.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/utils.js diff --git a/tests/utils.js b/tests/utils.js new file mode 100644 index 0000000..37cb4cb --- /dev/null +++ b/tests/utils.js @@ -0,0 +1,38 @@ +/** + * @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 { getCopyOnEnterAttributes } from '../src/utils'; +import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor'; + +describe( 'utils', () => { + describe( 'getCopyOnEnterAttributes()', () => { + it( 'filters attributes with copyOnEnter property', () => { + return ModelTestEditor.create() + .then( editor => { + const schema = editor.model.schema; + + schema.register( 'foo', { inheritAllFrom: '$block' } ); + schema.register( 'bar', { inheritAllFrom: '$block' } ); + schema.register( 'baz', { inheritAllFrom: '$block' } ); + + schema.setAttributeProperties( 'foo', { copyOnEnter: true } ); + schema.setAttributeProperties( 'baz', { copyOnEnter: true } ); + + const allAttributes = ( new Map( [ + [ 'foo', true ], + [ 'bar', true ], + [ 'baz', true ] + ] ) )[ Symbol.iterator ](); + + expect( Array.from( getCopyOnEnterAttributes( schema, allAttributes ) ) ).to.deep.equal( + [ + [ 'foo', true ], + [ 'baz', true ] + ] + ); + } ); + } ); + } ); +} ); From 682944016aa999a6cab1c24377cbefdcdfc94cbf Mon Sep 17 00:00:00 2001 From: Mateusz Samsel Date: Fri, 12 Jul 2019 12:01:22 +0200 Subject: [PATCH 06/10] Prevent shiftenter to copy attribtues without copyOnEnter flag. --- src/shiftentercommand.js | 2 ++ tests/shiftentercommand.js | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/shiftentercommand.js b/src/shiftentercommand.js index 0dd83c5..aea50fa 100644 --- a/src/shiftentercommand.js +++ b/src/shiftentercommand.js @@ -84,6 +84,8 @@ function softBreakAction( model, writer, selection ) { if ( isSelectionEmpty ) { const attributesToCopy = getCopyOnEnterAttributes( model.schema, selection.getAttributes() ); insertBreak( writer, range.end ); + // transforms: [ [ key1, value1 ], ... ] => [ key1, ... ] + writer.removeSelectionAttribute( Array.from( selection.getAttributes() ).map( x => x[ 0 ] ) ); writer.setSelectionAttribute( attributesToCopy ); } else { const leaveUnmerged = !( range.start.isAtStart && range.end.isAtEnd ); diff --git a/tests/shiftentercommand.js b/tests/shiftentercommand.js index ca12217..08b5be6 100644 --- a/tests/shiftentercommand.js +++ b/tests/shiftentercommand.js @@ -101,6 +101,31 @@ describe( 'ShiftEnterCommand', () => { '

x

[]foo

y

', '

x

[]foo

y

' ); + + describe( 'copyOnEnter', () => { + beforeEach( () => { + schema.extend( '$text', { allowAttributes: [ 'foo', 'bar' ] } ); + schema.setAttributeProperties( 'foo', { copyOnEnter: true } ); + } ); + + test( + 'allowed attributes are copied', + '

<$text foo="true">test[]

', + '

<$text foo="true">test<$text foo="true">[]

' + ); + + test( + 'unknown attributes are not copied', + '

<$text bar="true">test[]

', + '

<$text bar="true">test[]

' + ); + + test( + 'only allowed attributes are copied from mix set', + '

<$text bar="true" foo="true">test[]

', + '

<$text bar="true" foo="true">test<$text foo="true">[]

' + ); + } ); } ); describe( 'non-collapsed selection', () => { From 0f39ad7bb63faf49a7cbaec253751815a3ae58a9 Mon Sep 17 00:00:00 2001 From: Mateusz Samsel Date: Tue, 16 Jul 2019 11:19:40 +0200 Subject: [PATCH 07/10] Update description. Improve unit test. --- src/shiftentercommand.js | 2 +- src/utils.js | 6 +++--- tests/utils.js | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/shiftentercommand.js b/src/shiftentercommand.js index aea50fa..77a3b30 100644 --- a/src/shiftentercommand.js +++ b/src/shiftentercommand.js @@ -84,7 +84,7 @@ function softBreakAction( model, writer, selection ) { if ( isSelectionEmpty ) { const attributesToCopy = getCopyOnEnterAttributes( model.schema, selection.getAttributes() ); insertBreak( writer, range.end ); - // transforms: [ [ key1, value1 ], ... ] => [ key1, ... ] + writer.removeSelectionAttribute( Array.from( selection.getAttributes() ).map( x => x[ 0 ] ) ); writer.setSelectionAttribute( attributesToCopy ); } else { diff --git a/src/utils.js b/src/utils.js index f428c7c..831c79f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -8,13 +8,13 @@ */ /** - * Returns iterator which has filtered attributes read from {@link module:engine/model/selection~Selection#getAttributes selection}. + * Returns attributes that should be preserved on the enter key. * Filtering is realized based on `copyOnEnter` attribute property. Read more about attribute properties * {@link module:engine/model/schema~Schema#setAttributeProperties here}. * * @param {module:engine/model/schema~Schema} schema - * @param {Iterable.<*>} allAttributes iterator with attributes of current selection. - * @returns {Iterable.<*>} filtered attributes which has `copyOnEnter` property. + * @param {Iterable.<*>} allAttributes attributes to filter. + * @returns {Iterable.<*>} */ export function* getCopyOnEnterAttributes( schema, allAttributes ) { for ( const attribute of allAttributes ) { diff --git a/tests/utils.js b/tests/utils.js index 37cb4cb..e1a5831 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -13,9 +13,9 @@ describe( 'utils', () => { .then( editor => { const schema = editor.model.schema; - schema.register( 'foo', { inheritAllFrom: '$block' } ); - schema.register( 'bar', { inheritAllFrom: '$block' } ); - schema.register( 'baz', { inheritAllFrom: '$block' } ); + schema.extend( '$text', { + allowAttributes: [ 'foo', 'bar', 'baz' ] + } ); schema.setAttributeProperties( 'foo', { copyOnEnter: true } ); schema.setAttributeProperties( 'baz', { copyOnEnter: true } ); From 005c1558d5436e0d984de9efb167cf3ea7e71ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 16 Jul 2019 11:52:37 +0200 Subject: [PATCH 08/10] Improve doclet for getCopyOnEnterAttributes() function. --- src/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils.js b/src/utils.js index 831c79f..66a68ac 100644 --- a/src/utils.js +++ b/src/utils.js @@ -9,6 +9,7 @@ /** * Returns attributes that should be preserved on the enter key. + * * Filtering is realized based on `copyOnEnter` attribute property. Read more about attribute properties * {@link module:engine/model/schema~Schema#setAttributeProperties here}. * From a58c94f90a743e742e0d50e241ef04c930f25ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 16 Jul 2019 11:56:04 +0200 Subject: [PATCH 09/10] Use selection.getAttributeKeys() for selection attributes names retrieval. --- src/shiftentercommand.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shiftentercommand.js b/src/shiftentercommand.js index 77a3b30..fe79752 100644 --- a/src/shiftentercommand.js +++ b/src/shiftentercommand.js @@ -85,7 +85,7 @@ function softBreakAction( model, writer, selection ) { const attributesToCopy = getCopyOnEnterAttributes( model.schema, selection.getAttributes() ); insertBreak( writer, range.end ); - writer.removeSelectionAttribute( Array.from( selection.getAttributes() ).map( x => x[ 0 ] ) ); + writer.removeSelectionAttribute( Array.from( selection.getAttributeKeys() ) ); writer.setSelectionAttribute( attributesToCopy ); } else { const leaveUnmerged = !( range.start.isAtStart && range.end.isAtEnd ); From 88d750dfefa0261c958d0743602667b9c874afda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 16 Jul 2019 12:03:38 +0200 Subject: [PATCH 10/10] Use Itarable directly when calling writer.removeSelectionAttribute(). --- src/shiftentercommand.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shiftentercommand.js b/src/shiftentercommand.js index fe79752..ceb6d22 100644 --- a/src/shiftentercommand.js +++ b/src/shiftentercommand.js @@ -85,7 +85,7 @@ function softBreakAction( model, writer, selection ) { const attributesToCopy = getCopyOnEnterAttributes( model.schema, selection.getAttributes() ); insertBreak( writer, range.end ); - writer.removeSelectionAttribute( Array.from( selection.getAttributeKeys() ) ); + writer.removeSelectionAttribute( selection.getAttributeKeys() ); writer.setSelectionAttribute( attributesToCopy ); } else { const leaveUnmerged = !( range.start.isAtStart && range.end.isAtEnd );