Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Fix arrow functions inside ternary expressions #408

Merged
merged 6 commits into from
Aug 23, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 116 additions & 89 deletions grammars/javascript.cson
Original file line number Diff line number Diff line change
@@ -275,20 +275,8 @@
'name': 'meta.export.js'
}
{
'match': '''(?x)
(?<!\\.)\\b(super|this|arguments)(?!\\s*:)\\b
|
(?<=\\?)\\s*(super|this|arguments)(?=\\s*:)
|
(?<=[\\s}:;]case|^case)\\s+(super|this|arguments)(?=\\s*:)
'''
'captures':
'1':
'name': 'variable.language.js'
'2':
'name': 'variable.language.js'
'3':
'name': 'variable.language.js'
'match': '(?<!\\.)\\b(super|this|arguments)(?!\\s*:)\\b'
'name': 'variable.language.js'
}
{
# [async] function [name](params)
@@ -886,25 +874,20 @@
]
}
{
'match': '(?<!\\.)\\b(yield)(?!\\s*:)\\b(?:\\s*(\\*))?|(?<=\\?)(?:\\s*)(yield)(?=\\s*:)',
'match': '(?<!\\.)\\b(yield)(?!\\s*:)\\b(?:\\s*(\\*))?',
'captures':
'1':
'name': 'keyword.control.js'
'2':
'name': 'storage.modifier.js'
'3':
'name': 'keyword.control.js'
'4':
'name': 'storage.modifier.js'
'name': 'meta.control.yield.js'
}
{
'match': '(?<!\\.)\\b(await|break|case|catch|continue|do|else|finally|for|if|import|package|return|switch|throw|try|while|with)(?!\\s*:)\\b'
'match': '(?<!\\.)\\b(await|break|catch|continue|do|else|finally|for|if|import|package|return|throw|try|while|with)(?!\\s*:)\\b'
'name': 'keyword.control.js'
}
{
'match': '(?<!\\.)\\b(default)\\b'
'name': 'keyword.control.js'
'include': '#switch_statement'
}
{
'match': '(?<!\\.)\\b(delete|in|of|instanceof|new|typeof|void)(?!\\s*:)\\b'
@@ -915,52 +898,16 @@
'name': 'keyword.operator.spread.js'
}
{
'match': '''(?x)
(?<!\\.)\\b(true|false)(?!\\s*:)\\b
|
(?<=\\?)\\s*(true|false)(?=\\s*:)
|
(?<=[\\s}:;]case|^case)\\s+(true|false)(?=\\s*:)
'''
'captures':
'1':
'name': 'constant.language.boolean.$1.js'
'2':
'name': 'constant.language.boolean.$2.js'
'3':
'name': 'constant.language.boolean.$3.js'
'match': '(?<!\\.)\\b(true|false)(?!\\s*:)\\b'
'name': 'constant.language.boolean.$1.js'
}
{
'match': '''(?x)
(?<!\\.)\\b(null)(?!\\s*:)\\b
|
(?<=\\?)\\s*(null)(?=\\s*:)
|
(?<=[\\s}:;]case|^case)\\s+(null)(?=\\s*:)
'''
'captures':
'1':
'name': 'constant.language.null.js'
'2':
'name': 'constant.language.null.js'
'3':
'name': 'constant.language.null.js'
'match': '(?<!\\.)\\b(null)(?!\\s*:)\\b'
'name': 'constant.language.null.js'
}
{
'match': '''(?x)
(?<!\\.)\\b(debugger)(?!\\s*:)\\b
|
(?<=\\?)\\s*(debugger)(?=\\s*:)
|
(?<=[\\s}:;]case|^case)\\s+(debugger)(?=\\s*:)
'''
'captures':
'1':
'name': 'keyword.other.js'
'2':
'name': 'keyword.other.js'
'3':
'name': 'keyword.other.js'
'match': '(?<!\\.)\\b(debugger)(?!\\s*:)\\b'
'name': 'keyword.other.debugger.js'
}
{
'match': '(?<!\\$)\\b(Anchor|Applet|Area|Array|Boolean|Button|Checkbox|Date|document|event|FileUpload|Form|Frame|Function|Hidden|History|Image|JavaArray|JavaClass|JavaObject|JavaPackage|java|Layer|Link|Location|Map|MimeType|Number|navigator|netscape|Object|Option|Packages|Password|Plugin|performance|Radio|RegExp|Reset|Select|Set|String|Style|Submit|Symbol|screen|sun|Text|Textarea|WeakMap|WeakSet|window|XMLHttpRequest)\\b'
@@ -987,20 +934,8 @@
'name': 'support.constant.dom.js'
}
{
'match': '''(?x)
(?<!\\.)\\b(module|exports|__filename|__dirname|global|process)(?!\\s*:)\\b
|
(?<=\\?)\\s*(module|exports|__filename|__dirname|global|process)(?=\\s*:)
|
(?<=[\\s}:;]case|^case)\\s+(module|exports|__filename|__dirname|global|process)(?=\\s*:)
'''
'captures':
'1':
'name': 'support.variable.js'
'2':
'name': 'support.variable.js'
'3':
'name': 'support.variable.js'
'match': '(?<!\\.)\\b(module|exports|__filename|__dirname|global|process)(?!\\s*:)\\b'
'name': 'support.variable.js'
}
{
'match': '\\b(Infinity|NaN|undefined)\\b'
@@ -1022,6 +957,24 @@
}
]
}
{
'begin': '\\?'
'beginCaptures':
'0':
'name': 'keyword.operator.ternary.js'
'end': ':'
'endCaptures':
'0':
'name': 'keyword.operator.ternary.js'
'patterns': [
{
'include': '#prevent_object_keys_matching'
}
{
'include': '$self'
}
]
}
{
'include': '#operators'
}
@@ -1041,12 +994,8 @@
'include': '#properties'
}
{
'match': '(?<!\\.)\\b([A-Z][A-Z0-9_]+)(?!\\s*:)\\b|(?<=\\?)(?:\\s*)([A-Z][A-Z0-9_]+)(?=\\s*:)'
'captures':
'1':
'name': 'constant.other.js'
'2':
'name': 'constant.other.js'
'match': '(?<!\\.)\\b[A-Z][A-Z0-9_]+(?!\\s*:)\\b'
'name': 'constant.other.js'
}
{
'match': '(?<!\\$)\\b[0-9]+[\\w$]*'
@@ -1180,11 +1129,7 @@
'name': 'keyword.operator.bitwise.js'
}
{
'match': '\\?|:'
'name': 'keyword.operator.js'
}
{
'match': '='
'match': '=|:'
'name': 'keyword.operator.assignment.js'
}
{
@@ -1730,3 +1675,85 @@
]
}
]
'switch_statement':
'patterns': [
{
# switch(expression) {...}
'begin': '\\bswitch\\b'
'beginCaptures':
'0':
'name': 'keyword.control.switch.js'
'end': '}'
'endCaptures':
'0':
'name': 'punctuation.definition.section.switch-block.end.bracket.curly.js'
'name': 'meta.switch-statement.js'
'patterns': [
{
'begin': '\\('
'beginCaptures':
'0':
'name': 'punctuation.definition.switch-expression.begin.bracket.round.js'
'end': '\\)'
'endCaptures':
'0':
'name': 'punctuation.definition.switch-expression.end.bracket.round.js'
'patterns': [
'include': '$self'
]
}
{
'begin': '{'
'beginCaptures':
'0':
'name': 'punctuation.definition.section.switch-block.begin.bracket.curly.js'
'end': '(?=})'
'patterns': [
{
'begin': '\\bcase\\b'
'beginCaptures':
'0':
'name': 'keyword.control.case.js'
'end': ':'
'endCaptures':
'0':
'name': 'punctuation.definition.section.case-statement.js'
'patterns': [
{
'include': '#prevent_object_keys_matching'
}
{
'include': '$self'
}
]
}
{
'match': '(?:^\\s*)?\\b(default)\\b\\s*(:)'
'captures':
'1':
'name': 'keyword.control.default.js'
'2':
'name': 'punctuation.definition.section.case-statement.js'
}
{
'include': '$self'
}
]
}
]
}
]
'prevent_object_keys_matching':
'patterns': [
{
# e.g. don't treat null as an object key in
# ? null :
# case null:
'match': '(\\w+)(?=\\s*:)'
'captures':
'1':
'patterns': [
'include': '$self'
]
}
]
240 changes: 156 additions & 84 deletions spec/javascript-spec.coffee
Original file line number Diff line number Diff line change
@@ -55,42 +55,17 @@ describe "Javascript grammar", ->
expect(lines[2][1]).toEqual value: delim, scopes: ['source.js', scope, 'punctuation.definition.string.end.js']

