Skip to content

Commit 626aa5e

Browse files
committed
Merge branch 'feature/chat-agent-prepare' into develop
# Conflicts: # Tool/Sources/ChatTab/ChatTab.swift
2 parents bda2eb3 + fd7bb9b commit 626aa5e

File tree

26 files changed

+390
-198
lines changed

26 files changed

+390
-198
lines changed

Core/Sources/Service/GUI/ChatTabFactory.swift

Lines changed: 88 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,20 @@ import SuggestionBasic
88
import SuggestionWidget
99
import XcodeInspector
1010

11-
#if canImport(ProChatTabs)
12-
import ProChatTabs
13-
#endif
14-
1511
enum ChatTabFactory {
1612
static func chatTabBuilderCollection() -> [ChatTabBuilderCollection] {
17-
#if canImport(ProChatTabs)
18-
_ = lazyLoadDependency
19-
let collection = [
20-
folderIfNeeded(ChatGPTChatTab.chatBuilders(), title: ChatGPTChatTab.name),
21-
folderIfNeeded(BrowserChatTab.chatBuilders(), title: BrowserChatTab.name),
22-
folderIfNeeded(TerminalChatTab.chatBuilders(), title: TerminalChatTab.name),
23-
]
24-
#else
25-
let collection = [
26-
folderIfNeeded(ChatGPTChatTab.chatBuilders(), title: ChatGPTChatTab.name),
27-
]
28-
#endif
29-
30-
return collection.compactMap { $0 } + chatTabsFromExtensions()
13+
let chatGPTChatTab = folderIfNeeded(
14+
ChatGPTChatTab.chatBuilders(),
15+
title: ChatGPTChatTab.name
16+
)
17+
18+
let (defaultChatTab, othersChatTabs) = chatTabsFromExtensions()
19+
20+
if let defaultChatTab {
21+
return [defaultChatTab] + othersChatTabs + [chatGPTChatTab].compactMap(\.self)
22+
} else {
23+
return [chatGPTChatTab].compactMap(\.self) + othersChatTabs
24+
}
3125
}
3226

3327
private static func folderIfNeeded(
@@ -41,70 +35,85 @@ enum ChatTabFactory {
4135
return nil
4236
}
4337

44-
static func chatTabsFromExtensions() -> [ChatTabBuilderCollection] {
38+
static func chatTabsFromExtensions()
39+
-> (default: ChatTabBuilderCollection?, others: [ChatTabBuilderCollection])
40+
{
4541
let extensions = BuiltinExtensionManager.shared.extensions
4642
let chatTabTypes = extensions.flatMap(\.chatTabTypes)
47-
return chatTabTypes.compactMap { folderIfNeeded($0.chatBuilders(), title: $0.name) }
43+
var defaultChatTab: ChatTabBuilderCollection?
44+
var otherChatTabs = [ChatTabBuilderCollection]()
45+
for chatTabType in chatTabTypes {
46+
if chatTabType.isDefaultChatTabReplacement {
47+
defaultChatTab = folderIfNeeded(chatTabType.chatBuilders(), title: chatTabType.name)
48+
} else if let tab = folderIfNeeded(
49+
chatTabType.chatBuilders(),
50+
title: chatTabType.name
51+
) {
52+
otherChatTabs.append(tab)
53+
}
54+
}
55+
return (defaultChatTab, otherChatTabs)
4856
}
4957
}
5058

51-
#if canImport(ProChatTabs)
52-
let lazyLoadDependency: () = {
53-
BrowserChatTab.externalDependency = .init(
54-
handleCustomCommand: { command, prompt in
55-
switch command.feature {
56-
case let .chatWithSelection(extraSystemPrompt, _, useExtraSystemPrompt):
57-
let service = ChatService()
58-
return try await service.processMessage(
59-
systemPrompt: nil,
60-
extraSystemPrompt: (useExtraSystemPrompt ?? false) ? extraSystemPrompt :
61-
nil,
62-
prompt: prompt
63-
)
64-
case let .customChat(systemPrompt, _):
65-
let service = ChatService()
66-
return try await service.processMessage(
67-
systemPrompt: systemPrompt,
68-
extraSystemPrompt: nil,
69-
prompt: prompt
70-
)
71-
case let .singleRoundDialog(
72-
systemPrompt,
73-
overwriteSystemPrompt,
74-
_,
75-
_
76-
):
77-
let service = ChatService()
78-
return try await service.handleSingleRoundDialogCommand(
79-
systemPrompt: systemPrompt,
80-
overwriteSystemPrompt: overwriteSystemPrompt ?? false,
81-
prompt: prompt
82-
)
83-
case let .promptToCode(extraSystemPrompt, instruction, _, _):
84-
let service = OpenAIPromptToCodeService()
59+
// #if canImport(ProChatTabs)
60+
// let lazyLoadDependency: () = {
61+
// BrowserChatTab.externalDependency = .init(
62+
// handleCustomCommand: { command, prompt in
63+
// switch command.feature {
64+
// case let .chatWithSelection(extraSystemPrompt, _, useExtraSystemPrompt):
65+
// let service = ChatService()
66+
// return try await service.processMessage(
67+
// systemPrompt: nil,
68+
// extraSystemPrompt: (useExtraSystemPrompt ?? false) ? extraSystemPrompt :
69+
// nil,
70+
// prompt: prompt
71+
// )
72+
// case let .customChat(systemPrompt, _):
73+
// let service = ChatService()
74+
// return try await service.processMessage(
75+
// systemPrompt: systemPrompt,
76+
// extraSystemPrompt: nil,
77+
// prompt: prompt
78+
// )
79+
// case let .singleRoundDialog(
80+
// systemPrompt,
81+
// overwriteSystemPrompt,
82+
// _,
83+
// _
84+
// ):
85+
// let service = ChatService()
86+
// return try await service.handleSingleRoundDialogCommand(
87+
// systemPrompt: systemPrompt,
88+
// overwriteSystemPrompt: overwriteSystemPrompt ?? false,
89+
// prompt: prompt
90+
// )
91+
// case let .promptToCode(extraSystemPrompt, instruction, _, _):
92+
// let service = OpenAIPromptToCodeService()
93+
//
94+
// let result = try await service.modifyCode(
95+
// code: prompt,
96+
// requirement: instruction ?? "Modify content.",
97+
// source: .init(
98+
// language: .plaintext,
99+
// documentURL: .init(fileURLWithPath: "/"),
100+
// projectRootURL: .init(fileURLWithPath: "/"),
101+
// content: prompt,
102+
// lines: prompt.breakLines(),
103+
// range: .outOfScope
104+
// ),
105+
// isDetached: true,
106+
// extraSystemPrompt: extraSystemPrompt,
107+
// generateDescriptionRequirement: false
108+
// )
109+
// var code = ""
110+
// for try await (newCode, _) in result {
111+
// code = newCode
112+
// }
113+
// return code
114+
// }
115+
// }
116+
// )
117+
// }()
118+
// #endif
85119

86-
let result = try await service.modifyCode(
87-
code: prompt,
88-
requirement: instruction ?? "Modify content.",
89-
source: .init(
90-
language: .plaintext,
91-
documentURL: .init(fileURLWithPath: "/"),
92-
projectRootURL: .init(fileURLWithPath: "/"),
93-
content: prompt,
94-
lines: prompt.breakLines(),
95-
range: .outOfScope
96-
),
97-
isDetached: true,
98-
extraSystemPrompt: extraSystemPrompt,
99-
generateDescriptionRequirement: false
100-
)
101-
var code = ""
102-
for try await (newCode, _) in result {
103-
code = newCode
104-
}
105-
return code
106-
}
107-
}
108-
)
109-
}()
110-
#endif

Core/Sources/Service/GUI/GraphicalUserInterfaceController.swift

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ import Preferences
1010
import SuggestionBasic
1111
import SuggestionWidget
1212

13-
#if canImport(ProChatTabs)
14-
import ProChatTabs
15-
#endif
16-
1713
#if canImport(ChatTabPersistent)
1814
import ChatTabPersistent
1915
#endif
@@ -188,7 +184,7 @@ struct GUI {
188184
)
189185
}
190186
}
191-
187+
192188
case let .sendCustomCommandToActiveChat(command):
193189
@Sendable func stopAndHandleCommand(_ tab: ChatGPTChatTab) async {
194190
if tab.service.isReceivingMessage {
@@ -298,7 +294,7 @@ public final class GraphicalUserInterfaceController {
298294
dependencies.suggestionWidgetUserDefaultsObservers = .init()
299295
dependencies.chatTabPool = chatTabPool
300296
dependencies.chatTabBuilderCollection = ChatTabFactory.chatTabBuilderCollection
301-
297+
302298
#if canImport(ChatTabPersistent) && canImport(ProChatTabs)
303299
dependencies.restoreChatTabInPool = {
304300
await chatTabPool.restore($0)
@@ -377,26 +373,28 @@ extension ChatTabPool {
377373
) async -> (any ChatTab, ChatTabInfo)? {
378374
let id = UUID().uuidString
379375
let info = ChatTabInfo(id: id, title: "")
380-
let builder = kind?.builder ?? ChatGPTChatTab.defaultBuilder()
376+
let builder = kind?.builder ?? {
377+
for ext in BuiltinExtensionManager.shared.extensions {
378+
guard let tab = ext.chatTabTypes.first(where: { $0.isDefaultChatTabReplacement } )
379+
else { continue }
380+
return tab.defaultChatBuilder()
381+
}
382+
return ChatGPTChatTab.defaultBuilder()
383+
}()
381384
guard let chatTap = await builder.build(store: createStore(id)) else { return nil }
382385
setTab(chatTap)
383386
return (chatTap, info)
384387
}
385388

386-
#if canImport(ChatTabPersistent) && canImport(ProChatTabs)
389+
#if canImport(ChatTabPersistent)
387390
@MainActor
388391
func restore(
389392
_ data: ChatTabPersistent.RestorableTabData
390393
) async -> (any ChatTab, ChatTabInfo)? {
391394
switch data.name {
392395
case ChatGPTChatTab.name:
393-
guard let builder = try? await ChatGPTChatTab.restore(from: data.data) else { break }
394-
return await createTab(id: data.id, from: builder)
395-
case BrowserChatTab.name:
396-
guard let builder = try? BrowserChatTab.restore(from: data.data) else { break }
397-
return await createTab(id: data.id, from: builder)
398-
case TerminalChatTab.name:
399-
guard let builder = try? await TerminalChatTab.restore(from: data.data) else { break }
396+
guard let builder = try? await ChatGPTChatTab.restore(from: data.data)
397+
else { fallthrough }
400398
return await createTab(id: data.id, from: builder)
401399
default:
402400
let chatTabTypes = BuiltinExtensionManager.shared.extensions.flatMap(\.chatTabTypes)

Core/Sources/Service/Service.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,21 @@ public final class Service {
5454

5555
realtimeSuggestionController = .init()
5656
scheduledCleaner = .init()
57-
let guiController = GraphicalUserInterfaceController()
58-
self.guiController = guiController
59-
globalShortcutManager = .init(guiController: guiController)
60-
keyBindingManager = .init()
6157

6258
#if canImport(ProService)
6359
proService = ProService()
6460
#endif
6561

66-
BuiltinExtensionManager.shared.setupExtensions([
62+
BuiltinExtensionManager.shared.addExtensions([
6763
GitHubCopilotExtension(workspacePool: workspacePool),
6864
CodeiumExtension(workspacePool: workspacePool),
6965
])
7066

67+
let guiController = GraphicalUserInterfaceController()
68+
self.guiController = guiController
69+
globalShortcutManager = .init(guiController: guiController)
70+
keyBindingManager = .init()
71+
7172
workspacePool.registerPlugin {
7273
SuggestionServiceWorkspacePlugin(workspace: $0) { SuggestionService.service() }
7374
}

Core/Sources/Service/SuggestionCommandHandler/PseudoCommandHandler.swift

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ActiveApplicationMonitor
22
import AppKit
3+
import BuiltinExtension
34
import CodeiumService
45
import CommandHandler
56
import enum CopilotForXcodeKit.SuggestionServiceError
@@ -218,12 +219,12 @@ struct PseudoCommandHandler: CommandHandler {
218219
if now.timeIntervalSince(last) > 60 * 60 {
219220
Self.lastTimeCommandFailedToTriggerWithAccessibilityAPI = now
220221
toast.toast(content: """
221-
The app is using a fallback solution to accept suggestions. \
222-
For better experience, please restart Xcode to re-activate the Copilot \
223-
menu item.
224-
""", type: .warning)
222+
The app is using a fallback solution to accept suggestions. \
223+
For better experience, please restart Xcode to re-activate the Copilot \
224+
menu item.
225+
""", type: .warning)
225226
}
226-
227+
227228
throw error
228229
}
229230
}
@@ -340,6 +341,24 @@ struct PseudoCommandHandler: CommandHandler {
340341
func openChat(forceDetach: Bool, activateThisApp: Bool = true) {
341342
switch UserDefaults.shared.value(for: \.openChatMode) {
342343
case .chatPanel:
344+
for ext in BuiltinExtensionManager.shared.extensions {
345+
guard let tab = ext.chatTabTypes.first(where: { $0.isDefaultChatTabReplacement })
346+
else { continue }
347+
Task { @MainActor in
348+
let store = Service.shared.guiController.store
349+
await store.send(
350+
.createAndSwitchToChatTabIfNeededMatching(
351+
check: { $0.name == tab.name },
352+
kind: .init(tab.defaultChatBuilder())
353+
)
354+
).finish()
355+
store.send(.openChatPanel(
356+
forceDetach: forceDetach,
357+
activateThisApp: activateThisApp
358+
))
359+
}
360+
return
361+
}
343362
Task { @MainActor in
344363
let store = Service.shared.guiController.store
345364
await store.send(.createAndSwitchToChatGPTChatTabIfNeeded).finish()
@@ -395,13 +414,36 @@ struct PseudoCommandHandler: CommandHandler {
395414
await openURL(url)
396415
}
397416
}
398-
case .codeiumChat:
417+
case let .builtinExtension(extensionIdentifier, tabName):
418+
guard let ext = BuiltinExtensionManager.shared.extensions
419+
.first(where: { $0.extensionIdentifier == extensionIdentifier }),
420+
let tab = ext.chatTabTypes.first(where: { $0.name == tabName })
421+
else { return }
422+
Task { @MainActor in
423+
let store = Service.shared.guiController.store
424+
await store.send(
425+
.createAndSwitchToChatTabIfNeededMatching(
426+
check: { $0.name == tabName },
427+
kind: .init(tab.defaultChatBuilder())
428+
)
429+
).finish()
430+
store.send(.openChatPanel(
431+
forceDetach: forceDetach,
432+
activateThisApp: activateThisApp
433+
))
434+
}
435+
case let .externalExtension(extensionIdentifier, tabName):
436+
guard let ext = BuiltinExtensionManager.shared.extensions
437+
.first(where: { $0.extensionIdentifier == "plus" }),
438+
let tab = ext.chatTabTypes
439+
.first(where: { $0.name == "\(extensionIdentifier).\(tabName)" })
440+
else { return }
399441
Task { @MainActor in
400442
let store = Service.shared.guiController.store
401443
await store.send(
402444
.createAndSwitchToChatTabIfNeededMatching(
403-
check: { $0 is CodeiumChatTab },
404-
kind: .init(CodeiumChatTab.defaultChatBuilder())
445+
check: { $0.name == "\(extensionIdentifier).\(tabName)" },
446+
kind: .init(tab.defaultChatBuilder())
405447
)
406448
).finish()
407449
store.send(.openChatPanel(

EditorExtension/AcceptSuggestionCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Client
2-
import SuggestionBasic
32
import Foundation
3+
import SuggestionBasic
44
import XcodeKit
55
import XPCShared
66

ExtensionService/ExtensionService.entitlements

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
<array>
77
<string>$(TeamIdentifierPrefix)group.$(BUNDLE_IDENTIFIER_BASE)</string>
88
</array>
9-
<key>com.apple.security.cs.disable-library-validation</key>
10-
<true/>
119
<key>keychain-access-groups</key>
1210
<array>
1311
<string>$(AppIdentifierPrefix)$(BUNDLE_IDENTIFIER_BASE).Shared</string>

0 commit comments

Comments
 (0)