Skip to content

Commit

Permalink
🐎 Speed up rendering by sharing cache between instances
Browse files Browse the repository at this point in the history
  • Loading branch information
abe33 committed Dec 2, 2014
1 parent 6104fe1 commit 6fd00fa
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 46 deletions.
52 changes: 6 additions & 46 deletions lib/minimap-render-view.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
{CompositeDisposable, Disposable} = require 'event-kit'
Delegato = require 'delegato'
DecorationManagement = require './mixins/decoration-management'
DOMStylesReader = require './mixins/dom-styles-reader'
[deprecate] = []

# Public: The {MinimapRenderView} class is responsible to render the minimap
Expand All @@ -11,6 +12,7 @@ module.exports =
class MinimapRenderView extends ScrollView
Delegato.includeInto(this)
DecorationManagement.includeInto(this)
DOMStylesReader.includeInto(this)

@delegatesMethods 'getMarker', 'findMarkers', toProperty: 'editor'

Expand Down Expand Up @@ -65,6 +67,7 @@ class MinimapRenderView extends ScrollView
@emitter.emit 'did-change-scale'
@forceUpdate()
@subscriptions.add atom.config.observe 'minimap.textOpacity', (@textOpacity) =>
@invalidateCache()
@forceUpdate()

# Destroys the {MinimapRenderView} instance, unsubscribes from the listened
Expand Down Expand Up @@ -108,6 +111,7 @@ class MinimapRenderView extends ScrollView

@subscriptions.add @displayBuffer.onDidTokenize =>
@tokenized = true
@invalidateCache()
@forceUpdate()

@tokenized = true if @displayBuffer.tokenizedBuffer.fullyTokenized
Expand Down Expand Up @@ -169,8 +173,6 @@ class MinimapRenderView extends ScrollView
#
# All the caches are cleared when calling this method.
forceUpdate: ->
@tokenColorCache = {}
@decorationColorCache = {}
@offscreenFirstRow = null
@offscreenLastRow = null
@requestUpdate()
Expand Down Expand Up @@ -347,10 +349,7 @@ class MinimapRenderView extends ScrollView
getTokenColor: (token) ->
#Retrieve color from cache if available
flatScopes = (token.scopeDescriptor or token.scopes).join()
if flatScopes not of @tokenColorCache
color = @retrieveTokenColorFromDom(token)
@tokenColorCache[flatScopes] = color
@tokenColorCache[flatScopes]
@retrieveTokenColorFromDom(token)

# Returns the background color for the passed-in `decoration` object.
#
Expand All @@ -364,10 +363,7 @@ class MinimapRenderView extends ScrollView
getDecorationColor: (decoration) ->
properties = decoration.getProperties()
return properties.color if properties.color?
if properties.scope not of @decorationColorCache
color = @retrieveDecorationColorFromDom(decoration)
@decorationColorCache[properties.scope] = color
@decorationColorCache[properties.scope]
@retrieveDecorationColorFromDom(decoration)

# Internal: Returns the text color for the passed-in token.
#
Expand All @@ -388,42 +384,6 @@ class MinimapRenderView extends ScrollView
retrieveDecorationColorFromDom: (decoration) ->
@retrieveStyleFromDom(decoration.getProperties().scope.split(/\s+/), 'background-color', false)

# Internal: This function insert a dummy element in the DOM to compute
# its style, return the specified property, and remove the element
# from the DOM.
#
# scopes - An {Array} of {String} reprensenting the scope to reproduce.
# property - The property {String} name.
#
# Returns a {String} of the property value.
retrieveStyleFromDom: (scopes, property, shadowRoot=true) ->
@ensureDummyNodeExistence(shadowRoot)

parent = @dummyNode
for scope in scopes
node = document.createElement('span')
# css class is the scope without the dots,
# see pushScope @ atom/atom/src/lines-component.coffee
node.className = scope.replace(/\.+/g, ' ')
parent.appendChild(node) if parent?
parent = node

value = getComputedStyle(parent).getPropertyValue(property)
@dummyNode.innerHTML = ''

value

# Internal: Creates a DOM node container for all the operations that
# need to read styles properties from DOM.
ensureDummyNodeExistence: (shadowRoot) ->
unless @dummyNode?
@dummyNode = document.createElement('span')
@dummyNode.style.visibility = 'hidden'
if shadowRoot
@editorView.shadowRoot.appendChild(@dummyNode)
else
@editorView.appendChild(@dummyNode)

# Internal: Converts a `rgb(...)` color into a `rgba(...)` color
# with the specified opacity.
#
Expand Down
50 changes: 50 additions & 0 deletions lib/mixins/dom-styles-reader.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
Mixin = require 'mixto'

module.exports =
class DOMStylesReader extends Mixin
@domStylesCache: {}

# Internal: This function insert a dummy element in the DOM to compute
# its style, return the specified property, and remove the element
# from the DOM.
#
# scopes - An {Array} of {String} reprensenting the scope to reproduce.
# property - The property {String} name.
#
# Returns a {String} of the property value.
retrieveStyleFromDom: (scopes, property, shadowRoot=true) ->
key = scopes.join(' ')

if @constructor.domStylesCache[key]?[property]?
return @constructor.domStylesCache[key][property]

@ensureDummyNodeExistence(shadowRoot)
@constructor.domStylesCache[key] ?= {}

parent = @dummyNode
for scope in scopes
node = document.createElement('span')
# css class is the scope without the dots,
# see pushScope @ atom/atom/src/lines-component.coffee
node.className = scope.replace(/\.+/g, ' ')
parent.appendChild(node) if parent?
parent = node

value = getComputedStyle(parent).getPropertyValue(property)
@dummyNode.innerHTML = ''

@constructor.domStylesCache[key][property] = value

# Internal: Creates a DOM node container for all the operations that
# need to read styles properties from DOM.
ensureDummyNodeExistence: (shadowRoot) ->
unless @dummyNode?
@dummyNode = document.createElement('span')
@dummyNode.style.visibility = 'hidden'
if shadowRoot
@editorView.shadowRoot.appendChild(@dummyNode)
else
@editorView.appendChild(@dummyNode)

invalidateCache: ->
@constructor.domStylesCache = {}

0 comments on commit 6fd00fa

Please sign in to comment.