describe "keywords", ->
it "tokenizes with as a keyword", ->
{tokens} = grammar.tokenizeLine('with')
expect(tokens[0]).toEqual value: 'with', scopes: ['source.js', 'keyword.control.js']
keywords = ['await', 'break', 'catch', 'continue', 'do']

map =
super: 'variable.language.js'
this: 'variable.language.js'
null: 'constant.language.null.js'
true: 'constant.language.boolean.true.js'
false: 'constant.language.boolean.false.js'
debugger: 'keyword.other.js'
exports: 'support.variable.js'
__filename: 'support.variable.js'

for keyword, scope of map
do (keyword, scope) ->
it "does not tokenize `#{keyword}` when it is an object key", ->
{tokens} = grammar.tokenizeLine("#{keyword}: 1")
expect(tokens[0]).toEqual value: keyword, scopes: ['source.js']
expect(tokens[1]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.js']
for keyword in keywords
it "tokenizes the #{keyword} keyword", ->
{tokens} = grammar.tokenizeLine(keyword)
expect(tokens[0]).toEqual value: keyword, scopes: ['source.js', 'keyword.control.js']

it "tokenizes `#{keyword}` in the middle of ternary expressions", ->
{tokens} = grammar.tokenizeLine("a ? #{keyword} : b")
expect(tokens[2]).toEqual value: ' ', scopes: ['source.js']
expect(tokens[3]).toEqual value: keyword, scopes: ['source.js', scope]

it "tokenizes `#{keyword}` at the end of ternary expressions", ->
{tokens} = grammar.tokenizeLine("a ? b : #{keyword}")
expect(tokens[4]).toEqual value: ' ', scopes: ['source.js']
expect(tokens[5]).toEqual value: keyword, scopes: ['source.js', scope]

it "tokenises `#{keyword}` in case statements", ->
{tokens} = grammar.tokenizeLine("case #{keyword}:")
expect(tokens[0]).toEqual value: 'case', scopes: ['source.js', 'keyword.control.js']
expect(tokens[2]).toEqual value: keyword, scopes: ['source.js', scope]
expect(tokens[3]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.js']
it "tokenizes the debugger statement", ->
{tokens} = grammar.tokenizeLine("debugger;")
expect(tokens[0]).toEqual value: "debugger", scopes: ['source.js', 'keyword.other.debugger.js']
expect(tokens[1]).toEqual value: ";", scopes: ['source.js', 'punctuation.terminator.statement.js']

describe "built-in globals", ->
it "tokenizes built-in classes", ->
@@ -160,21 +135,6 @@ describe "Javascript grammar", ->
expect(tokens[6]).toEqual value: '/', scopes: ['source.js', 'string.regexp.js', 'punctuation.definition.string.end.js']
expect(tokens[7]).toEqual value: ']', scopes: ['source.js', 'meta.brace.square.js']

it "tokenizes regular expressions inside ternary expressions", ->
{tokens} = grammar.tokenizeLine('a ? /b/ : /c/')
expect(tokens[ 0]).toEqual value: 'a ', scopes: ['source.js']
expect(tokens[ 1]).toEqual value: '?', scopes: ['source.js', 'keyword.operator.js']
expect(tokens[ 2]).toEqual value: ' ', scopes: ['source.js', 'string.regexp.js']
expect(tokens[ 3]).toEqual value: '/', scopes: ['source.js', 'string.regexp.js', 'punctuation.definition.string.begin.js']
expect(tokens[ 4]).toEqual value: 'b', scopes: ['source.js', 'string.regexp.js']
expect(tokens[ 5]).toEqual value: '/', scopes: ['source.js', 'string.regexp.js', 'punctuation.definition.string.end.js']
expect(tokens[ 6]).toEqual value: ' ', scopes: ['source.js']
expect(tokens[ 7]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.js']
expect(tokens[ 8]).toEqual value: ' ', scopes: ['source.js', 'string.regexp.js']
expect(tokens[ 9]).toEqual value: '/', scopes: ['source.js', 'string.regexp.js', 'punctuation.definition.string.begin.js']
expect(tokens[10]).toEqual value: 'c', scopes: ['source.js', 'string.regexp.js']
expect(tokens[11]).toEqual value: '/', scopes: ['source.js', 'string.regexp.js', 'punctuation.definition.string.end.js']

it "tokenizes regular expressions inside arrow function expressions", ->
{tokens} = grammar.tokenizeLine('getRegex = () => /^helloworld$/;')
expect(tokens[9]).toEqual value: '/', scopes: ['source.js', 'string.regexp.js', 'punctuation.definition.string.begin.js']
@@ -283,15 +243,6 @@ describe "Javascript grammar", ->
expect(tokens[0]).toEqual value: 'i', scopes: ['source.js']
expect(tokens[1]).toEqual value: '--', scopes: ['source.js', 'keyword.operator.decrement.js']

describe "conditional ternary", ->
it "tokenizes them", ->
{tokens} = grammar.tokenizeLine('test ? expr1 : expr2')
expect(tokens[0]).toEqual value: 'test ', scopes: ['source.js']
expect(tokens[1]).toEqual value: '?', scopes: ['source.js', 'keyword.operator.js']
expect(tokens[2]).toEqual value: ' expr1 ', scopes: ['source.js']
expect(tokens[3]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.js']
expect(tokens[4]).toEqual value: ' expr2', scopes: ['source.js']

describe "logical", ->
operators = ["&&", "||", "!"]

@@ -523,18 +474,13 @@ describe "Javascript grammar", ->
expect(tokens[5]).toEqual value: 'systemLanguage', scopes: ['source.js', 'support.constant.js']
expect(tokens[6]).toEqual value: ';', scopes: ['source.js', 'punctuation.terminator.statement.js']

it "does not tokenize constants when they are object keys", ->
{tokens} = grammar.tokenizeLine('FOO: 1')
expect(tokens[0]).toEqual value: 'FOO', scopes: ['source.js']
expect(tokens[1]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.js']

it "tokenizes constants in the middle of ternary expressions", ->
{tokens} = grammar.tokenizeLine('a ? FOO : b')
expect(tokens[3]).toEqual value: 'FOO', scopes: ['source.js', 'constant.other.js']

it "tokenizes constants at the end of ternary expressions", ->
{tokens} = grammar.tokenizeLine('a ? b : FOO')
expect(tokens[5]).toEqual value: 'FOO', scopes: ['source.js', 'constant.other.js']
expect(tokens[7]).toEqual value: 'FOO', scopes: ['source.js', 'constant.other.js']

describe "ES6 string templates", ->
it "tokenizes them as strings", ->
@@ -823,7 +769,7 @@ describe "Javascript grammar", ->
expect(tokens[6]).toEqual value: "'", scopes: ['source.js', 'string.quoted.single.js', 'punctuation.definition.string.begin.js']
expect(tokens[7]).toEqual value: "prop", scopes: ['source.js', 'string.quoted.single.js']
expect(tokens[8]).toEqual value: "'", scopes: ['source.js', 'string.quoted.single.js', 'punctuation.definition.string.end.js']
expect(tokens[9]).toEqual value: ":", scopes: ['source.js', 'keyword.operator.js']
expect(tokens[9]).toEqual value: ":", scopes: ['source.js', 'keyword.operator.assignment.js']
expect(tokens[11]).toEqual value: "'", scopes: ['source.js', 'string.quoted.single.js', 'punctuation.definition.string.begin.js']
expect(tokens[12]).toEqual value: "value", scopes: ['source.js', 'string.quoted.single.js']
expect(tokens[13]).toEqual value: "'", scopes: ['source.js', 'string.quoted.single.js', 'punctuation.definition.string.end.js']
@@ -908,24 +854,6 @@ describe "Javascript grammar", ->
expect(tokens[0]).toEqual value: 'yield', scopes: ['source.js', 'meta.control.yield.js', 'keyword.control.js']
expect(tokens[2]).toEqual value: '*', scopes: ['source.js', 'meta.control.yield.js', 'storage.modifier.js']

it "does not tokenize yield when it is an object key", ->
{tokens} = grammar.tokenizeLine('yield: 1')
expect(tokens[0]).toEqual value: 'yield', scopes: ['source.js']
expect(tokens[1]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.js']

it "tokenizes yield in the middle of ternary expressions", ->
{tokens} = grammar.tokenizeLine('a ? yield : b')
expect(tokens[3]).toEqual value: 'yield', scopes: ['source.js', 'meta.control.yield.js', 'keyword.control.js']

it "tokenizes yield at the end of ternary expressions", ->
{tokens} = grammar.tokenizeLine('a ? b : yield')
expect(tokens[5]).toEqual value: 'yield', scopes: ['source.js', 'meta.control.yield.js', 'keyword.control.js']

describe "default: in a switch statement", ->
it "tokenizes it as a keyword", ->
{tokens} = grammar.tokenizeLine('default: ')
expect(tokens[0]).toEqual value: 'default', scopes: ['source.js', 'keyword.control.js']

describe "functions", ->
it "tokenizes regular function declarations", ->
{tokens} = grammar.tokenizeLine('function foo(){}')
@@ -1253,7 +1181,7 @@ describe "Javascript grammar", ->
expect(tokens[8]).toEqual value: ',', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'meta.delimiter.object.comma.js']
expect(tokens[10]).toEqual value: '{', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'meta.brace.curly.js']
expect(tokens[11]).toEqual value: 'a', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js']
expect(tokens[12]).toEqual value: ':', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'keyword.operator.js']
expect(tokens[12]).toEqual value: ':', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'keyword.operator.assignment.js']
expect(tokens[14]).toEqual value: '123', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'constant.numeric.decimal.js']
expect(tokens[15]).toEqual value: '}', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'meta.brace.curly.js']
expect(tokens[16]).toEqual value: ')', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'punctuation.definition.arguments.end.bracket.round.js']
@@ -1736,6 +1664,150 @@ describe "Javascript grammar", ->
expect(tokens[4]).toEqual value: ')', scopes: ['source.js', 'meta.method-call.js', 'meta.arguments.js', 'punctuation.definition.arguments.end.bracket.round.js']
expect(tokens[5]).toEqual value: ';', scopes: ['source.js', 'punctuation.terminator.statement.js']

