diff --git a/spec/app/text-mate-scope-selector-spec.coffee b/spec/app/text-mate-scope-selector-spec.coffee index 708424ff1..7ea2bf547 100644 --- a/spec/app/text-mate-scope-selector-spec.coffee +++ b/spec/app/text-mate-scope-selector-spec.coffee @@ -1,52 +1,63 @@ TextMateScopeSelector = require 'text-mate-scope-selector' describe "TextMateScopeSelector", -> - it "matches the asterix", -> - expect(new TextMateScopeSelector('*').matches(['a'])).toBeTruthy() - expect(new TextMateScopeSelector('*').matches(['b', 'c'])).toBeTruthy() - expect(new TextMateScopeSelector('a.*.c').matches(['a.b.c'])).toBeTruthy() - expect(new TextMateScopeSelector('a.*.c').matches(['a.b.c.d'])).toBeTruthy() - expect(new TextMateScopeSelector('a.*.c').matches(['a.b.d.c'])).toBeFalsy() + describe ".matches(scopes)", -> + it "matches the asterix", -> + expect(new TextMateScopeSelector('*').matches(['a'])).toBeTruthy() + expect(new TextMateScopeSelector('*').matches(['b', 'c'])).toBeTruthy() + expect(new TextMateScopeSelector('a.*.c').matches(['a.b.c'])).toBeTruthy() + expect(new TextMateScopeSelector('a.*.c').matches(['a.b.c.d'])).toBeTruthy() + expect(new TextMateScopeSelector('a.*.c').matches(['a.b.d.c'])).toBeFalsy() - it "matches prefixes", -> - expect(new TextMateScopeSelector('a').matches(['a'])).toBeTruthy() - expect(new TextMateScopeSelector('a').matches(['a.b'])).toBeTruthy() - expect(new TextMateScopeSelector('a.b').matches(['a.b.c'])).toBeTruthy() - expect(new TextMateScopeSelector('a').matches(['abc'])).toBeFalsy() - expect(new TextMateScopeSelector('a.b-c').matches(['a.b-c.d'])).toBeTruthy() - expect(new TextMateScopeSelector('a.b').matches(['a.b-d'])).toBeFalsy() + it "matches prefixes", -> + expect(new TextMateScopeSelector('a').matches(['a'])).toBeTruthy() + expect(new TextMateScopeSelector('a').matches(['a.b'])).toBeTruthy() + expect(new TextMateScopeSelector('a.b').matches(['a.b.c'])).toBeTruthy() + expect(new TextMateScopeSelector('a').matches(['abc'])).toBeFalsy() + expect(new TextMateScopeSelector('a.b-c').matches(['a.b-c.d'])).toBeTruthy() + expect(new TextMateScopeSelector('a.b').matches(['a.b-d'])).toBeFalsy() - it "matches disjunction", -> - expect(new TextMateScopeSelector('a | b').matches(['b'])).toBeTruthy() - expect(new TextMateScopeSelector('a|b|c').matches(['c'])).toBeTruthy() - expect(new TextMateScopeSelector('a|b|c').matches(['d'])).toBeFalsy() + it "matches disjunction", -> + expect(new TextMateScopeSelector('a | b').matches(['b'])).toBeTruthy() + expect(new TextMateScopeSelector('a|b|c').matches(['c'])).toBeTruthy() + expect(new TextMateScopeSelector('a|b|c').matches(['d'])).toBeFalsy() - it "matches negation", -> - expect(new TextMateScopeSelector('a - c').matches(['a', 'b'])).toBeTruthy() - expect(new TextMateScopeSelector('a-b').matches(['a', 'b'])).toBeFalsy() - expect(new TextMateScopeSelector('a -b').matches(['a', 'b'])).toBeFalsy() - expect(new TextMateScopeSelector('a -c').matches(['a', 'b'])).toBeTruthy() - expect(new TextMateScopeSelector('a-c').matches(['a', 'b'])).toBeFalsy() + it "matches negation", -> + expect(new TextMateScopeSelector('a - c').matches(['a', 'b'])).toBeTruthy() + expect(new TextMateScopeSelector('a-b').matches(['a', 'b'])).toBeFalsy() + expect(new TextMateScopeSelector('a -b').matches(['a', 'b'])).toBeFalsy() + expect(new TextMateScopeSelector('a -c').matches(['a', 'b'])).toBeTruthy() + expect(new TextMateScopeSelector('a-c').matches(['a', 'b'])).toBeFalsy() - it "matches conjunction", -> - expect(new TextMateScopeSelector('a & b').matches(['b', 'a'])).toBeTruthy() - expect(new TextMateScopeSelector('a&b&c').matches(['c'])).toBeFalsy() - expect(new TextMateScopeSelector('a&b&c').matches(['a', 'b', 'd'])).toBeFalsy() + it "matches conjunction", -> + expect(new TextMateScopeSelector('a & b').matches(['b', 'a'])).toBeTruthy() + expect(new TextMateScopeSelector('a&b&c').matches(['c'])).toBeFalsy() + expect(new TextMateScopeSelector('a&b&c').matches(['a', 'b', 'd'])).toBeFalsy() - it "matches composites", -> - expect(new TextMateScopeSelector('a,b,c').matches(['b', 'c'])).toBeTruthy() - expect(new TextMateScopeSelector('a, b, c').matches(['d', 'e'])).toBeFalsy() - expect(new TextMateScopeSelector('a, b, c').matches(['d', 'c.e'])).toBeTruthy() + it "matches composites", -> + expect(new TextMateScopeSelector('a,b,c').matches(['b', 'c'])).toBeTruthy() + expect(new TextMateScopeSelector('a, b, c').matches(['d', 'e'])).toBeFalsy() + expect(new TextMateScopeSelector('a, b, c').matches(['d', 'c.e'])).toBeTruthy() - it "matches groups", -> - expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['a'])).toBeTruthy() - expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['b'])).toBeTruthy() - expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['c'])).toBeTruthy() - expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['d'])).toBeTruthy() - expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['e'])).toBeFalsy() + it "matches groups", -> + expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['a'])).toBeTruthy() + expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['b'])).toBeTruthy() + expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['c'])).toBeTruthy() + expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['d'])).toBeTruthy() + expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['e'])).toBeFalsy() - it "matches paths", -> - expect(new TextMateScopeSelector('a b').matches(['a', 'b'])).toBeTruthy() - expect(new TextMateScopeSelector('a b').matches(['b', 'a'])).toBeFalsy() - expect(new TextMateScopeSelector('a c').matches(['a', 'b', 'c', 'd', 'e'])).toBeTruthy() - expect(new TextMateScopeSelector('a b e').matches(['a', 'b', 'c', 'd', 'e'])).toBeTruthy() + it "matches paths", -> + expect(new TextMateScopeSelector('a b').matches(['a', 'b'])).toBeTruthy() + expect(new TextMateScopeSelector('a b').matches(['b', 'a'])).toBeFalsy() + expect(new TextMateScopeSelector('a c').matches(['a', 'b', 'c', 'd', 'e'])).toBeTruthy() + expect(new TextMateScopeSelector('a b e').matches(['a', 'b', 'c', 'd', 'e'])).toBeTruthy() + + describe ".toCssSelector()", -> + it "converts the TextMate scope selector to a CSS selector", -> + expect(new TextMateScopeSelector('a b c').toCssSelector()).toBe '.a .b .c' + expect(new TextMateScopeSelector('a.b.c').toCssSelector()).toBe '.a.b.c' + expect(new TextMateScopeSelector('*').toCssSelector()).toBe '*' + expect(new TextMateScopeSelector('a - b').toCssSelector()).toBe '.a :not(.b)' + expect(new TextMateScopeSelector('a & b').toCssSelector()).toBe '.a .b' + expect(new TextMateScopeSelector('a | b').toCssSelector()).toBe '.a .b' + expect(new TextMateScopeSelector('a - (b.c d)').toCssSelector()).toBe '.a :not(.b.c .d)' diff --git a/src/app/text-mate-scope-selector-matchers.coffee b/src/app/text-mate-scope-selector-matchers.coffee index 062bc93fc..3bc8dd21f 100644 --- a/src/app/text-mate-scope-selector-matchers.coffee +++ b/src/app/text-mate-scope-selector-matchers.coffee @@ -6,14 +6,19 @@ class SegmentMatcher constructor: (segment) -> @segment = _.flatten(segment).join('') - matches: (scope) -> - scope is @segment + matches: (scope) -> scope is @segment + + toCssSelector: -> + @segment.split('.').map((dotFragment) -> + '.' + dotFragment.replace(/\+/g, '\\+') + ).join('') class TrueMatcher constructor: -> - matches: -> - true + matches: -> true + + toCssSelector: -> '*' class ScopeMatcher constructor: (first, others) -> @@ -29,6 +34,9 @@ class ScopeMatcher true + toCssSelector: -> + @segments.map((matcher) -> matcher.toCssSelector()).join('') + class PathMatcher constructor: (first, others) -> @matchers = [first] @@ -42,24 +50,36 @@ class PathMatcher return true unless matcher? false + toCssSelector: -> + @matchers.map((matcher) -> matcher.toCssSelector()).join(' ') + class OrMatcher constructor: (@left, @right) -> matches: (scopes) -> @left.matches(scopes) or @right.matches(scopes) + toCssSelector: -> + "#{@left.toCssSelector()} #{@right.toCssSelector()}" + class AndMatcher constructor: (@left, @right) -> matches: (scopes) -> @left.matches(scopes) and @right.matches(scopes) + toCssSelector: -> + "#{@left.toCssSelector()} #{@right.toCssSelector()}" + class NegateMatcher constructor: (@left, @right) -> matches: (scopes) -> @left.matches(scopes) and not @right.matches(scopes) + toCssSelector: -> + "#{@left.toCssSelector()} :not(#{@right.toCssSelector()})" + class CompositeMatcher constructor: (left, operator, right) -> switch operator @@ -70,6 +90,8 @@ class CompositeMatcher matches: (scopes) -> @matcher.matches(scopes) + toCssSelector: -> @matcher.toCssSelector() + module.exports = { AndMatcher CompositeMatcher diff --git a/src/app/text-mate-scope-selector.coffee b/src/app/text-mate-scope-selector.coffee index d5713356b..1e93b9ab7 100644 --- a/src/app/text-mate-scope-selector.coffee +++ b/src/app/text-mate-scope-selector.coffee @@ -28,3 +28,5 @@ class TextMateScopeSelector # Return a {Boolean}. matches: (scopes) -> @matcher.matches(scopes) + + toCssSelector: -> @matcher.toCssSelector()