2019-01-07 12:47:27 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-12-04 11:15:29 +00:00
|
|
|
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
2019-01-07 12:47:27 +00:00
|
|
|
|
|
|
|
import Cocoa
|
|
|
|
|
2019-01-08 10:35:06 +00:00
|
|
|
private let fontSize: CGFloat = 15
|
|
|
|
|
2019-01-07 12:47:27 +00:00
|
|
|
class ConfTextStorage: NSTextStorage {
|
2019-01-08 10:35:06 +00:00
|
|
|
let defaultFont = NSFontManager.shared.convertWeight(true, of: NSFont.systemFont(ofSize: fontSize))
|
|
|
|
private let boldFont = NSFont.boldSystemFont(ofSize: fontSize)
|
|
|
|
private lazy var italicFont = NSFontManager.shared.convert(defaultFont, toHaveTrait: .italicFontMask)
|
2019-01-07 12:47:27 +00:00
|
|
|
|
2019-01-21 21:13:14 +00:00
|
|
|
private var textColorTheme: ConfTextColorTheme.Type?
|
2019-01-07 12:47:27 +00:00
|
|
|
|
|
|
|
private let backingStore: NSMutableAttributedString
|
|
|
|
private(set) var hasError = false
|
2019-01-08 19:11:36 +00:00
|
|
|
private(set) var privateKeyString: String?
|
2019-01-07 12:47:27 +00:00
|
|
|
|
2019-02-15 18:43:56 +00:00
|
|
|
private(set) var hasOnePeer: Bool = false
|
2019-02-15 19:01:28 +00:00
|
|
|
private(set) var lastOnePeerAllowedIPs = [String]()
|
|
|
|
private(set) var lastOnePeerDNSServers = [String]()
|
2019-03-22 10:22:39 +00:00
|
|
|
private(set) var lastOnePeerHasPublicKey = false
|
2019-02-15 18:43:56 +00:00
|
|
|
|
2019-01-07 12:47:27 +00:00
|
|
|
override init() {
|
|
|
|
backingStore = NSMutableAttributedString(string: "")
|
|
|
|
super.init()
|
|
|
|
}
|
|
|
|
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
required init?(pasteboardPropertyList propertyList: Any, ofType type: NSPasteboard.PasteboardType) {
|
|
|
|
fatalError("init(pasteboardPropertyList:ofType:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
2019-01-21 19:35:10 +00:00
|
|
|
func nonColorAttributes(for highlightType: highlight_type) -> [NSAttributedString.Key: Any] {
|
|
|
|
switch highlightType.rawValue {
|
|
|
|
case HighlightSection.rawValue, HighlightField.rawValue:
|
|
|
|
return [.font: boldFont]
|
|
|
|
case HighlightPublicKey.rawValue, HighlightPrivateKey.rawValue, HighlightPresharedKey.rawValue,
|
|
|
|
HighlightIP.rawValue, HighlightCidr.rawValue, HighlightHost.rawValue, HighlightPort.rawValue,
|
|
|
|
HighlightMTU.rawValue, HighlightKeepalive.rawValue, HighlightDelimiter.rawValue:
|
|
|
|
return [.font: defaultFont]
|
|
|
|
case HighlightComment.rawValue:
|
|
|
|
return [.font: italicFont]
|
|
|
|
case HighlightError.rawValue:
|
|
|
|
return [.font: defaultFont, .underlineStyle: 1]
|
|
|
|
default:
|
|
|
|
return [:]
|
|
|
|
}
|
|
|
|
}
|
2019-01-07 12:47:27 +00:00
|
|
|
|
2019-01-21 21:13:14 +00:00
|
|
|
func updateAttributes(for textColorTheme: ConfTextColorTheme.Type) {
|
2019-01-21 19:35:10 +00:00
|
|
|
self.textColorTheme = textColorTheme
|
2019-01-07 12:47:27 +00:00
|
|
|
highlightSyntax()
|
|
|
|
}
|
|
|
|
|
|
|
|
override var string: String {
|
|
|
|
return backingStore.string
|
|
|
|
}
|
|
|
|
|
|
|
|
override func attributes(at location: Int, effectiveRange range: NSRangePointer?) -> [NSAttributedString.Key: Any] {
|
|
|
|
return backingStore.attributes(at: location, effectiveRange: range)
|
|
|
|
}
|
|
|
|
|
|
|
|
override func replaceCharacters(in range: NSRange, with str: String) {
|
|
|
|
beginEditing()
|
|
|
|
backingStore.replaceCharacters(in: range, with: str)
|
|
|
|
edited(.editedCharacters, range: range, changeInLength: str.count - range.length)
|
|
|
|
endEditing()
|
|
|
|
}
|
|
|
|
|
|
|
|
override func replaceCharacters(in range: NSRange, with attrString: NSAttributedString) {
|
|
|
|
beginEditing()
|
|
|
|
backingStore.replaceCharacters(in: range, with: attrString)
|
|
|
|
edited(.editedCharacters, range: range, changeInLength: attrString.length - range.length)
|
|
|
|
endEditing()
|
|
|
|
}
|
|
|
|
|
|
|
|
override func setAttributes(_ attrs: [NSAttributedString.Key: Any]?, range: NSRange) {
|
|
|
|
beginEditing()
|
|
|
|
backingStore.setAttributes(attrs, range: range)
|
|
|
|
edited(.editedAttributes, range: range, changeInLength: 0)
|
|
|
|
endEditing()
|
|
|
|
}
|
|
|
|
|
2019-02-15 18:43:56 +00:00
|
|
|
func resetLastPeer() {
|
|
|
|
hasOnePeer = false
|
|
|
|
lastOnePeerAllowedIPs = []
|
|
|
|
lastOnePeerDNSServers = []
|
2019-03-22 10:22:39 +00:00
|
|
|
lastOnePeerHasPublicKey = false
|
2019-02-15 18:43:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func evaluateExcludePrivateIPs(highlightSpans: UnsafePointer<highlight_span>) {
|
|
|
|
var spans = highlightSpans
|
2019-02-15 18:56:49 +00:00
|
|
|
enum FieldType: String {
|
|
|
|
case dns
|
|
|
|
case allowedips
|
|
|
|
}
|
|
|
|
var fieldType: FieldType?
|
2019-02-15 18:43:56 +00:00
|
|
|
resetLastPeer()
|
|
|
|
while spans.pointee.type != HighlightEnd {
|
|
|
|
let span = spans.pointee
|
2019-02-15 20:10:58 +00:00
|
|
|
var substring = backingStore.attributedSubstring(from: NSRange(location: span.start, length: span.len)).string.lowercased()
|
2019-02-15 18:43:56 +00:00
|
|
|
|
|
|
|
if span.type == HighlightError {
|
|
|
|
resetLastPeer()
|
|
|
|
return
|
2019-02-15 20:10:58 +00:00
|
|
|
} else if span.type == HighlightSection {
|
|
|
|
if substring == "[peer]" {
|
2019-02-15 18:43:56 +00:00
|
|
|
if hasOnePeer {
|
|
|
|
resetLastPeer()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
hasOnePeer = true
|
|
|
|
}
|
|
|
|
} else if span.type == HighlightField {
|
2019-02-15 20:10:58 +00:00
|
|
|
fieldType = FieldType(rawValue: substring)
|
2019-02-15 18:56:49 +00:00
|
|
|
} else if span.type == HighlightIP && fieldType == .dns {
|
2019-02-15 19:01:28 +00:00
|
|
|
lastOnePeerDNSServers.append(substring)
|
2019-02-15 18:56:49 +00:00
|
|
|
} else if span.type == HighlightIP && fieldType == .allowedips {
|
2019-02-15 18:43:56 +00:00
|
|
|
let next = spans.successor()
|
|
|
|
let nextnext = next.successor()
|
|
|
|
if next.pointee.type == HighlightDelimiter && nextnext.pointee.type == HighlightCidr {
|
|
|
|
substring += backingStore.attributedSubstring(from: NSRange(location: next.pointee.start, length: next.pointee.len)).string +
|
|
|
|
backingStore.attributedSubstring(from: NSRange(location: nextnext.pointee.start, length: nextnext.pointee.len)).string
|
|
|
|
}
|
2019-02-15 19:01:28 +00:00
|
|
|
lastOnePeerAllowedIPs.append(substring)
|
2019-03-22 10:22:39 +00:00
|
|
|
} else if span.type == HighlightPublicKey {
|
|
|
|
lastOnePeerHasPublicKey = true
|
2019-02-15 18:43:56 +00:00
|
|
|
}
|
|
|
|
spans = spans.successor()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-07 12:47:27 +00:00
|
|
|
func highlightSyntax() {
|
2019-01-21 21:13:14 +00:00
|
|
|
guard let textColorTheme = textColorTheme else { return }
|
2019-01-07 12:47:27 +00:00
|
|
|
hasError = false
|
2019-01-08 19:11:36 +00:00
|
|
|
privateKeyString = nil
|
2019-01-07 12:47:27 +00:00
|
|
|
|
2019-01-21 19:49:16 +00:00
|
|
|
let fullTextRange = NSRange(location: 0, length: (backingStore.string as NSString).length)
|
|
|
|
|
2019-01-07 12:47:27 +00:00
|
|
|
backingStore.beginEditing()
|
2019-01-21 21:13:14 +00:00
|
|
|
let defaultAttributes: [NSAttributedString.Key: Any] = [
|
|
|
|
.foregroundColor: textColorTheme.defaultColor,
|
|
|
|
.font: defaultFont
|
|
|
|
]
|
|
|
|
backingStore.setAttributes(defaultAttributes, range: fullTextRange)
|
2019-03-22 10:01:02 +00:00
|
|
|
var spans = highlight_config(backingStore.string)!
|
2019-02-15 18:43:56 +00:00
|
|
|
evaluateExcludePrivateIPs(highlightSpans: spans)
|
2019-01-07 12:47:27 +00:00
|
|
|
|
2019-03-22 12:23:34 +00:00
|
|
|
let spansStart = spans
|
2019-01-07 12:47:27 +00:00
|
|
|
while spans.pointee.type != HighlightEnd {
|
|
|
|
let span = spans.pointee
|
|
|
|
|
2019-01-21 19:35:10 +00:00
|
|
|
let range = NSRange(location: span.start, length: span.len)
|
|
|
|
backingStore.setAttributes(nonColorAttributes(for: span.type), range: range)
|
2019-01-21 21:22:20 +00:00
|
|
|
let color = textColorTheme.colorMap[span.type.rawValue, default: textColorTheme.defaultColor]
|
2019-01-21 21:13:14 +00:00
|
|
|
backingStore.addAttribute(.foregroundColor, value: color, range: range)
|
2019-01-07 12:47:27 +00:00
|
|
|
|
|
|
|
if span.type == HighlightError {
|
|
|
|
hasError = true
|
|
|
|
}
|
|
|
|
|
2019-01-08 19:11:36 +00:00
|
|
|
if span.type == HighlightPrivateKey {
|
|
|
|
privateKeyString = backingStore.attributedSubstring(from: NSRange(location: span.start, length: span.len)).string
|
|
|
|
}
|
|
|
|
|
2019-01-07 12:47:27 +00:00
|
|
|
spans = spans.successor()
|
|
|
|
}
|
|
|
|
backingStore.endEditing()
|
2019-03-22 12:23:34 +00:00
|
|
|
free(spansStart)
|
2019-01-07 12:47:27 +00:00
|
|
|
|
|
|
|
beginEditing()
|
2019-01-21 19:49:16 +00:00
|
|
|
edited(.editedAttributes, range: fullTextRange, changeInLength: 0)
|
2019-01-07 12:47:27 +00:00
|
|
|
endEditing()
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|