Use scoped-properties-store for faster property lookup

Previously we were creating DOM elements on every keystroke to look for
certain properties. Now it happens entirely in JavaScript.
This commit is contained in:
Nathan Sobo 2014-03-21 18:23:18 -06:00
parent 7ecb0dcd4b
commit 4aa6e78a4e
2 changed files with 22 additions and 56 deletions

View File

@ -28,10 +28,10 @@
"git-utils": "^1.1.1",
"guid": "0.0.10",
"jasmine-tagged": ">=1.1.1 <2.0",
"mkdirp": "0.3.5",
"keytar": "1.x",
"less-cache": "0.12.0",
"mixto": "1.x",
"mkdirp": "0.3.5",
"nslog": "0.5.0",
"oniguruma": "^1.0.4",
"optimist": "0.4.0",
@ -41,6 +41,7 @@
"random-words": "0.0.1",
"runas": "0.5.x",
"scandal": "0.15.0",
"scoped-property-store": "^0.3.0",
"scrollbar-style": "^0.1.0",
"season": ">=1.0.2 <2.0",
"semver": "1.1.4",
@ -49,7 +50,7 @@
"temp": "0.5.0",
"text-buffer": "^1.4.4",
"theorist": "1.x",
"underscore-plus": ">=1.0.5 <2.0",
"underscore-plus": "^1.1.0",
"vm-compatibility-layer": "0.1.0"
},
"packageDependencies": {

View File

@ -2,6 +2,7 @@ _ = require 'underscore-plus'
{specificity} = require 'clear-cut'
{Subscriber} = require 'emissary'
{GrammarRegistry, ScopeSelector} = require 'first-mate'
ScopedPropertyStore = require 'scoped-property-store'
{$, $$} = require './space-pen-extensions'
Token = require './token'
@ -24,9 +25,7 @@ class Syntax extends GrammarRegistry
constructor: ->
super
@scopedPropertiesIndex = 0
@scopedProperties = []
@scopedProperties = new ScopedPropertyStore
serialize: ->
{deserializer: @constructor.name, @grammarOverridesByPath}
@ -36,22 +35,15 @@ class Syntax extends GrammarRegistry
addProperties: (args...) ->
name = args.shift() if args.length > 2
[selector, properties] = args
@scopedProperties.unshift(
name: name
selector: selector,
properties: properties,
specificity: specificity(selector),
index: @scopedPropertiesIndex++
)
propertiesBySelector = {}
propertiesBySelector[selector] = properties
@scopedProperties.addProperties(name, propertiesBySelector)
removeProperties: (name) ->
for properties in @scopedProperties.filter((properties) -> properties.name is name)
_.remove(@scopedProperties, properties)
@scopedProperties.removeProperties(name)
clearProperties: ->
@scopedProperties = []
@scopedPropertiesIndex = 0
@scopedProperties = new ScopedPropertyStore
# Public: Get a property for the given scope and key path.
#
@ -66,48 +58,21 @@ class Syntax extends GrammarRegistry
#
# Returns a {String} property value or undefined.
getProperty: (scope, keyPath) ->
for object in @propertiesForScope(scope, keyPath)
value = _.valueForKeyPath(object, keyPath)
return value if value?
undefined
scopeChain = scope
.map (scope) ->
scope = ".#{scope}" unless scope.indexOf('.') is 0
scope
.join(' ')
@scopedProperties.getPropertyValue(scopeChain, keyPath)
propertiesForScope: (scope, keyPath) ->
matchingProperties = []
candidates = @scopedProperties.filter ({properties}) -> _.valueForKeyPath(properties, keyPath)?
if candidates.length
element = @buildScopeElement(scope)
while element
matchingProperties.push(@matchingPropertiesForElement(element, candidates)...)
element = element.parentNode
matchingProperties
scopeChain = scope
.map (scope) ->
scope = ".#{scope}" unless scope.indexOf('.') is 0
scope
.join(' ')
matchingPropertiesForElement: (element, candidates) ->
matchingScopedProperties = candidates.filter ({selector}) ->
$.find.matchesSelector(element, selector)
matchingScopedProperties.sort (a, b) ->
if a.specificity == b.specificity
b.index - a.index
else
b.specificity - a.specificity
_.pluck matchingScopedProperties, 'properties'
buildScopeElement: (scope) ->
scope = scope.slice()
element = $$ ->
elementsForRemainingScopes = =>
classString = scope.shift()
classes = classString.replace(/^\./, '').replace(/\./g, ' ')
if scope.length
@div class: classes, elementsForRemainingScopes
else
@div class: classes
elementsForRemainingScopes()
deepestChild = element.find(":not(:has(*))")
if deepestChild.length
deepestChild[0]
else
element[0]
@scopedProperties.getProperties(scopeChain, keyPath)
cssSelectorFromScopeSelector: (scopeSelector) ->
new ScopeSelector(scopeSelector).toCssSelector()