После некоторых исследований я обнаружил, что мой вопрос был больше о подсветке синтаксиса с помощью NSTextView.Я знаю, что это вопрос, который задают многие разработчики MacOS, и для этого есть множество решений.Возможно, это не лучший вариант, но именно так я решил эту проблему.
NSTextStorage
Для этого я использовал подкласс NSTextStorage.Здесь будет выполнена вся работа с синтаксисом.NSTextStorage не ориентирован на протокол, поэтому вы должны переопределить метод самостоятельно, как указано в документации Apple:
class SyntaxTextStorage: NSTextStorage {
private var storage: NSTextStorage
override var string: String {
return storage.string
}
override init() {
self.storage = NSTextStorage(string: "")
super.init()
}
override func attributes(at location: Int, effectiveRange range: NSRangePointer?) -> [NSAttributedString.Key : Any] {
return storage.attributes(at: location, effectiveRange: range)
}
override func replaceCharacters(in range: NSRange, with str: String) {
beginEditing()
storage.replaceCharacters(in: range, with: str)
edited(.editedCharacters, range: range, changeInLength: str.count - range.length)
endEditing()
}
override func setAttributes(_ attrs: [NSAttributedString.Key : Any]?, range: NSRange) {
beginEditing()
storage.setAttributes(attrs, range: range)
edited(.editedAttributes, range: range, changeInLength: 0)
endEditing()
}
}
Это базовое значение для создания текстового хранилища.
NSTextStorage + NSTextView
Следующим шагом является установка хранилища текста в вашем textView.Для этого вы можете использовать метод replaceTextStorage()
, доступный в textView layoutManager
.
class SyntaxTextView: NSTextView {
var storage: SyntaxTextStorage!
override func awakeFromNib() {
super.awakeFromNib()
configureTextStorage()
}
private func configureTextStorage() {
storage = SyntaxTextStorage()
layoutManager?.replaceTextStorage(storage)
}
}
Синтаксис
Последний шаг - выполнить синтаксическую работу.Стоимость процессора этого процесса очень высока.Есть много способов сделать это, чтобы получить лучшие выступления.Я предлагаю вам реализовать класс, который будет возвращать вам список NSAttributedString
и NSRange
.Работа с хранилищем текста должна заключаться только в применении стиля к вашему тексту.Лично я использовал метод processEditing
для анализа текста:
override func processEditing() {
super.processEditing()
syntaxCurrentParagraph()
}
Я рекомендую вам выполнить синтаксический анализ в фоновом режиме, а затем, если с момента вашего последнего анализа не произошло никаких изменений текста,применить изменения к вашему тексту.Всегда в моем текстовом хранилище я реализовал метод syntax
, который применяет стиль к тексту:
private func syntax(range: NSRange, completion: ((_ succeed: Bool) -> Void)? = nil) {
guard range.length > 0 else {
completion?(true)
return
}
// Save your data to do the job in background
let currentString = self.string
let substring = currentString.substring(range: range)
let mutableAttributedString = NSMutableAttributedString(string: substring, attributes: NSAttributedString.defaultAttributes as [NSAttributedString.Key : Any])
DispatchQueue.global(qos: .background).async {
ARSyntaxManager.tokens(text: substring, language: self.language) { (tokens) in
// Fill you attributed string
for token in tokens {
mutableAttributedString.addAttributes(token.attributes, range: token.range)
}
DispatchQueue.main.async {
// Check if there is no change
guard self.string.count == currentString.count else {
completion?(false)
return
}
completion?(true)
// Apply your change
self.storage.replaceCharacters(in: range, with: mutableAttributedString)
self.displayVisibleRect()
}
}
}
}
Вот и все.Надеюсь, это поможет некоторым из вас.