diff --git a/grammars/coffeescript.cson b/grammars/coffeescript.cson
index 4d884f0..aba77dc 100644
--- a/grammars/coffeescript.cson
+++ b/grammars/coffeescript.cson
@@ -6,6 +6,7 @@
'coffee.erb'
'cson'
'_coffee'
+ 'cjsx'
]
'firstLineMatch': '''(?x)
# Hashbang
@@ -27,6 +28,9 @@
)
'''
'patterns': [
+ {
+ 'include': '#jsx'
+ }
{
'match': '(new)\\s+(?:(?:(class)\\s+(\\w+(?:\\.\\w*)*)?)|(\\w+(?:\\.\\w*)*))'
'name': 'meta.class.instance.constructor.coffee'
@@ -1148,3 +1152,84 @@
'include': '#embedded_comment'
}
]
+
+ 'jsx':
+ 'patterns': [
+ {
+ 'include': '#jsx-tag'
+ }
+ {
+ 'include': '#jsx-end-tag'
+ }
+ ]
+
+ 'jsx-expression':
+ 'begin': '{'
+ 'beginCaptures':
+ '0':
+ 'name': 'meta.brace.curly.coffee'
+ 'end': '}'
+ 'endCaptures':
+ '0':
+ 'name': 'meta.brace.curly.coffee'
+
+ 'patterns': [
+ {
+ 'include': '#double_quoted_string'
+ }
+ {
+ 'include': '$self'
+ }
+ ]
+
+ 'jsx-attribute':
+ 'patterns': [
+ {
+ 'captures':
+ '1':
+ 'name': 'entity.other.attribute-name.coffee'
+ '2':
+ 'name': 'keyword.operator.assignment.coffee'
+ 'match': '(?:^|\\s+)([-\\w.]+)\\s*(=)'
+ }
+ {
+ 'include': '#double_quoted_string'
+ }
+ {
+ 'include': '#single_quoted_string'
+ }
+ {
+ 'include': '#jsx-expression'
+ }
+ ]
+
+ 'jsx-tag':
+ 'patterns': [
+ {
+ 'begin': '(<)([-\\w\\.]+)'
+ 'beginCaptures':
+ '1':
+ 'name': 'punctuation.definition.tag.coffee'
+ '2':
+ 'name': 'entity.name.tag.coffee'
+ 'end': '(/?>)'
+ 'name': 'meta.tag.coffee'
+ 'patterns': [
+ 'include': '#jsx-attribute'
+ ]
+ }
+ ]
+
+ 'jsx-end-tag':
+ 'patterns': [
+ {
+ 'begin': '()([-\\w\\.]+)'
+ 'beginCaptures':
+ '1':
+ 'name': 'punctuation.definition.tag.coffee'
+ '2':
+ 'name': 'entity.name.tag.coffee'
+ 'end': '(/?>)'
+ 'name': 'meta.tag.coffee'
+ }
+ ]
diff --git a/spec/coffee-script-spec.coffee b/spec/coffee-script-spec.coffee
index 9727f8f..1389597 100644
--- a/spec/coffee-script-spec.coffee
+++ b/spec/coffee-script-spec.coffee
@@ -1322,6 +1322,59 @@ describe "CoffeeScript grammar", ->
expect(tokens[8]).toEqual value: 'b', scopes: ['source.coffee', 'string.quoted.single.coffee', 'constant.character.escape.backslash.coffee']
expect(tokens[9]).toEqual value: "'", scopes: ['source.coffee', 'string.quoted.single.coffee', 'punctuation.definition.string.end.coffee']
+ describe "jsx", ->
+ it "tokenises HTML tags", ->
+ {tokens} = grammar.tokenizeLine("
")
+ expect(tokens[0]).toEqual value: '<', scopes: ['source.coffee', 'meta.tag.coffee', 'punctuation.definition.tag.coffee']
+ expect(tokens[1]).toEqual value: 'div', scopes: ['source.coffee', 'meta.tag.coffee', 'entity.name.tag.coffee' ]
+ expect(tokens[2]).toEqual value: ' ', scopes: ['source.coffee', 'meta.tag.coffee' ]
+ expect(tokens[3]).toEqual value: 'class', scopes: ['source.coffee', 'meta.tag.coffee', 'entity.other.attribute-name.coffee' ]
+ expect(tokens[4]).toEqual value: '=', scopes: ['source.coffee', 'meta.tag.coffee', 'keyword.operator.assignment.coffee' ]
+ expect(tokens[5]).toEqual value: '\'', scopes: ['source.coffee', 'meta.tag.coffee', 'string.quoted.single.coffee', 'punctuation.definition.string.begin.coffee' ]
+ expect(tokens[6]).toEqual value: 'myclass', scopes: ['source.coffee', 'meta.tag.coffee', 'string.quoted.single.coffee' ]
+ expect(tokens[7]).toEqual value: '\'', scopes: ['source.coffee', 'meta.tag.coffee', 'string.quoted.single.coffee', 'punctuation.definition.string.end.coffee' ]
+ expect(tokens[8]).toEqual value: ' ', scopes: ['source.coffee', 'meta.tag.coffee' ]
+ expect(tokens[9]).toEqual value: 'id', scopes: ['source.coffee', 'meta.tag.coffee', 'entity.other.attribute-name.coffee' ]
+ expect(tokens[10]).toEqual value: '=', scopes: ['source.coffee', 'meta.tag.coffee', 'keyword.operator.assignment.coffee' ]
+ expect(tokens[11]).toEqual value: '"', scopes: ['source.coffee', 'meta.tag.coffee', 'string.quoted.double.coffee', 'punctuation.definition.string.begin.coffee' ]
+ expect(tokens[12]).toEqual value: 'myid', scopes: ['source.coffee', 'meta.tag.coffee', 'string.quoted.double.coffee' ]
+ expect(tokens[13]).toEqual value: '"', scopes: ['source.coffee', 'meta.tag.coffee', 'string.quoted.double.coffee', 'punctuation.definition.string.end.coffee' ]
+ expect(tokens[14]).toEqual value: '>', scopes: ['source.coffee', 'meta.tag.coffee' ]
+
+ it "tokenises HTML tags with attributes that have expressions", ->
+ {tokens} = grammar.tokenizeLine("
@handleClick(e)}>")
+ expect(tokens[0]).toEqual value: '<', scopes: ['source.coffee', 'meta.tag.coffee', 'punctuation.definition.tag.coffee']
+ expect(tokens[1]).toEqual value: 'div', scopes: ['source.coffee', 'meta.tag.coffee', 'entity.name.tag.coffee' ]
+ expect(tokens[2]).toEqual value: ' ', scopes: ['source.coffee', 'meta.tag.coffee' ]
+ expect(tokens[3]).toEqual value: 'on-click', scopes: ['source.coffee', 'meta.tag.coffee', 'entity.other.attribute-name.coffee' ]
+ expect(tokens[4]).toEqual value: '=', scopes: ['source.coffee', 'meta.tag.coffee', 'keyword.operator.assignment.coffee' ]
+ expect(tokens[5]).toEqual value: '{', scopes: ['source.coffee', 'meta.tag.coffee', 'meta.brace.curly.coffee']
+ expect(tokens[6]).toEqual value: '(', scopes: ['source.coffee', 'meta.tag.coffee', 'meta.function.inline.coffee', 'meta.parameters.coffee', 'punctuation.definition.parameters.begin.bracket.round.coffee' ]
+ expect(tokens[7]).toEqual value: 'e', scopes: ['source.coffee', 'meta.tag.coffee', 'meta.function.inline.coffee', 'meta.parameters.coffee', 'variable.parameter.function.coffee' ]
+ expect(tokens[8]).toEqual value: ')', scopes: ['source.coffee', 'meta.tag.coffee', 'meta.function.inline.coffee', 'meta.parameters.coffee', 'punctuation.definition.parameters.end.bracket.round.coffee' ]
+ expect(tokens[9]).toEqual value: '->', scopes: ['source.coffee', 'meta.tag.coffee', 'meta.function.inline.coffee', 'storage.type.function.coffee' ]
+ expect(tokens[10]).toEqual value: '@', scopes: ['source.coffee', 'meta.tag.coffee', 'meta.function-call.coffee', 'variable.other.readwrite.instance.coffee' ]
+ expect(tokens[11]).toEqual value: 'handleClick', scopes: ['source.coffee', 'meta.tag.coffee', 'meta.function-call.coffee', 'entity.name.function.coffee' ]
+ expect(tokens[12]).toEqual value: '(', scopes: ['source.coffee', 'meta.tag.coffee', 'meta.function-call.coffee', 'meta.arguments.coffee', 'punctuation.definition.arguments.begin.bracket.round.coffee' ]
+ expect(tokens[13]).toEqual value: 'e', scopes: ['source.coffee', 'meta.tag.coffee', 'meta.function-call.coffee', 'meta.arguments.coffee' ]
+ expect(tokens[14]).toEqual value: ')', scopes: ['source.coffee', 'meta.tag.coffee', 'meta.function-call.coffee', 'meta.arguments.coffee', 'punctuation.definition.arguments.end.bracket.round.coffee' ]
+ expect(tokens[15]).toEqual value: '}', scopes: ['source.coffee', 'meta.tag.coffee', 'meta.brace.curly.coffee']
+ expect(tokens[16]).toEqual value: '>', scopes: ['source.coffee', 'meta.tag.coffee']
+
describe "firstLineMatch", ->
it "recognises interpreter directives", ->
valid = """