-
Notifications
You must be signed in to change notification settings - Fork 40
Fix: Prevented from losing selection attributes between operations - IME. #1730
Changes from 2 commits
72d6b60
809f3a0
87d79b6
c7a0c74
961383f
01fa7f9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -307,6 +307,9 @@ export default class Document { | |
if ( this._hasDocumentChangedFromTheLastChangeBlock() ) { | ||
this._callPostFixers( writer ); | ||
|
||
// Refresh selection attributes according to the final position in the model after the change. | ||
this.selection.refreshAttributes(); | ||
|
||
if ( this.differ.hasDataChanges() ) { | ||
this.fire( 'change:data', writer.batch ); | ||
} else { | ||
|
@@ -391,6 +394,10 @@ export default class Document { | |
|
||
do { | ||
for ( const callback of this._postFixers ) { | ||
// Ensure selection attributes are up to date before each post-fixer. | ||
// https://github.com/ckeditor/ckeditor5-engine/issues/1673. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd add here additional comment on why it is done like this and not after every operation so we won't be re-thinking this problem again in a year. |
||
this.selection.refreshAttributes(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like hard-coding it like this here but I can't find a better idea :(. |
||
|
||
wasFixed = callback( writer ); | ||
|
||
if ( wasFixed ) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -359,6 +359,14 @@ export default class DocumentSelection { | |
return this._selection.hasAttribute( key ); | ||
} | ||
|
||
/** | ||
* Refreshes selection attributes and markers according to the current position in the model. | ||
*/ | ||
refreshAttributes() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be named There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or... different. It doesn't look good when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
this._selection._updateMarkers(); | ||
this._selection._updateAttributes( false ); | ||
} | ||
|
||
/** | ||
* Checks whether object is of given type following the convention set by | ||
* {@link module:engine/model/node~Node#is `Node#is()`}. | ||
|
@@ -598,6 +606,26 @@ class LiveSelection extends Selection { | |
// @type {Set} | ||
this._overriddenGravityRegister = new Set(); | ||
|
||
// Ensure selection is correct after each operation. | ||
this.listenTo( this._model, 'applyOperation', ( evt, args ) => { | ||
const operation = args[ 0 ]; | ||
|
||
if ( !operation.isDocumentOperation || operation.type == 'marker' || operation.type == 'rename' || operation.type == 'noop' ) { | ||
return; | ||
} | ||
|
||
while ( this._fixGraveyardRangesData.length ) { | ||
const { liveRange, sourcePosition } = this._fixGraveyardRangesData.shift(); | ||
|
||
this._fixGraveyardSelection( liveRange, sourcePosition ); | ||
} | ||
|
||
if ( this._hasChangedRange ) { | ||
this._hasChangedRange = false; | ||
this.fire( 'change:range', { directChange: false } ); | ||
} | ||
}, { priority: 'lowest' } ); | ||
|
||
// Ensure selection is correct and up to date after each range change. | ||
this.on( 'change:range', () => { | ||
for ( const range of this.getRanges() ) { | ||
|
@@ -615,38 +643,12 @@ class LiveSelection extends Selection { | |
); | ||
} | ||
} | ||
|
||
this._updateMarkers(); | ||
this._updateAttributes( false ); | ||
} ); | ||
|
||
// Update markers data stored by the selection after each marker change. | ||
this.listenTo( this._model.markers, 'update', () => this._updateMarkers() ); | ||
|
||
// Ensure selection is correct and up to date after each operation. | ||
this.listenTo( this._model, 'applyOperation', ( evt, args ) => { | ||
const operation = args[ 0 ]; | ||
|
||
if ( !operation.isDocumentOperation || operation.type == 'marker' || operation.type == 'rename' || operation.type == 'noop' ) { | ||
return; | ||
} | ||
|
||
while ( this._fixGraveyardRangesData.length ) { | ||
const { liveRange, sourcePosition } = this._fixGraveyardRangesData.shift(); | ||
|
||
this._fixGraveyardSelection( liveRange, sourcePosition ); | ||
} | ||
|
||
if ( this._hasChangedRange ) { | ||
this._hasChangedRange = false; | ||
this.fire( 'change:range', { directChange: false } ); | ||
} | ||
|
||
this._updateMarkers(); | ||
this._updateAttributes( false ); | ||
}, { priority: 'lowest' } ); | ||
|
||
// Clear selection attributes from element if no longer empty. | ||
// Ensure selection is up to date after each change block. | ||
this.listenTo( this._document, 'change', ( evt, batch ) => { | ||
clearAttributesStoredInElement( this._model, batch ); | ||
} ); | ||
|
@@ -715,12 +717,12 @@ class LiveSelection extends Selection { | |
|
||
setTo( selectable, optionsOrPlaceOrOffset, options ) { | ||
super.setTo( selectable, optionsOrPlaceOrOffset, options ); | ||
this._refreshAttributes(); | ||
this._updateAttributes( true ); | ||
} | ||
|
||
setFocus( itemOrPosition, offset ) { | ||
super.setFocus( itemOrPosition, offset ); | ||
this._refreshAttributes(); | ||
this._updateAttributes( true ); | ||
} | ||
|
||
setAttribute( key, value ) { | ||
|
@@ -747,7 +749,7 @@ class LiveSelection extends Selection { | |
this._overriddenGravityRegister.add( overrideUid ); | ||
|
||
if ( this._overriddenGravityRegister.size === 1 ) { | ||
this._refreshAttributes(); | ||
this._updateAttributes( true ); | ||
} | ||
|
||
return overrideUid; | ||
|
@@ -773,15 +775,10 @@ class LiveSelection extends Selection { | |
|
||
// Restore gravity only when all overriding have been restored. | ||
if ( !this.isGravityOverridden ) { | ||
this._refreshAttributes(); | ||
this._updateAttributes( true ); | ||
} | ||
} | ||
|
||
// Removes all attributes from the selection and sets attributes according to the surrounding nodes. | ||
_refreshAttributes() { | ||
this._updateAttributes( true ); | ||
} | ||
|
||
_popRange() { | ||
this._ranges.pop().detach(); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't it be both before and after
change
event?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Theoretically, it should be not necessary. Changing the model on
change
event is not recommended, post fixers are the last who should change the model.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or I missed something?