describe "object literals", ->
keywords = ['super', 'this', 'null', 'true', 'false', 'debugger', 'exports', '__filename']

for keyword in keywords
it "tokenizes the #{keyword} keyword when it is an object key", ->
{tokens} = grammar.tokenizeLine("#{keyword}: 1")
expect(tokens[0]).toEqual value: keyword, scopes: ['source.js']
expect(tokens[1]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.assignment.js']

it "tokenizes object keys", ->
{tokens} = grammar.tokenizeLine('foo: 1')
expect(tokens[0]).toEqual value: 'foo', scopes: ['source.js']
expect(tokens[1]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.assignment.js']

{tokens} = grammar.tokenizeLine('$abc$: 1')
expect(tokens[0]).toEqual value: '$abc$', scopes: ['source.js']
expect(tokens[1]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.assignment.js']

{tokens} = grammar.tokenizeLine('0abc: 1')
expect(tokens[0]).toEqual value: '0abc', scopes: ['source.js', 'invalid.illegal.identifier.js']
expect(tokens[1]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.assignment.js']

{tokens} = grammar.tokenizeLine('"key": 1')
expect(tokens[0]).toEqual value: '"', scopes: ['source.js', 'string.quoted.double.js', 'punctuation.definition.string.begin.js']
expect(tokens[1]).toEqual value: 'key', scopes: ['source.js', 'string.quoted.double.js']
expect(tokens[2]).toEqual value: '"', scopes: ['source.js', 'string.quoted.double.js', 'punctuation.definition.string.end.js']
expect(tokens[3]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.assignment.js']

it "tokenizes numbers when they are object keys", ->
{tokens} = grammar.tokenizeLine('123: 1')
expect(tokens[0]).toEqual value: '123', scopes: ['source.js', 'constant.numeric.decimal.js']
expect(tokens[1]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.assignment.js']

it "tokenizes constants when they are object keys", ->
{tokens} = grammar.tokenizeLine('FOO: 1')
expect(tokens[0]).toEqual value: 'FOO', scopes: ['source.js']
expect(tokens[1]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.assignment.js']

describe "ternary expressions", ->
map =
FOO: 'constant.other.js'
super: 'variable.language.js'
this: 'variable.language.js'
null: 'constant.language.null.js'
true: 'constant.language.boolean.true.js'
false: 'constant.language.boolean.false.js'
exports: 'support.variable.js'
__filename: 'support.variable.js'

for keyword, scope of map
do (keyword, scope) ->
it "tokenizes `#{keyword}` in the middle of ternary expressions", ->
{tokens} = grammar.tokenizeLine("a ? #{keyword} : b")
expect(tokens[3]).toEqual value: keyword, scopes: ['source.js', scope]

it "tokenizes `#{keyword}` at the end of ternary expressions", ->
{tokens} = grammar.tokenizeLine("a ? b : #{keyword}")
expect(tokens[7]).toEqual value: keyword, scopes: ['source.js', scope]

it "tokenizes yield at the end of ternary expressions", ->
{tokens} = grammar.tokenizeLine('a ? b : yield')
expect(tokens[7]).toEqual value: 'yield', scopes: ['source.js', 'meta.control.yield.js', 'keyword.control.js']

it "tokenizes yield in the middle of ternary expressions", ->
{tokens} = grammar.tokenizeLine('a ? yield : b')
expect(tokens[3]).toEqual value: 'yield', scopes: ['source.js', 'meta.control.yield.js', 'keyword.control.js']

it "tokenizes regular expressions inside ternary expressions", ->
{tokens} = grammar.tokenizeLine('a ? /b/ : /c/')
expect(tokens[0]).toEqual value: 'a ', scopes: ['source.js']
expect(tokens[1]).toEqual value: '?', scopes: ['source.js', 'keyword.operator.ternary.js']
expect(tokens[2]).toEqual value: ' ', scopes: ['source.js', 'string.regexp.js']
expect(tokens[3]).toEqual value: '/', scopes: ['source.js', 'string.regexp.js', 'punctuation.definition.string.begin.js']
expect(tokens[4]).toEqual value: 'b', scopes: ['source.js', 'string.regexp.js']
expect(tokens[5]).toEqual value: '/', scopes: ['source.js', 'string.regexp.js', 'punctuation.definition.string.end.js']
expect(tokens[6]).toEqual value: ' ', scopes: ['source.js']
expect(tokens[7]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.ternary.js']
expect(tokens[8]).toEqual value: ' ', scopes: ['source.js', 'string.regexp.js']
expect(tokens[9]).toEqual value: '/', scopes: ['source.js', 'string.regexp.js', 'punctuation.definition.string.begin.js']
expect(tokens[10]).toEqual value: 'c', scopes: ['source.js', 'string.regexp.js']
expect(tokens[11]).toEqual value: '/', scopes: ['source.js', 'string.regexp.js', 'punctuation.definition.string.end.js']

it "tokenizes object literals in the middle of ternary expressions", ->
{tokens} = grammar.tokenizeLine('a ? {key: value} : b')
expect(tokens[1]).toEqual value: '?', scopes: ['source.js', 'keyword.operator.ternary.js']
expect(tokens[9]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.ternary.js']

it "tokenizes arrow functions inside ternary expressions", ->
{tokens} = grammar.tokenizeLine('result = condition ? something : (a, b) => a + b')
expect(tokens[3]).toEqual value: '?', scopes: ['source.js', 'keyword.operator.ternary.js']
expect(tokens[7]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.ternary.js']
expect(tokens[16]).toEqual value: '=>', scopes: ['source.js', 'meta.function.arrow.js', 'storage.type.function.arrow.js']

{tokens} = grammar.tokenizeLine('result = condition ? (a, b) => a + b : something')
expect(tokens[3]).toEqual value: '?', scopes: ['source.js', 'keyword.operator.ternary.js']
expect(tokens[12]).toEqual value: '=>', scopes: ['source.js', 'meta.function.arrow.js', 'storage.type.function.arrow.js']
expect(tokens[18]).toEqual value: ':', scopes: ['source.js', 'keyword.operator.ternary.js']

describe "switch statements", ->
it "tokenizes the switch keyword", ->
{tokens} = grammar.tokenizeLine('switch(){}')
expect(tokens[0]).toEqual value: 'switch', scopes: ['source.js', 'meta.switch-statement.js', 'keyword.control.switch.js']

it "tokenizes switch expression", ->
{tokens} = grammar.tokenizeLine('switch(foo + bar){}')
expect(tokens[1]).toEqual value: '(', scopes: ['source.js', 'meta.switch-statement.js', 'punctuation.definition.switch-expression.begin.bracket.round.js']
expect(tokens[2]).toEqual value: 'foo ', scopes: ['source.js', 'meta.switch-statement.js']
expect(tokens[3]).toEqual value: '+', scopes: ['source.js', 'meta.switch-statement.js', 'keyword.operator.js']
expect(tokens[4]).toEqual value: ' bar', scopes: ['source.js', 'meta.switch-statement.js']
expect(tokens[5]).toEqual value: ')', scopes: ['source.js', 'meta.switch-statement.js', 'punctuation.definition.switch-expression.end.bracket.round.js']

it "tokenizes switch block", ->
lines = grammar.tokenizeLines '''
switch (foo())
{
case abc:
case 1+1:
2+2
break;
case null:
default:
}
'''
expect(lines[1][0]).toEqual value: '{', scopes: ['source.js', 'meta.switch-statement.js', 'punctuation.definition.section.switch-block.begin.bracket.curly.js']
expect(lines[2][1]).toEqual value: 'case', scopes: ['source.js', 'meta.switch-statement.js', 'keyword.control.case.js']
expect(lines[2][3]).toEqual value: 'abc', scopes: ['source.js', 'meta.switch-statement.js']
expect(lines[2][4]).toEqual value: ':', scopes: ['source.js', 'meta.switch-statement.js', 'punctuation.definition.section.case-statement.js']
expect(lines[3][1]).toEqual value: 'case', scopes: ['source.js', 'meta.switch-statement.js', 'keyword.control.case.js']
expect(lines[3][3]).toEqual value: '1', scopes: ['source.js', 'meta.switch-statement.js', 'constant.numeric.decimal.js']
expect(lines[3][4]).toEqual value: '+', scopes: ['source.js', 'meta.switch-statement.js', 'keyword.operator.js']
expect(lines[3][5]).toEqual value: '1', scopes: ['source.js', 'meta.switch-statement.js', 'constant.numeric.decimal.js']
expect(lines[3][6]).toEqual value: ':', scopes: ['source.js', 'meta.switch-statement.js', 'punctuation.definition.section.case-statement.js']
expect(lines[4][1]).toEqual value: '2', scopes: ['source.js', 'meta.switch-statement.js', 'constant.numeric.decimal.js']
expect(lines[4][2]).toEqual value: '+', scopes: ['source.js', 'meta.switch-statement.js', 'keyword.operator.js']
expect(lines[4][3]).toEqual value: '2', scopes: ['source.js', 'meta.switch-statement.js', 'constant.numeric.decimal.js']
expect(lines[5][1]).toEqual value: 'break', scopes: ['source.js', 'meta.switch-statement.js', 'keyword.control.js']
expect(lines[5][2]).toEqual value: ';', scopes: ['source.js', 'meta.switch-statement.js', 'punctuation.terminator.statement.js']
expect(lines[6][1]).toEqual value: 'case', scopes: ['source.js', 'meta.switch-statement.js', 'keyword.control.case.js']
expect(lines[6][3]).toEqual value: 'null', scopes: ['source.js', 'meta.switch-statement.js', 'constant.language.null.js']
expect(lines[6][4]).toEqual value: ':', scopes: ['source.js', 'meta.switch-statement.js', 'punctuation.definition.section.case-statement.js']
expect(lines[7][1]).toEqual value: 'default', scopes: ['source.js', 'meta.switch-statement.js', 'keyword.control.default.js']
expect(lines[7][2]).toEqual value: ':', scopes: ['source.js', 'meta.switch-statement.js', 'punctuation.definition.section.case-statement.js']
expect(lines[8][0]).toEqual value: '}', scopes: ['source.js', 'meta.switch-statement.js', 'punctuation.definition.section.switch-block.end.bracket.curly.js']

describe "indentation", ->
editor = null