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

Commit 4f9ac0e

Browse files
author
Piotr Jasiun
authored
Merge pull request #1674 from ckeditor/t/1673
Fix: Update model selection attributes and markers after each change that affects the selection. Closes #1673.
2 parents 5862d70 + 082847c commit 4f9ac0e

File tree

2 files changed

+84
-11
lines changed

2 files changed

+84
-11
lines changed

src/model/documentselection.js

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ class LiveSelection extends Selection {
598598
// @type {Set}
599599
this._overriddenGravityRegister = new Set();
600600

601-
// Add events that will ensure selection correctness.
601+
// Ensure selection is correct and up to date after each range change.
602602
this.on( 'change:range', () => {
603603
for ( const range of this.getRanges() ) {
604604
if ( !this._document._validateSelectionRange( range ) ) {
@@ -615,20 +615,22 @@ class LiveSelection extends Selection {
615615
);
616616
}
617617
}
618-
} );
619618

620-
this.listenTo( this._document, 'change', ( evt, batch ) => {
621-
// Update selection's markers.
622619
this._updateMarkers();
623-
624-
// Update selection's attributes.
625620
this._updateAttributes( false );
626-
627-
// Clear selection attributes from element if no longer empty.
628-
clearAttributesStoredInElement( this._model, batch );
629621
} );
630622

631-
this.listenTo( this._model, 'applyOperation', () => {
623+
// Update markers data stored by the selection after each marker change.
624+
this.listenTo( this._model.markers, 'update', () => this._updateMarkers() );
625+
626+
// Ensure selection is correct and up to date after each operation.
627+
this.listenTo( this._model, 'applyOperation', ( evt, args ) => {
628+
const operation = args[ 0 ];
629+
630+
if ( !operation.isDocumentOperation || operation.type == 'marker' || operation.type == 'rename' || operation.type == 'noop' ) {
631+
return;
632+
}
633+
632634
while ( this._fixGraveyardRangesData.length ) {
633635
const { liveRange, sourcePosition } = this._fixGraveyardRangesData.shift();
634636

@@ -637,10 +639,17 @@ class LiveSelection extends Selection {
637639

638640
if ( this._hasChangedRange ) {
639641
this._hasChangedRange = false;
640-
641642
this.fire( 'change:range', { directChange: false } );
642643
}
644+
645+
this._updateMarkers();
646+
this._updateAttributes( false );
643647
}, { priority: 'lowest' } );
648+
649+
// Clear selection attributes from element if no longer empty.
650+
this.listenTo( this._document, 'change', ( evt, batch ) => {
651+
clearAttributesStoredInElement( this._model, batch );
652+
} );
644653
}
645654

646655
get isCollapsed() {

tests/model/documentselection.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,70 @@ describe( 'DocumentSelection', () => {
11411141
} );
11421142
} );
11431143

1144+
// https://github.com/ckeditor/ckeditor5-engine/issues/1673
1145+
describe( 'refreshing selection data', () => {
1146+
it( 'should be up to date when post fixers are called', done => {
1147+
model.schema.extend( '$text', { allowAttributes: 'foo' } );
1148+
1149+
const p = doc.getRoot().getChild( 0 );
1150+
1151+
doc.registerPostFixer( () => {
1152+
expect( model.document.selection.getAttribute( 'foo' ) ).to.equal( 'bar' );
1153+
expect( Array.from( model.document.selection.markers, m => m.name ) ).to.deep.equal( [ 'marker' ] );
1154+
done();
1155+
} );
1156+
1157+
model.change( writer => {
1158+
writer.insertText( 'abcdef', { foo: 'bar' }, p );
1159+
1160+
writer.addMarker( 'marker', {
1161+
range: writer.createRange(
1162+
writer.createPositionFromPath( p, [ 1 ] ),
1163+
writer.createPositionFromPath( p, [ 5 ] )
1164+
),
1165+
usingOperation: false
1166+
} );
1167+
1168+
writer.setSelection( writer.createPositionFromPath( p, [ 3 ] ) );
1169+
} );
1170+
} );
1171+
1172+
it( 'should be up to date when post fixers have changed the data', () => {
1173+
model.schema.extend( '$text', { allowAttributes: 'foo' } );
1174+
1175+
const p = doc.getRoot().getChild( 0 );
1176+
1177+
doc.registerPostFixer( writer => {
1178+
writer.setAttribute( 'foo', 'biz', p.getChild( 0 ) );
1179+
writer.removeMarker( 'marker-1' );
1180+
writer.addMarker( 'marker-2', {
1181+
range: writer.createRange(
1182+
writer.createPositionFromPath( p, [ 1 ] ),
1183+
writer.createPositionFromPath( p, [ 5 ] )
1184+
),
1185+
usingOperation: false
1186+
} );
1187+
} );
1188+
1189+
model.change( writer => {
1190+
writer.insertText( 'abcdef', { foo: 'bar' }, p );
1191+
1192+
writer.addMarker( 'marker-1', {
1193+
range: writer.createRange(
1194+
writer.createPositionFromPath( p, [ 1 ] ),
1195+
writer.createPositionFromPath( p, [ 5 ] )
1196+
),
1197+
usingOperation: false
1198+
} );
1199+
1200+
writer.setSelection( writer.createPositionFromPath( p, [ 3 ] ) );
1201+
} );
1202+
1203+
expect( model.document.selection.getAttribute( 'foo' ) ).to.equal( 'biz' );
1204+
expect( Array.from( model.document.selection.markers, m => m.name ) ).to.deep.equal( [ 'marker-2' ] );
1205+
} );
1206+
} );
1207+
11441208
// DocumentSelection uses LiveRanges so here are only simple test to see if integration is
11451209
// working well, without getting into complicated corner cases.
11461210
describe( 'after applying an operation should get updated and fire events', () => {

0 commit comments

Comments
 (0)