Spans have a class for each dot-separated portion of their token's scope

Previously, we were rendering every prefix of the dot-separated scope as its own class. So the scope meta.delimiter.method.period.coffee would make a token w/ classes:

class="meta, meta-delimiter, meta-delimiter-method, meta-delimiter-method-period…"

Now we just give the token each piece of the scope as a class:

class="meta delimiter method period coffee"

We lose a bit of meaning, in that a scope selector method.period.coffee would match this element in CSS even though it *wouldn't* in TextMate. But we also gain the behavior where longer prefixes are more specific by naturally producing more specific css selectors. So '.meta.delimiter.method' is always more specific than '.meta.delimiter', whereas '.meta-delimiter-method' ties with '.meta-delimiter'. 

If prefix ambiguities become a problem later we may need to revisit this approach, but I think it's good enough for now.
This commit is contained in:
Nathan Sobo
2012-09-28 17:00:31 -06:00
parent dc4d981e2a
commit 3a8fe2b24e
4 changed files with 15 additions and 18 deletions

View File

@@ -997,25 +997,25 @@ describe "Editor", ->
it "syntax highlights code based on the file type", ->
line0 = editor.renderedLines.find('.line:first')
span0 = line0.children('span:eq(0)')
expect(span0).toMatchSelector '.source-js'
expect(span0.children('span:eq(0)')).toMatchSelector '.storage-type-js'
expect(span0).toMatchSelector '.source.js'
expect(span0.children('span:eq(0)')).toMatchSelector '.storage.type.js'
expect(span0.children('span:eq(0)').text()).toBe 'var'
span0_1 = span0.children('span:eq(1)')
expect(span0_1).toMatchSelector '.meta-function-js'
expect(span0_1).toMatchSelector '.meta.function.js'
expect(span0_1.text()).toBe 'quicksort = function ()'
expect(span0_1.children('span:eq(0)')).toMatchSelector '.entity-name-function-js'
expect(span0_1.children('span:eq(0)')).toMatchSelector '.entity.name.function.js'
expect(span0_1.children('span:eq(0)').text()).toBe "quicksort"
expect(span0_1.children('span:eq(1)')).toMatchSelector '.keyword-operator-js'
expect(span0_1.children('span:eq(1)')).toMatchSelector '.keyword.operator.js'
expect(span0_1.children('span:eq(1)').text()).toBe "="
expect(span0_1.children('span:eq(2)')).toMatchSelector '.storage-type-function-js'
expect(span0_1.children('span:eq(2)')).toMatchSelector '.storage.type.function.js'
expect(span0_1.children('span:eq(2)').text()).toBe "function"
expect(span0_1.children('span:eq(3)')).toMatchSelector '.punctuation-definition-parameters-begin-js'
expect(span0_1.children('span:eq(3)')).toMatchSelector '.punctuation.definition.parameters.begin.js'
expect(span0_1.children('span:eq(3)').text()).toBe "("
expect(span0_1.children('span:eq(4)')).toMatchSelector '.punctuation-definition-parameters-end-js'
expect(span0_1.children('span:eq(4)')).toMatchSelector '.punctuation.definition.parameters.end.js'
expect(span0_1.children('span:eq(4)').text()).toBe ")"
expect(span0.children('span:eq(2)')).toMatchSelector '.meta-brace-curly-js'
expect(span0.children('span:eq(2)')).toMatchSelector '.meta.brace.curly.js'
expect(span0.children('span:eq(2)').text()).toBe "{"
line12 = editor.renderedLines.find('.line:eq(11)')
@@ -1023,9 +1023,9 @@ describe "Editor", ->
describe "when lines are updated in the buffer", ->
it "syntax highlights the updated lines", ->
expect(editor.renderedLines.find('.line:eq(0) > span:first > span:first')).toMatchSelector '.storage-type-js'
expect(editor.renderedLines.find('.line:eq(0) > span:first > span:first')).toMatchSelector '.storage.type.js'
buffer.insert([0, 0], "q")
expect(editor.renderedLines.find('.line:eq(0) > span:first > span:first')).not.toMatchSelector '.storage-type-js'
expect(editor.renderedLines.find('.line:eq(0) > span:first > span:first')).not.toMatchSelector '.storage.type.js'
# verify that re-highlighting can occur below the changed line
buffer.insert([5,0], "/* */")

View File

@@ -51,7 +51,7 @@ describe "TextMateTheme", ->
it "returns an array of objects representing the theme's scope selectors", ->
expect(rulesets[11]).toEqual
comment: "Invalid Deprecated"
selector: ".invalid-deprecated"
selector: ".invalid.deprecated"
properties:
'color': "#D2A8A1"
# 'font-style': 'italic'
@@ -59,7 +59,7 @@ describe "TextMateTheme", ->
expect(rulesets[12]).toEqual
comment: "Invalid Illegal"
selector: ".invalid-illegal"
selector: ".invalid.illegal"
properties:
'color': "#F8F8F8"
'background-color': 'rgba(86, 45, 86, 0.75)'

View File

@@ -832,10 +832,7 @@ class Editor extends View
pushScope = (scope) ->
scopeStack.push(scope)
classes = []
scopeComponents = scope.split('.')
classes.push scopeComponents[0..i].join('-') for i in [0...scopeComponents.length]
line.push("<span class=\"#{classes.join(' ')}\">")
line.push("<span class=\"#{scope.replace(/\./g, ' ')}\">")
popScope = ->
scopeStack.pop()

View File

@@ -81,7 +81,7 @@ class TextMateTheme
properties: @translateScopeSelectorSettings(settings)
translateScopeSelector: (textmateScopeSelector) ->
scopes = textmateScopeSelector.replace(/\./g, '-').split(/\s+/).map (scope) -> '.' + scope
scopes = textmateScopeSelector.split(/\s+/).map (scope) -> '.' + scope
scopes.join(' ')
translateScopeSelectorSettings: ({ foreground, background, fontStyle }) ->