Skip to content

Commit b090725

Browse files
committed
Merge branch 'feature/enhance-suggestion-presentation' into develop
2 parents 300f457 + 5cc5f7a commit b090725

40 files changed

Lines changed: 2411 additions & 532 deletions

Core/Package.swift

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ let package = Package(
1414
name: "Service",
1515
targets: [
1616
"Service",
17-
"SuggestionInjector",
1817
"FileChangeChecker",
1918
"LaunchAgentManager",
2019
"UpdateChecker",
@@ -83,13 +82,15 @@ let package = Package(
8382
.product(name: "XPCShared", package: "Tool"),
8483
.product(name: "SuggestionProvider", package: "Tool"),
8584
.product(name: "Workspace", package: "Tool"),
85+
.product(name: "WorkspaceSuggestionService", package: "Tool"),
8686
.product(name: "UserDefaultsObserver", package: "Tool"),
8787
.product(name: "AppMonitoring", package: "Tool"),
8888
.product(name: "SuggestionBasic", package: "Tool"),
8989
.product(name: "ChatTab", package: "Tool"),
9090
.product(name: "Logger", package: "Tool"),
9191
.product(name: "OpenAIService", package: "Tool"),
9292
.product(name: "Preferences", package: "Tool"),
93+
.product(name: "CommandHandler", package: "Tool"),
9394
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
9495
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
9596
.product(name: "Dependencies", package: "swift-dependencies"),
@@ -103,7 +104,6 @@ let package = Package(
103104
dependencies: [
104105
"Service",
105106
"Client",
106-
"SuggestionInjector",
107107
.product(name: "XPCShared", package: "Tool"),
108108
.product(name: "SuggestionProvider", package: "Tool"),
109109
.product(name: "SuggestionBasic", package: "Tool"),
@@ -146,14 +146,6 @@ let package = Package(
146146
"ProExtension",
147147
])
148148
),
149-
.target(
150-
name: "SuggestionInjector",
151-
dependencies: [.product(name: "SuggestionBasic", package: "Tool")]
152-
),
153-
.testTarget(
154-
name: "SuggestionInjectorTests",
155-
dependencies: ["SuggestionInjector"]
156-
),
157149

158150
// MARK: - Prompt To Code
159151

@@ -234,6 +226,7 @@ let package = Package(
234226
.product(name: "ChatTab", package: "Tool"),
235227
.product(name: "Logger", package: "Tool"),
236228
.product(name: "CustomAsyncAlgorithms", package: "Tool"),
229+
.product(name: "CodeDiff", package: "Tool"),
237230
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
238231
.product(name: "MarkdownUI", package: "swift-markdown-ui"),
239232
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
@@ -388,6 +381,7 @@ var isProIncluded: Bool {
388381
let rootURL = fileURL
389382
.deletingLastPathComponent()
390383
.deletingLastPathComponent()
384+
.deletingLastPathComponent()
391385
let confURL = rootURL.appendingPathComponent("PLUS")
392386
return FileManager.default.fileExists(atPath: confURL.path)
393387
}

Core/Sources/Service/GUI/GraphicalUserInterfaceController.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ struct GUI {
5555

5656
enum Action {
5757
case start
58-
case openChatPanel(forceDetach: Bool)
58+
case openChatPanel(forceDetach: Bool, activateThisApp: Bool)
5959
case createAndSwitchToChatGPTChatTabIfNeeded
6060
case createAndSwitchToChatTabIfNeededMatching(
6161
check: (any ChatTab) -> Bool,
@@ -138,7 +138,7 @@ struct GUI {
138138
return .none
139139
#endif
140140

141-
case let .openChatPanel(forceDetach):
141+
case let .openChatPanel(forceDetach, activate):
142142
return .run { send in
143143
await send(
144144
.suggestionWidget(
@@ -147,7 +147,9 @@ struct GUI {
147147
)
148148
await send(.suggestionWidget(.updateKeyWindow(.chatPanel)))
149149

150-
activateThisApp()
150+
if activate {
151+
activateThisApp()
152+
}
151153
}
152154

153155
case .createAndSwitchToChatGPTChatTabIfNeeded:
@@ -186,7 +188,7 @@ struct GUI {
186188
)
187189
}
188190
}
189-
191+
190192
case let .sendCustomCommandToActiveChat(command):
191193
@Sendable func stopAndHandleCommand(_ tab: ChatGPTChatTab) async {
192194
if tab.service.isReceivingMessage {
@@ -199,7 +201,7 @@ struct GUI {
199201
let activeTab = chatTabPool.getTab(of: info.id) as? ChatGPTChatTab
200202
{
201203
return .run { send in
202-
await send(.openChatPanel(forceDetach: false))
204+
await send(.openChatPanel(forceDetach: false, activateThisApp: false))
203205
await stopAndHandleCommand(activeTab)
204206
}
205207
}
@@ -211,18 +213,16 @@ struct GUI {
211213
{
212214
state.chatTabGroup.selectedTabId = chatTab.id
213215
return .run { send in
214-
await send(.openChatPanel(forceDetach: false))
216+
await send(.openChatPanel(forceDetach: false, activateThisApp: false))
215217
await stopAndHandleCommand(chatTab)
216218
}
217219
}
218220

219221
return .run { send in
220222
guard let (chatTab, chatTabInfo) = await chatTabPool.createTab(for: nil)
221-
else {
222-
return
223-
}
223+
else { return }
224224
await send(.suggestionWidget(.chatPanel(.appendAndSelectTab(chatTabInfo))))
225-
await send(.openChatPanel(forceDetach: false))
225+
await send(.openChatPanel(forceDetach: false, activateThisApp: false))
226226
if let chatTab = chatTab as? ChatGPTChatTab {
227227
await stopAndHandleCommand(chatTab)
228228
}
@@ -349,7 +349,7 @@ public final class GraphicalUserInterfaceController {
349349
suggestionDependency.onOpenChatClicked = { [weak self] in
350350
Task { [weak self] in
351351
await self?.store.send(.createAndSwitchToChatGPTChatTabIfNeeded).finish()
352-
self?.store.send(.openChatPanel(forceDetach: false))
352+
self?.store.send(.openChatPanel(forceDetach: false, activateThisApp: true))
353353
}
354354
}
355355
suggestionDependency.onCustomCommandClicked = { command in

Core/Sources/Service/GUI/WidgetDataSource.swift

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import SuggestionWidget
1414
final class WidgetDataSource {}
1515

1616
extension WidgetDataSource: SuggestionWidgetDataSource {
17-
func suggestionForFile(at url: URL) async -> CodeSuggestionProvider? {
17+
func suggestionForFile(at url: URL) async -> PresentingCodeSuggestion? {
1818
for workspace in Service.shared.workspacePool.workspaces.values {
1919
if let filespace = workspace.filespaces[url],
2020
let suggestion = filespace.presentingSuggestion
@@ -25,39 +25,9 @@ extension WidgetDataSource: SuggestionWidgetDataSource {
2525
startLineIndex: suggestion.position.line,
2626
suggestionCount: filespace.suggestions.count,
2727
currentSuggestionIndex: filespace.suggestionIndex,
28-
onSelectPreviousSuggestionTapped: {
29-
Task {
30-
let handler = PseudoCommandHandler()
31-
await handler.presentPreviousSuggestion()
32-
}
33-
},
34-
onSelectNextSuggestionTapped: {
35-
Task {
36-
let handler = PseudoCommandHandler()
37-
await handler.presentNextSuggestion()
38-
}
39-
},
40-
onRejectSuggestionTapped: {
41-
Task {
42-
let handler = PseudoCommandHandler()
43-
await handler.rejectSuggestions()
44-
NSWorkspace.activatePreviousActiveXcode()
45-
}
46-
},
47-
onAcceptSuggestionTapped: {
48-
Task {
49-
let handler = PseudoCommandHandler()
50-
await handler.acceptSuggestion()
51-
NSWorkspace.activatePreviousActiveXcode()
52-
}
53-
},
54-
onDismissSuggestionTapped: {
55-
Task {
56-
let handler = PseudoCommandHandler()
57-
await handler.dismissSuggestion()
58-
NSWorkspace.activatePreviousActiveXcode()
59-
}
60-
}
28+
replacingRange: suggestion.range,
29+
replacingLines: suggestion.replacingLines,
30+
descriptions: suggestion.descriptions
6131
)
6232
}
6333
}

Core/Sources/Service/GlobalShortcutManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ final class GlobalShortcutManager {
2828
!guiController.store.state.suggestionWidgetState.chatPanelState.isPanelDisplayed,
2929
UserDefaults.shared.value(for: \.showHideWidgetShortcutGlobally)
3030
{
31-
guiController.store.send(.openChatPanel(forceDetach: true))
31+
guiController.store.send(.openChatPanel(forceDetach: true, activateThisApp: true))
3232
} else {
3333
guiController.store.send(.toggleWidgetsHotkeyPressed)
3434
}

Core/Sources/Service/RealtimeSuggestionController.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,10 @@ public actor RealtimeSuggestionController {
163163

164164
func cancelInFlightTasks(excluding: Task<Void, Never>? = nil) async {
165165
inflightPrefetchTask?.cancel()
166-
166+
let workspaces = await Service.shared.workspacePool.workspaces
167167
// cancel in-flight tasks
168168
await withTaskGroup(of: Void.self) { group in
169-
for (_, workspace) in Service.shared.workspacePool.workspaces {
169+
for (_, workspace) in workspaces {
170170
group.addTask {
171171
await workspace.cancelInFlightRealtimeSuggestionRequests()
172172
}

Core/Sources/Service/Service.swift

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import BuiltinExtension
22
import CodeiumService
33
import Combine
4+
import CommandHandler
45
import Dependencies
56
import Foundation
67
import GitHubCopilotService
@@ -24,13 +25,14 @@ import ProService
2425

2526
/// The running extension service.
2627
public final class Service {
28+
@MainActor
2729
public static let shared = Service()
2830

29-
@WorkspaceActor
30-
let workspacePool: WorkspacePool
31+
@Dependency(\.workspacePool) var workspacePool
3132
@MainActor
32-
public let guiController = GraphicalUserInterfaceController()
33-
public let realtimeSuggestionController = RealtimeSuggestionController()
33+
public let guiController: GraphicalUserInterfaceController
34+
public let commandHandler: CommandHandler
35+
public let realtimeSuggestionController: RealtimeSuggestionController
3436
public let scheduledCleaner: ScheduledCleaner
3537
let globalShortcutManager: GlobalShortcutManager
3638
let keyBindingManager: KeyBindingManager
@@ -43,14 +45,38 @@ public final class Service {
4345
@Dependency(\.toast) var toast
4446
var cancellable = Set<AnyCancellable>()
4547

48+
@MainActor
4649
private init() {
4750
@Dependency(\.workspacePool) var workspacePool
51+
let commandHandler = PseudoCommandHandler()
52+
UniversalCommandHandler.shared.commandHandler = commandHandler
53+
self.commandHandler = commandHandler
54+
55+
realtimeSuggestionController = .init()
56+
scheduledCleaner = .init()
57+
let guiController = GraphicalUserInterfaceController()
58+
self.guiController = guiController
59+
globalShortcutManager = .init(guiController: guiController)
60+
61+
keyBindingManager = .init(
62+
workspacePool: workspacePool,
63+
acceptSuggestion: {
64+
Task { await PseudoCommandHandler().acceptSuggestion() }
65+
},
66+
dismissSuggestion: {
67+
Task { await PseudoCommandHandler().dismissSuggestion() }
68+
}
69+
)
70+
71+
#if canImport(ProService)
72+
proService = ProService()
73+
#endif
4874

4975
BuiltinExtensionManager.shared.setupExtensions([
5076
GitHubCopilotExtension(workspacePool: workspacePool),
5177
CodeiumExtension(workspacePool: workspacePool),
5278
])
53-
scheduledCleaner = .init()
79+
5480
workspacePool.registerPlugin {
5581
SuggestionServiceWorkspacePlugin(workspace: $0) { SuggestionService.service() }
5682
}
@@ -63,21 +89,6 @@ public final class Service {
6389
workspacePool.registerPlugin {
6490
BuiltinExtensionWorkspacePlugin(workspace: $0)
6591
}
66-
self.workspacePool = workspacePool
67-
globalShortcutManager = .init(guiController: guiController)
68-
keyBindingManager = .init(
69-
workspacePool: workspacePool,
70-
acceptSuggestion: {
71-
Task { await PseudoCommandHandler().acceptSuggestion() }
72-
},
73-
dismissSuggestion: {
74-
Task { await PseudoCommandHandler().dismissSuggestion() }
75-
}
76-
)
77-
78-
#if canImport(ProService)
79-
proService = ProService()
80-
#endif
8192

8293
scheduledCleaner.service = self
8394
}
@@ -100,9 +111,10 @@ public final class Service {
100111
.removeDuplicates()
101112
.filter { $0 != .init(fileURLWithPath: "/") }
102113
.compactMap { $0 }
103-
.sink { [weak self] fileURL in
114+
.sink { fileURL in
104115
Task {
105-
try await self?.workspacePool
116+
@Dependency(\.workspacePool) var workspacePool
117+
return try await workspacePool
106118
.fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL)
107119
}
108120
}.store(in: &cancellable)
@@ -128,7 +140,7 @@ public extension Service {
128140
) {
129141
do {
130142
#if canImport(ProService)
131-
try Service.shared.proService.handleXPCServiceRequests(
143+
try proService.handleXPCServiceRequests(
132144
endpoint: endpoint,
133145
requestBody: requestBody,
134146
reply: reply

0 commit comments

Comments
 (0)