Skip to content

Commit 0e889cf

Browse files
committed
Add command handler
1 parent 40d3ac2 commit 0e889cf

File tree

6 files changed

+111
-6
lines changed

6 files changed

+111
-6
lines changed

Core/Package.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ let package = Package(
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"),
@@ -388,6 +389,7 @@ var isProIncluded: Bool {
388389
let rootURL = fileURL
389390
.deletingLastPathComponent()
390391
.deletingLastPathComponent()
392+
.deletingLastPathComponent()
391393
let confURL = rootURL.appendingPathComponent("PLUS")
392394
return FileManager.default.fileExists(atPath: confURL.path)
393395
}

Core/Sources/Service/GUI/GraphicalUserInterfaceController.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ struct GUI {
186186
)
187187
}
188188
}
189-
189+
190190
case let .sendCustomCommandToActiveChat(command):
191191
@Sendable func stopAndHandleCommand(_ tab: ChatGPTChatTab) async {
192192
if tab.service.isReceivingMessage {
@@ -218,9 +218,7 @@ struct GUI {
218218

219219
return .run { send in
220220
guard let (chatTab, chatTabInfo) = await chatTabPool.createTab(for: nil)
221-
else {
222-
return
223-
}
221+
else { return }
224222
await send(.suggestionWidget(.chatPanel(.appendAndSelectTab(chatTabInfo))))
225223
await send(.openChatPanel(forceDetach: false))
226224
if let chatTab = chatTab as? ChatGPTChatTab {

Core/Sources/Service/Service.swift

Lines changed: 2 additions & 0 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
@@ -45,6 +46,7 @@ public final class Service {
4546

4647
private init() {
4748
@Dependency(\.workspacePool) var workspacePool
49+
CommandHandlerDependencyKey.liveValue = PseudoCommandHandler()
4850

4951
BuiltinExtensionManager.shared.setupExtensions([
5052
GitHubCopilotExtension(workspacePool: workspacePool),

Core/Sources/Service/SuggestionCommandHandler/PseudoCommandHandler.swift

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import ActiveApplicationMonitor
22
import AppKit
33
import CodeiumService
4+
import CommandHandler
45
import enum CopilotForXcodeKit.SuggestionServiceError
56
import Dependencies
67
import Logger
78
import PlusFeatureFlag
89
import Preferences
9-
import SuggestionInjector
1010
import SuggestionBasic
11+
import SuggestionInjector
1112
import Toast
1213
import Workspace
1314
import WorkspaceSuggestionService
@@ -21,7 +22,7 @@ import BrowserChatTab
2122
/// It's used to run some commands without really triggering the menu bar item.
2223
///
2324
/// For example, we can use it to generate real-time suggestions without Apple Scripts.
24-
struct PseudoCommandHandler {
25+
struct PseudoCommandHandler: CommandHandler {
2526
static var lastTimeCommandFailedToTriggerWithAccessibilityAPI = Date(timeIntervalSince1970: 0)
2627
private var toast: ToastController { ToastControllerDependencyKey.liveValue }
2728

@@ -397,6 +398,33 @@ struct PseudoCommandHandler {
397398
}
398399
}
399400
}
401+
402+
func sendChatMessage(_ message: String) async {
403+
let store = Service.shared.guiController.store
404+
await store.send(.sendCustomCommandToActiveChat(CustomCommand(
405+
commandId: "",
406+
name: "",
407+
feature: .chatWithSelection(
408+
extraSystemPrompt: nil,
409+
prompt: message,
410+
useExtraSystemPrompt: nil
411+
)
412+
))).finish()
413+
}
414+
415+
@WorkspaceActor
416+
func presentSuggestions(_ suggestions: [SuggestionBasic.CodeSuggestion]) async {
417+
guard let filespace = await getFilespace() else { return }
418+
filespace.setSuggestions(suggestions)
419+
PresentInWindowSuggestionPresenter().presentSuggestion(fileURL: filespace.fileURL)
420+
}
421+
422+
func toast(_ message: String, as type: ToastType) {
423+
Task { @MainActor in
424+
let store = Service.shared.guiController.store
425+
store.send(.suggestionWidget(.toastPanel(.toast(.toast(message, type, nil)))))
426+
}
427+
}
400428
}
401429

402430
extension PseudoCommandHandler {

Tool/Package.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ let package = Package(
4646
.library(name: "DebounceFunction", targets: ["DebounceFunction"]),
4747
.library(name: "AsyncPassthroughSubject", targets: ["AsyncPassthroughSubject"]),
4848
.library(name: "CustomAsyncAlgorithms", targets: ["CustomAsyncAlgorithms"]),
49+
.library(name: "CommandHandler", targets: ["CommandHandler"]),
4950
],
5051
dependencies: [
5152
// A fork of https://github.com/aespinilla/Tiktoken to allow loading from local files.
@@ -292,6 +293,15 @@ let package = Package(
292293
),
293294
]
294295
),
296+
297+
.target(
298+
name: "CommandHandler",
299+
dependencies: [
300+
"XcodeInspector",
301+
"Preferences",
302+
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
303+
]
304+
),
295305

296306
// MARK: - Services
297307

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import Dependencies
2+
import Foundation
3+
import Preferences
4+
import SuggestionBasic
5+
import Toast
6+
import XcodeInspector
7+
8+
/// Provides an interface to handle commands.
9+
public protocol CommandHandler {
10+
// MARK: Suggestion
11+
12+
func presentSuggestions(_ suggestions: [CodeSuggestion]) async
13+
func presentPreviousSuggestion() async
14+
func presentNextSuggestion() async
15+
func rejectSuggestions() async
16+
func acceptSuggestion() async
17+
func dismissSuggestion() async
18+
func generateRealtimeSuggestions(sourceEditor: SourceEditor?) async
19+
20+
// MARK: Chat
21+
22+
func openChat(forceDetach: Bool)
23+
func sendChatMessage(_ message: String) async
24+
25+
// MARK: Prompt to Code
26+
27+
func acceptPromptToCode() async
28+
29+
// MARK: Custom Command
30+
31+
func handleCustomCommand(_ command: CustomCommand) async
32+
33+
// MARK: Toast
34+
35+
func toast(_ string: String, as type: ToastType)
36+
}
37+
38+
public struct CommandHandlerDependencyKey: DependencyKey {
39+
public static var liveValue: CommandHandler = NoopCommandHandler()
40+
}
41+
42+
public extension DependencyValues {
43+
var commandHandler: CommandHandler {
44+
get { self[CommandHandlerDependencyKey.self] }
45+
set { self[CommandHandlerDependencyKey.self] = newValue }
46+
}
47+
}
48+
49+
struct NoopCommandHandler: CommandHandler {
50+
static let shared: CommandHandler = NoopCommandHandler()
51+
52+
func presentSuggestions(_: [CodeSuggestion]) async {}
53+
func presentPreviousSuggestion() async {}
54+
func presentNextSuggestion() async {}
55+
func rejectSuggestions() async {}
56+
func acceptSuggestion() async {}
57+
func dismissSuggestion() async {}
58+
func generateRealtimeSuggestions(sourceEditor: SourceEditor?) async {}
59+
func openChat(forceDetach: Bool) {}
60+
func sendChatMessage(_: String) async {}
61+
func acceptPromptToCode() async {}
62+
func handleCustomCommand(_: CustomCommand) async {}
63+
func toast(_: String, as: ToastType) {}
64+
}
65+

0 commit comments

Comments
 (0)