Skip to content

Commit 192669e

Browse files
committed
Hide pro chat tabs from opensource code
1 parent abf479a commit 192669e

7 files changed

Lines changed: 128 additions & 104 deletions

File tree

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: 5 additions & 14 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)
@@ -383,20 +379,15 @@ extension ChatTabPool {
383379
return (chatTap, info)
384380
}
385381

386-
#if canImport(ChatTabPersistent) && canImport(ProChatTabs)
382+
#if canImport(ChatTabPersistent)
387383
@MainActor
388384
func restore(
389385
_ data: ChatTabPersistent.RestorableTabData
390386
) async -> (any ChatTab, ChatTabInfo)? {
391387
switch data.name {
392388
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 }
389+
guard let builder = try? await ChatGPTChatTab.restore(from: data.data)
390+
else { fallthrough }
400391
return await createTab(id: data.id, from: builder)
401392
default:
402393
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
}

Tool/Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ let package = Package(
5151
.library(name: "CustomAsyncAlgorithms", targets: ["CustomAsyncAlgorithms"]),
5252
.library(name: "CommandHandler", targets: ["CommandHandler"]),
5353
.library(name: "CodeDiff", targets: ["CodeDiff"]),
54+
.library(name: "BuiltinExtension", targets: ["BuiltinExtension"]),
5455
],
5556
dependencies: [
5657
// A fork of https://github.com/aespinilla/Tiktoken to allow loading from local files.

Tool/Sources/BuiltinExtension/BuiltinExtension.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Preferences
66

77
public protocol BuiltinExtension: CopilotForXcodeExtensionCapability {
88
/// An id that let the extension manager determine whether the extension is in use.
9-
var suggestionServiceId: BuiltInSuggestionFeatureProvider { get }
9+
var suggestionServiceId: BuiltInSuggestionFeatureProvider? { get }
1010
/// An identifier for the extension.
1111
var extensionIdentifier: String { get }
1212

@@ -21,6 +21,7 @@ public protocol BuiltinExtension: CopilotForXcodeExtensionCapability {
2121
// MARK: - Default Implementation
2222

2323
public extension BuiltinExtension {
24+
var suggestionServiceId: BuiltInSuggestionFeatureProvider? { nil }
2425
var chatTabTypes: [any ChatTab.Type] { [] }
2526
}
2627

Tool/Sources/BuiltinExtension/BuiltinExtensionManager.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ public final class BuiltinExtensionManager {
2222
checkAppConfiguration()
2323
}
2424

25+
public func addExtensions(_ extensions: [any BuiltinExtension]) {
26+
self.extensions.append(contentsOf: extensions)
27+
checkAppConfiguration()
28+
}
29+
2530
public func terminate() {
2631
for ext in extensions {
2732
ext.terminate()
@@ -33,8 +38,11 @@ extension BuiltinExtensionManager {
3338
func checkAppConfiguration() {
3439
let suggestionFeatureProvider = UserDefaults.shared.value(for: \.suggestionFeatureProvider)
3540
for ext in extensions {
36-
let isSuggestionFeatureInUse = suggestionFeatureProvider ==
37-
.builtIn(ext.suggestionServiceId)
41+
let isSuggestionFeatureInUse = if let id = ext.suggestionServiceId {
42+
suggestionFeatureProvider == .builtIn(id)
43+
} else {
44+
false
45+
}
3846
let isChatFeatureInUse = false
3947
ext.extensionUsageDidChange(.init(
4048
isSuggestionServiceInUse: isSuggestionFeatureInUse,

Tool/Sources/ChatTab/ChatTab.swift

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ public protocol ChatTabType {
4545
/// It will be called only once so long as you don't call it yourself.
4646
/// It will be called from MainActor.
4747
func start()
48+
/// Whenever the user close the tab, this method will be called.
49+
func close()
50+
51+
/// Whether this chat tab should be the default chat tab replacement.
52+
static var isDefaultChatTabReplacement: Bool { get }
53+
/// Whether this chat tab can handle open chat command.
54+
static var canHandleOpenChatCommand: Bool { get }
4855
}
4956

5057
/// The base class for all chat tabs.
@@ -67,10 +74,10 @@ open class BaseChatTab {
6774

6875
public init(store: StoreOf<ChatTabItem>) {
6976
chatTabStore = store
70-
self.id = store.id
71-
self.title = store.title
72-
77+
7378
Task { @MainActor in
79+
self.title = store.title
80+
self.id = store.id
7481
storeObserver.observe { [weak self] in
7582
guard let self else { return }
7683
self.title = store.title
@@ -166,6 +173,12 @@ public struct DisabledChatTabBuilder: ChatTabBuilder {
166173
public extension ChatTabType {
167174
/// The name of this chat tab type.
168175
var name: String { Self.name }
176+
177+
/// Default implementation that does nothing.
178+
func close() {}
179+
180+
static var canHandleOpenChatCommand: Bool { false }
181+
static var isDefaultChatTabReplacement: Bool { false }
169182
}
170183

171184
/// A chat tab that does nothing.

0 commit comments

Comments
 (0)