@@ -85,15 +85,12 @@ export default class EditingController {
8585
8686 const doc = this . model . document ;
8787
88- // When all changes are done, get the model diff containing all the changes and convert them to view and then render to DOM.
8988 this . listenTo ( doc , 'change' , ( ) => {
90- // Convert changes stored in `modelDiffer`.
9189 this . modelToView . convertChanges ( doc . differ ) ;
90+ } , { priority : 'low' } ) ;
9291
93- // After the view is ready, convert selection from model to view.
92+ this . listenTo ( model , '_change' , ( ) => {
9493 this . modelToView . convertSelection ( doc . selection ) ;
95-
96- // When everything is converted to the view, render it to DOM.
9794 this . view . render ( ) ;
9895 } , { priority : 'low' } ) ;
9996
@@ -110,6 +107,51 @@ export default class EditingController {
110107 this . modelToView . on ( 'selection' , convertRangeSelection ( ) , { priority : 'low' } ) ;
111108 this . modelToView . on ( 'selection' , convertCollapsedSelection ( ) , { priority : 'low' } ) ;
112109
110+ // Convert markers removal.
111+ //
112+ // Markers should be removed from the view before changes to the model are applied. This is because otherwise
113+ // it would be impossible to map some markers to the view (if, for example, the marker's boundary parent got removed).
114+ //
115+ // `removedMarkers` keeps information which markers already has been removed to prevent removing them twice.
116+ const removedMarkers = new Set ( ) ;
117+
118+ this . listenTo ( model , 'applyOperation' , ( evt , args ) => {
119+ // Before operation is applied...
120+ const operation = args [ 0 ] ;
121+
122+ for ( const marker of model . markers ) {
123+ // Check all markers, that aren't already removed...
124+ if ( removedMarkers . has ( marker . name ) ) {
125+ continue ;
126+ }
127+
128+ const markerRange = marker . getRange ( ) ;
129+
130+ if ( _operationAffectsMarker ( operation , marker ) ) {
131+ // And if the operation in any way modifies the marker, remove the marker from the view.
132+ removedMarkers . add ( marker . name ) ;
133+ this . modelToView . convertMarkerRemove ( marker . name , markerRange ) ;
134+
135+ // TODO: This stinks but this is the safest place to have this code.
136+ this . model . document . differ . bufferMarkerChange ( marker . name , markerRange , markerRange ) ;
137+ }
138+ }
139+ } , { priority : 'high' } ) ;
140+
141+ // If a marker is removed through `model.Model#markers` directly (not through operation), just remove it (if
142+ // it was not removed already).
143+ this . listenTo ( model . markers , 'remove' , ( evt , marker ) => {
144+ if ( ! removedMarkers . has ( marker . name ) ) {
145+ removedMarkers . add ( marker . name ) ;
146+ this . modelToView . convertMarkerRemove ( marker . name , marker . getRange ( ) ) ;
147+ }
148+ } ) ;
149+
150+ // When all changes are done, clear `removedMarkers` set.
151+ this . listenTo ( model , '_change' , ( ) => {
152+ removedMarkers . clear ( ) ;
153+ } , { priority : 'low' } ) ;
154+
113155 // Binds {@link module:engine/view/document~Document#roots view roots collection } to
114156 // {@link module:engine/model/document~Document#roots model roots collection } so creating
115157 // model root automatically creates corresponding view root.
@@ -140,3 +182,23 @@ export default class EditingController {
140182}
141183
142184mix ( EditingController , ObservableMixin ) ;
185+
186+ // Helper function which checks whether given operation will affect given marker after the operation is applied.
187+ function _operationAffectsMarker ( operation , marker ) {
188+ const range = marker . getRange ( ) ;
189+
190+ if ( operation . type == 'insert' || operation . type == 'rename' ) {
191+ return _positionAffectsRange ( operation . position , range ) ;
192+ } else if ( operation . type == 'move' || operation . type == 'remove' || operation . type == 'reinsert' ) {
193+ return _positionAffectsRange ( operation . targetPosition , range ) || _positionAffectsRange ( operation . sourcePosition , range ) ;
194+ } else if ( operation . type == 'marker' && operation . name == marker . name ) {
195+ return true ;
196+ }
197+
198+ return false ;
199+ }
200+
201+ // Helper function which checks whether change at given position affects given range.
202+ function _positionAffectsRange ( position , range ) {
203+ return range . containsPosition ( position ) || ! range . start . _getTransformedByInsertion ( position , 1 , true ) . isEqual ( range . start ) ;
204+ }
0 commit comments