Skip to content

Commit acea895

Browse files
committed
Finish Delegate Methods, Add Mock For Example
1 parent 5a27645 commit acea895

File tree

12 files changed

+87
-29
lines changed

12 files changed

+87
-29
lines changed

Example/CodeEditSourceEditorExample/CodeEditSourceEditorExample.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
6C1365462B8A7F2D004A1D18 /* LanguagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C1365452B8A7F2D004A1D18 /* LanguagePicker.swift */; };
2121
6C1365482B8A7FBF004A1D18 /* EditorTheme+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C1365472B8A7FBF004A1D18 /* EditorTheme+Default.swift */; };
2222
6C13654D2B8A821E004A1D18 /* NSColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C13654C2B8A821E004A1D18 /* NSColor+Hex.swift */; };
23+
6C730A042E32CA2A00FE1F32 /* MockJumpToDefinitionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C730A032E32CA2A00FE1F32 /* MockJumpToDefinitionDelegate.swift */; };
2324
6C8B564C2E3018CC00DC3F29 /* MockCompletionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8B564B2E3018CC00DC3F29 /* MockCompletionDelegate.swift */; };
2425
6CF31D4E2DB6A252006A77FD /* StatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF31D4D2DB6A252006A77FD /* StatusBar.swift */; };
2526
/* End PBXBuildFile section */
@@ -39,6 +40,7 @@
3940
6C1365452B8A7F2D004A1D18 /* LanguagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguagePicker.swift; sourceTree = "<group>"; };
4041
6C1365472B8A7FBF004A1D18 /* EditorTheme+Default.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EditorTheme+Default.swift"; sourceTree = "<group>"; };
4142
6C13654C2B8A821E004A1D18 /* NSColor+Hex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSColor+Hex.swift"; sourceTree = "<group>"; };
43+
6C730A032E32CA2A00FE1F32 /* MockJumpToDefinitionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockJumpToDefinitionDelegate.swift; sourceTree = "<group>"; };
4244
6C8B564B2E3018CC00DC3F29 /* MockCompletionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCompletionDelegate.swift; sourceTree = "<group>"; };
4345
6CF31D4D2DB6A252006A77FD /* StatusBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBar.swift; sourceTree = "<group>"; };
4446
/* End PBXFileReference section */
@@ -123,6 +125,7 @@
123125
6CF31D4D2DB6A252006A77FD /* StatusBar.swift */,
124126
6C1365452B8A7F2D004A1D18 /* LanguagePicker.swift */,
125127
1CB30C392DAA1C28008058A7 /* IndentPicker.swift */,
128+
6C730A032E32CA2A00FE1F32 /* MockJumpToDefinitionDelegate.swift */,
126129
);
127130
path = Views;
128131
sourceTree = "<group>";
@@ -212,6 +215,7 @@
212215
isa = PBXSourcesBuildPhase;
213216
buildActionMask = 2147483647;
214217
files = (
218+
6C730A042E32CA2A00FE1F32 /* MockJumpToDefinitionDelegate.swift in Sources */,
215219
6C1365482B8A7FBF004A1D18 /* EditorTheme+Default.swift in Sources */,
216220
6C13654D2B8A821E004A1D18 /* NSColor+Hex.swift in Sources */,
217221
6C1365302B8A7B94004A1D18 /* CodeEditSourceEditorExampleDocument.swift in Sources */,

Example/CodeEditSourceEditorExample/CodeEditSourceEditorExample/Views/ContentView.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct ContentView: View {
2323
cursorPositions: [CursorPosition(line: 1, column: 1)]
2424
)
2525
@StateObject private var suggestions: MockCompletionDelegate = MockCompletionDelegate()
26+
@StateObject private var jumpToDefinition: MockJumpToDefinitionDelegate = MockJumpToDefinitionDelegate()
2627

2728
@State private var font: NSFont = NSFont.monospacedSystemFont(ofSize: 12, weight: .medium)
2829
@AppStorage("wrapLines") private var wrapLines: Bool = true
@@ -73,7 +74,8 @@ struct ContentView: View {
7374
)
7475
),
7576
state: $editorState,
76-
completionDelegate: suggestions
77+
completionDelegate: suggestions,
78+
jumpToDefinitionDelegate: jumpToDefinition
7779
)
7880
.overlay(alignment: .bottom) {
7981
StatusBar(

Example/CodeEditSourceEditorExample/CodeEditSourceEditorExample/Views/MockCompletionDelegate.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,13 @@ class MockCompletionDelegate: CodeSuggestionDelegate, ObservableObject {
9494
func completionWindowApplyCompletion(
9595
item: CodeSuggestionEntry,
9696
textView: TextViewController,
97-
cursorPosition: CursorPosition
97+
cursorPosition: CursorPosition?
9898
) {
99-
guard let suggestion = item as? Suggestion else {
99+
guard let suggestion = item as? Suggestion, let cursorPosition else {
100100
return
101101
}
102102
textView.textView.undoManager?.beginUndoGrouping()
103+
textView.textView.selectionManager.setSelectedRange(cursorPosition.range)
103104
textView.textView.insertText(suggestion.label)
104105
textView.textView.undoManager?.endUndoGrouping()
105106
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// MockJumpToDefinitionDelegate.swift
3+
// CodeEditSourceEditorExample
4+
//
5+
// Created by Khan Winter on 7/24/25.
6+
//
7+
8+
import AppKit
9+
import CodeEditSourceEditor
10+
11+
final class MockJumpToDefinitionDelegate: JumpToDefinitionDelegate, ObservableObject {
12+
func queryLinks(forRange range: NSRange) async -> [JumpToDefinitionLink]? {
13+
Bool.random() ? [
14+
JumpToDefinitionLink(
15+
url: nil,
16+
targetPosition: CursorPosition(line: 0, column: 0),
17+
targetRange: NSRange(start: 0, end: 10),
18+
typeName: "Start of Document",
19+
sourcePreview: "// Comment at start"
20+
),
21+
JumpToDefinitionLink(
22+
url: URL(string: "https://codeedit.app/"),
23+
targetPosition: CursorPosition(line: 1024, column: 10),
24+
targetRange: NSRange(location: 30, length: 100),
25+
typeName: "CodeEdit Website",
26+
sourcePreview: "https://codeedit.app/"
27+
)
28+
] : nil
29+
}
30+
31+
func openLink(url: URL, targetRange: NSRange) {
32+
NSWorkspace.shared.open(url)
33+
}
34+
}

Sources/CodeEditSourceEditor/CodeSuggestion/Model/CodeSuggestionDelegate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public protocol CodeSuggestionDelegate: AnyObject {
2626
func completionWindowApplyCompletion(
2727
item: CodeSuggestionEntry,
2828
textView: TextViewController,
29-
cursorPosition: CursorPosition
29+
cursorPosition: CursorPosition?
3030
)
3131
// Optional
3232
func completionWindowDidSelect(item: CodeSuggestionEntry)

Sources/CodeEditSourceEditor/CodeSuggestion/Model/SuggestionViewModel.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ final class SuggestionViewModel: ObservableObject {
1414
var itemsRequestTask: Task<Void, Never>?
1515
weak var activeTextView: TextViewController?
1616

17-
var delegate: CodeSuggestionDelegate? {
18-
activeTextView?.completionDelegate
19-
}
17+
weak var delegate: CodeSuggestionDelegate?
2018

2119
func showCompletions(
2220
textView: TextViewController,
@@ -25,11 +23,13 @@ final class SuggestionViewModel: ObservableObject {
2523
showWindowOnParent: @escaping @MainActor (NSWindow, NSRect) -> Void
2624
) {
2725
self.activeTextView = nil
26+
self.delegate = nil
2827
itemsRequestTask?.cancel()
2928

3029
guard let targetParentWindow = textView.view.window else { return }
3130

3231
self.activeTextView = textView
32+
self.delegate = delegate
3333
itemsRequestTask = Task {
3434
do {
3535
guard let completionItems = await delegate.completionSuggestionsRequested(
@@ -89,14 +89,13 @@ final class SuggestionViewModel: ObservableObject {
8989
}
9090

9191
func applySelectedItem(item: CodeSuggestionEntry, window: NSWindow?) {
92-
guard let activeTextView,
93-
let cursorPosition = activeTextView.cursorPositions.first else {
92+
guard let activeTextView else {
9493
return
9594
}
9695
self.delegate?.completionWindowApplyCompletion(
9796
item: item,
9897
textView: activeTextView,
99-
cursorPosition: cursorPosition
98+
cursorPosition: activeTextView.cursorPositions.first
10099
)
101100
window?.close()
102101
}

Sources/CodeEditSourceEditor/CodeSuggestion/TableView/SuggestionViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ class SuggestionViewController: NSViewController {
105105
let newHeight = rowHeight * numberOfVisibleRows + SuggestionController.WINDOW_PADDING * 2
106106

107107
let maxLength = min((model?.items.max(by: { $0.label.count < $1.label.count })?.label.count ?? 16) + 4, 48)
108-
let newWidth = CGFloat(maxLength) * controller.font.charWidth
108+
let newWidth = max(256, CGFloat(maxLength) * controller.font.charWidth)
109109

110110
view.constraints.filter({ $0.firstAnchor == view.heightAnchor }).forEach { $0.isActive = false }
111111
view.heightAnchor.constraint(equalToConstant: newHeight).isActive = true

Sources/CodeEditSourceEditor/CodeSuggestion/Window/SuggestionController.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,10 @@ public final class SuggestionController: NSWindowController {
8383
self.showWindow(attachedTo: parentWindow)
8484
self.constrainWindowToScreenEdges(cursorRect: cursorRect)
8585
}
86-
(self.contentViewController as? SuggestionViewController)?.styleView(using: textView)
86+
if let controller = self.contentViewController as? SuggestionViewController {
87+
controller.styleView(using: textView)
88+
self.popover?.contentSize = controller.preferredContentSize
89+
}
8790
}
8891
}
8992

Sources/CodeEditSourceEditor/Controller/TextViewController+Lifecycle.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ extension TextViewController {
190190

191191
func setUpKeyBindings(eventMonitor: inout Any?) {
192192
eventMonitor = NSEvent.addLocalMonitorForEvents(
193-
matching: [.keyDown, .flagsChanged, .mouseMoved]
193+
matching: [.keyDown, .flagsChanged, .mouseMoved, .leftMouseUp]
194194
) { [weak self] event -> NSEvent? in
195195
guard let self = self else { return event }
196196

@@ -228,6 +228,12 @@ extension TextViewController {
228228
}
229229
self.jumpToDefinitionModel?.mouseHovered(windowCoordinates: event.locationInWindow)
230230
return event
231+
case .leftMouseUp:
232+
if let range = jumpToDefinitionModel?.hoveredRange {
233+
self.jumpToDefinitionModel?.performJump(at: range)
234+
return nil
235+
}
236+
return event
231237
default:
232238
return event
233239
}

Sources/CodeEditSourceEditor/Controller/TextViewController.swift

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,11 @@ public class TextViewController: NSViewController {
173173
/// The tree sitter client managed by the source editor.
174174
///
175175
/// This will be `nil` if another highlighter provider is passed to the source editor.
176-
internal(set) public var treeSitterClient: TreeSitterClient?
176+
internal(set) public var treeSitterClient: TreeSitterClient? {
177+
didSet {
178+
jumpToDefinitionModel?.treeSitterClient = treeSitterClient
179+
}
180+
}
177181

178182
var foldProvider: LineFoldProvider
179183

@@ -245,13 +249,11 @@ public class TextViewController: NSViewController {
245249
}
246250
self.textCoordinators = coordinators.map { WeakCoordinator($0) }
247251

248-
if let treeSitterClient {
249-
jumpToDefinitionModel = JumpToDefinitionModel(
250-
controller: self,
251-
treeSitterClient: treeSitterClient,
252-
delegate: nil
253-
)
254-
}
252+
jumpToDefinitionModel = JumpToDefinitionModel(
253+
controller: self,
254+
treeSitterClient: treeSitterClient,
255+
delegate: nil
256+
)
255257
}
256258

257259
required init?(coder: NSCoder) {

0 commit comments

Comments
 (0)