From 6b19f58d0ce4672b5faed72c938efcd1b6e7c0bd Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Fri, 21 Apr 2023 12:17:53 -0500 Subject: [PATCH 1/3] Update text container width correctly --- Sources/CodeEditTextView/CEScrollView.swift | 6 +++ .../Controller/STTextViewController.swift | 44 +++++++++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/Sources/CodeEditTextView/CEScrollView.swift b/Sources/CodeEditTextView/CEScrollView.swift index 6e8dcc0c7..0d89531e3 100644 --- a/Sources/CodeEditTextView/CEScrollView.swift +++ b/Sources/CodeEditTextView/CEScrollView.swift @@ -10,6 +10,12 @@ import STTextView class CEScrollView: NSScrollView { + override open var contentSize: NSSize { + var proposedSize = super.contentSize + proposedSize.width -= verticalRulerView?.requiredThickness ?? 0.0 + return proposedSize + } + override func mouseDown(with event: NSEvent) { if let textView = self.documentView as? STTextView, diff --git a/Sources/CodeEditTextView/Controller/STTextViewController.swift b/Sources/CodeEditTextView/Controller/STTextViewController.swift index 34ae6789f..77102b692 100644 --- a/Sources/CodeEditTextView/Controller/STTextViewController.swift +++ b/Sources/CodeEditTextView/Controller/STTextViewController.swift @@ -183,7 +183,6 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt textView.selectedLineHighlightColor = theme.lineHighlight textView.string = self.text.wrappedValue textView.isEditable = self.isEditable - textView.widthTracksTextView = self.wrapLines textView.highlightSelectedLine = true textView.allowsUndo = true textView.setupMenus() @@ -223,6 +222,7 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt queue: .main) { [weak self] _ in guard let self = self else { return } (self.view as? NSScrollView)?.contentView.contentInsets.bottom = self.bottomContentInsets + self.updateTextContainerWidthIfNeeded() } NotificationCenter.default.addObserver( @@ -232,16 +232,53 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt ) { [weak self] _ in self?.updateCursorPosition() } + + NotificationCenter.default.addObserver( + forName: NSView.frameDidChangeNotification, + object: (self.view as? NSScrollView)?.verticalRulerView, + queue: .main + ) { [weak self] _ in + self?.updateTextContainerWidthIfNeeded() + } } - public override func viewDidAppear() { - super.viewDidAppear() + public override func viewWillAppear() { + super.viewWillAppear() + updateTextContainerWidthIfNeeded() } public func textViewDidChangeText(_ notification: Notification) { self.text.wrappedValue = textView.string } + /// Update the text view's text container if needed. + /// + /// Effectively updates the container to reflect the `wrapLines` setting, and to reflect any updates to the ruler, + /// scroll view, or window frames. + private func updateTextContainerWidthIfNeeded() { + let previousTrackingSetting = textView.widthTracksTextView + textView.widthTracksTextView = wrapLines + if wrapLines { + var proposedSize = ((view as? NSScrollView)?.contentSize ?? .zero) + proposedSize.height = .greatestFiniteMagnitude + + if textView.textContainer.size != proposedSize || textView.frame.size != proposedSize { + textView.textContainer.size = proposedSize + textView.setFrameSize(proposedSize) + } + } else { + var proposedSize = textView.frame.size + proposedSize.width = ((view as? NSScrollView)?.contentSize ?? .zero).width + if previousTrackingSetting != wrapLines { + textView.textContainer.size = CGSize( + width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude + ) + textView.setFrameSize(proposedSize) + textView.textLayoutManager.textViewportLayoutController.layoutViewport() + } + } + } + // MARK: UI /// A default `NSParagraphStyle` with a set `lineHeight` @@ -299,6 +336,7 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt } highlighter?.invalidate() + updateTextContainerWidthIfNeeded() } /// Gets all attributes for the given capture including the line height, background color, and text color. From ffa7441aa4822919a8c169c59199979a20e2835a Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Fri, 21 Apr 2023 12:30:42 -0500 Subject: [PATCH 2/3] Break up `STTextViewController` --- .../STTextViewController+Highlighter.swift | 40 ++++++++++++ .../STTextViewController+TextContainer.swift | 39 ++++++++++++ .../Controller/STTextViewController.swift | 62 +------------------ 3 files changed, 80 insertions(+), 61 deletions(-) create mode 100644 Sources/CodeEditTextView/Controller/STTextViewController+Highlighter.swift create mode 100644 Sources/CodeEditTextView/Controller/STTextViewController+TextContainer.swift diff --git a/Sources/CodeEditTextView/Controller/STTextViewController+Highlighter.swift b/Sources/CodeEditTextView/Controller/STTextViewController+Highlighter.swift new file mode 100644 index 000000000..a20f47aa7 --- /dev/null +++ b/Sources/CodeEditTextView/Controller/STTextViewController+Highlighter.swift @@ -0,0 +1,40 @@ +// +// STTextViewController+Highlighter.swift +// +// +// Created by Khan Winter on 4/21/23. +// + +import AppKit +import SwiftTreeSitter + +extension STTextViewController { + /// Configures the `Highlighter` object + internal func setUpHighlighter() { + self.highlighter = Highlighter(textView: textView, + highlightProvider: highlightProvider, + theme: theme, + attributeProvider: self, + language: language) + } + + /// Sets the highlight provider and re-highlights all text. This method should be used sparingly. + internal func setHighlightProvider(_ highlightProvider: HighlightProviding? = nil) { + var provider: HighlightProviding? + + if let highlightProvider = highlightProvider { + provider = highlightProvider + } else { + let textProvider: ResolvingQueryCursor.TextProvider = { [weak self] range, _ -> String? in + return self?.textView.textContentStorage.textStorage?.mutableString.substring(with: range) + } + + provider = TreeSitterClient(codeLanguage: language, textProvider: textProvider) + } + + if let provider = provider { + self.highlightProvider = provider + highlighter?.setHighlightProvider(provider) + } + } +} diff --git a/Sources/CodeEditTextView/Controller/STTextViewController+TextContainer.swift b/Sources/CodeEditTextView/Controller/STTextViewController+TextContainer.swift new file mode 100644 index 000000000..54a08e393 --- /dev/null +++ b/Sources/CodeEditTextView/Controller/STTextViewController+TextContainer.swift @@ -0,0 +1,39 @@ +// +// STTextViewController+TextContainer.swift +// +// +// Created by Khan Winter on 4/21/23. +// + +import AppKit +import STTextView + +extension STTextViewController { + /// Update the text view's text container if needed. + /// + /// Effectively updates the container to reflect the `wrapLines` setting, and to reflect any updates to the ruler, + /// scroll view, or window frames. + internal func updateTextContainerWidthIfNeeded() { + let previousTrackingSetting = textView.widthTracksTextView + textView.widthTracksTextView = wrapLines + if wrapLines { + var proposedSize = ((view as? NSScrollView)?.contentSize ?? .zero) + proposedSize.height = .greatestFiniteMagnitude + + if textView.textContainer.size != proposedSize || textView.frame.size != proposedSize { + textView.textContainer.size = proposedSize + textView.setFrameSize(proposedSize) + } + } else { + var proposedSize = textView.frame.size + proposedSize.width = ((view as? NSScrollView)?.contentSize ?? .zero).width + if previousTrackingSetting != wrapLines { + textView.textContainer.size = CGSize( + width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude + ) + textView.setFrameSize(proposedSize) + textView.textLayoutManager.textViewportLayoutController.layoutViewport() + } + } + } +} diff --git a/Sources/CodeEditTextView/Controller/STTextViewController.swift b/Sources/CodeEditTextView/Controller/STTextViewController.swift index 77102b692..22ec37949 100644 --- a/Sources/CodeEditTextView/Controller/STTextViewController.swift +++ b/Sources/CodeEditTextView/Controller/STTextViewController.swift @@ -9,7 +9,6 @@ import AppKit import SwiftUI import Combine import STTextView -import SwiftTreeSitter import CodeEditLanguages import TextFormation @@ -97,7 +96,7 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt internal var highlighter: Highlighter? /// The provided highlight provider. - private var highlightProvider: HighlightProviding? + internal var highlightProvider: HighlightProviding? // MARK: Init @@ -251,34 +250,6 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt self.text.wrappedValue = textView.string } - /// Update the text view's text container if needed. - /// - /// Effectively updates the container to reflect the `wrapLines` setting, and to reflect any updates to the ruler, - /// scroll view, or window frames. - private func updateTextContainerWidthIfNeeded() { - let previousTrackingSetting = textView.widthTracksTextView - textView.widthTracksTextView = wrapLines - if wrapLines { - var proposedSize = ((view as? NSScrollView)?.contentSize ?? .zero) - proposedSize.height = .greatestFiniteMagnitude - - if textView.textContainer.size != proposedSize || textView.frame.size != proposedSize { - textView.textContainer.size = proposedSize - textView.setFrameSize(proposedSize) - } - } else { - var proposedSize = textView.frame.size - proposedSize.width = ((view as? NSScrollView)?.contentSize ?? .zero).width - if previousTrackingSetting != wrapLines { - textView.textContainer.size = CGSize( - width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude - ) - textView.setFrameSize(proposedSize) - textView.textLayoutManager.textViewportLayoutController.layoutViewport() - } - } - } - // MARK: UI /// A default `NSParagraphStyle` with a set `lineHeight` @@ -362,37 +333,6 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt ((self.lineHeight) - font.lineHeight) / 2 } - // MARK: - Highlighting - - /// Configures the `Highlighter` object - private func setUpHighlighter() { - self.highlighter = Highlighter(textView: textView, - highlightProvider: highlightProvider, - theme: theme, - attributeProvider: self, - language: language) - } - - /// Sets the highlight provider and re-highlights all text. This method should be used sparingly. - public func setHighlightProvider(_ highlightProvider: HighlightProviding? = nil) { - var provider: HighlightProviding? - - if let highlightProvider = highlightProvider { - provider = highlightProvider - } else { - let textProvider: ResolvingQueryCursor.TextProvider = { [weak self] range, _ -> String? in - return self?.textView.textContentStorage.textStorage?.mutableString.substring(with: range) - } - - provider = TreeSitterClient(codeLanguage: language, textProvider: textProvider) - } - - if let provider = provider { - self.highlightProvider = provider - highlighter?.setHighlightProvider(provider) - } - } - // MARK: Selectors override public func keyDown(with event: NSEvent) { From 38cdd3cd143b39f53aea7bee1a33672ab7157e2e Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Sat, 22 Apr 2023 10:40:26 -0500 Subject: [PATCH 3/3] Fix Indent --- .../STTextViewController+Highlighter.swift | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Sources/CodeEditTextView/Controller/STTextViewController+Highlighter.swift b/Sources/CodeEditTextView/Controller/STTextViewController+Highlighter.swift index a20f47aa7..307dfffe0 100644 --- a/Sources/CodeEditTextView/Controller/STTextViewController+Highlighter.swift +++ b/Sources/CodeEditTextView/Controller/STTextViewController+Highlighter.swift @@ -11,11 +11,13 @@ import SwiftTreeSitter extension STTextViewController { /// Configures the `Highlighter` object internal func setUpHighlighter() { - self.highlighter = Highlighter(textView: textView, - highlightProvider: highlightProvider, - theme: theme, - attributeProvider: self, - language: language) + self.highlighter = Highlighter( + textView: textView, + highlightProvider: highlightProvider, + theme: theme, + attributeProvider: self, + language: language + ) } /// Sets the highlight provider and re-highlights all text. This method should be used sparingly.