MapperCache
Cache mechanism for Mapper.
MapperCache improves performance for model-to-view position mapping, which is the main Mapper task. Asking for a mapping is much more frequent than actually performing changes, and even if the change happens, we can still partially keep the cache. This makes caching a useful strategy for Mapper.
MapperCache will store some data for view elements or view document fragments that are mapped by the Mapper. These view items are "tracked" by the MapperCache. For such view items, we will keep entries of model offsets inside their mapped counterpart. For the cached model offsets, we will keep a view position that is inside the tracked item. This allows us to either get the mapping instantly, or at least in less steps than when calculating it from the beginning.
Important problem related to caching is invalidating the cache. The cache must be invalidated each time the tracked view item changes. Additionally, we should invalidate as small part of the cache as possible. Since all the logic is encapsulated inside MapperCache, the MapperCache listens to view items change event and reacts to it. Then, it invalidates just the part of the cache that is "after" the changed part of the view.
As mentioned, MapperCache currently is used only for model-to-view position mapping as it was much bigger problem than view-to-model mapping. However, it should be possible to use it also for view-to-model.
The main assumptions regarding MapperCache are:
- it is an internal tool, used by
Mapper, transparent to the outside (no additional effort when developing a plugin or a converter), - it stores all the necessary data internally, which makes it easier to disable or debug,
- it is optimized for initial downcast process (long insertions), which is crucial for editor init and data save,
- it does not save all possible positions mapping for memory considerations, although it is a possible improvement, which may increase performance, as well as simplify some parts of the
MapperCachelogic.
Properties
_cachedMapping : WeakMap<ViewElement | ViewDocumentFragment, MappingCache>privatemodule:engine/conversion/mapper~MapperCache#_cachedMappingFor every view element or document fragment tracked by
MapperCache, it holds currently cached data, or more precisely, model offset to view position mappings. See alsoMappingCacheandCacheItem.If an item is tracked by
MapperCacheit has an entry in this structure, so this structure can be used to check which items are tracked byMapperCache. When an item is no longer tracked, it is removed from this structure.Although
MappingCacheandCacheItemstructures allows for caching any model offsets and view positions, we only cache values for model offsets that are after a view node. So, in essence, positions inside text nodes are not cached. However, it takes from one to at most a few steps, to get from a cached position to a position that is inside a view text node.Additionally, only one item per
modelOffsetis cached. There can be several view positions that map to the samemodelOffset. Only the first save formodelOffsetis stored.module:engine/conversion/mapper~MapperCache#_invalidateOnChildrenChangeCallbackCallback fired whenever there is a direct or indirect children change in tracked view element or tracked view document fragment.
This is specified as a property to make it easier to set as an event callback and to later turn off that event.
module:engine/conversion/mapper~MapperCache#_invalidateOnTextChangeCallbackCallback fired whenever a view text node directly or indirectly inside a tracked view element or tracked view document fragment changes its text data.
This is specified as a property to make it easier to set as an event callback and to later turn off that event.
_nodeToCacheListIndex : WeakMap<ViewNode, number>privatemodule:engine/conversion/mapper~MapperCache#_nodeToCacheListIndexWhen
MapperCachesaves view position -> model offset mapping, aCacheItemis inserted into certainMappingCache#cacheListat some index. Additionally, we store that index with the view node that is before the cached view position.This allows to quickly get a cache list item related to certain view node, and hence, for fast cache invalidation.
For example, consider view:
<p>Some <strong>bold</strong> text.</p>, where<p>is a view element tracked byMapperCache. If all<p>children were visited byMapperCache, then<p>cache list would have four items, related to following model offsets:0,5,9,15. Then, view node"Some "would have index1,<strong>index2, and" text." index3`.Note that the index related with a node is always greater than
0. The first item in cache list is always for model offset0(and view offset0), and it is not related to any node.
Methods
constructor()inheritedmodule:engine/conversion/mapper~MapperCache#constructordelegate( events ) → EmitterMixinDelegateChaininheritedmodule:engine/conversion/mapper~MapperCache#delegateDelegates selected events to another
Emitter. For instance:emitterA.delegate( 'eventX' ).to( emitterB ); emitterA.delegate( 'eventX', 'eventY' ).to( emitterC );Copy codethen
eventXis delegated (fired by)emitterBandemitterCalong withdata:emitterA.fire( 'eventX', data );Copy codeand
eventYis delegated (fired by)emitterCalong withdata:emitterA.fire( 'eventY', data );Copy codeParameters
events : Array<string>Event names that will be delegated to another emitter.
Returns
fire( eventOrInfo, args ) → GetEventInfo<TEvent>[ 'return' ]inheritedmodule:engine/conversion/mapper~MapperCache#fireFires an event, executing all callbacks registered for it.
The first parameter passed to callbacks is an
EventInfoobject, followed by the optionalargsprovided in thefire()method call.Type parameters
Parameters
eventOrInfo : GetNameOrEventInfo<TEvent>The name of the event or
EventInfoobject if event is delegated.args : TEvent[ 'args' ]Additional arguments to be passed to the callbacks.
Returns
GetEventInfo<TEvent>[ 'return' ]By default the method returns
undefined. However, the return value can be changed by listeners through modification of theevt.return's property (the event info is the first param of every callback).
getClosest( viewContainer, modelOffset ) → CacheItemmodule:engine/conversion/mapper~MapperCache#getClosestFor given
modelOffsetinside a model element mapped to givenviewContainer, it returns the closest saved cache item (view position and related model offset) to the requested one.It can be exactly the requested mapping, or it can be mapping that is the closest starting point to look for the requested mapping.
viewContainermust be a view element or document fragment that is mapped by the Mapper.If
viewContaineris not yet tracked by theMapperCache, it will be automatically tracked after calling this method.Note: this method will automatically "hoist" cached positions, i.e. it will return a position that is closest to the tracked element.
For example, if
<p>is tracked element, and^is cached position:<p>This is <strong>some <em>heavily <u>formatted</u>^</em></strong> text.</p>Copy codeIf this position would be returned, instead, a position directly in
<p>would be returned:<p>This is <strong>some <em>heavily <u>formatted</u></em></strong>^ text.</p>Copy codeNote, that
modelOffsetfor both positions is the same.Parameters
viewContainer : ViewElement | ViewDocumentFragmentTracked view element or document fragment, which cache will be used.
modelOffset : numberModel offset in a model element or document fragment, which is mapped to
viewContainer.
Returns
CacheItem
listenTo( emitter, event, callback, [ options ] ) → voidinheritedmodule:engine/conversion/mapper~MapperCache#listenTo:BASE_EMITTERRegisters a callback function to be executed when an event is fired in a specific (emitter) object.
Events can be grouped in namespaces using
:. When namespaced event is fired, it additionally fires all callbacks for that namespace.// myEmitter.on( ... ) is a shorthand for myEmitter.listenTo( myEmitter, ... ). myEmitter.on( 'myGroup', genericCallback ); myEmitter.on( 'myGroup:myEvent', specificCallback ); // genericCallback is fired. myEmitter.fire( 'myGroup' ); // both genericCallback and specificCallback are fired. myEmitter.fire( 'myGroup:myEvent' ); // genericCallback is fired even though there are no callbacks for "foo". myEmitter.fire( 'myGroup:foo' );Copy codeAn event callback can stop the event and set the return value of the
firemethod.Type parameters
Parameters
emitter : EmitterThe object that fires the event.
event : TEvent[ 'name' ]The name of the event.
callback : GetCallback<TEvent>The function to be called on event.
[ options ] : GetCallbackOptions<TEvent>Additional options.
Returns
void
off( event, callback ) → voidinheritedmodule:engine/conversion/mapper~MapperCache#offStops executing the callback on the given event. Shorthand for
this.stopListening( this, event, callback ).Parameters
event : stringThe name of the event.
callback : FunctionThe function to stop being called.
Returns
void
on( event, callback, [ options ] ) → voidinheritedmodule:engine/conversion/mapper~MapperCache#onRegisters a callback function to be executed when an event is fired.
Shorthand for
this.listenTo( this, event, callback, options )(it makes the emitter listen on itself).Type parameters
Parameters
event : TEvent[ 'name' ]The name of the event.
callback : GetCallback<TEvent>The function to be called on event.
[ options ] : GetCallbackOptions<TEvent>Additional options.
Returns
void
once( event, callback, [ options ] ) → voidinheritedmodule:engine/conversion/mapper~MapperCache#onceRegisters a callback function to be executed on the next time the event is fired only. This is similar to calling
onfollowed byoffin the callback.Type parameters
Parameters
event : TEvent[ 'name' ]The name of the event.
callback : GetCallback<TEvent>The function to be called on event.
[ options ] : GetCallbackOptions<TEvent>Additional options.
Returns
void
save( viewParent, viewOffset, viewContainer, modelOffset ) → voidmodule:engine/conversion/mapper~MapperCache#saveSaves cache for given view position mapping <-> model offset mapping. The view position should be after a node (i.e. it cannot be the first position inside its parent, or in other words,
viewOffsetmust be greater than0).Note, that if
modelOffsetfor givenviewContainerwas already saved, the stored view position (i.e. parent+offset) will not be overwritten. However, it is important to still save it, as we still store additional data related to cached view positions.Parameters
viewParent : ViewElement | ViewDocumentFragmentView position parent.
viewOffset : numberView position offset. Must be greater than
0.viewContainer : ViewElement | ViewDocumentFragmentTracked view position ascendant (it may be the direct parent of the view position).
modelOffset : numberModel offset in the model element or document fragment which is mapped to
viewContainer.
Returns
void
startTracking( viewContainer ) → CacheItemmodule:engine/conversion/mapper~MapperCache#startTrackingStarts tracking given
viewContainer, which must be mapped to a model element or model document fragment.Note, that this method is automatically called by
MapperCache#getClosest()and there is no need to call it manually.This method initializes the cache for
viewContainerand adds callbacks forchangeevent fired byviewContainer.MapperCachelistens tochangeevent on the tracked elements to invalidate the stored cache.Parameters
viewContainer : ViewElement | ViewDocumentFragment
Returns
CacheItem
stopDelegating( [ event ], [ emitter ] ) → voidinheritedmodule:engine/conversion/mapper~MapperCache#stopDelegatingStops delegating events. It can be used at different levels:
- To stop delegating all events.
- To stop delegating a specific event to all emitters.
- To stop delegating a specific event to a specific emitter.
Parameters
[ event ] : stringThe name of the event to stop delegating. If omitted, stops it all delegations.
[ emitter ] : Emitter(requires
event) The object to stop delegating a particular event to. If omitted, stops delegation ofeventto all emitters.
Returns
void
stopListening( [ emitter ], [ event ], [ callback ] ) → voidinheritedmodule:engine/conversion/mapper~MapperCache#stopListening:BASE_STOPStops listening for events. It can be used at different levels:
- To stop listening to a specific callback.
- To stop listening to a specific event.
- To stop listening to all events fired by a specific object.
- To stop listening to all events fired by all objects.
Parameters
[ emitter ] : EmitterThe object to stop listening to. If omitted, stops it for all objects.
[ event ] : string(Requires the
emitter) The name of the event to stop listening to. If omitted, stops it for all events fromemitter.[ callback ] : Function(Requires the
event) The function to be removed from the call list for the givenevent.
Returns
void
stopTracking( viewContainer ) → voidmodule:engine/conversion/mapper~MapperCache#stopTrackingStops tracking given
viewContainer.It removes the cached data and stops listening to
changeevent on theviewContainer.Parameters
viewContainer : ViewElement | ViewDocumentFragment
Returns
void
_clearCacheAfter( viewNode ) → voidprivatemodule:engine/conversion/mapper~MapperCache#_clearCacheAfterClears all the stored cache that is after given
viewNode. TheviewNodecan be any node that is inside a tracked view element or view document fragment.In reality, this function may clear a bit more cache than just "starting after"
viewNode, but it is guaranteed that at least all cache afterviewNodeis invalidated.Parameters
viewNode : ViewNode
Returns
void
_clearCacheAll( viewContainer ) → voidprivatemodule:engine/conversion/mapper~MapperCache#_clearCacheAllClears all the cache for given tracked
viewContainer.Parameters
viewContainer : ViewElement | ViewDocumentFragment
Returns
void
_clearCacheFromCacheIndex( viewContainer, index ) → voidprivatemodule:engine/conversion/mapper~MapperCache#_clearCacheFromCacheIndexClears all the cache in the cache list related to given
viewContainer, starting fromindex(inclusive).Parameters
viewContainer : ViewElement | ViewDocumentFragmentindex : number
Returns
void
_clearCacheInsideParent( viewParent, index ) → voidprivatemodule:engine/conversion/mapper~MapperCache#_clearCacheInsideParentInvalidates cache inside
viewParent, starting from givenindexin that parent.This method may clear a bit more cache than just what was saved after given
index, but it is guaranteed that at least it will invalidate everything afterindex.Parameters
viewParent : ViewElement | ViewDocumentFragmentindex : number
Returns
void
_findInCacheList( cacheList, offset ) → CacheItemprivatemodule:engine/conversion/mapper~MapperCache#_findInCacheListFinds a cache item in the given cache list, which
modelOffsetis closest (but smaller or equal) to givenoffset.Since
cacheListis a sorted array, this uses binary search to retrieve the item quickly.Parameters
cacheList : Array<CacheItem>offset : number
Returns
CacheItem