From eccf815630674549ee8d6ee80194e5bfd6e8f031 Mon Sep 17 00:00:00 2001 From: Joe Hinkle Date: Wed, 15 Jun 2022 11:47:16 -0500 Subject: [PATCH 1/7] Bug fix for changing language of a Firefly view --- Sources/Highlighter/Syntax/Syntax.swift | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Sources/Highlighter/Syntax/Syntax.swift b/Sources/Highlighter/Syntax/Syntax.swift index 7ecaf1c..cccddfc 100644 --- a/Sources/Highlighter/Syntax/Syntax.swift +++ b/Sources/Highlighter/Syntax/Syntax.swift @@ -46,17 +46,22 @@ public class Syntax { /// Changes the current language & updates the definitions /// - Parameter name: Name of the language func setLanguage(to name: String) { + // clear previous definitions + definitions = [] + + // install language definitions if let language = languages[name.lowercased()] { for item in language { let type = item.key - let dict: [String: Any] = item.value as! [String : Any] - let regex: String = dict["regex"] as? String ?? "" - let group: Int = dict["group"] as? Int ?? 0 - let relevance: Int = dict["relevance"] as? Int ?? 0 - let options: [NSRegularExpression.Options] = dict["options"] as? [NSRegularExpression.Options] ?? [] - let multi: Bool = dict["multiline"] as? Bool ?? false + if let dict: [String: Any] = item.value as? [String : Any] { + let regex: String = dict["regex"] as? String ?? "" + let group: Int = dict["group"] as? Int ?? 0 + let relevance: Int = dict["relevance"] as? Int ?? 0 + let options: [NSRegularExpression.Options] = dict["options"] as? [NSRegularExpression.Options] ?? [] + let multi: Bool = dict["multiline"] as? Bool ?? false - definitions.append(Definition(type: type, regex: regex, group: group, relevance: relevance, matches: options, multiLine: multi)) + definitions.append(Definition(type: type, regex: regex, group: group, relevance: relevance, matches: options, multiLine: multi)) + } } } var editorPlaceholderPattern = "(<#)([^\"\\n]*?)" From e2bac00489284e9f4dc393c3a2ebbac66fe63bd5 Mon Sep 17 00:00:00 2001 From: Joe Hinkle Date: Wed, 15 Jun 2022 11:49:39 -0500 Subject: [PATCH 2/7] Opened up languages and themes to parent modules. Languages also have a new "display_name" property suitable to show to users --- Sources/Highlighter/Data/Languages.swift | 15 +++++++++++++-- Sources/Highlighter/Data/Themes.swift | 11 ++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Sources/Highlighter/Data/Languages.swift b/Sources/Highlighter/Data/Languages.swift index bdbb1a5..3599473 100644 --- a/Sources/Highlighter/Data/Languages.swift +++ b/Sources/Highlighter/Data/Languages.swift @@ -7,13 +7,22 @@ import Foundation //TODO: if( is matched as a function not a keyword. Figure out a system to match if as a keyword not a function -let languages: [String: [String: Any]] = [ - "default": [:], +var languages: [String: [String: Any]] = [ + "default": defaultLanguage, "jelly": jellyLanguage, "swift": swiftLanguage ] +public var fireflyLanguages: [String: [String: Any]] { + get { + languages + } + set { + languages = newValue + } +} let defaultLanguage: [String: Any] = [ + "display_name": "Generic", "string": [ "regex": #"(? Date: Wed, 15 Jun 2022 12:18:19 -0500 Subject: [PATCH 3/7] - A `notifyWillChange` is added allowing for more close monitoring of text changes. - A `replace` function allowing for a parent module to change the text view's contents programmatically - Some properties are made publicly readable (not writable) --- Sources/Extensions/String+NSRange.swift | 5 ++ .../FireflySyntaxView + TextDelegate.swift | 45 +++++++++++++++++ Sources/Views/FireflySyntaxView.swift | 50 +++++++++++++++++-- 3 files changed, 95 insertions(+), 5 deletions(-) diff --git a/Sources/Extensions/String+NSRange.swift b/Sources/Extensions/String+NSRange.swift index 312da2d..e875571 100644 --- a/Sources/Extensions/String+NSRange.swift +++ b/Sources/Extensions/String+NSRange.swift @@ -11,4 +11,9 @@ extension String { func nsRange(fromRange range: Range) -> NSRange { return NSRange(range, in: self) } + init(fromRange range: NSRange, in fullString: T) { + let start: String.Index = fullString.index(fullString.startIndex, offsetBy: range.location) + let end: String.Index = fullString.index(start, offsetBy: range.length) + self = String(fullString[start.. Bool in return token == token } self.textStorage.endEditing() + self.notifyWillChange( + oldText: String(fromRange: token.range, in: textView.textStorage.string), + location: token.range.location, + newText: text + ) updateSelectedRange(NSRange(location: token.range.location + text.utf16.count, length: 0)) textStorage.highlight(getVisibleRange(), cursorRange: selectedRange) @@ -71,6 +76,11 @@ extension FireflySyntaxView: TextViewDelegate { // Update on backspace updateGutterNow = true self.textStorage.endEditing() + self.notifyWillChange( + oldText: String(fromRange: range, in: textView.textStorage.string), + location: range.location, + newText: text + ) return true } } @@ -111,6 +121,11 @@ extension FireflySyntaxView: TextViewDelegate { textView.setNeedsDisplay(textView.bounds) textView.didChangeText() #endif + self.notifyWillChange( + oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + location: selectedRange.location, + newText: insertingText + ) shouldHighlightOnChange = false textStorage.editingRange = selectedRange @@ -136,6 +151,11 @@ extension FireflySyntaxView: TextViewDelegate { textView.setNeedsDisplay(textView.bounds) textView.didChangeText() #endif + self.notifyWillChange( + oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + location: selectedRange.location, + newText: insertingText + ) shouldHighlightOnChange = false textStorage.editingRange = selectedRange @@ -183,6 +203,11 @@ extension FireflySyntaxView: TextViewDelegate { shouldHighlightOnChange = false textStorage.editingRange = selectedRange self.textStorage.endEditing() + self.notifyWillChange( + oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + location: selectedRange.location, + newText: insertingText + ) textStorage.highlight(getVisibleRange(), cursorRange: selectedRange) delegate?.didChangeText(tView) @@ -214,6 +239,11 @@ extension FireflySyntaxView: TextViewDelegate { textView.setNeedsDisplay(textView.bounds) textView.didChangeText() #endif + self.notifyWillChange( + oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + location: selectedRange.location, + newText: insertingText + ) shouldHighlightOnChange = false textStorage.editingRange = selectedRange @@ -243,6 +273,11 @@ extension FireflySyntaxView: TextViewDelegate { textView.didChangeText() #endif + self.notifyWillChange( + oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + location: selectedRange.location, + newText: insertingText + ) updateGutterWidth() shouldHighlightOnChange = false @@ -274,6 +309,11 @@ extension FireflySyntaxView: TextViewDelegate { textView.setNeedsDisplay(textView.bounds) textView.didChangeText() #endif + self.notifyWillChange( + oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + location: selectedRange.location, + newText: insertingText + ) shouldHighlightOnChange = false textStorage.editingRange = selectedRange @@ -288,6 +328,11 @@ extension FireflySyntaxView: TextViewDelegate { } self.textStorage.endEditing() + self.notifyWillChange( + oldText: String(fromRange: range, in: textView.textStorage.string), + location: range.location, + newText: text + ) return true } diff --git a/Sources/Views/FireflySyntaxView.swift b/Sources/Views/FireflySyntaxView.swift index e31efb0..8d52983 100644 --- a/Sources/Views/FireflySyntaxView.swift +++ b/Sources/Views/FireflySyntaxView.swift @@ -13,12 +13,15 @@ import UIKit public class FireflySyntaxView: FireflyView { - ///The highlighting language + /// A closure called whenever the text contents is modified. + private var notifyWillChangeClosure: ((_ oldText: String, _ location: Int, _ newText: String) -> Void)? = nil + + /// The highlighting language @IBInspectable - internal var language: String = "default" + public internal(set) var language: String = "default" @IBInspectable - internal var theme: String = "Basic" + public internal(set) var theme: String = "Basic" /// The name of the highlighters font @IBInspectable @@ -41,13 +44,32 @@ public class FireflySyntaxView: FireflyView { } } + /// Safely replace a section of text in the editor. + public func replace(range: NSRange, to newText: String, updateHighlighting: Bool = true) { + #if canImport(UIKit) + self.textStorage.beginEditing() + textView.textStorage.replaceCharacters(in: range, with: newText) + self.textStorage.endEditing() + if updateHighlighting { + textView.setNeedsDisplay() + } + #elseif canImport(AppKit) + self.textStorage.beginEditing() + textView.textStorage!.replaceCharacters(in: range, with: newText) + self.textStorage.endEditing() + if updateHighlighting { + textView.setNeedsDisplay(textView.bounds) + } + #endif + } + /// The minimum / standard gutter width. Becomes the minimum if dynamicGutterWidth is true otherwise it is the standard gutterWidth @IBInspectable - internal var gutterWidth: CGFloat = 20 + public internal(set) var gutterWidth: CGFloat = 20 /// If set the editor will use a dynamic gutter width @IBInspectable - internal var dynamicGutterWidth: Bool = true + public internal(set) var dynamicGutterWidth: Bool = true /// The editor's offset from the top of the keyboard @IBInspectable @@ -428,6 +450,13 @@ public class FireflySyntaxView: FireflyView { #endif + + /// Sets the closure to be called whenever the text contents is modified/ + /// - Parameter notifyWillChange: The closure. + public func setNotifyWillChange(_ notifyWillChange: ((_ oldText: String, _ location: Int, _ newText: String) -> Void)?) { + self.notifyWillChangeClosure = notifyWillChange + } + /// Sets the theme of the view. Supply with a theme name /// - Parameters: /// - name: The name of theme @@ -681,4 +710,15 @@ extension FireflySyntaxView { }) #endif } + + /// Sends out notification of a section of text changing. Note that because calculating `oldText` is usually an extra step required, the param is + /// marked as an auto-closure so that the body is only calcuated in the case that there is a `notifyWillChangeClosure` to actually notify. + /// - Parameter oldText: Contents of the section before the change. + /// - Parameter location: The index of the change. + /// - Parameter newText: Contents of the section after the change.. + internal func notifyWillChange(oldText: @autoclosure () -> String, location: Int, newText: String) { + if let notifyWillChangeClosure = self.notifyWillChangeClosure { + notifyWillChangeClosure(oldText(), location, newText) + } + } } From f960dd4777d0475260343308e0832796af00a082 Mon Sep 17 00:00:00 2001 From: Joe Hinkle Date: Wed, 15 Jun 2022 13:21:25 -0500 Subject: [PATCH 4/7] notification call needs to be before the underlying text storage is changed --- .../FireflySyntaxView + TextDelegate.swift | 85 +++++++++++-------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/Sources/Views/FireflySyntaxView + TextDelegate.swift b/Sources/Views/FireflySyntaxView + TextDelegate.swift index d187dc5..ec0255d 100644 --- a/Sources/Views/FireflySyntaxView + TextDelegate.swift +++ b/Sources/Views/FireflySyntaxView + TextDelegate.swift @@ -39,6 +39,12 @@ extension FireflySyntaxView: TextViewDelegate { if let token = inside.1 { let fullRange = NSRange(location: 0, length: self.text.utf16.count) if token.range.upperBound < fullRange.upperBound { + self.notifyWillChange( + oldText: String(fromRange: token.range, in: textView.textStorage.string), + location: token.range.location, + newText: text + ) + textStorage.removeAttribute(.font, range: token.range) textStorage.removeAttribute(.foregroundColor, range: token.range) textStorage.removeAttribute(.editorPlaceholder, range: token.range) @@ -48,11 +54,6 @@ extension FireflySyntaxView: TextViewDelegate { textStorage.cachedTokens.removeAll { (token) -> Bool in return token == token } self.textStorage.endEditing() - self.notifyWillChange( - oldText: String(fromRange: token.range, in: textView.textStorage.string), - location: token.range.location, - newText: text - ) updateSelectedRange(NSRange(location: token.range.location + text.utf16.count, length: 0)) textStorage.highlight(getVisibleRange(), cursorRange: selectedRange) @@ -107,6 +108,12 @@ extension FireflySyntaxView: TextViewDelegate { } else if characterBuffer[1, default: ""] == "\"" && characterBuffer[0, default: ""] != "\"" && characterBuffer[2, default: ""] != "\"" { insertingText += "\"" + self.notifyWillChange( + oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + location: selectedRange.location, + newText: insertingText + ) + #if canImport(UIKit) textView.textStorage.replaceCharacters(in: selectedRange, with: insertingText) @@ -121,11 +128,6 @@ extension FireflySyntaxView: TextViewDelegate { textView.setNeedsDisplay(textView.bounds) textView.didChangeText() #endif - self.notifyWillChange( - oldText: String(fromRange: selectedRange, in: textView.textStorage.string), - location: selectedRange.location, - newText: insertingText - ) shouldHighlightOnChange = false textStorage.editingRange = selectedRange @@ -137,6 +139,12 @@ extension FireflySyntaxView: TextViewDelegate { } else if characterBuffer[1, default: ""] == "(" && characterBuffer[0, default: ""] != ")" { insertingText += ")" + self.notifyWillChange( + oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + location: selectedRange.location, + newText: insertingText + ) + #if canImport(UIKit) textView.textStorage.replaceCharacters(in: selectedRange, with: insertingText) @@ -151,11 +159,6 @@ extension FireflySyntaxView: TextViewDelegate { textView.setNeedsDisplay(textView.bounds) textView.didChangeText() #endif - self.notifyWillChange( - oldText: String(fromRange: selectedRange, in: textView.textStorage.string), - location: selectedRange.location, - newText: insertingText - ) shouldHighlightOnChange = false textStorage.editingRange = selectedRange @@ -168,6 +171,13 @@ extension FireflySyntaxView: TextViewDelegate { // Update on new line if text == "\n" { insertingText += "\t\(newlineInsert)\n\(newlineInsert)}" + + self.notifyWillChange( + oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + location: selectedRange.location, + newText: insertingText + ) + #if canImport(UIKit) textView.textStorage.replaceCharacters(in: selectedRange, with: insertingText) @@ -184,6 +194,13 @@ extension FireflySyntaxView: TextViewDelegate { #endif } else { insertingText += "}" + + self.notifyWillChange( + oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + location: selectedRange.location, + newText: insertingText + ) + #if canImport(UIKit) textView.textStorage.replaceCharacters(in: selectedRange, with: insertingText) @@ -203,11 +220,6 @@ extension FireflySyntaxView: TextViewDelegate { shouldHighlightOnChange = false textStorage.editingRange = selectedRange self.textStorage.endEditing() - self.notifyWillChange( - oldText: String(fromRange: selectedRange, in: textView.textStorage.string), - location: selectedRange.location, - newText: insertingText - ) textStorage.highlight(getVisibleRange(), cursorRange: selectedRange) delegate?.didChangeText(tView) @@ -225,6 +237,12 @@ extension FireflySyntaxView: TextViewDelegate { lastCompleted = "" insertingText += "\t\(newlineInsert)\n\(newlineInsert)*/" + + self.notifyWillChange( + oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + location: selectedRange.location, + newText: insertingText + ) #if canImport(UIKit) textView.textStorage.replaceCharacters(in: selectedRange, with: insertingText) @@ -239,11 +257,6 @@ extension FireflySyntaxView: TextViewDelegate { textView.setNeedsDisplay(textView.bounds) textView.didChangeText() #endif - self.notifyWillChange( - oldText: String(fromRange: selectedRange, in: textView.textStorage.string), - location: selectedRange.location, - newText: insertingText - ) shouldHighlightOnChange = false textStorage.editingRange = selectedRange @@ -257,7 +270,12 @@ extension FireflySyntaxView: TextViewDelegate { } else { // insertingText.removeFirst() insertingText += newlineInsert - + + self.notifyWillChange( + oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + location: selectedRange.location, + newText: insertingText + ) #if canImport(UIKit) textView.textStorage.replaceCharacters(in: selectedRange, with: insertingText) @@ -273,11 +291,6 @@ extension FireflySyntaxView: TextViewDelegate { textView.didChangeText() #endif - self.notifyWillChange( - oldText: String(fromRange: selectedRange, in: textView.textStorage.string), - location: selectedRange.location, - newText: insertingText - ) updateGutterWidth() shouldHighlightOnChange = false @@ -295,6 +308,11 @@ extension FireflySyntaxView: TextViewDelegate { lastCompleted = "" insertingText += "*/" + self.notifyWillChange( + oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + location: selectedRange.location, + newText: insertingText + ) #if canImport(UIKit) textView.textStorage.replaceCharacters(in: selectedRange, with: insertingText) @@ -309,11 +327,6 @@ extension FireflySyntaxView: TextViewDelegate { textView.setNeedsDisplay(textView.bounds) textView.didChangeText() #endif - self.notifyWillChange( - oldText: String(fromRange: selectedRange, in: textView.textStorage.string), - location: selectedRange.location, - newText: insertingText - ) shouldHighlightOnChange = false textStorage.editingRange = selectedRange From 0c5ed0e60dea6293e63c00c4cfefcca51082881c Mon Sep 17 00:00:00 2001 From: Joe Hinkle Date: Wed, 15 Jun 2022 13:27:48 -0500 Subject: [PATCH 5/7] removed smart dashes (when -- is converted into a long dash) --- Sources/Views/FireflySyntaxView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/Views/FireflySyntaxView.swift b/Sources/Views/FireflySyntaxView.swift index 8d52983..3ddcb96 100644 --- a/Sources/Views/FireflySyntaxView.swift +++ b/Sources/Views/FireflySyntaxView.swift @@ -216,6 +216,7 @@ public class FireflySyntaxView: FireflyView { textView.autocorrectionType = .no textView.spellCheckingType = .no textView.smartQuotesType = .no + textView.smartDashesType = .no textView.smartInsertDeleteType = .no if self.textStorage.syntax.theme.style == .dark { From 92cfc3682b0a4a0d444e64fe843bb10e548d2db4 Mon Sep 17 00:00:00 2001 From: Joe Hinkle Date: Wed, 15 Jun 2022 19:05:03 -0500 Subject: [PATCH 6/7] on text change notification --- .../FireflySyntaxView + TextDelegate.swift | 23 ++++++----- Sources/Views/FireflySyntaxView.swift | 41 ++++++++++++++----- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/Sources/Views/FireflySyntaxView + TextDelegate.swift b/Sources/Views/FireflySyntaxView + TextDelegate.swift index ec0255d..cd42b57 100644 --- a/Sources/Views/FireflySyntaxView + TextDelegate.swift +++ b/Sources/Views/FireflySyntaxView + TextDelegate.swift @@ -39,7 +39,7 @@ extension FireflySyntaxView: TextViewDelegate { if let token = inside.1 { let fullRange = NSRange(location: 0, length: self.text.utf16.count) if token.range.upperBound < fullRange.upperBound { - self.notifyWillChange( + self.onTextChange( oldText: String(fromRange: token.range, in: textView.textStorage.string), location: token.range.location, newText: text @@ -77,7 +77,7 @@ extension FireflySyntaxView: TextViewDelegate { // Update on backspace updateGutterNow = true self.textStorage.endEditing() - self.notifyWillChange( + self.onTextChange( oldText: String(fromRange: range, in: textView.textStorage.string), location: range.location, newText: text @@ -108,7 +108,7 @@ extension FireflySyntaxView: TextViewDelegate { } else if characterBuffer[1, default: ""] == "\"" && characterBuffer[0, default: ""] != "\"" && characterBuffer[2, default: ""] != "\"" { insertingText += "\"" - self.notifyWillChange( + self.onTextChange( oldText: String(fromRange: selectedRange, in: textView.textStorage.string), location: selectedRange.location, newText: insertingText @@ -139,7 +139,7 @@ extension FireflySyntaxView: TextViewDelegate { } else if characterBuffer[1, default: ""] == "(" && characterBuffer[0, default: ""] != ")" { insertingText += ")" - self.notifyWillChange( + self.onTextChange( oldText: String(fromRange: selectedRange, in: textView.textStorage.string), location: selectedRange.location, newText: insertingText @@ -172,7 +172,7 @@ extension FireflySyntaxView: TextViewDelegate { if text == "\n" { insertingText += "\t\(newlineInsert)\n\(newlineInsert)}" - self.notifyWillChange( + self.onTextChange( oldText: String(fromRange: selectedRange, in: textView.textStorage.string), location: selectedRange.location, newText: insertingText @@ -195,7 +195,7 @@ extension FireflySyntaxView: TextViewDelegate { } else { insertingText += "}" - self.notifyWillChange( + self.onTextChange( oldText: String(fromRange: selectedRange, in: textView.textStorage.string), location: selectedRange.location, newText: insertingText @@ -238,7 +238,7 @@ extension FireflySyntaxView: TextViewDelegate { insertingText += "\t\(newlineInsert)\n\(newlineInsert)*/" - self.notifyWillChange( + self.onTextChange( oldText: String(fromRange: selectedRange, in: textView.textStorage.string), location: selectedRange.location, newText: insertingText @@ -271,7 +271,7 @@ extension FireflySyntaxView: TextViewDelegate { // insertingText.removeFirst() insertingText += newlineInsert - self.notifyWillChange( + self.onTextChange( oldText: String(fromRange: selectedRange, in: textView.textStorage.string), location: selectedRange.location, newText: insertingText @@ -308,7 +308,7 @@ extension FireflySyntaxView: TextViewDelegate { lastCompleted = "" insertingText += "*/" - self.notifyWillChange( + self.onTextChange( oldText: String(fromRange: selectedRange, in: textView.textStorage.string), location: selectedRange.location, newText: insertingText @@ -341,7 +341,7 @@ extension FireflySyntaxView: TextViewDelegate { } self.textStorage.endEditing() - self.notifyWillChange( + self.onTextChange( oldText: String(fromRange: range, in: textView.textStorage.string), location: range.location, newText: text @@ -399,6 +399,7 @@ extension FireflySyntaxView: TextViewDelegate { public func textViewDidChangeSelection(_ textView: UITextView) { updateCursorPosition() delegate?.didChangeSelectedRange(self.textView, selectedRange: self.textView.selectedRange) + self.onSelectionChange(selectionRange: self.textView.selectedRange) } /// Override the key commands recognized by the view @@ -423,6 +424,7 @@ extension FireflySyntaxView: TextViewDelegate { public func textViewDidChangeSelection(_ notification: Notification) { updateCursorPosition() delegate?.didChangeSelectedRange(textView, selectedRange: textView.selectedRange()) + self.onSelectionChange(selectionRange: textView.selectedRange()) } /// Handles highlighting the correct amount of the view when text changes @@ -486,6 +488,7 @@ extension FireflySyntaxView { #elseif canImport(AppKit) textView.setSelectedRange(range) #endif + self.onSelectionChange(selectionRange: range) } } diff --git a/Sources/Views/FireflySyntaxView.swift b/Sources/Views/FireflySyntaxView.swift index 3ddcb96..4eed7f5 100644 --- a/Sources/Views/FireflySyntaxView.swift +++ b/Sources/Views/FireflySyntaxView.swift @@ -14,7 +14,10 @@ import UIKit public class FireflySyntaxView: FireflyView { /// A closure called whenever the text contents is modified. - private var notifyWillChangeClosure: ((_ oldText: String, _ location: Int, _ newText: String) -> Void)? = nil + private var onTextChangeClosure: ((_ oldText: String, _ location: Int, _ newText: String) -> Void)? = nil + + /// A closure called whenever the text selection changes. + private var onSelectionChangeClosure: ((_ selectionRange: NSRange) -> Void)? /// The highlighting language @IBInspectable @@ -40,7 +43,7 @@ public class FireflySyntaxView: FireflyView { if dynamicGutterWidth { updateGutterWidth() } - textView.selectedRange = NSRange(location: 0, length: 0) + self.updateSelectedRange(NSRange(location: 0, length: 0)) } } @@ -451,11 +454,16 @@ public class FireflySyntaxView: FireflyView { #endif - - /// Sets the closure to be called whenever the text contents is modified/ - /// - Parameter notifyWillChange: The closure. - public func setNotifyWillChange(_ notifyWillChange: ((_ oldText: String, _ location: Int, _ newText: String) -> Void)?) { - self.notifyWillChangeClosure = notifyWillChange + /// Sets the closure to be called whenever the text contents is modified + /// - Parameter onTextChange: The closure. + public func setOnTextChange(_ onTextChange: ((_ oldText: String, _ location: Int, _ newText: String) -> Void)?) { + self.onTextChangeClosure = onTextChange + } + + /// Sets the closure to be called whenever the text selection changes + /// - Parameter onSelectionChange: The closure. + public func setOnSelectionChange(_ onSelectionChange: ((_ selectionRange: NSRange) -> Void)?) { + self.onSelectionChangeClosure = onSelectionChange } /// Sets the theme of the view. Supply with a theme name @@ -712,14 +720,25 @@ extension FireflySyntaxView { #endif } - /// Sends out notification of a section of text changing. Note that because calculating `oldText` is usually an extra step required, the param is + /// Sends out notification of a section of text changing. Note that because calculating `oldText` is usually an extra step, the param is /// marked as an auto-closure so that the body is only calcuated in the case that there is a `notifyWillChangeClosure` to actually notify. /// - Parameter oldText: Contents of the section before the change. /// - Parameter location: The index of the change. /// - Parameter newText: Contents of the section after the change.. - internal func notifyWillChange(oldText: @autoclosure () -> String, location: Int, newText: String) { - if let notifyWillChangeClosure = self.notifyWillChangeClosure { - notifyWillChangeClosure(oldText(), location, newText) + internal func onTextChange(oldText: @autoclosure () -> String, location: Int, newText: String) { + if let onTextChangeClosure = self.onTextChangeClosure { + onTextChangeClosure(oldText(), location, newText) + } + } + + /// Sends out notification that the text selection range has changed. Note that because calculating `selectionRange` is usually an extra step, + /// the param is marked as an auto-closure so that the body is only calcuated when needed. + /// - Parameter oldText: Contents of the section before the change. + /// - Parameter location: The index of the change. + /// - Parameter newText: Contents of the section after the change.. + internal func onSelectionChange(selectionRange: @autoclosure () -> NSRange) { + if let onSelectionChangeClosure = self.onSelectionChangeClosure { + onSelectionChangeClosure(selectionRange()) } } } From 72331071aaaa50cfc827643cd71ab89323652be2 Mon Sep 17 00:00:00 2001 From: Joe Hinkle Date: Wed, 29 Jun 2022 04:19:28 -0400 Subject: [PATCH 7/7] macOS builds again --- .../FireflySyntaxView + TextDelegate.swift | 71 ++++++++++++++++--- Tests/FireflyTests/FireflyTextViewTests.swift | 2 + 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/Sources/Views/FireflySyntaxView + TextDelegate.swift b/Sources/Views/FireflySyntaxView + TextDelegate.swift index cd42b57..48a9075 100644 --- a/Sources/Views/FireflySyntaxView + TextDelegate.swift +++ b/Sources/Views/FireflySyntaxView + TextDelegate.swift @@ -39,8 +39,13 @@ extension FireflySyntaxView: TextViewDelegate { if let token = inside.1 { let fullRange = NSRange(location: 0, length: self.text.utf16.count) if token.range.upperBound < fullRange.upperBound { + #if os(iOS) + let textViewTextStorageString: String = textView.textStorage.string + #else + let textViewTextStorageString: String = textView.textStorage!.string + #endif self.onTextChange( - oldText: String(fromRange: token.range, in: textView.textStorage.string), + oldText: String(fromRange: token.range, in: textViewTextStorageString), location: token.range.location, newText: text ) @@ -77,8 +82,13 @@ extension FireflySyntaxView: TextViewDelegate { // Update on backspace updateGutterNow = true self.textStorage.endEditing() + #if os(iOS) + let textViewTextStorageString: String = textView.textStorage.string + #else + let textViewTextStorageString: String = textView.textStorage!.string + #endif self.onTextChange( - oldText: String(fromRange: range, in: textView.textStorage.string), + oldText: String(fromRange: range, in: textViewTextStorageString), location: range.location, newText: text ) @@ -108,8 +118,13 @@ extension FireflySyntaxView: TextViewDelegate { } else if characterBuffer[1, default: ""] == "\"" && characterBuffer[0, default: ""] != "\"" && characterBuffer[2, default: ""] != "\"" { insertingText += "\"" + #if os(iOS) + let textViewTextStorageString: String = textView.textStorage.string + #else + let textViewTextStorageString: String = textView.textStorage!.string + #endif self.onTextChange( - oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + oldText: String(fromRange: selectedRange, in: textViewTextStorageString), location: selectedRange.location, newText: insertingText ) @@ -139,8 +154,13 @@ extension FireflySyntaxView: TextViewDelegate { } else if characterBuffer[1, default: ""] == "(" && characterBuffer[0, default: ""] != ")" { insertingText += ")" + #if os(iOS) + let textViewTextStorageString: String = textView.textStorage.string + #else + let textViewTextStorageString: String = textView.textStorage!.string + #endif self.onTextChange( - oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + oldText: String(fromRange: selectedRange, in: textViewTextStorageString), location: selectedRange.location, newText: insertingText ) @@ -172,8 +192,13 @@ extension FireflySyntaxView: TextViewDelegate { if text == "\n" { insertingText += "\t\(newlineInsert)\n\(newlineInsert)}" + #if os(iOS) + let textViewTextStorageString: String = textView.textStorage.string + #else + let textViewTextStorageString: String = textView.textStorage!.string + #endif self.onTextChange( - oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + oldText: String(fromRange: selectedRange, in: textViewTextStorageString), location: selectedRange.location, newText: insertingText ) @@ -195,8 +220,13 @@ extension FireflySyntaxView: TextViewDelegate { } else { insertingText += "}" + #if os(iOS) + let textViewTextStorageString: String = textView.textStorage.string + #else + let textViewTextStorageString: String = textView.textStorage!.string + #endif self.onTextChange( - oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + oldText: String(fromRange: selectedRange, in: textViewTextStorageString), location: selectedRange.location, newText: insertingText ) @@ -237,9 +267,13 @@ extension FireflySyntaxView: TextViewDelegate { lastCompleted = "" insertingText += "\t\(newlineInsert)\n\(newlineInsert)*/" - + #if os(iOS) + let textViewTextStorageString: String = textView.textStorage.string + #else + let textViewTextStorageString: String = textView.textStorage!.string + #endif self.onTextChange( - oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + oldText: String(fromRange: selectedRange, in: textViewTextStorageString), location: selectedRange.location, newText: insertingText ) @@ -271,8 +305,13 @@ extension FireflySyntaxView: TextViewDelegate { // insertingText.removeFirst() insertingText += newlineInsert + #if os(iOS) + let textViewTextStorageString: String = textView.textStorage.string + #else + let textViewTextStorageString: String = textView.textStorage!.string + #endif self.onTextChange( - oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + oldText: String(fromRange: selectedRange, in: textViewTextStorageString), location: selectedRange.location, newText: insertingText ) @@ -308,8 +347,13 @@ extension FireflySyntaxView: TextViewDelegate { lastCompleted = "" insertingText += "*/" + #if os(iOS) + let textViewTextStorageString: String = textView.textStorage.string + #else + let textViewTextStorageString: String = textView.textStorage!.string + #endif self.onTextChange( - oldText: String(fromRange: selectedRange, in: textView.textStorage.string), + oldText: String(fromRange: selectedRange, in: textViewTextStorageString), location: selectedRange.location, newText: insertingText ) @@ -341,8 +385,13 @@ extension FireflySyntaxView: TextViewDelegate { } self.textStorage.endEditing() + #if os(iOS) + let textViewTextStorageString: String = textView.textStorage.string + #else + let textViewTextStorageString: String = textView.textStorage!.string + #endif self.onTextChange( - oldText: String(fromRange: range, in: textView.textStorage.string), + oldText: String(fromRange: range, in: textViewTextStorageString), location: range.location, newText: text ) diff --git a/Tests/FireflyTests/FireflyTextViewTests.swift b/Tests/FireflyTests/FireflyTextViewTests.swift index 128ddc3..2dc7e68 100644 --- a/Tests/FireflyTests/FireflyTextViewTests.swift +++ b/Tests/FireflyTests/FireflyTextViewTests.swift @@ -9,9 +9,11 @@ import XCTest class FireflyTextViewTests: XCTestCase { + #if canImport(AppKit) private func keyDownEvent(keyCode: UInt16, modifiers: NSEvent.ModifierFlags) -> NSEvent? { return NSEvent.keyEvent(with: .keyDown, location: .zero, modifierFlags: modifiers, timestamp: Date().timeIntervalSinceReferenceDate, windowNumber: 0, context: nil, characters: "", charactersIgnoringModifiers: "", isARepeat: false, keyCode: keyCode) } + #endif func testKeyCommandModifiersMatching() throws { #if canImport(AppKit)