@@ -3,6 +3,7 @@ import AppKit
33import Preferences
44import SuggestionInjector
55import SuggestionModel
6+ import Toast
67import Workspace
78import WorkspaceSuggestionService
89import XcodeInspector
@@ -12,6 +13,9 @@ import XPCShared
1213///
1314/// For example, we can use it to generate real-time suggestions without Apple Scripts.
1415struct PseudoCommandHandler {
16+ static var lastTimeCommandFailedToTriggerWithAccessibilityAPI = Date ( timeIntervalSince1970: 0 )
17+ private var toast : ToastController { ToastControllerDependencyKey . liveValue }
18+
1519 func presentPreviousSuggestion( ) async {
1620 let handler = WindowBaseCommandHandler ( )
1721 _ = try ? await handler. presentPreviousSuggestion ( editor: . init(
@@ -43,11 +47,11 @@ struct PseudoCommandHandler {
4347 @WorkspaceActor
4448 func generateRealtimeSuggestions( sourceEditor: SourceEditor ? ) async {
4549 guard let filespace = await getFilespace ( ) ,
46- let ( workspace, _) = try ? await Service . shared. workspacePool
47- . fetchOrCreateWorkspaceAndFilespace ( fileURL: filespace. fileURL) else { return }
48-
50+ let ( workspace, _) = try ? await Service . shared. workspacePool
51+ . fetchOrCreateWorkspaceAndFilespace ( fileURL: filespace. fileURL) else { return }
52+
4953 if Task . isCancelled { return }
50-
54+
5155 // Can't use handler if content is not available.
5256 guard let editor = await getEditorContent ( sourceEditor: sourceEditor)
5357 else { return }
@@ -107,7 +111,7 @@ struct PseudoCommandHandler {
107111 if filespace. presentingSuggestion == nil {
108112 return // skip if there's no suggestion presented.
109113 }
110-
114+
111115 let content = sourceEditor. getContent ( )
112116 if !filespace. validateSuggestions (
113117 lines: content. lines,
@@ -178,8 +182,23 @@ struct PseudoCommandHandler {
178182 if UserDefaults . shared. value ( for: \. alwaysAcceptSuggestionWithAccessibilityAPI) {
179183 throw CancellationError ( )
180184 }
181- try await XcodeInspector . shared. latestActiveXcode?
182- . triggerCopilotCommand ( name: " Accept Prompt to Code " )
185+ do {
186+ try await XcodeInspector . shared. latestActiveXcode?
187+ . triggerCopilotCommand ( name: " Accept Prompt to Code " )
188+ } catch {
189+ let last = Self . lastTimeCommandFailedToTriggerWithAccessibilityAPI
190+ let now = Date ( )
191+ if now. timeIntervalSince ( last) > 60 * 60 {
192+ Self . lastTimeCommandFailedToTriggerWithAccessibilityAPI = now
193+ toast. toast ( content: """
194+ The app is using a fallback solution to accept suggestions. \
195+ For better experience, please restart Xcode to re-activate the Copilot \
196+ menu item.
197+ """ , type: . warning)
198+ }
199+
200+ throw error
201+ }
183202 } catch {
184203 guard let xcode = ActiveApplicationMonitor . shared. activeXcode
185204 ?? ActiveApplicationMonitor . shared. latestXcode else { return }
@@ -218,8 +237,23 @@ struct PseudoCommandHandler {
218237 if UserDefaults . shared. value ( for: \. alwaysAcceptSuggestionWithAccessibilityAPI) {
219238 throw CancellationError ( )
220239 }
221- try await XcodeInspector . shared. latestActiveXcode?
222- . triggerCopilotCommand ( name: " Accept Suggestion " )
240+ do {
241+ try await XcodeInspector . shared. latestActiveXcode?
242+ . triggerCopilotCommand ( name: " Accept Suggestion " )
243+ } catch {
244+ let last = Self . lastTimeCommandFailedToTriggerWithAccessibilityAPI
245+ let now = Date ( )
246+ if now. timeIntervalSince ( last) > 60 * 60 {
247+ Self . lastTimeCommandFailedToTriggerWithAccessibilityAPI = now
248+ toast. toast ( content: """
249+ The app is using a fallback solution to accept suggestions. \
250+ For better experience, please restart Xcode to re-activate the Copilot \
251+ menu item.
252+ """ , type: . warning)
253+ }
254+
255+ throw error
256+ }
223257 } catch {
224258 guard let xcode = ActiveApplicationMonitor . shared. activeXcode
225259 ?? ActiveApplicationMonitor . shared. latestXcode else { return }
@@ -252,12 +286,12 @@ struct PseudoCommandHandler {
252286 }
253287 }
254288 }
255-
289+
256290 func dismissSuggestion( ) async {
257291 guard let documentURL = XcodeInspector . shared. activeDocumentURL else { return }
258292 guard let ( _, filespace) = try ? await Service . shared. workspacePool
259293 . fetchOrCreateWorkspaceAndFilespace ( fileURL: documentURL) else { return }
260-
294+
261295 await filespace. reset ( )
262296 PresentInWindowSuggestionPresenter ( ) . discardSuggestion ( fileURL: documentURL)
263297 }
@@ -432,3 +466,4 @@ extension PseudoCommandHandler {
432466 return cursorRange
433467 }
434468}
469+
0 commit comments