|
|
@ -78,7 +78,24 @@ class JavaScriptHighlighter {
|
|
|
|
return attributed
|
|
|
|
return attributed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func emit(token: TokenType, range: NSRange) {
|
|
|
|
|
|
|
|
let color: NSColor
|
|
|
|
|
|
|
|
switch token {
|
|
|
|
|
|
|
|
case .string:
|
|
|
|
|
|
|
|
color = .systemRed
|
|
|
|
|
|
|
|
case .number:
|
|
|
|
|
|
|
|
color = .systemBlue
|
|
|
|
|
|
|
|
case .punctuation:
|
|
|
|
|
|
|
|
color = .systemTeal
|
|
|
|
|
|
|
|
case .identifier:
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
attributed.addAttribute(.foregroundColor, value: color, range: range)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func consumeExpression() {
|
|
|
|
private func consumeExpression() {
|
|
|
|
|
|
|
|
consumeWhitespace()
|
|
|
|
|
|
|
|
|
|
|
|
guard let char = peek() else { return }
|
|
|
|
guard let char = peek() else { return }
|
|
|
|
|
|
|
|
|
|
|
|
if identifierStarts.contains(char) {
|
|
|
|
if identifierStarts.contains(char) {
|
|
|
@ -102,6 +119,15 @@ class JavaScriptHighlighter {
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
consume()
|
|
|
|
consume()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
consumeWhitespace()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func consumeWhitespace(newlines: Bool = true) {
|
|
|
|
|
|
|
|
let charSet = newlines ? CharacterSet.whitespacesAndNewlines : .whitespaces
|
|
|
|
|
|
|
|
while let char = peek(), charSet.contains(char) {
|
|
|
|
|
|
|
|
consume()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func consumeIdentifier() {
|
|
|
|
private func consumeIdentifier() {
|
|
|
@ -124,7 +150,7 @@ class JavaScriptHighlighter {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
print("Number: \(text[numberStart..<currentIndex])")
|
|
|
|
print("Number: \(text[numberStart..<currentIndex])")
|
|
|
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemBlue, range: range(from: numberStart, to: currentIndex))
|
|
|
|
emit(token: .number, range: range(from: numberStart, to: currentIndex))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func consumeString() {
|
|
|
|
private func consumeString() {
|
|
|
@ -137,7 +163,7 @@ class JavaScriptHighlighter {
|
|
|
|
consume() // string closing quote
|
|
|
|
consume() // string closing quote
|
|
|
|
}
|
|
|
|
}
|
|
|
|
print("String: \(text[stringStart..<currentIndex])")
|
|
|
|
print("String: \(text[stringStart..<currentIndex])")
|
|
|
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemRed, range: range(from: stringStart, to: currentIndex))
|
|
|
|
emit(token: .string, range: range(from: stringStart, to: currentIndex))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func consumeTemplateString() {
|
|
|
|
private func consumeTemplateString() {
|
|
|
@ -148,7 +174,7 @@ class JavaScriptHighlighter {
|
|
|
|
consume() // $
|
|
|
|
consume() // $
|
|
|
|
consume() // {
|
|
|
|
consume() // {
|
|
|
|
print("Template string fragment: '\(text[stringFragmentStart!..<currentIndex])'")
|
|
|
|
print("Template string fragment: '\(text[stringFragmentStart!..<currentIndex])'")
|
|
|
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemRed, range: range(from: stringFragmentStart!, to: currentIndex))
|
|
|
|
emit(token: .string, range: range(from: stringFragmentStart!, to: currentIndex))
|
|
|
|
consumeTemplateStringExpression()
|
|
|
|
consumeTemplateStringExpression()
|
|
|
|
stringFragmentStart = currentIndex
|
|
|
|
stringFragmentStart = currentIndex
|
|
|
|
if currentIndex < text.endIndex && peek() == "}" {
|
|
|
|
if currentIndex < text.endIndex && peek() == "}" {
|
|
|
@ -158,7 +184,7 @@ class JavaScriptHighlighter {
|
|
|
|
stringFragmentStart = stringFragmentStart ?? currentIndex
|
|
|
|
stringFragmentStart = stringFragmentStart ?? currentIndex
|
|
|
|
consume() // `
|
|
|
|
consume() // `
|
|
|
|
print("Template string fragment: '\(text[stringFragmentStart!..<currentIndex])'")
|
|
|
|
print("Template string fragment: '\(text[stringFragmentStart!..<currentIndex])'")
|
|
|
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemRed, range: range(from: stringFragmentStart!, to: currentIndex))
|
|
|
|
emit(token: .string, range: range(from: stringFragmentStart!, to: currentIndex))
|
|
|
|
stringFragmentStart = nil
|
|
|
|
stringFragmentStart = nil
|
|
|
|
break
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@ -167,7 +193,7 @@ class JavaScriptHighlighter {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let start = stringFragmentStart {
|
|
|
|
if let start = stringFragmentStart {
|
|
|
|
print("Template string fragment: '\(text[start..<currentIndex])'")
|
|
|
|
print("Template string fragment: '\(text[start..<currentIndex])'")
|
|
|
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemRed, range: range(from: start, to: currentIndex))
|
|
|
|
emit(token: .string, range: range(from: start, to: currentIndex))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -187,7 +213,7 @@ class JavaScriptHighlighter {
|
|
|
|
private func consumeFunctionCallOrGrouping() {
|
|
|
|
private func consumeFunctionCallOrGrouping() {
|
|
|
|
consume() // (
|
|
|
|
consume() // (
|
|
|
|
print("Opening (")
|
|
|
|
print("Opening (")
|
|
|
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
|
|
|
emit(token: .punctuation, range: prevCharRange())
|
|
|
|
indent += " "
|
|
|
|
indent += " "
|
|
|
|
while currentIndex < text.endIndex && peek() != ")" {
|
|
|
|
while currentIndex < text.endIndex && peek() != ")" {
|
|
|
|
consumeExpression()
|
|
|
|
consumeExpression()
|
|
|
@ -196,27 +222,31 @@ class JavaScriptHighlighter {
|
|
|
|
if currentIndex < text.endIndex {
|
|
|
|
if currentIndex < text.endIndex {
|
|
|
|
consume() // )
|
|
|
|
consume() // )
|
|
|
|
print("Closing )")
|
|
|
|
print("Closing )")
|
|
|
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
|
|
|
emit(token: .punctuation, range: prevCharRange())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func consumeObject() {
|
|
|
|
private func consumeObject() {
|
|
|
|
consume() // {
|
|
|
|
consume() // {
|
|
|
|
print("Opening {")
|
|
|
|
print("Opening {")
|
|
|
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
|
|
|
emit(token: .punctuation, range: prevCharRange())
|
|
|
|
indent += " "
|
|
|
|
indent += " "
|
|
|
|
object:
|
|
|
|
object:
|
|
|
|
while currentIndex < text.endIndex && peek() != "}" {
|
|
|
|
while currentIndex < text.endIndex && peek() != "}" {
|
|
|
|
consumeObjectKey()
|
|
|
|
consumeObjectKey()
|
|
|
|
if peek() == ":" {
|
|
|
|
if peek() == ":" {
|
|
|
|
consume() // :
|
|
|
|
consume() // :
|
|
|
|
|
|
|
|
emit(token: .punctuation, range: prevCharRange())
|
|
|
|
|
|
|
|
consumeWhitespace()
|
|
|
|
while currentIndex < text.endIndex && peek() != "," && peek() != "}" {
|
|
|
|
while currentIndex < text.endIndex && peek() != "," && peek() != "}" {
|
|
|
|
consumeExpression()
|
|
|
|
consumeExpression()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if currentIndex < text.endIndex {
|
|
|
|
|
|
|
|
break object
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if peek() == "," {
|
|
|
|
consumeWhitespace()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if peek() == "," {
|
|
|
|
|
|
|
|
consume() // ,
|
|
|
|
|
|
|
|
emit(token: .punctuation, range: prevCharRange())
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
break
|
|
|
@ -226,11 +256,12 @@ class JavaScriptHighlighter {
|
|
|
|
if currentIndex < text.endIndex {
|
|
|
|
if currentIndex < text.endIndex {
|
|
|
|
consume() // }
|
|
|
|
consume() // }
|
|
|
|
print("Closing }")
|
|
|
|
print("Closing }")
|
|
|
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
|
|
|
emit(token: .punctuation, range: prevCharRange())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func consumeObjectKey() {
|
|
|
|
private func consumeObjectKey() {
|
|
|
|
|
|
|
|
consumeWhitespace()
|
|
|
|
guard let char = peek() else { return }
|
|
|
|
guard let char = peek() else { return }
|
|
|
|
let keyStart = currentIndex!
|
|
|
|
let keyStart = currentIndex!
|
|
|
|
if identifierStarts.contains(char) {
|
|
|
|
if identifierStarts.contains(char) {
|
|
|
@ -239,18 +270,19 @@ class JavaScriptHighlighter {
|
|
|
|
consumeString()
|
|
|
|
consumeString()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
print("Object key: '\(text[keyStart..<currentIndex])'")
|
|
|
|
print("Object key: '\(text[keyStart..<currentIndex])'")
|
|
|
|
|
|
|
|
consumeWhitespace()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func consumeDotLookup() {
|
|
|
|
private func consumeDotLookup() {
|
|
|
|
consume() // .
|
|
|
|
consume() // .
|
|
|
|
print("Dot lookup")
|
|
|
|
print("Dot lookup")
|
|
|
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
|
|
|
emit(token: .punctuation, range: prevCharRange())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func consumeArray() {
|
|
|
|
private func consumeArray() {
|
|
|
|
consume() // [
|
|
|
|
consume() // [
|
|
|
|
print("Opening [")
|
|
|
|
print("Opening [")
|
|
|
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
|
|
|
emit(token: .punctuation, range: prevCharRange())
|
|
|
|
indent += " "
|
|
|
|
indent += " "
|
|
|
|
array:
|
|
|
|
array:
|
|
|
|
while currentIndex < text.endIndex && peek() != "]" {
|
|
|
|
while currentIndex < text.endIndex && peek() != "]" {
|
|
|
@ -258,6 +290,8 @@ class JavaScriptHighlighter {
|
|
|
|
while currentIndex < text.endIndex {
|
|
|
|
while currentIndex < text.endIndex {
|
|
|
|
if peek() == "," {
|
|
|
|
if peek() == "," {
|
|
|
|
consume() // ,
|
|
|
|
consume() // ,
|
|
|
|
|
|
|
|
print("Array separator")
|
|
|
|
|
|
|
|
emit(token: .punctuation, range: prevCharRange())
|
|
|
|
break
|
|
|
|
break
|
|
|
|
} else if peek() == "]" {
|
|
|
|
} else if peek() == "]" {
|
|
|
|
break array
|
|
|
|
break array
|
|
|
@ -272,8 +306,17 @@ class JavaScriptHighlighter {
|
|
|
|
if currentIndex < text.endIndex {
|
|
|
|
if currentIndex < text.endIndex {
|
|
|
|
consume() // ]
|
|
|
|
consume() // ]
|
|
|
|
print("Closing ]")
|
|
|
|
print("Closing ]")
|
|
|
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
|
|
|
emit(token: .punctuation, range: prevCharRange())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension JavaScriptHighlighter {
|
|
|
|
|
|
|
|
enum TokenType {
|
|
|
|
|
|
|
|
case identifier
|
|
|
|
|
|
|
|
case punctuation
|
|
|
|
|
|
|
|
case number
|
|
|
|
|
|
|
|
case string
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|