diff --git a/Projects/NeonExample/ViewController.swift b/Projects/NeonExample/ViewController.swift index 61d3b33..8638cf9 100644 --- a/Projects/NeonExample/ViewController.swift +++ b/Projects/NeonExample/ViewController.swift @@ -13,10 +13,20 @@ final class ViewController: NSViewController { scrollView.documentView = textView - let provider: TextViewSystemInterface.AttributeProvider = { token in - guard token.name.hasPrefix("keyword") else { return [:] } + let regularFont = NSFont.monospacedSystemFont(ofSize: 16, weight: .regular) + let boldFont = NSFont.monospacedSystemFont(ofSize: 16, weight: .bold) + let italicFont = NSFont(descriptor: regularFont.fontDescriptor.withSymbolicTraits(.italic), size: 16) ?? regularFont + + // Alternatively, set `textView.typingAttributes = [.font: regularFont, ...]` + // if you want to customize other default (fallback) attributes. + textView.font = regularFont - return [NSAttributedString.Key.foregroundColor: NSColor.red] + let provider: TextViewSystemInterface.AttributeProvider = { token in + return switch token.name { + case let keyword where keyword.hasPrefix("keyword"): [.foregroundColor: NSColor.red, .font: boldFont] + case "comment": [.foregroundColor: NSColor.green, .font: italicFont] + default: [.foregroundColor: NSColor.textColor, .font: regularFont] + } } let language = Language(language: tree_sitter_swift()) @@ -52,6 +62,10 @@ final class ViewController: NSViewController { } override func viewWillAppear() { - textView.string = "let value = \"hello world\"\nprint(value)" + textView.string = """ + // Example Code! + let value = "hello world" + print(value) + """ } } diff --git a/Sources/Neon/TextViewHighlighter.swift b/Sources/Neon/TextViewHighlighter.swift index c015464..71a4cb9 100644 --- a/Sources/Neon/TextViewHighlighter.swift +++ b/Sources/Neon/TextViewHighlighter.swift @@ -80,10 +80,21 @@ public final class TextViewHighlighter: NSObject { treeSitterClient.invalidationHandler = { [weak self] in self?.handleInvalidation($0) } } - public convenience init(textView: TextView, language: Language, highlightQuery: Query, attributeProvider: @escaping TextViewSystemInterface.AttributeProvider) throws { + public convenience init( + textView: TextView, + language: Language, + highlightQuery: Query, + executionMode: TreeSitterClient.ExecutionMode = .asynchronous(prefetch: true), + attributeProvider: @escaping TextViewSystemInterface.AttributeProvider + ) throws { let client = try TreeSitterClient(language: language, transformer: { _ in return .zero }) - try self.init(textView: textView, client: client, highlightQuery: highlightQuery, attributeProvider: attributeProvider) + try self.init( + textView: textView, + client: client, + highlightQuery: highlightQuery, + executionMode: executionMode, + attributeProvider: attributeProvider) } @objc private func visibleContentChanged(_ notification: NSNotification) { diff --git a/Sources/Neon/TextViewSystemInterface.swift b/Sources/Neon/TextViewSystemInterface.swift index e54b532..4bfc309 100644 --- a/Sources/Neon/TextViewSystemInterface.swift +++ b/Sources/Neon/TextViewSystemInterface.swift @@ -61,15 +61,10 @@ extension TextViewSystemInterface: TextSystemInterface { return } - // next, textkit 1 - #if os(macOS) - if let layoutManager = layoutManager { - layoutManager.setTemporaryAttributes(attrs, forCharacterRange: clampedRange) - return - } - #endif - - // finally, fall back to applying color directly to the storage + // For TextKit 1: Fall back to applying styles directly to the storage. + // `NSLayoutManager.setTemporaryAttributes` is limited to attributes + // that don't affect layout, like color. So it ignores fonts, + // making font weight changes or italicizing text impossible. assert(textStorage != nil, "TextView's NSTextStorage cannot be nil") textStorage?.setAttributes(attrs, range: clampedRange) } diff --git a/Tests/NeonTests/TextViewSystemInterfaceTests.swift b/Tests/NeonTests/TextViewSystemInterfaceTests.swift index 13f8500..7e0d4d7 100644 --- a/Tests/NeonTests/TextViewSystemInterfaceTests.swift +++ b/Tests/NeonTests/TextViewSystemInterfaceTests.swift @@ -54,13 +54,13 @@ final class TextViewSystemInterfaceTests: XCTestCase { var effectiveRange: NSRange = .zero #if os(macOS) - let attrs = textView.layoutManager?.temporaryAttributes(atCharacterIndex: 0, effectiveRange: &effectiveRange) ?? [:] + let allAttrs = textView.textStorage?.attributes(at: 0, effectiveRange: &effectiveRange) ?? [:] #else let allAttrs = textView.textStorage.attributes(at: 0, effectiveRange: &effectiveRange) + #endif // we have to remove some attributes, like font, that are normal for the textStorage. let attrs = allAttrs.filter({ $0.key == .foregroundColor }) - #endif XCTAssertEqual(attrs.count, 1) XCTAssertEqual(attrs[.foregroundColor] as? PlatformColor, PlatformColor.red)