@@ -61,6 +61,40 @@ export default class Differ {
6161 * @type {Number }
6262 */
6363 this . _changeCount = 0 ;
64+
65+ /**
66+ * For efficiency purposes, `Differ` stores the change set returned by the differ after {@link #getChanges} call.
67+ * Cache is reset each time a new operation is buffered. If the cache has not been reset, {@link #getChanges} will
68+ * return the cached value instead of calculating it again.
69+ *
70+ * This property stores those changes that did not take place in graveyard root.
71+ *
72+ * @private
73+ * @type {Array.<Object>|null }
74+ */
75+ this . _cachedChanges = null ;
76+
77+ /**
78+ * For efficiency purposes, `Differ` stores the change set returned by the differ after {@link #getChanges} call.
79+ * Cache is reset each time a new operation is buffered. If the cache has not been reset, {@link #getChanges} will
80+ * return the cached value instead of calculating it again.
81+ *
82+ * This property stores all changes evaluated by `Differ`, also those that took place in graveyard.
83+ *
84+ * @private
85+ * @type {Array.<Object>|null }
86+ */
87+ this . _cachedChangesWithGraveyard = null ;
88+ }
89+
90+ /**
91+ * Informs whether there are any changes buffered in `Differ`.
92+ *
93+ * @readonly
94+ * @type {Boolean }
95+ */
96+ get isEmpty ( ) {
97+ return this . _changesInElement . size == 0 && this . _changedMarkers . size == 0 ;
6498 }
6599
66100 /**
@@ -98,6 +132,9 @@ export default class Differ {
98132
99133 break ;
100134 }
135+
136+ // Clear cache after each buffered operation as it is no longer valid.
137+ this . _cachedChanges = null ;
101138 }
102139
103140 /**
@@ -168,9 +205,24 @@ export default class Differ {
168205 * the position on which the change happened. If a position {@link module:engine/model/position~Position#isBefore is before}
169206 * another one, it will be on an earlier index in the diff set.
170207 *
208+ * Because calculating diff is a costly operation, the result is cached. If no new operation was buffered since the
209+ * previous {@link #getChanges} call, the next call with return the cached value.
210+ *
211+ * @param {Object } options Additional options.
212+ * @param {Boolean } [options.includeChangesInGraveyard=false] If set to `true`, also changes that happened
213+ * in graveyard root will be returned. By default, changes in graveyard root are not returned.
171214 * @returns {Array.<Object> } Diff between old and new model tree state.
172215 */
173- getChanges ( ) {
216+ getChanges ( options = { includeChangesInGraveyard : false } ) {
217+ // If there are cached changes, just return them instead of calculating changes again.
218+ if ( this . _cachedChanges ) {
219+ if ( options . includeChangesInGraveyard ) {
220+ return this . _cachedChangesWithGraveyard . slice ( ) ;
221+ } else {
222+ return this . _cachedChanges . slice ( ) ;
223+ }
224+ }
225+
174226 // Will contain returned results.
175227 const diffSet = [ ] ;
176228
@@ -320,7 +372,15 @@ export default class Differ {
320372
321373 this . _changeCount = 0 ;
322374
323- return diffSet ;
375+ // Cache changes.
376+ this . _cachedChangesWithGraveyard = diffSet . slice ( ) ;
377+ this . _cachedChanges = diffSet . slice ( ) . filter ( _changesInGraveyardFilter ) ;
378+
379+ if ( options . includeChangesInGraveyard ) {
380+ return this . _cachedChangesWithGraveyard ;
381+ } else {
382+ return this . _cachedChanges ;
383+ }
324384 }
325385
326386 /**
@@ -330,6 +390,7 @@ export default class Differ {
330390 this . _changesInElement . clear ( ) ;
331391 this . _elementSnapshots . clear ( ) ;
332392 this . _changedMarkers . clear ( ) ;
393+ this . _cachedChanges = null ;
333394 }
334395
335396 /**
@@ -865,3 +926,11 @@ function _generateActionsFromChanges( oldChildrenLength, changes ) {
865926
866927 return actions ;
867928}
929+
930+ // Filter callback for Array.filter that filters out change entries that are in graveyard.
931+ function _changesInGraveyardFilter ( entry ) {
932+ const posInGy = entry . position && entry . position . root . rootName == '$graveyard' ;
933+ const rangeInGy = entry . range && entry . range . root . rootName == '$graveyard' ;
934+
935+ return ! posInGy && ! rangeInGy ;
936+ }
0 commit comments