mirror of
https://github.com/atom/atom.git
synced 2026-01-23 22:08:08 -05:00
Most recently added scoped properties win in case of a specificity tie
This makes the scoped property system mimic the behavior of CSS. When there is a tie, the scoped properties loaded later in the cascade win. I also optimize the scanning of all the properties, checking only those sets of properties that have a value for the desired key path, to reduce the need to match a ton of scope selectors.
This commit is contained in:
@@ -10,3 +10,10 @@ describe "the `syntax` global", ->
|
||||
expect(syntax.getProperty([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBe 22
|
||||
expect(syntax.getProperty([".source.js", ".variable.assignment.js"], "foo.bar.baz")).toBe 11
|
||||
expect(syntax.getProperty([".text"], "foo.bar.baz")).toBe 1
|
||||
|
||||
it "favors the most recently added properties in the event of a specificity tie", ->
|
||||
syntax.addProperties(".source.coffee .string.quoted.single", foo: bar: baz: 42)
|
||||
syntax.addProperties(".source.coffee .string.quoted.double", foo: bar: baz: 22)
|
||||
|
||||
expect(syntax.getProperty([".source.coffee", ".string.quoted.single"], "foo.bar.baz")).toBe 42
|
||||
expect(syntax.getProperty([".source.coffee", ".string.quoted.single.double"], "foo.bar.baz")).toBe 22
|
||||
|
||||
@@ -7,38 +7,48 @@ module.exports =
|
||||
class Syntax
|
||||
constructor: ->
|
||||
@globalProperties = {}
|
||||
@scopedPropertiesIndex = 0
|
||||
@scopedProperties = []
|
||||
@propertiesBySelector = {}
|
||||
|
||||
addProperties: (args...) ->
|
||||
scopeSelector = args.shift() if args.length > 1
|
||||
selector = args.shift() if args.length > 1
|
||||
properties = args.shift()
|
||||
|
||||
if scopeSelector
|
||||
@propertiesBySelector[scopeSelector] ?= {}
|
||||
_.extend(@propertiesBySelector[scopeSelector], properties)
|
||||
if selector
|
||||
@scopedProperties.unshift(
|
||||
selector: selector,
|
||||
properties: properties,
|
||||
specificity: Specificity(selector),
|
||||
index: @scopedPropertiesIndex++
|
||||
)
|
||||
else
|
||||
_.extend(@globalProperties, properties)
|
||||
|
||||
getProperty: (scope, keyPath) ->
|
||||
for object in @propertiesForScope(scope)
|
||||
for object in @propertiesForScope(scope, keyPath)
|
||||
value = _.valueForKeyPath(object, keyPath)
|
||||
return value if value?
|
||||
undefined
|
||||
|
||||
propertiesForScope: (scope) ->
|
||||
matchingSelectors = []
|
||||
element = @buildScopeElement(scope)
|
||||
while element
|
||||
matchingSelectors.push(@matchingSelectorsForElement(element)...)
|
||||
element = element.parentNode
|
||||
properties = matchingSelectors.map (selector) => @propertiesBySelector[selector]
|
||||
properties.concat([@globalProperties])
|
||||
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.concat([@globalProperties])
|
||||
|
||||
matchingSelectorsForElement: (element) ->
|
||||
matchingSelectors = []
|
||||
for selector of @propertiesBySelector
|
||||
matchingSelectors.push(selector) if jQuery.find.matchesSelector(element, selector)
|
||||
matchingSelectors.sort (a, b) -> Specificity(b) - Specificity(a)
|
||||
matchingPropertiesForElement: (element, candidates) ->
|
||||
matchingScopedProperties = candidates.filter ({selector}) -> jQuery.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 = new Array(scope...)
|
||||
|
||||
Reference in New Issue
Block a user