// // 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 } }