MongoView/MongoView/Views/JavaScriptEditorView.swift

86 lines
2.6 KiB
Swift

//
// JavaScriptEditorView.swift
// MongoView
//
// Created by Shadowfacts on 4/4/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import AppKit
class JavaScriptEditorView: NSTextView {
var highlighter: JavaScriptHighlighter?
override var string: String {
didSet {
rehighlight()
}
}
func rehighlight() {
highlighter = JavaScriptHighlighter(mutableAttributed: self.textStorage!)
highlighter!.highlight()
}
override func shouldChangeText(in affectedCharRange: NSRange, replacementString: String?) -> Bool {
guard super.shouldChangeText(in: affectedCharRange, replacementString: replacementString) else {
return false
}
if affectedCharRange.length == 0,
let string = replacementString,
string.count == 1,
tryAutocompleteCharacter(for: affectedCharRange, string: string) {
return false
}
return true
}
override func didChangeText() {
rehighlight()
super.didChangeText()
}
func tryAutocompleteCharacter(for range: NSRange, string inserted: String) -> Bool {
if let end = autocompleteResultFor(string: inserted, in: range) {
textStorage!.insert(NSAttributedString(string: "\(inserted)\(end)"), at: range.location)
didChangeText()
setSelectedRange(NSRange(location: range.location + 1, length: 0))
return true
} else {
return false
}
}
private func autocompleteResultFor(string: String, in range: NSRange) -> String? {
switch string {
case "'", "\"", "`":
return token(at: range.location) != .string ? string : nil
case "(":
return token(at: range.location) != .string ? ")" : nil
case "[":
return token(at: range.location) != .string ? "]" : nil
case "{":
var prevChar: Unicode.Scalar?
if range.location > 0 {
let index = self.string.index(self.string.startIndex, offsetBy: range.location - 1)
prevChar = self.string.unicodeScalars[index]
}
return token(at: range.location) != .string || (prevChar == "$") ? "}" : nil
default:
return nil
}
}
private func token(at index: Int) -> JavaScriptHighlighter.TokenType? {
guard let highlighter = highlighter else { return nil }
for (token, range) in highlighter.tokens where range.contains(index) {
return token
}
return nil
}
}