From c711521b1f596e8a193fddf8954cdaae10bffdca Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Thu, 12 Jan 2023 06:47:20 +0000 Subject: [PATCH] Remove `NSLayoutManager` --- .../CodeEditTextView/CodeEditTextView.swift | 11 +++++-- .../NSRange+/NSRange+NSTextRange.swift | 13 ++++++++ .../STTextView+/STTextView+VisibleRange.swift | 30 ++++++++++++------- .../Highlighting/HighlightRange.swift | 2 +- .../Highlighting/Highlighter.swift | 12 ++++++-- 5 files changed, 53 insertions(+), 15 deletions(-) diff --git a/Sources/CodeEditTextView/CodeEditTextView.swift b/Sources/CodeEditTextView/CodeEditTextView.swift index 3cb9823ca..0030d4fa6 100644 --- a/Sources/CodeEditTextView/CodeEditTextView.swift +++ b/Sources/CodeEditTextView/CodeEditTextView.swift @@ -73,12 +73,19 @@ public struct CodeEditTextView: NSViewControllerRepresentable { public func updateNSViewController(_ controller: NSViewControllerType, context: Context) { controller.font = font - controller.language = language - controller.theme = theme controller.tabWidth = tabWidth controller.wrapLines = wrapLines controller.lineHeightMultiple = lineHeight controller.editorOverscroll = editorOverscroll + + // Updating the language and theme needlessly can cause highlights to be re-calculated. + if controller.language.id != language.id { + controller.language = language + } + if controller.theme != theme { + controller.theme = theme + } + controller.reloadUI() return } diff --git a/Sources/CodeEditTextView/Extensions/NSRange+/NSRange+NSTextRange.swift b/Sources/CodeEditTextView/Extensions/NSRange+/NSRange+NSTextRange.swift index a9a0a8737..daaee99c1 100644 --- a/Sources/CodeEditTextView/Extensions/NSRange+/NSRange+NSTextRange.swift +++ b/Sources/CodeEditTextView/Extensions/NSRange+/NSRange+NSTextRange.swift @@ -21,4 +21,17 @@ public extension NSTextRange { self.init(location: start, end: end) } + + /// Creates an `NSRange` using document information from the given provider. + /// - Parameter provider: The `NSTextElementProvider` to use to convert this range into an `NSRange` + /// - Returns: An `NSRange` if possible + func nsRange(using provider: NSTextElementProvider) -> NSRange? { + guard let location = provider.offset?(from: provider.documentRange.location, to: location) else { + return nil + } + guard let length = provider.offset?(from: self.location, to: endLocation) else { + return nil + } + return NSRange(location: location, length: length) + } } diff --git a/Sources/CodeEditTextView/Extensions/STTextView+/STTextView+VisibleRange.swift b/Sources/CodeEditTextView/Extensions/STTextView+/STTextView+VisibleRange.swift index d6bb88bd6..476e953d8 100644 --- a/Sources/CodeEditTextView/Extensions/STTextView+/STTextView+VisibleRange.swift +++ b/Sources/CodeEditTextView/Extensions/STTextView+/STTextView+VisibleRange.swift @@ -7,22 +7,32 @@ import Foundation import STTextView +import AppKit extension STTextView { - func textRange(for rect: CGRect) -> NSRange { - let length = self.textContentStorage.textStorage?.length ?? 0 + /// A helper for calculating the visible range on the text view with some small vertical padding. + var visibleTextRange: NSRange? { + // This helper finds the visible rect of the text using the enclosing scroll view, then finds the nearest + // `NSTextElement`s to those points and uses those elements to create the returned range. - guard let layoutManager = self.textContainer.layoutManager else { - return NSRange(0..