@@ -30,23 +30,26 @@ final class SuggestionPanelController {
3030
3131 private var windowChangeObservationTask : Task < Void , Error > ?
3232 private var activeApplicationMonitorTask : Task < Void , Error > ?
33- private var activeApplication : NSRunningApplication ? {
34- ActiveApplicationMonitor . activeApplication
33+ private var xcode : NSRunningApplication ?
34+ private var suggestionForFiles : [ URL : Suggestion ] = [ : ]
35+
36+ enum Suggestion {
37+ case code( [ String ] , startLineIndex: Int )
3538 }
3639
3740 nonisolated init ( ) {
3841 Task { @MainActor in
3942 activeApplicationMonitorTask = Task { [ weak self] in
40- guard let self else { return }
4143 var previousApp : NSRunningApplication ?
4244 for await app in ActiveApplicationMonitor . createStream ( ) {
45+ guard let self else { return }
4346 try Task . checkCancellation ( )
4447 defer { previousApp = app }
45- if let app, app . bundleIdentifier == " com.apple.dt.Xcode " {
48+ if let app = ActiveApplicationMonitor . activeXcode {
4649 if app != previousApp {
4750 windowChangeObservationTask? . cancel ( )
4851 windowChangeObservationTask = nil
49- self . observeXcodeWindowChangeIfNeeded ( )
52+ self . observeXcodeWindowChangeIfNeeded ( app )
5053 }
5154 }
5255
@@ -55,18 +58,44 @@ final class SuggestionPanelController {
5558 }
5659 }
5760 }
61+
62+ func suggestCode( _ code: String , startLineIndex: Int , fileURL: URL ) {
63+ viewModel. suggestion = code. split ( separator: " \n " ) . map ( String . init)
64+ viewModel. startLineIndex = startLineIndex
65+ suggestionForFiles [ fileURL] = . code( viewModel. suggestion, startLineIndex: startLineIndex)
66+ }
5867
59- private func observeXcodeWindowChangeIfNeeded( ) {
68+ private func observeXcodeWindowChangeIfNeeded( _ app: NSRunningApplication ) {
69+ xcode = app
6070 guard windowChangeObservationTask == nil else { return }
6171 windowChangeObservationTask = Task { [ weak self] in
62- guard let self else { return }
6372 let notifications = AXNotificationStream (
64- app: activeApplication!,
65- notificationNames: kAXMovedNotification
73+ app: app,
74+ notificationNames:
75+ kAXMovedNotification,
76+ kAXResizedNotification,
77+ kAXMainWindowChangedNotification,
78+ kAXFocusedWindowChangedNotification,
79+ kAXFocusedUIElementChangedNotification
6680 )
67- for await _ in notifications {
81+ for await notification in notifications {
82+ guard let self else { return }
6883 try Task . checkCancellation ( )
6984 self . updateWindowLocation ( )
85+
86+ if notification. name == kAXFocusedUIElementChangedNotification {
87+ guard let fileURL = try ? await Environment . fetchCurrentFileURL ( ) ,
88+ let suggestion = suggestionForFiles [ fileURL]
89+ else {
90+ viewModel. suggestion = [ ]
91+ continue
92+ }
93+ switch suggestion {
94+ case let . code( code, startLineIndex) :
95+ viewModel. suggestion = code
96+ viewModel. startLineIndex = startLineIndex
97+ }
98+ }
7099 }
71100 }
72101 }
@@ -76,10 +105,8 @@ final class SuggestionPanelController {
76105 /// - note: It's possible to get the scroll view's postion by getting position on the focus
77106 /// element.
78107 private func updateWindowLocation( ) {
79- if let activeXcode = activeApplication,
80- activeXcode. bundleIdentifier == " com.apple.dt.Xcode "
81- {
82- let application = AXUIElementCreateApplication ( activeXcode. processIdentifier)
108+ if let xcode {
109+ let application = AXUIElementCreateApplication ( xcode. processIdentifier)
83110 if let focusElement: AXUIElement = try ? application
84111 . copyValue ( key: kAXFocusedUIElementAttribute) ,
85112 let focusElementType: String = try ? focusElement
@@ -126,11 +153,6 @@ final class SuggestionPanelViewModel: ObservableObject {
126153 }
127154
128155 @Published var isPanelDisplayed = true
129-
130- func suggestCode( _ code: String , startLineIndex: Int ) {
131- suggestion = code. split ( separator: " \n " ) . map ( String . init)
132- self . startLineIndex = startLineIndex
133- }
134156}
135157
136158struct SuggestionPanelView : View {
0 commit comments