86 lines
2.6 KiB
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
|
|
}
|
|
}
|