diff --git a/Sources/CodeEditSourceEditor/Controller/TextViewController+Lifecycle.swift b/Sources/CodeEditSourceEditor/Controller/TextViewController+Lifecycle.swift index bb654a45b..caca6be0c 100644 --- a/Sources/CodeEditSourceEditor/Controller/TextViewController+Lifecycle.swift +++ b/Sources/CodeEditSourceEditor/Controller/TextViewController+Lifecycle.swift @@ -218,23 +218,23 @@ extension TextViewController { case .flagsChanged: if modifierFlags.contains(.command), let coords = view.window?.convertPoint(fromScreen: NSEvent.mouseLocation) { - self.jumpToDefinitionModel?.mouseHovered(windowCoordinates: coords) + self.jumpToDefinitionModel.mouseHovered(windowCoordinates: coords) } if !modifierFlags.contains(.command) { - self.jumpToDefinitionModel?.cancelHover() + self.jumpToDefinitionModel.cancelHover() } return event case .mouseMoved: guard modifierFlags.contains(.command) else { - self.jumpToDefinitionModel?.cancelHover() + self.jumpToDefinitionModel.cancelHover() return event } - self.jumpToDefinitionModel?.mouseHovered(windowCoordinates: event.locationInWindow) + self.jumpToDefinitionModel.mouseHovered(windowCoordinates: event.locationInWindow) return event case .leftMouseUp: - if let range = jumpToDefinitionModel?.hoveredRange { - self.jumpToDefinitionModel?.performJump(at: range) + if let range = jumpToDefinitionModel.hoveredRange { + self.jumpToDefinitionModel.performJump(at: range) return nil } return event @@ -274,7 +274,7 @@ extension TextViewController { guard let cursor = cursorPositions.first else { return event } - jumpToDefinitionModel?.performJump(at: cursor.range) + jumpToDefinitionModel.performJump(at: cursor.range) return nil case (_, _): return event diff --git a/Sources/CodeEditSourceEditor/Controller/TextViewController.swift b/Sources/CodeEditSourceEditor/Controller/TextViewController.swift index 2bf2d962e..fb95c81c1 100644 --- a/Sources/CodeEditSourceEditor/Controller/TextViewController.swift +++ b/Sources/CodeEditSourceEditor/Controller/TextViewController.swift @@ -24,8 +24,6 @@ public class TextViewController: NSViewController { // MARK: - Views and Child VCs - // MARK: - Views and Child VCs - weak var findViewController: FindViewController? internal(set) public var scrollView: NSScrollView! @@ -87,8 +85,24 @@ public class TextViewController: NSViewController { /// The provided highlight provider. public var highlightProviders: [HighlightProviding] + /// A delegate object that can respond to requests for completion items, filtering completion items, and triggering + /// the suggestion window. See ``CodeSuggestionDelegate``. + /// - Note: The ``TextViewController`` keeps only a `weak` reference to this object. To function properly, ensure a + /// strong reference to the delegate is kept *outside* of this variable. public weak var completionDelegate: CodeSuggestionDelegate? + /// A delegate object that responds to requests for jump to definition actions. see ``JumpToDefinitionDelegate``. + /// - Note: The ``TextViewController`` keeps only a `weak` reference to this object. To function properly, ensure a + /// strong reference to the delegate is kept *outside* of this variable. + public var jumpToDefinitionDelegate: JumpToDefinitionDelegate? { + get { + jumpToDefinitionModel.delegate + } + set { + jumpToDefinitionModel.delegate = newValue + } + } + // MARK: - Config Helpers /// The font to use in the `textView` @@ -177,7 +191,7 @@ public class TextViewController: NSViewController { /// This will be `nil` if another highlighter provider is passed to the source editor. internal(set) public var treeSitterClient: TreeSitterClient? { didSet { - jumpToDefinitionModel?.treeSitterClient = treeSitterClient + jumpToDefinitionModel.treeSitterClient = treeSitterClient } } @@ -186,7 +200,7 @@ public class TextViewController: NSViewController { /// Filters used when applying edits.. var textFilters: [TextFormation.Filter] = [] - var jumpToDefinitionModel: JumpToDefinitionModel? + var jumpToDefinitionModel: JumpToDefinitionModel var cancellables = Set() @@ -214,7 +228,9 @@ public class TextViewController: NSViewController { highlightProviders: [HighlightProviding] = [TreeSitterClient()], foldProvider: LineFoldProvider? = nil, undoManager: CEUndoManager? = nil, - coordinators: [TextViewCoordinator] = [] + coordinators: [TextViewCoordinator] = [], + completionDelegate: CodeSuggestionDelegate? = nil, + jumpToDefinitionDelegate: JumpToDefinitionDelegate? = nil ) { self.language = language self.configuration = configuration @@ -223,9 +239,16 @@ public class TextViewController: NSViewController { self.foldProvider = foldProvider ?? LineIndentationFoldProvider() self._undoManager = undoManager self.invisibleCharactersCoordinator = InvisibleCharactersCoordinator(configuration: configuration) + self.completionDelegate = completionDelegate + self.jumpToDefinitionModel = JumpToDefinitionModel( + controller: nil, + treeSitterClient: treeSitterClient, + delegate: jumpToDefinitionDelegate + ) super.init(nibName: nil, bundle: nil) + jumpToDefinitionModel.controller = self suggestionTriggerModel.controller = self if let idx = highlightProviders.firstIndex(where: { $0 is TreeSitterClient }), @@ -253,11 +276,6 @@ public class TextViewController: NSViewController { } self.textCoordinators = coordinators.map { WeakCoordinator($0) } - jumpToDefinitionModel = JumpToDefinitionModel( - controller: self, - treeSitterClient: treeSitterClient, - delegate: nil - ) } required init?(coder: NSCoder) { diff --git a/Sources/CodeEditSourceEditor/Documentation.docc/SourceEditorView.md b/Sources/CodeEditSourceEditor/Documentation.docc/SourceEditorView.md index 9326844ba..a7f2b9b3c 100644 --- a/Sources/CodeEditSourceEditor/Documentation.docc/SourceEditorView.md +++ b/Sources/CodeEditSourceEditor/Documentation.docc/SourceEditorView.md @@ -86,10 +86,29 @@ let editorController = TextViewController( cursorPositions: [CursorPosition(line: 0, column: 0)], highlightProviders: [], // Use the tree-sitter syntax highlighting provider by default undoManager: nil, - coordinators: [] // Optionally inject editing behavior or other plugins. + coordinators: [], // Optionally inject editing behavior or other plugins. + completionDelegate: nil, // Provide code suggestions while typing via a delegate object. + jumpToDefinitionDelegate // Allow users to perform the 'jump to definition' using a delegate object. ) ``` +To add the controller to your view, add it as a child view controller and add the editor's view to your view hierarchy. + +```swift +final class MyController: NSViewController { + override func loadView() { + super.loadView() + let editorController: TextViewController = /**/ + + addChild(editorController) + view.addSubview(editorController.view) + editorController.view.viewDidMoveToSuperview() + } +} +``` + +For more AppKit API options, see the documentation on ``TextViewController``. + ## Topics - ``SourceEditor`` diff --git a/Sources/CodeEditSourceEditor/JumpToDefinition/JumpToDefinitionModel.swift b/Sources/CodeEditSourceEditor/JumpToDefinition/JumpToDefinitionModel.swift index e3ad64b59..dabbc62d8 100644 --- a/Sources/CodeEditSourceEditor/JumpToDefinition/JumpToDefinitionModel.swift +++ b/Sources/CodeEditSourceEditor/JumpToDefinition/JumpToDefinitionModel.swift @@ -20,7 +20,7 @@ final class JumpToDefinitionModel { weak var delegate: JumpToDefinitionDelegate? weak var treeSitterClient: TreeSitterClient? - private weak var controller: TextViewController? + weak var controller: TextViewController? private(set) public var hoveredRange: NSRange? @@ -33,7 +33,7 @@ final class JumpToDefinitionModel { controller?.textView } - init(controller: TextViewController, treeSitterClient: TreeSitterClient?, delegate: JumpToDefinitionDelegate?) { + init(controller: TextViewController?, treeSitterClient: TreeSitterClient?, delegate: JumpToDefinitionDelegate?) { self.controller = controller self.treeSitterClient = treeSitterClient self.delegate = delegate diff --git a/Sources/CodeEditSourceEditor/SourceEditor/SourceEditor.swift b/Sources/CodeEditSourceEditor/SourceEditor/SourceEditor.swift index 088bdc3a3..ed3ee508b 100644 --- a/Sources/CodeEditSourceEditor/SourceEditor/SourceEditor.swift +++ b/Sources/CodeEditSourceEditor/SourceEditor/SourceEditor.swift @@ -103,7 +103,9 @@ public struct SourceEditor: NSViewControllerRepresentable { cursorPositions: state.cursorPositions ?? [], highlightProviders: context.coordinator.highlightProviders, undoManager: undoManager, - coordinators: coordinators + coordinators: coordinators, + completionDelegate: completionDelegate, + jumpToDefinitionDelegate: jumpToDefinitionDelegate ) switch text { case .binding(let binding): @@ -118,9 +120,6 @@ public struct SourceEditor: NSViewControllerRepresentable { controller.setCursorPositions(state.cursorPositions ?? []) } - controller.completionDelegate = completionDelegate - controller.jumpToDefinitionModel?.delegate = jumpToDefinitionDelegate - context.coordinator.setController(controller) return controller } @@ -131,7 +130,7 @@ public struct SourceEditor: NSViewControllerRepresentable { public func updateNSViewController(_ controller: TextViewController, context: Context) { controller.completionDelegate = completionDelegate - controller.jumpToDefinitionModel?.delegate = jumpToDefinitionDelegate + controller.jumpToDefinitionDelegate = jumpToDefinitionDelegate context.coordinator.updateHighlightProviders(highlightProviders)