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

Commit 8eba5e9

Browse files
authored
Merge pull request #1288 from ckeditor/t/1284
Other: Keep the same marker instance when marker is updated.
2 parents dd9ae51 + 776e604 commit 8eba5e9

File tree

5 files changed

+229
-91
lines changed

5 files changed

+229
-91
lines changed

src/controller/editingcontroller.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,13 @@ export default class EditingController {
131131
}
132132
}, { priority: 'high' } );
133133

134-
// If a marker is removed through `model.Model#markers` directly (not through operation), just remove it (if
135-
// it was not removed already).
136-
this.listenTo( model.markers, 'remove', ( evt, marker ) => {
137-
if ( !removedMarkers.has( marker.name ) ) {
134+
// If an existing marker is updated through `model.Model#markers` directly (not through operation), just remove it.
135+
this.listenTo( model.markers, 'update', ( evt, marker, oldRange ) => {
136+
if ( oldRange && !removedMarkers.has( marker.name ) ) {
138137
removedMarkers.add( marker.name );
138+
139139
this.view.change( writer => {
140-
this.downcastDispatcher.convertMarkerRemove( marker.name, marker.getRange(), writer );
140+
this.downcastDispatcher.convertMarkerRemove( marker.name, oldRange, writer );
141141
} );
142142
}
143143
} );

src/model/document.js

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -161,21 +161,16 @@ export default class Document {
161161
// Buffer marker changes.
162162
// This is not covered in buffering operations because markers may change outside of them (when they
163163
// are modified using `model.markers` collection, not through `MarkerOperation`).
164-
this.listenTo( model.markers, 'set', ( evt, marker ) => {
165-
// TODO: Should filter out changes of markers that are not in document.
166-
// Whenever a new marker is added, buffer that change.
167-
this.differ.bufferMarkerChange( marker.name, null, marker.getRange() );
168-
169-
// Whenever marker changes, buffer that.
170-
marker.on( 'change', ( evt, oldRange ) => {
171-
this.differ.bufferMarkerChange( marker.name, oldRange, marker.getRange() );
172-
} );
173-
} );
174-
175-
this.listenTo( model.markers, 'remove', ( evt, marker ) => {
176-
// TODO: Should filter out changes of markers that are not in document.
177-
// Whenever marker is removed, buffer that change.
178-
this.differ.bufferMarkerChange( marker.name, marker.getRange(), null );
164+
this.listenTo( model.markers, 'update', ( evt, marker, oldRange, newRange ) => {
165+
// Whenever marker is updated, buffer that change.
166+
this.differ.bufferMarkerChange( marker.name, oldRange, newRange );
167+
168+
if ( !oldRange ) {
169+
// Whenever marker changes, buffer that.
170+
marker.on( 'change', ( evt, oldRange ) => {
171+
this.differ.bufferMarkerChange( marker.name, oldRange, marker.getRange() );
172+
} );
173+
}
179174
} );
180175
}
181176

src/model/markercollection.js

Lines changed: 88 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
1717
/**
1818
* The collection of all {@link module:engine/model/markercollection~Marker markers} attached to the document.
1919
* It lets you {@link module:engine/model/markercollection~MarkerCollection#get get} markers or track them using
20-
* {@link module:engine/model/markercollection~MarkerCollection#event:set} and
21-
* {@link module:engine/model/markercollection~MarkerCollection#event:remove} events.
20+
* {@link module:engine/model/markercollection~MarkerCollection#event:update} event.
2221
*
2322
* To create, change or remove makers use {@link module:engine/model/writer~Writer model writers'} methods:
2423
* {@link module:engine/model/writer~Writer#setMarker} or {@link module:engine/model/writer~Writer#removeMarker}. Since
@@ -79,37 +78,47 @@ export default class MarkerCollection {
7978
* Creates and adds a {@link ~Marker marker} to the `MarkerCollection` with given name on given
8079
* {@link module:engine/model/range~Range range}.
8180
*
82-
* If `MarkerCollection` already had a marker with given name (or {@link ~Marker marker} was passed) and the range to
83-
* set is different, the marker in collection is removed and then new marker is added. If the range was same, nothing
84-
* happens and `false` is returned.
81+
* If `MarkerCollection` already had a marker with given name (or {@link ~Marker marker} was passed), the marker in
82+
* collection is updated and {@link module:engine/model/markercollection~MarkerCollection#event:update} event is fired
83+
* but only if there was a change (marker range or {@link ~Marker#managedUsingOperations} flag has changed.
8584
*
8685
* @protected
87-
* @fires module:engine/model/markercollection~MarkerCollection#event:set
88-
* @fires module:engine/model/markercollection~MarkerCollection#event:remove
86+
* @fires module:engine/model/markercollection~MarkerCollection#event:update
8987
* @param {String|module:engine/model/markercollection~Marker} markerOrName Name of marker to set or marker instance to update.
9088
* @param {module:engine/model/range~Range} range Marker range.
91-
* @param {Boolean} managedUsingOperations Specifies whether the marker is managed using operations.
92-
* @returns {module:engine/model/markercollection~Marker} `Marker` instance added to the collection.
89+
* @param {Boolean} [managedUsingOperations=false] Specifies whether the marker is managed using operations.
90+
* @returns {module:engine/model/markercollection~Marker} `Marker` instance which was added or updated.
9391
*/
94-
_set( markerOrName, range, managedUsingOperations ) {
92+
_set( markerOrName, range, managedUsingOperations = false ) {
9593
const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
9694
const oldMarker = this._markers.get( markerName );
9795

9896
if ( oldMarker ) {
9997
const oldRange = oldMarker.getRange();
98+
let hasChanged = false;
99+
100+
if ( !oldRange.isEqual( range ) ) {
101+
oldMarker._attachLiveRange( LiveRange.createFromRange( range ) );
102+
hasChanged = true;
103+
}
100104

101-
if ( oldRange.isEqual( range ) && managedUsingOperations === oldMarker.managedUsingOperations ) {
102-
return oldMarker;
105+
if ( managedUsingOperations != oldMarker.managedUsingOperations ) {
106+
oldMarker._managedUsingOperations = managedUsingOperations;
107+
hasChanged = true;
103108
}
104109

105-
this._remove( markerName );
110+
if ( hasChanged ) {
111+
this.fire( 'update:' + markerName, oldMarker, oldRange, range );
112+
}
113+
114+
return oldMarker;
106115
}
107116

108117
const liveRange = LiveRange.createFromRange( range );
109118
const marker = new Marker( markerName, liveRange, managedUsingOperations );
110119

111120
this._markers.set( markerName, marker );
112-
this.fire( 'set:' + markerName, marker );
121+
this.fire( 'update:' + markerName, marker, null, range );
113122

114123
return marker;
115124
}
@@ -118,6 +127,7 @@ export default class MarkerCollection {
118127
* Removes given {@link ~Marker marker} or a marker with given name from the `MarkerCollection`.
119128
*
120129
* @protected
130+
* @fires module:engine/model/markercollection~MarkerCollection#event:update
121131
* @param {String} markerOrName Marker or name of a marker to remove.
122132
* @returns {Boolean} `true` if marker was found and removed, `false` otherwise.
123133
*/
@@ -127,7 +137,7 @@ export default class MarkerCollection {
127137

128138
if ( oldMarker ) {
129139
this._markers.delete( markerName );
130-
this.fire( 'remove:' + markerName, oldMarker );
140+
this.fire( 'update:' + markerName, oldMarker, oldMarker.getRange(), null );
131141

132142
this._destroyMarker( oldMarker );
133143

@@ -193,22 +203,18 @@ export default class MarkerCollection {
193203
*/
194204
_destroyMarker( marker ) {
195205
marker.stopListening();
196-
marker._liveRange.detach();
197-
marker._liveRange = null;
206+
marker._detachLiveRange();
198207
}
199208

200209
/**
201-
* Fired whenever marker is added or updated in `MarkerCollection`.
210+
* Fired whenever marker is added, updated or removed from `MarkerCollection`.
202211
*
203-
* @event set
204-
* @param {module:engine/model/markercollection~Marker} The set marker.
205-
*/
206-
207-
/**
208-
* Fired whenever marker is removed from `MarkerCollection`.
209-
*
210-
* @event remove
211-
* @param {module:engine/model/markercollection~Marker} marker The removed marker.
212+
* @event update
213+
* @param {module:engine/model/markercollection~Marker} Updated Marker.
214+
* @param {module:engine/model/range~Range|null} oldRange Marker range before the update. When is not defined it
215+
* means that marker is just added.
216+
* @param {module:engine/model/range~Range|null} newRange Marker range after update. When is not defined it
217+
* means that marker is just removed.
212218
*/
213219
}
214220

@@ -291,6 +297,7 @@ class Marker {
291297
*
292298
* @param {String} name Marker name.
293299
* @param {module:engine/model/liverange~LiveRange} liveRange Range marked by the marker.
300+
* @param {Boolean} managedUsingOperations Specifies whether the marker is managed using operations.
294301
*/
295302
constructor( name, liveRange, managedUsingOperations ) {
296303
/**
@@ -302,25 +309,35 @@ class Marker {
302309
this.name = name;
303310

304311
/**
305-
* Flag indicates if the marker is managed using operations or not. See {@link ~Marker marker class description}
306-
* to learn more about marker types. See {@link module:engine/model/writer~Writer#setMarker}.
312+
* Flag indicates if the marker is managed using operations or not.
307313
*
308-
* @readonly
309-
* @type {Boolean}
314+
* @protected
315+
* @member {Boolean}
310316
*/
311-
this.managedUsingOperations = managedUsingOperations;
317+
this._managedUsingOperations = managedUsingOperations;
312318

313319
/**
314320
* Range marked by the marker.
315321
*
316-
* @protected
317-
* @type {module:engine/model/liverange~LiveRange}
322+
* @private
323+
* @member {module:engine/model/liverange~LiveRange} #_liveRange
318324
*/
319-
this._liveRange = liveRange;
325+
this._liveRange = this._attachLiveRange( liveRange );
326+
}
320327

321-
// Delegating does not work with namespaces. Alternatively, we could delegate all events (using `*`).
322-
this._liveRange.delegate( 'change:range' ).to( this );
323-
this._liveRange.delegate( 'change:content' ).to( this );
328+
/**
329+
* Returns value of flag indicates if the marker is managed using operations or not.
330+
* See {@link ~Marker marker class description} to learn more about marker types.
331+
* See {@link module:engine/model/writer~Writer#setMarker}.
332+
*
333+
* @returns {Boolean}
334+
*/
335+
get managedUsingOperations() {
336+
if ( !this._liveRange ) {
337+
throw new CKEditorError( 'marker-destroyed: Cannot use a destroyed marker instance.' );
338+
}
339+
340+
return this._managedUsingOperations;
324341
}
325342

326343
/**
@@ -369,6 +386,39 @@ class Marker {
369386
return Range.createFromRange( this._liveRange );
370387
}
371388

389+
/**
390+
* Binds new live range to marker and detach the old one if is attached.
391+
*
392+
* @protected
393+
* @param {module:engine/model/liverange~LiveRange} liveRange Live range to attach
394+
* @return {module:engine/model/liverange~LiveRange} Attached live range.
395+
*/
396+
_attachLiveRange( liveRange ) {
397+
if ( this._liveRange ) {
398+
this._detachLiveRange();
399+
}
400+
401+
// Delegating does not work with namespaces. Alternatively, we could delegate all events (using `*`).
402+
liveRange.delegate( 'change:range' ).to( this );
403+
liveRange.delegate( 'change:content' ).to( this );
404+
405+
this._liveRange = liveRange;
406+
407+
return liveRange;
408+
}
409+
410+
/**
411+
* Unbinds and destroys currently attached live range.
412+
*
413+
* @protected
414+
*/
415+
_detachLiveRange() {
416+
this._liveRange.stopDelegating( 'change:range', this );
417+
this._liveRange.stopDelegating( 'change:content', this );
418+
this._liveRange.detach();
419+
this._liveRange = null;
420+
}
421+
372422
/**
373423
* Fired whenever {@link ~Marker#_liveRange marker range} is changed due to changes on {@link module:engine/model/document~Document}.
374424
* This is a delegated {@link module:engine/model/liverange~LiveRange#event:change:range LiveRange change:range event}.

0 commit comments

Comments
 (0)