├── Core ├── README.md ├── Tests │ ├── SuggestionWidgetTests │ │ └── File.swift │ ├── GitHubCopilotServiceTests │ │ └── FileExtensionToLanguageIdentifierTests.swift │ ├── ChatServiceTests │ │ └── ParseScopesTests.swift │ ├── ServiceTests │ │ ├── PseudoCommandHandlerFileProcessingTests.swift │ │ ├── Environment.swift │ │ └── ExtractSelectedCodeTests.swift │ └── PromptToCodeServiceTests │ │ └── ExtractCodeFromChatGPTTests.swift ├── Sources │ ├── HostApp │ │ ├── AccountSettings │ │ │ ├── EmbeddingModel.swift │ │ │ ├── SharedModelManagement │ │ │ │ ├── BaseURLPicker.swift │ │ │ │ └── BaseURLSelection.swift │ │ │ ├── BingSearchView.swift │ │ │ └── APIKeyManagement │ │ │ │ ├── APIKeySubmission.swift │ │ │ │ ├── APIKeyPicker.swift │ │ │ │ └── APIKeySelection.swift │ │ ├── IsPreview.swift │ │ ├── LaunchAgentManager.swift │ │ ├── FeatureSettingsView.swift │ │ ├── CustomCommandSettings │ │ │ └── CustomCommand.swift │ │ └── ServiceView.swift │ ├── ChatContextCollectors │ │ ├── ActiveDocumentChatContextCollector │ │ │ ├── GetEditorInfo.swift │ │ │ ├── ReadableCursorRange.swift │ │ │ └── Functions │ │ │ │ ├── MoveToFocusedCodeFunction.swift │ │ │ │ ├── ExpandFocusRangeFunction.swift │ │ │ │ └── MoveToCodeAroundLineFunction.swift │ │ ├── SystemInfoChatContextCollector │ │ │ └── SystemInfoChatContextCollector.swift │ │ └── WebChatContextCollector │ │ │ └── WebChatContextCollector.swift │ ├── ChatService │ │ ├── AllPlugins.swift │ │ ├── ChatFunctionProvider.swift │ │ ├── AllContextCollector.swift │ │ ├── CustomCommandTemplateProcessor.swift │ │ └── ContextAwareAutoManagedChatGPTMemory.swift │ ├── SuggestionWidget │ │ ├── SuggestionWidgetDataSource.swift │ │ ├── FeatureReducers │ │ │ ├── SuggestionPanelFeature.swift │ │ │ └── SharedPanelFeature.swift │ │ ├── SuggestionPanelContent │ │ │ └── ErrorPanel.swift │ │ ├── TabView.swift │ │ └── Providers │ │ │ └── SuggestionProvider.swift │ ├── ChatPlugin │ │ ├── AskChatGPT.swift │ │ ├── ChatPlugin.swift │ │ ├── Translate.swift │ │ └── CallAIFunction.swift │ ├── GitHubCopilotService │ │ ├── GitHubCopilotAccountStatus.swift │ │ └── CustomStdioTransport.swift │ ├── UserDefaultsObserver │ │ └── UserDefaultsObserver.swift │ ├── ServiceUpdateMigration │ │ ├── ServiceUpdateMigrator.swift │ │ └── MigrateTo135.swift │ ├── UpdateChecker │ │ └── UpdateChecker.swift │ ├── FileChangeChecker │ │ └── FileChangeChecker.swift │ ├── PlusFeatureFlag │ │ └── PlusFeatureFlag.swift │ ├── Service │ │ ├── WorkspaceExtension │ │ │ └── Workspace+Cleanup.swift │ │ ├── SuggestionCommandHandler │ │ │ └── SuggestionCommandHandler.swift │ │ ├── SuggestionPresenter │ │ │ └── PresentInWindowSuggestionPresenter.swift │ │ └── Service.swift │ ├── PromptToCodeService │ │ ├── PreviewPromptToCodeService.swift │ │ └── PromptToCodeServiceType.swift │ ├── Client │ │ └── XPCService.swift │ ├── CodeiumService │ │ ├── OpendDocumentPool.swift │ │ └── CodeiumAuthService.swift │ ├── XPCShared │ │ └── XPCServiceProtocol.swift │ └── ChatPlugins │ │ └── MathChatPlugin │ │ └── MathChatPlugin.swift ├── .gitignore └── .swiftpm │ └── xcode │ └── xcshareddata │ └── xcschemes │ └── Core-Package.xcscheme ├── Tool ├── README.md ├── Sources │ ├── Preferences │ │ └── Types │ │ │ ├── ChatFeatureProvider.swift │ │ │ ├── EmbeddingFeatureProvider.swift │ │ │ ├── NodeRunner.swift │ │ │ ├── PresentationMode.swift │ │ │ ├── WidgetColorScheme.swift │ │ │ ├── SuggestionWidgetPositionMode.swift │ │ │ ├── SuggestionFeatureProvider.swift │ │ │ ├── PromptToCodeFeatureProvider.swift │ │ │ ├── OpenAIEmbeddingModel.swift │ │ │ ├── Locale.swift │ │ │ ├── ChatGPTModel.swift │ │ │ └── CustomCommand.swift │ ├── LangChain │ │ ├── DocumentTransformer │ │ │ └── DocumentTransformer.swift │ │ ├── Embedding │ │ │ └── Embedding.swift │ │ ├── ChatModel │ │ │ ├── ChatModel.swift │ │ │ └── OpenAIChat.swift │ │ ├── VectorStore │ │ │ └── VectorStore.swift │ │ ├── DocumentLoader │ │ │ ├── DocumentLoader.swift │ │ │ └── TextLoader.swift │ │ ├── Chains │ │ │ ├── LLMChain.swift │ │ │ └── CombineAnswersChain.swift │ │ └── Callback.swift │ ├── TokenEncoder │ │ ├── Tokenizer.swift │ │ └── TiktokenCl100kBaseTokenEncoder.swift │ ├── ObjectiveCExceptionHandling │ │ ├── include │ │ │ └── ObjectiveCExceptionHandling.h │ │ └── ObjectiveCExceptionHandling.m │ ├── OpenAIService │ │ ├── Memory │ │ │ ├── EmptyChatGPTMemory.swift │ │ │ └── ConversationChatGPTMemory.swift │ │ ├── FucntionCall │ │ │ └── ChatGPTFuntionProvider.swift │ │ ├── Configuration │ │ │ ├── EmbeddingConfiguration.swift │ │ │ └── ChatGPTConfiguration.swift │ │ └── Models.swift │ ├── SharedUIComponents │ │ ├── DynamicHeightTextInFormWorkaround.swift │ │ ├── CopyButton.swift │ │ └── CustomScrollView.swift │ ├── Configs │ │ └── Configurations.swift │ ├── XcodeInspector │ │ └── Helpers.swift │ ├── ChatContextCollector │ │ └── ChatContextCollector.swift │ ├── SuggestionModel │ │ ├── CodeSuggestion.swift │ │ ├── Modification.swift │ │ └── ExportedFromLSP.swift │ ├── Workspace │ │ ├── FileSaveWatcher.swift │ │ └── OpenedFileRocoverableStorage.swift │ ├── UserDefaultsObserver │ │ └── UserDefaultsObserver.swift │ ├── ChatTab │ │ ├── ChatTabItem.swift │ │ └── ChatTabPool.swift │ ├── Toast │ │ └── Toast.swift │ ├── ASTParser │ │ ├── TreeCursor.swift │ │ └── DumpSyntaxTree.swift │ └── Logger │ │ └── Logger.swift ├── .gitignore ├── Tests │ ├── KeychainTests │ │ └── KeychainTests.swift │ ├── SuggestionModelTests │ │ ├── LineAnnotationParsingTests.swift │ │ └── ModificationTests.swift │ ├── TokenEncoderTests │ │ └── TiktokenCl100kBaseTokenEncoderTests.swift │ ├── OpenAIServiceTests │ │ └── ChatGPTServiceFieldTests.swift │ └── LangChainTests │ │ ├── VectorStoreTests │ │ └── TemporaryUSearchTests.swift │ │ └── TextSplitterTests │ │ └── TextSplitterTests.swift └── .swiftpm │ └── xcode │ └── xcshareddata │ └── xcschemes │ └── Tool-Package.xcscheme ├── Version.xcconfig ├── AppIcon.png ├── Screenshot.png ├── .github ├── FUNDING.yml ├── workflows │ └── close_inactive_issues.yml └── ISSUE_TEMPLATE │ ├── feature_reqeust.yaml │ ├── help_wanted.yml │ └── bug_report.yaml ├── .gitmodules ├── accessibility_api_permission.png ├── ExtensionService ├── Assets.xcassets │ ├── Contents.json │ ├── AppIcon.appiconset │ │ ├── 64.png │ │ ├── 16x16.png │ │ ├── 32x32.png │ │ ├── 1024x1024.png │ │ ├── 128x128.png │ │ ├── 256x256 1.png │ │ ├── 256x256.png │ │ ├── 32x32 1.png │ │ ├── 512x512 1.png │ │ ├── 512x512.png │ │ └── Contents.json │ ├── MenuBarIcon.imageset │ │ ├── 16.png │ │ ├── 32.png │ │ ├── 48.png │ │ └── Contents.json │ └── AccentColor.colorset │ │ └── Contents.json ├── ServiceDelegate.swift ├── ExtensionService.entitlements └── Info.plist ├── Copilot for Xcode ├── Assets.xcassets │ ├── Contents.json │ ├── AppIcon.appiconset │ │ ├── 64.png │ │ ├── 16x16.png │ │ ├── 32x32.png │ │ ├── 128x128.png │ │ ├── 256x256.png │ │ ├── 32x32 1.png │ │ ├── 512x512.png │ │ ├── 1024x1024.png │ │ ├── 256x256 1.png │ │ ├── 512x512 1.png │ │ ├── 1024 x 1024 your icon.png │ │ ├── 1024 x 1024 your icon@16w.png │ │ ├── 1024 x 1024 your icon@32w.png │ │ ├── 1024 x 1024 your icon@64w.png │ │ ├── 1024 x 1024 your icon@128w.png │ │ ├── 1024 x 1024 your icon@256w 1.png │ │ ├── 1024 x 1024 your icon@256w.png │ │ ├── 1024 x 1024 your icon@32w 1.png │ │ ├── 1024 x 1024 your icon@512w 1.png │ │ ├── 1024 x 1024 your icon@512w.png │ │ └── Contents.json │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── BackgroundColor.colorset │ │ └── Contents.json │ ├── BackgroundColorTop.colorset │ │ └── Contents.json │ ├── ButtonBackgroundColorDefault.colorset │ │ └── Contents.json │ └── ButtonBackgroundColorPressed.colorset │ │ └── Contents.json ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Copilot_for_Xcode.entitlements └── App.swift ├── VERSIONS ├── Playground.playground ├── Pages │ ├── WebScrapper.xcplaygroundpage │ │ ├── timeline.xctimeline │ │ └── Contents.swift │ └── RetrievalQAChain.xcplaygroundpage │ │ └── timeline.xctimeline └── contents.xcplayground ├── Copilot for Xcode.xcodeproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── Copilot for Xcode.xcworkspace ├── xcshareddata │ └── IDEWorkspaceChecks.plist └── contents.xcworkspacedata ├── Helper ├── main.swift └── ReloadLaunchAgent.swift ├── Config.debug.xcconfig ├── Makefile ├── EditorExtension ├── SeparatorCommand.swift ├── PromptToCodeCommand.swift ├── ChatWithSelection.swift ├── EditorExtension.entitlements ├── GetSuggestionsCommand.swift ├── NextSuggestionCommand.swift ├── PrefetchSuggestionsCommand.swift ├── RejectSuggestionCommand.swift ├── PreviousSuggestionCommand.swift ├── RealtimeSuggestionCommand.swift ├── CustomCommand.swift ├── ToggleRealtimeSuggestionsCommand.swift ├── AcceptPromptToCodeCommand.swift ├── Info.plist └── AcceptSuggestionCommand.swift ├── Config.xcconfig ├── launchAgent.plist ├── Copilot-for-Xcode-Info.plist └── .swiftformat /Core/README.md: -------------------------------------------------------------------------------- 1 | # Core 2 | 3 | A description of this package. 4 | -------------------------------------------------------------------------------- /Core/Tests/SuggestionWidgetTests/File.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | -------------------------------------------------------------------------------- /Tool/README.md: -------------------------------------------------------------------------------- 1 | # Tool 2 | 3 | A description of this package. 4 | -------------------------------------------------------------------------------- /Version.xcconfig: -------------------------------------------------------------------------------- 1 | APP_VERSION = 0.24.1 2 | APP_BUILD = 251 3 | 4 | -------------------------------------------------------------------------------- /AppIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/AppIcon.png -------------------------------------------------------------------------------- /Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Screenshot.png -------------------------------------------------------------------------------- /Core/Sources/HostApp/AccountSettings/EmbeddingModel.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import Keychain 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://intii.lemonsqueezy.com", "https://www.buymeacoffee.com/intitni"] 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Pro"] 2 | path = Pro 3 | url = git@github.com:intitni/CopilotForXcodePro.git 4 | -------------------------------------------------------------------------------- /accessibility_api_permission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/accessibility_api_permission.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Copilot for Xcode/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Core/Sources/HostApp/IsPreview.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | var isPreview: Bool { ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" } 4 | -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/ExtensionService/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/MenuBarIcon.imageset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/ExtensionService/Assets.xcassets/MenuBarIcon.imageset/16.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/MenuBarIcon.imageset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/ExtensionService/Assets.xcassets/MenuBarIcon.imageset/32.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/MenuBarIcon.imageset/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/ExtensionService/Assets.xcassets/MenuBarIcon.imageset/48.png -------------------------------------------------------------------------------- /Tool/Sources/Preferences/Types/ChatFeatureProvider.swift: -------------------------------------------------------------------------------- 1 | public enum ChatFeatureProvider: String, CaseIterable, Codable { 2 | case openAI 3 | case azureOpenAI 4 | } 5 | -------------------------------------------------------------------------------- /Tool/Sources/Preferences/Types/EmbeddingFeatureProvider.swift: -------------------------------------------------------------------------------- 1 | public enum EmbeddingFeatureProvider: String, CaseIterable { 2 | case openAI 3 | case azureOpenAI 4 | } 5 | -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/16x16.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/32x32.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/AppIcon.appiconset/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/ExtensionService/Assets.xcassets/AppIcon.appiconset/16x16.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/AppIcon.appiconset/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/ExtensionService/Assets.xcassets/AppIcon.appiconset/32x32.png -------------------------------------------------------------------------------- /Tool/Sources/Preferences/Types/NodeRunner.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum NodeRunner: Int, CaseIterable { 4 | case env 5 | case bash 6 | case shell 7 | } 8 | -------------------------------------------------------------------------------- /Tool/Sources/Preferences/Types/PresentationMode.swift: -------------------------------------------------------------------------------- 1 | public enum PresentationMode: Int, CaseIterable { 2 | case nearbyTextCursor = 0 3 | case floatingWidget = 1 4 | } 5 | -------------------------------------------------------------------------------- /VERSIONS: -------------------------------------------------------------------------------- 1 | Python version: 3.11.0 2 | Build: b1 3 | Min macOS version: 10.15 4 | --------------------- 5 | libFFI: macOS native 6 | BZip2: 1.0.8 7 | OpenSSL: 3.0.5 8 | XZ: 5.2.6 9 | -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/128x128.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/256x256.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/32x32 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/32x32 1.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/512x512.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/AppIcon.appiconset/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/ExtensionService/Assets.xcassets/AppIcon.appiconset/1024x1024.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/AppIcon.appiconset/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/ExtensionService/Assets.xcassets/AppIcon.appiconset/128x128.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/AppIcon.appiconset/256x256 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/ExtensionService/Assets.xcassets/AppIcon.appiconset/256x256 1.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/AppIcon.appiconset/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/ExtensionService/Assets.xcassets/AppIcon.appiconset/256x256.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/AppIcon.appiconset/32x32 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/ExtensionService/Assets.xcassets/AppIcon.appiconset/32x32 1.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/AppIcon.appiconset/512x512 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/ExtensionService/Assets.xcassets/AppIcon.appiconset/512x512 1.png -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/AppIcon.appiconset/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/ExtensionService/Assets.xcassets/AppIcon.appiconset/512x512.png -------------------------------------------------------------------------------- /Tool/Sources/Preferences/Types/WidgetColorScheme.swift: -------------------------------------------------------------------------------- 1 | public enum WidgetColorScheme: Int, CaseIterable { 2 | case system = 0 3 | case light = 1 4 | case dark = 2 5 | } 6 | -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024x1024.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/256x256 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/256x256 1.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/512x512 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/512x512 1.png -------------------------------------------------------------------------------- /Tool/Sources/Preferences/Types/SuggestionWidgetPositionMode.swift: -------------------------------------------------------------------------------- 1 | public enum SuggestionWidgetPositionMode: Int, CaseIterable { 2 | case fixedToBottom = 0 3 | case alignToTextCursor = 1 4 | } 5 | -------------------------------------------------------------------------------- /Tool/Sources/Preferences/Types/SuggestionFeatureProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum SuggestionFeatureProvider: Int, CaseIterable { 4 | case gitHubCopilot 5 | case codeium 6 | } 7 | -------------------------------------------------------------------------------- /Tool/Sources/Preferences/Types/PromptToCodeFeatureProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum PromptToCodeFeatureProvider: Int, CaseIterable { 4 | case openAI 5 | case githubCopilot 6 | } 7 | -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@16w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@16w.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@32w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@32w.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@64w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@64w.png -------------------------------------------------------------------------------- /Playground.playground/Pages/WebScrapper.xcplaygroundpage/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@128w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@128w.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@256w 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@256w 1.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@256w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@256w.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@32w 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@32w 1.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@512w 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@512w 1.png -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@512w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basuke/CodeiumForXcode/main/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/1024 x 1024 your icon@512w.png -------------------------------------------------------------------------------- /Core/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/config/registries.json 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | .netrc 10 | -------------------------------------------------------------------------------- /Playground.playground/Pages/RetrievalQAChain.xcplaygroundpage/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Tool/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/config/registries.json 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | .netrc 10 | -------------------------------------------------------------------------------- /Tool/Sources/LangChain/DocumentTransformer/DocumentTransformer.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol DocumentTransformer { 4 | func transformDocuments(_ documents: [Document]) async throws -> [Document] 5 | } 6 | -------------------------------------------------------------------------------- /Copilot for Xcode.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Core/Sources/ChatContextCollectors/ActiveDocumentChatContextCollector/GetEditorInfo.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SuggestionModel 3 | import XcodeInspector 4 | 5 | func getEditorInformation() -> EditorInformation? { 6 | return XcodeInspector.shared.focusedEditorContent 7 | } 8 | 9 | -------------------------------------------------------------------------------- /Tool/Sources/TokenEncoder/Tokenizer.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol TokenEncoder { 4 | func encode(text: String) -> [Int] 5 | } 6 | 7 | public extension TokenEncoder { 8 | func countToken(text: String) -> Int { 9 | encode(text: text).count 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Tool/Sources/ObjectiveCExceptionHandling/include/ObjectiveCExceptionHandling.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface ObjcExceptionHandler : NSObject 4 | 5 | + (BOOL)catchException:(void (^)(void))tryBlock 6 | error:(__autoreleasing NSError **)error; 7 | 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /Copilot for Xcode.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Playground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Copilot for Xcode.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Copilot for Xcode.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Core/Sources/ChatContextCollectors/ActiveDocumentChatContextCollector/ReadableCursorRange.swift: -------------------------------------------------------------------------------- 1 | import SuggestionModel 2 | 3 | extension CursorPosition { 4 | var text: String { 5 | "[\(line), \(character)]" 6 | } 7 | } 8 | 9 | extension CursorRange { 10 | var text: String { 11 | "\(start.description) - \(end.description)" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Helper/main.swift: -------------------------------------------------------------------------------- 1 | import ArgumentParser 2 | import Foundation 3 | 4 | struct Helper: ParsableCommand { 5 | static var configuration = CommandConfiguration( 6 | commandName: "helper", 7 | abstract: "Helper CLI for Codeium for Xcode", 8 | subcommands: [ 9 | ReloadLaunchAgent.self, 10 | ] 11 | ) 12 | } 13 | 14 | Helper.main() 15 | -------------------------------------------------------------------------------- /Tool/Sources/Preferences/Types/OpenAIEmbeddingModel.swift: -------------------------------------------------------------------------------- 1 | public enum OpenAIEmbeddingModel: String, CaseIterable { 2 | case textEmbeddingAda002 = "text-embedding-ada-002" 3 | } 4 | 5 | public extension OpenAIEmbeddingModel { 6 | var maxToken: Int { 7 | switch self { 8 | case .textEmbeddingAda002: 9 | return 8191 10 | } 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /Core/Sources/ChatService/AllPlugins.swift: -------------------------------------------------------------------------------- 1 | import ChatPlugin 2 | import MathChatPlugin 3 | import SearchChatPlugin 4 | import ShortcutChatPlugin 5 | 6 | let allPlugins: [ChatPlugin.Type] = [ 7 | TerminalChatPlugin.self, 8 | AITerminalChatPlugin.self, 9 | MathChatPlugin.self, 10 | SearchChatPlugin.self, 11 | ShortcutChatPlugin.self, 12 | ShortcutInputChatPlugin.self, 13 | ] 14 | 15 | -------------------------------------------------------------------------------- /Tool/Sources/LangChain/Embedding/Embedding.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol Embeddings { 4 | /// Embed search docs. 5 | func embed(documents: [Document]) async throws -> [EmbeddedDocument] 6 | /// Embed query text. 7 | func embed(query: String) async throws -> [Float] 8 | } 9 | 10 | public struct EmbeddedDocument: Codable { 11 | var document: Document 12 | var embeddings: [Float] 13 | } 14 | -------------------------------------------------------------------------------- /Tool/Sources/OpenAIService/Memory/EmptyChatGPTMemory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public actor EmptyChatGPTMemory: ChatGPTMemory { 4 | public var messages: [ChatMessage] = [] 5 | public var remainingTokens: Int? { nil } 6 | 7 | public init() {} 8 | 9 | public func mutateHistory(_ update: (inout [ChatMessage]) -> Void) { 10 | update(&messages) 11 | } 12 | 13 | public func refresh() async {} 14 | } 15 | 16 | -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/BackgroundColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "40", 9 | "green" : "23", 10 | "red" : "25" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/BackgroundColorTop.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "54", 9 | "green" : "25", 10 | "red" : "30" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/ButtonBackgroundColorDefault.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x46", 9 | "green" : "0x24", 10 | "red" : "0x2C" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/ButtonBackgroundColorPressed.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.275", 9 | "green" : "0.141", 10 | "red" : "0.290" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tool/Tests/KeychainTests/KeychainTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | 4 | @testable import Keychain 5 | 6 | class KeychainTests: XCTestCase { 7 | func test_scope_key() { 8 | let keychain = Keychain(scope: "scope") 9 | XCTAssertEqual(keychain.scopeKey("key"), "scope::key") 10 | } 11 | 12 | func test_escape_scope() { 13 | let keychain = Keychain(scope: "scope") 14 | XCTAssertEqual(keychain.escapeScope("scope::key"), "key") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tool/Sources/Preferences/Types/Locale.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public extension Locale { 4 | static var availableLocalizedLocales: [String] { 5 | let localizedLocales = Locale.isoLanguageCodes.compactMap { 6 | Locale(identifier: "en-US").localizedString(forLanguageCode: $0) 7 | } 8 | .sorted() 9 | return localizedLocales 10 | } 11 | 12 | var languageName: String { 13 | localizedString(forLanguageCode: languageCode ?? "") ?? "" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Config.debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Version.xcconfig" 2 | SLASH = / 3 | 4 | HOST_APP_NAME = Codeium for Xcode Dev 5 | BUNDLE_IDENTIFIER_BASE = dev.com.intii.CodeiumForXcode 6 | SPARKLE_FEED_URL = http:$(SLASH)$(SLASH)127.0.0.1:9433/appcast.xml 7 | SPARKLE_PUBLIC_KEY = WDzm5GHnc6c8kjeJEgX5GuGiPpW6Lc/ovGjLnrrZvPY= 8 | APPLICATION_SUPPORT_FOLDER = dev.com.intii.CodeiumForXcode 9 | EXTENSION_BUNDLE_NAME = Codeium Dev 10 | EXTENSION_BUNDLE_DISPLAY_NAME = Codeium Dev 11 | EXTENSION_SERVICE_NAME = CodeiumForXcodeExtensionService 12 | 13 | // see also target Configs 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | setup: 2 | echo "Setup." 3 | 4 | setup-langchain: 5 | echo "Don't setup LangChain!" 6 | cd Python; \ 7 | curl -L https://github.com/beeware/Python-Apple-support/releases/download/3.11-b1/Python-3.11-macOS-support.b1.tar.gz -o Python-3.11-macOS-support.b1.tar.gz; \ 8 | tar -xzvf Python-3.11-macOS-support.b1.tar.gz; \ 9 | rm Python-3.11-macOS-support.b1.tar.gz; \ 10 | cp module.modulemap.copy Python.xcframework/macos-arm64_x86_64/Headers/module.modulemap 11 | cd Python/site-packages; \ 12 | sh ./install.sh 13 | 14 | .PHONY: setup setup-langchain 15 | -------------------------------------------------------------------------------- /Tool/Sources/SharedUIComponents/DynamicHeightTextInFormWorkaround.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct DynamicHeightTextInFormWorkaroundModifier: ViewModifier { 4 | func body(content: Content) -> some View { 5 | HStack(spacing: 0) { 6 | content 7 | Spacer() 8 | } 9 | .fixedSize(horizontal: false, vertical: true) 10 | } 11 | } 12 | 13 | public extension View { 14 | func dynamicHeightTextInFormWorkaround() -> some View { 15 | modifier(DynamicHeightTextInFormWorkaroundModifier()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /EditorExtension/SeparatorCommand.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import SuggestionModel 3 | import Foundation 4 | import XcodeKit 5 | 6 | class SeparatorCommand: NSObject, XCSourceEditorCommand, CommandType { 7 | var name: String = "" 8 | 9 | func perform( 10 | with invocation: XCSourceEditorCommandInvocation, 11 | completionHandler: @escaping (Error?) -> Void 12 | ) { 13 | completionHandler(nil) 14 | } 15 | 16 | func named(_ name: String) -> Self { 17 | self.name = name 18 | return self 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Config.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Version.xcconfig" 2 | SLASH = / 3 | 4 | HOST_APP_NAME = Codeium for Xcode 5 | BUNDLE_IDENTIFIER_BASE = com.intii.CodeiumForXcode 6 | SPARKLE_FEED_URL = https:$(SLASH)$(SLASH)raw.githubusercontent.com/intitni/CodeiumForXcode/main/appcast.xml 7 | SPARKLE_PUBLIC_KEY = WDzm5GHnc6c8kjeJEgX5GuGiPpW6Lc/ovGjLnrrZvPY= 8 | APPLICATION_SUPPORT_FOLDER = com.intii.CodeiumForXcode 9 | EXTENSION_BUNDLE_NAME = Codeium 10 | EXTENSION_BUNDLE_DISPLAY_NAME = Codeium 11 | EXTENSION_SERVICE_NAME = CodeiumForXcodeExtensionService 12 | 13 | // see also target Configs 14 | -------------------------------------------------------------------------------- /Tool/Tests/SuggestionModelTests/LineAnnotationParsingTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | 4 | @testable import SuggestionModel 5 | 6 | class LineAnnotationParsingTests: XCTestCase { 7 | func test_parse_line_annotation() { 8 | let annotation = "Error Line 25: FileName.swift:25 Cannot convert Type" 9 | let parsed = EditorInformation.parseLineAnnotation(annotation) 10 | XCTAssertEqual(parsed.type, "Error") 11 | XCTAssertEqual(parsed.line, 25) 12 | XCTAssertEqual(parsed.message, "Cannot convert Type") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tool/Sources/Configs/Configurations.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public var userDefaultSuiteName: String { 4 | "5YKZ4Y3DAW.group.com.intii.CodeiumForXcode" 5 | } 6 | 7 | public var keychainAccessGroup: String { 8 | #if DEBUG 9 | return "5YKZ4Y3DAW.dev.com.intii.CodeiumForXcode.Shared" 10 | #else 11 | return "5YKZ4Y3DAW.com.intii.CodeiumForXcode.Shared" 12 | #endif 13 | } 14 | 15 | public var keychainService: String { 16 | #if DEBUG 17 | return "dev.com.intii.CodeiumForXcode" 18 | #else 19 | return "com.intii.CodeiumForXcode" 20 | #endif 21 | } 22 | 23 | -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/MenuBarIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "16.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "32.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "48.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "template" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Core/Sources/ChatService/ChatFunctionProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import OpenAIService 3 | 4 | final class ChatFunctionProvider { 5 | var functions: [any ChatGPTFunction] = [] 6 | 7 | init() {} 8 | 9 | func removeAll() { 10 | functions = [] 11 | } 12 | 13 | func append(functions others: [any ChatGPTFunction]) { 14 | functions.append(contentsOf: others) 15 | } 16 | } 17 | 18 | extension ChatFunctionProvider: ChatGPTFunctionProvider { 19 | var functionCallStrategy: OpenAIService.FunctionCallStrategy? { 20 | nil 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Tool/Sources/XcodeInspector/Helpers.swift: -------------------------------------------------------------------------------- 1 | import AppKit 2 | import Foundation 3 | 4 | extension NSRunningApplication { 5 | var isXcode: Bool { bundleIdentifier == "com.apple.dt.Xcode" } 6 | var isCopilotForXcodeExtensionService: Bool { 7 | bundleIdentifier == Bundle.main.bundleIdentifier 8 | } 9 | } 10 | 11 | extension FileManager { 12 | func fileIsDirectory(atPath path: String) -> Bool { 13 | var isDirectory: ObjCBool = false 14 | let exists = fileExists(atPath: path, isDirectory: &isDirectory) 15 | return isDirectory.boolValue && exists 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /ExtensionService/ServiceDelegate.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Service 3 | import XPCShared 4 | 5 | class ServiceDelegate: NSObject, NSXPCListenerDelegate { 6 | func listener( 7 | _: NSXPCListener, 8 | shouldAcceptNewConnection newConnection: NSXPCConnection 9 | ) -> Bool { 10 | newConnection.exportedInterface = NSXPCInterface( 11 | with: XPCServiceProtocol.self 12 | ) 13 | 14 | let exportedObject = XPCService() 15 | newConnection.exportedObject = exportedObject 16 | newConnection.resume() 17 | return true 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Tool/Sources/ObjectiveCExceptionHandling/ObjectiveCExceptionHandling.m: -------------------------------------------------------------------------------- 1 | #import "include/ObjectiveCExceptionHandling.h" 2 | 3 | @implementation ObjcExceptionHandler 4 | 5 | + (BOOL)catchException:(void (^)(void))tryBlock 6 | error:(__autoreleasing NSError **)error { 7 | @try { 8 | tryBlock(); 9 | return YES; 10 | } @catch (NSException *exception) { 11 | *error = [[NSError alloc] initWithDomain:exception.name 12 | code:0 13 | userInfo:exception.userInfo]; 14 | return NO; 15 | } 16 | } 17 | 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /Tool/Sources/OpenAIService/Memory/ConversationChatGPTMemory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public actor ConversationChatGPTMemory: ChatGPTMemory { 4 | public var messages: [ChatMessage] = [] 5 | public var remainingTokens: Int? { nil } 6 | 7 | public init(systemPrompt: String, systemMessageId: String = UUID().uuidString) { 8 | messages.append(.init(id: systemMessageId, role: .system, content: systemPrompt)) 9 | } 10 | 11 | public func mutateHistory(_ update: (inout [ChatMessage]) -> Void) { 12 | update(&messages) 13 | } 14 | 15 | public func refresh() async {} 16 | } 17 | 18 | -------------------------------------------------------------------------------- /launchAgent.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.intii.CodeiumForXcode.ExtensionService 7 | Program 8 | /Applications/Codeium for Xcode.app/Contents/Applications/CodeiumForXcodeExtensionService.app/Contents/MacOS/CodeiumForXcodeExtensionService 9 | MachServices 10 | 11 | com.intii.CodeiumForXcode.ExtensionService 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /EditorExtension/PromptToCodeCommand.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import SuggestionModel 3 | import Foundation 4 | import XcodeKit 5 | 6 | class PromptToCodeCommand: NSObject, XCSourceEditorCommand, CommandType { 7 | var name: String { "Prompt to Code" } 8 | 9 | func perform( 10 | with invocation: XCSourceEditorCommandInvocation, 11 | completionHandler: @escaping (Error?) -> Void 12 | ) { 13 | completionHandler(nil) 14 | Task { 15 | let service = try getService() 16 | _ = try await service.promptToCode(editorContent: .init(invocation)) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /EditorExtension/ChatWithSelection.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import SuggestionModel 3 | import Foundation 4 | import XcodeKit 5 | 6 | class ChatWithSelectionCommand: NSObject, XCSourceEditorCommand, CommandType { 7 | var name: String { "Open Chat" } 8 | 9 | func perform( 10 | with invocation: XCSourceEditorCommandInvocation, 11 | completionHandler: @escaping (Error?) -> Void 12 | ) { 13 | completionHandler(nil) 14 | Task { 15 | let service = try getService() 16 | _ = try await service.chatWithSelection(editorContent: .init(invocation)) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ExtensionService/ExtensionService.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | 7 | $(TeamIdentifierPrefix)group.$(BUNDLE_IDENTIFIER_BASE) 8 | 9 | com.apple.security.cs.disable-library-validation 10 | 11 | keychain-access-groups 12 | 13 | $(AppIdentifierPrefix)$(BUNDLE_IDENTIFIER_BASE).Shared 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /EditorExtension/EditorExtension.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.application-groups 8 | 9 | $(TeamIdentifierPrefix)group.$(BUNDLE_IDENTIFIER_BASE) 10 | 11 | com.apple.security.temporary-exception.mach-lookup.global-name 12 | 13 | $(BUNDLE_IDENTIFIER_BASE).ExtensionService 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /EditorExtension/GetSuggestionsCommand.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import Foundation 3 | import SuggestionModel 4 | import XcodeKit 5 | 6 | class GetSuggestionsCommand: NSObject, XCSourceEditorCommand, CommandType { 7 | var name: String { "Get Suggestions" } 8 | 9 | func perform( 10 | with invocation: XCSourceEditorCommandInvocation, 11 | completionHandler: @escaping (Error?) -> Void 12 | ) { 13 | completionHandler(nil) 14 | Task { 15 | let service = try getService() 16 | _ = try await service.getSuggestedCode(editorContent: .init(invocation)) 17 | } 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /EditorExtension/NextSuggestionCommand.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import Foundation 3 | import SuggestionModel 4 | import XcodeKit 5 | 6 | class NextSuggestionCommand: NSObject, XCSourceEditorCommand, CommandType { 7 | var name: String { "Next Suggestion" } 8 | 9 | func perform( 10 | with invocation: XCSourceEditorCommandInvocation, 11 | completionHandler: @escaping (Error?) -> Void 12 | ) { 13 | completionHandler(nil) 14 | Task { 15 | let service = try getService() 16 | _ = try await service.getNextSuggestedCode(editorContent: .init(invocation)) 17 | } 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Tool/Sources/LangChain/ChatModel/ChatModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import OpenAIService 3 | 4 | public protocol ChatModel { 5 | func generate( 6 | prompt: [ChatMessage], 7 | stops: [String], 8 | callbackManagers: [CallbackManager] 9 | ) async throws -> ChatMessage 10 | } 11 | 12 | public typealias ChatMessage = OpenAIService.ChatMessage 13 | 14 | public extension CallbackEvents { 15 | struct LLMDidProduceNewToken: CallbackEvent { 16 | public let info: String 17 | } 18 | 19 | var llmDidProduceNewToken: LLMDidProduceNewToken.Type { 20 | LLMDidProduceNewToken.self 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /EditorExtension/PrefetchSuggestionsCommand.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import SuggestionModel 3 | import Foundation 4 | import XcodeKit 5 | 6 | class PrefetchSuggestionsCommand: NSObject, XCSourceEditorCommand, CommandType { 7 | var name: String { "Prefetch Suggestions" } 8 | 9 | func perform( 10 | with invocation: XCSourceEditorCommandInvocation, 11 | completionHandler: @escaping (Error?) -> Void 12 | ) { 13 | completionHandler(nil) 14 | Task { 15 | let service = try getService() 16 | await service.prefetchRealtimeSuggestions(editorContent: .init(invocation)) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /EditorExtension/RejectSuggestionCommand.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import Foundation 3 | import SuggestionModel 4 | import XcodeKit 5 | 6 | class RejectSuggestionCommand: NSObject, XCSourceEditorCommand, CommandType { 7 | var name: String { "Reject Suggestion" } 8 | 9 | func perform( 10 | with invocation: XCSourceEditorCommandInvocation, 11 | completionHandler: @escaping (Error?) -> Void 12 | ) { 13 | completionHandler(nil) 14 | Task { 15 | let service = try getService() 16 | _ = try await service.getSuggestionRejectedCode(editorContent: .init(invocation)) 17 | } 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /EditorExtension/PreviousSuggestionCommand.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import Foundation 3 | import SuggestionModel 4 | import XcodeKit 5 | 6 | class PreviousSuggestionCommand: NSObject, XCSourceEditorCommand, CommandType { 7 | var name: String { "Previous Suggestion" } 8 | 9 | func perform( 10 | with invocation: XCSourceEditorCommandInvocation, 11 | completionHandler: @escaping (Error?) -> Void 12 | ) { 13 | completionHandler(nil) 14 | Task { 15 | let service = try getService() 16 | _ = try await service.getPreviousSuggestedCode(editorContent: .init(invocation)) 17 | } 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /EditorExtension/RealtimeSuggestionCommand.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import SuggestionModel 3 | import Foundation 4 | import XcodeKit 5 | 6 | class RealtimeSuggestionsCommand: NSObject, XCSourceEditorCommand, CommandType { 7 | var name: String { "Real-time Suggestions" } 8 | 9 | func perform( 10 | with invocation: XCSourceEditorCommandInvocation, 11 | completionHandler: @escaping (Error?) -> Void 12 | ) { 13 | completionHandler(nil) 14 | Task { 15 | let service = try getService() 16 | _ = try await service.getRealtimeSuggestedCode(editorContent: .init(invocation)) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tool/Sources/ChatContextCollector/ChatContextCollector.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import OpenAIService 3 | 4 | public struct ChatContext { 5 | public var systemPrompt: String 6 | public var functions: [any ChatGPTFunction] 7 | public init(systemPrompt: String, functions: [any ChatGPTFunction]) { 8 | self.systemPrompt = systemPrompt 9 | self.functions = functions 10 | } 11 | } 12 | 13 | public protocol ChatContextCollector { 14 | func generateContext( 15 | history: [ChatMessage], 16 | scopes: Set, 17 | content: String, 18 | configuration: ChatGPTConfiguration 19 | ) -> ChatContext? 20 | } 21 | 22 | -------------------------------------------------------------------------------- /Tool/Sources/OpenAIService/FucntionCall/ChatGPTFuntionProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol ChatGPTFunctionProvider { 4 | var functions: [any ChatGPTFunction] { get } 5 | var functionCallStrategy: FunctionCallStrategy? { get } 6 | } 7 | 8 | extension ChatGPTFunctionProvider { 9 | func function(named: String) -> (any ChatGPTFunction)? { 10 | functions.first(where: { $0.name == named }) 11 | } 12 | } 13 | 14 | public struct NoChatGPTFunctionProvider: ChatGPTFunctionProvider { 15 | public var functionCallStrategy: FunctionCallStrategy? 16 | public var functions: [any ChatGPTFunction] { [] } 17 | public init() {} 18 | } 19 | -------------------------------------------------------------------------------- /Copilot for Xcode/Copilot_for_Xcode.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.application-groups 8 | 9 | $(TeamIdentifierPrefix)group.$(BUNDLE_IDENTIFIER_BASE) 10 | 11 | com.apple.security.files.user-selected.read-only 12 | 13 | keychain-access-groups 14 | 15 | $(AppIdentifierPrefix)$(BUNDLE_IDENTIFIER_BASE).Shared 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Core/Sources/ChatService/AllContextCollector.swift: -------------------------------------------------------------------------------- 1 | import ActiveDocumentChatContextCollector 2 | import ChatContextCollector 3 | import SystemInfoChatContextCollector 4 | import WebChatContextCollector 5 | #if canImport(ProChatContextCollectors) 6 | import ProChatContextCollectors 7 | let allContextCollectors: [any ChatContextCollector] = [ 8 | SystemInfoChatContextCollector(), 9 | ActiveDocumentChatContextCollector(), 10 | WebChatContextCollector(), 11 | ProChatContextCollectors(), 12 | ] 13 | #else 14 | let allContextCollectors: [any ChatContextCollector] = [ 15 | SystemInfoChatContextCollector(), 16 | ActiveDocumentChatContextCollector(), 17 | WebChatContextCollector(), 18 | ] 19 | #endif 20 | 21 | -------------------------------------------------------------------------------- /EditorExtension/CustomCommand.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import Foundation 3 | import SuggestionModel 4 | import XcodeKit 5 | 6 | class CustomCommand: NSObject, XCSourceEditorCommand, CommandType { 7 | var name: String = "" 8 | 9 | func perform( 10 | with invocation: XCSourceEditorCommandInvocation, 11 | completionHandler: @escaping (Error?) -> Void 12 | ) { 13 | completionHandler(nil) 14 | Task { 15 | let service = try getService() 16 | _ = try await service.customCommand( 17 | id: customCommandMap[invocation.commandIdentifier] ?? "", 18 | editorContent: .init(invocation) 19 | ) 20 | } 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Core/Tests/GitHubCopilotServiceTests/FileExtensionToLanguageIdentifierTests.swift: -------------------------------------------------------------------------------- 1 | import LanguageServerProtocol 2 | import XCTest 3 | 4 | @testable import GitHubCopilotService 5 | 6 | final class FileExtensionToLanguageIdentifierTests: XCTestCase { 7 | func test_no_conflicts_in_map() { 8 | var dict = [String: [String]]() 9 | for languageId in LanguageIdentifier.allCases { 10 | for e in languageId.fileExtensions { 11 | if dict[e] == nil { 12 | dict[e] = [] 13 | } 14 | dict[e]?.append(languageId.rawValue) 15 | } 16 | } 17 | 18 | let confilicts = dict.filter { $0.value.count > 1 } 19 | XCTAssertEqual(confilicts, [:]) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tool/Sources/LangChain/VectorStore/VectorStore.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol VectorStore { 4 | func add(_ documents: [EmbeddedDocument]) async throws 5 | func set(_ documents: [EmbeddedDocument]) async throws 6 | func clear() async throws 7 | func searchWithDistance(embeddings: [Float], count: Int) async throws 8 | -> [(document: Document, distance: Float)] 9 | } 10 | 11 | public extension VectorStore { 12 | func search(embeddings: [Float], count: Int) async throws -> [Document] { 13 | try await searchWithDistance(embeddings: embeddings, count: count).map { $0.document } 14 | } 15 | 16 | func add(_ document: EmbeddedDocument) async throws { 17 | try await add([document]) 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Core/Sources/SuggestionWidget/SuggestionWidgetDataSource.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol SuggestionWidgetDataSource { 4 | func suggestionForFile(at url: URL) async -> SuggestionProvider? 5 | } 6 | 7 | struct MockWidgetDataSource: SuggestionWidgetDataSource { 8 | func suggestionForFile(at url: URL) async -> SuggestionProvider? { 9 | return SuggestionProvider( 10 | code: """ 11 | func test() { 12 | let x = 1 13 | let y = 2 14 | let z = x + y 15 | } 16 | """, 17 | language: "swift", 18 | startLineIndex: 1, 19 | suggestionCount: 3, 20 | currentSuggestionIndex: 0 21 | ) 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Core/Sources/ChatPlugin/AskChatGPT.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import OpenAIService 3 | 4 | /// Quickly ask a question to ChatGPT. 5 | public func askChatGPT( 6 | systemPrompt: String, 7 | question: String, 8 | temperature: Double? = nil 9 | ) async throws -> String? { 10 | let configuration = UserPreferenceChatGPTConfiguration() 11 | .overriding(.init(temperature: temperature)) 12 | let memory = AutoManagedChatGPTMemory( 13 | systemPrompt: systemPrompt, 14 | configuration: configuration, 15 | functionProvider: NoChatGPTFunctionProvider() 16 | ) 17 | let service = ChatGPTService( 18 | memory: memory, 19 | configuration: configuration 20 | ) 21 | return try await service.sendAndWait(content: question) 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Core/Sources/GitHubCopilotService/GitHubCopilotAccountStatus.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum GitHubCopilotAccountStatus: String, Codable, CustomStringConvertible { 4 | case alreadySignedIn = "AlreadySignedIn" 5 | case maybeOk = "MaybeOk" 6 | case notAuthorized = "NotAuthorized" 7 | case notSignedIn = "NotSignedIn" 8 | case ok = "OK" 9 | 10 | public var description: String { 11 | switch self { 12 | case .alreadySignedIn: 13 | return "Already Signed In" 14 | case .maybeOk: 15 | return "Maybe OK" 16 | case .notAuthorized: 17 | return "Not Authorized" 18 | case .notSignedIn: 19 | return "Not Signed In" 20 | case .ok: 21 | return "OK" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tool/Tests/TokenEncoderTests/TiktokenCl100kBaseTokenEncoderTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | 4 | @testable import TokenEncoder 5 | 6 | class TiktokenCl100kBaseTokenEncoderTests: XCTestCase { 7 | func test_encoding() async throws { 8 | let encoder = TiktokenCl100kBaseTokenEncoder() 9 | let encoded = encoder.encode(text: """ 10 | 我可以吞下玻璃而不伤身体 11 | The quick brown fox jumps over the lazy dog 12 | """) 13 | XCTAssertEqual(encoded.count, 26) 14 | XCTAssertEqual( 15 | encoded, 16 | [ 17 | 37046, 74770, 7305, 252, 17297, 29207, 119, 163, 240, 225, 69636, 16937, 17885, 97, 18 | 96356, 33014, 198, 791, 4062, 14198, 39935, 35308, 927, 279, 16053, 5679, 19 | ] 20 | ) 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Core/Sources/ChatPlugin/ChatPlugin.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import OpenAIService 3 | 4 | public protocol ChatPlugin: AnyObject { 5 | /// Should be [a-zA-Z0-9]+ 6 | static var command: String { get } 7 | var name: String { get } 8 | 9 | init(inside chatGPTService: any ChatGPTServiceType, delegate: ChatPluginDelegate) 10 | func send(content: String, originalMessage: String) async 11 | func cancel() async 12 | func stopResponding() async 13 | } 14 | 15 | public protocol ChatPluginDelegate: AnyObject { 16 | func pluginDidStart(_ plugin: ChatPlugin) 17 | func pluginDidEnd(_ plugin: ChatPlugin) 18 | func pluginDidStartResponding(_ plugin: ChatPlugin) 19 | func pluginDidEndResponding(_ plugin: ChatPlugin) 20 | func shouldStartAnotherPlugin(_ type: ChatPlugin.Type, withContent: String) 21 | } 22 | -------------------------------------------------------------------------------- /ExtensionService/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | APPLICATION_SUPPORT_FOLDER 6 | $(APPLICATION_SUPPORT_FOLDER) 7 | APP_ID_PREFIX 8 | $(AppIdentifierPrefix) 9 | BUNDLE_IDENTIFIER_BASE 10 | $(BUNDLE_IDENTIFIER_BASE) 11 | EXTENSION_BUNDLE_NAME 12 | $(EXTENSION_BUNDLE_NAME) 13 | HOST_APP_NAME 14 | $(HOST_APP_NAME) 15 | TEAM_ID_PREFIX 16 | $(TeamIdentifierPrefix) 17 | XPCService 18 | 19 | ServiceType 20 | Application 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /EditorExtension/ToggleRealtimeSuggestionsCommand.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import SuggestionModel 3 | import Foundation 4 | import XcodeKit 5 | 6 | class ToggleRealtimeSuggestionsCommand: NSObject, XCSourceEditorCommand, CommandType { 7 | var name: String { "Toggle Real-time Suggestions" } 8 | 9 | func perform( 10 | with invocation: XCSourceEditorCommandInvocation, 11 | completionHandler: @escaping (Error?) -> Void 12 | ) { 13 | Task { 14 | do { 15 | let service = try getService() 16 | try await service.toggleRealtimeSuggestion() 17 | completionHandler(nil) 18 | } catch is CancellationError { 19 | completionHandler(nil) 20 | } catch { 21 | completionHandler(error) 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Core/Sources/HostApp/LaunchAgentManager.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import LaunchAgentManager 3 | 4 | extension LaunchAgentManager { 5 | init() { 6 | self.init( 7 | serviceIdentifier: Bundle.main 8 | .object(forInfoDictionaryKey: "BUNDLE_IDENTIFIER_BASE") as! String + 9 | ".ExtensionService", 10 | executablePath: Bundle.main.bundleURL 11 | .appendingPathComponent("Contents") 12 | .appendingPathComponent("Applications") 13 | .appendingPathComponent( 14 | "CodeiumForXcodeExtensionService.app/Contents/MacOS/CodeiumForXcodeExtensionService" 15 | ) 16 | .path, 17 | bundleIdentifier: Bundle.main 18 | .object(forInfoDictionaryKey: "BUNDLE_IDENTIFIER_BASE") as! String 19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/close_inactive_issues.yml: -------------------------------------------------------------------------------- 1 | name: Close inactive issues 2 | on: 3 | schedule: 4 | - cron: "30 1 * * *" 5 | 6 | jobs: 7 | close-issues: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | steps: 13 | - uses: actions/stale@v5 14 | with: 15 | days-before-issue-stale: 30 16 | days-before-issue-close: 14 17 | stale-issue-label: "stale" 18 | exempt-issue-labels: "low priority, help wanted, planned" 19 | stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." 20 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." 21 | days-before-pr-stale: -1 22 | days-before-pr-close: -1 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /Core/Sources/GitHubCopilotService/CustomStdioTransport.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JSONRPC 3 | import os.log 4 | 5 | public class CustomDataTransport: DataTransport { 6 | let nextTransport: DataTransport 7 | 8 | var onWriteRequest: (JSONRPCRequest) -> Void = { _ in } 9 | 10 | init(nextTransport: DataTransport) { 11 | self.nextTransport = nextTransport 12 | } 13 | 14 | public func write(_ data: Data) { 15 | if let request = try? JSONDecoder().decode(JSONRPCRequest.self, from: data) { 16 | onWriteRequest(request) 17 | } 18 | 19 | nextTransport.write(data) 20 | } 21 | 22 | public func setReaderHandler(_ handler: @escaping ReadHandler) { 23 | nextTransport.setReaderHandler(handler) 24 | } 25 | 26 | public func close() { 27 | nextTransport.close() 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /Copilot-for-Xcode-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | APPLICATION_SUPPORT_FOLDER 6 | $(APPLICATION_SUPPORT_FOLDER) 7 | APP_ID_PREFIX 8 | $(AppIdentifierPrefix) 9 | BUNDLE_IDENTIFIER_BASE 10 | $(BUNDLE_IDENTIFIER_BASE) 11 | EXTENSION_BUNDLE_NAME 12 | $(EXTENSION_BUNDLE_NAME) 13 | HOST_APP_NAME 14 | $(HOST_APP_NAME) 15 | SUEnableJavaScript 16 | YES 17 | SUFeedURL 18 | $(SPARKLE_FEED_URL) 19 | SUPublicEDKey 20 | $(SPARKLE_PUBLIC_KEY) 21 | TEAM_ID_PREFIX 22 | $(TeamIdentifierPrefix) 23 | 24 | 25 | -------------------------------------------------------------------------------- /Core/Sources/SuggestionWidget/FeatureReducers/SuggestionPanelFeature.swift: -------------------------------------------------------------------------------- 1 | import ComposableArchitecture 2 | import Foundation 3 | import SwiftUI 4 | 5 | public struct SuggestionPanelFeature: ReducerProtocol { 6 | public struct State: Equatable { 7 | var content: SuggestionProvider? 8 | var colorScheme: ColorScheme = .light 9 | var alignTopToAnchor = false 10 | var isPanelDisplayed: Bool = false 11 | var isPanelOutOfFrame: Bool = false 12 | var opacity: Double { 13 | guard isPanelDisplayed else { return 0 } 14 | if isPanelOutOfFrame { return 0 } 15 | guard content != nil else { return 0 } 16 | return 1 17 | } 18 | } 19 | 20 | public enum Action: Equatable { 21 | case noAction 22 | } 23 | 24 | public var body: some ReducerProtocol { 25 | Reduce { _, _ in .none } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tool/Sources/TokenEncoder/TiktokenCl100kBaseTokenEncoder.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Tiktoken 3 | 4 | public final class TiktokenCl100kBaseTokenEncoder: TokenEncoder { 5 | static var encoding: Encoding? 6 | static var isLoadingEncoding = false 7 | 8 | public init() {} 9 | 10 | public func encode(text: String) -> [Int] { 11 | guard let encoding = Self.createEncodingIfNeeded() else { return [] } 12 | return encoding.encode(value: text) 13 | } 14 | 15 | static func createEncodingIfNeeded() -> Encoding? { 16 | if let encoding = Self.encoding { return encoding } 17 | let encoding = Tiktoken.shared.getEncoding( 18 | for: Vocab.cl100kBase, 19 | name: "gpt-4", 20 | fileURL: Bundle.module.url(forResource: "cl100k_base", withExtension: "tiktoken")! 21 | )! 22 | Self.encoding = encoding 23 | return encoding 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /Copilot for Xcode/App.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import HostApp 3 | import LaunchAgentManager 4 | import SwiftUI 5 | import UpdateChecker 6 | import XPCShared 7 | 8 | struct VisualEffect: NSViewRepresentable { 9 | func makeNSView(context: Self.Context) -> NSView { return NSVisualEffectView() } 10 | func updateNSView(_ nsView: NSView, context: Context) { } 11 | } 12 | 13 | @main 14 | struct CopilotForXcodeApp: App { 15 | var body: some Scene { 16 | WindowGroup { 17 | TabContainer() 18 | .frame(minWidth: 800, minHeight: 600) 19 | .background(VisualEffect().ignoresSafeArea()) 20 | .onAppear { 21 | UserDefaults.setupDefaultSettings() 22 | } 23 | .environment(\.updateChecker, UpdateChecker(hostBundle: Bundle.main)) 24 | } 25 | } 26 | } 27 | 28 | var isPreview: Bool { ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" } 29 | 30 | -------------------------------------------------------------------------------- /Core/Sources/SuggestionWidget/SuggestionPanelContent/ErrorPanel.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ErrorPanel: View { 4 | var description: String 5 | var onCloseButtonTap: () -> Void 6 | 7 | var body: some View { 8 | ZStack(alignment: .topTrailing) { 9 | Text(description) 10 | .multilineTextAlignment(.leading) 11 | .frame(maxWidth: .infinity, alignment: .leading) 12 | .foregroundColor(.white) 13 | .padding() 14 | .background(Color.red) 15 | 16 | // close button 17 | Button(action: onCloseButtonTap) { 18 | Image(systemName: "xmark") 19 | .padding([.leading, .bottom], 16) 20 | .padding([.top, .trailing], 8) 21 | .foregroundColor(.white) 22 | } 23 | .buttonStyle(.plain) 24 | } 25 | .xcodeStyleFrame() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tool/Sources/SuggestionModel/CodeSuggestion.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct CodeSuggestion: Codable, Equatable { 4 | public init( 5 | text: String, 6 | position: CursorPosition, 7 | uuid: String, 8 | range: CursorRange, 9 | displayText: String 10 | ) { 11 | self.text = text 12 | self.position = position 13 | self.uuid = uuid 14 | self.range = range 15 | self.displayText = displayText 16 | } 17 | 18 | /// The new code to be inserted and the original code on the first line. 19 | public var text: String 20 | /// The position of the cursor before generating the completion. 21 | public var position: CursorPosition 22 | /// An id. 23 | public var uuid: String 24 | /// The range of the original code that should be replaced. 25 | public var range: CursorRange 26 | /// The new code to be inserted. 27 | public var displayText: String 28 | } 29 | -------------------------------------------------------------------------------- /Core/Sources/ChatContextCollectors/SystemInfoChatContextCollector/SystemInfoChatContextCollector.swift: -------------------------------------------------------------------------------- 1 | import ChatContextCollector 2 | import Foundation 3 | import OpenAIService 4 | 5 | public final class SystemInfoChatContextCollector: ChatContextCollector { 6 | static let dateFormatter: DateFormatter = { 7 | let formatter = DateFormatter() 8 | formatter.dateFormat = "EEEE, yyyy-MM-dd HH:mm:ssZ" 9 | return formatter 10 | }() 11 | 12 | public init() {} 13 | 14 | public func generateContext( 15 | history: [ChatMessage], 16 | scopes: Set, 17 | content: String, 18 | configuration: ChatGPTConfiguration 19 | ) -> ChatContext? { 20 | return .init( 21 | systemPrompt: """ 22 | Current Time: \(Self.dateFormatter.string(from: Date())) (You can use it to calculate time in another time zone) 23 | """, 24 | functions: [] 25 | ) 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /Tool/Sources/LangChain/DocumentLoader/DocumentLoader.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JSONRPC 3 | 4 | public struct Document: Codable { 5 | public typealias Metadata = [String: JSONValue] 6 | public var pageContent: String 7 | public var metadata: Metadata 8 | public init(pageContent: String, metadata: Metadata) { 9 | self.pageContent = pageContent 10 | self.metadata = metadata 11 | } 12 | 13 | public func metadata(_ keyPath: KeyPath) -> JSONValue? { 14 | let key = Key.self[keyPath: keyPath] 15 | return metadata[key] 16 | } 17 | } 18 | 19 | public protocol DocumentLoader { 20 | func load() async throws -> [Document] 21 | } 22 | 23 | extension DocumentLoader { 24 | func loadAndSplit( 25 | with textSplitter: TextSplitter = RecursiveCharacterTextSplitter() 26 | ) async throws -> [Document] { 27 | let docs = try await load() 28 | return try await textSplitter.splitDocuments(docs) 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_reqeust.yaml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Request a feature 3 | title: "[Enhancement]: " 4 | labels: ["enhancement"] 5 | assignees: 6 | - intitni 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | Thanks for taking the time to fill out this feature request! 12 | - type: checkboxes 13 | id: before-reporting 14 | attributes: 15 | label: Before Requesting 16 | description: Before requesting the feature, we suggestion that you first search for existing issues to avoid duplication. 17 | options: 18 | - label: I have searched the existing issues, and there is no existing issue for my feature request 19 | required: true 20 | - type: textarea 21 | id: what-feature 22 | attributes: 23 | label: What feature do you want? 24 | description: Please describe the feature you want. 25 | placeholder: Tell us what you want! 26 | value: "I want a feature!" 27 | validations: 28 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/help_wanted.yml: -------------------------------------------------------------------------------- 1 | name: Help Wanted 2 | description: Ask for help from the developer and the community 3 | title: "[Help Wanted]: " 4 | labels: ["help wanted"] 5 | body: 6 | - type: checkboxes 7 | id: before-reporting 8 | attributes: 9 | label: Before Reporting 10 | description: Before asking for help, we suggestion that you first refer to the [FAQ](https://github.com/intitni/CopilotForXcode/wiki/Frequently-Asked-Questions) to check if it may address your issue. And search for existing issues to avoid duplication. 11 | options: 12 | - label: I have checked FAQ, and there is no solution to my issue 13 | required: true 14 | - label: I have searched the existing issues, and there is no existing issue for my issue 15 | required: true 16 | - type: textarea 17 | id: what-help 18 | attributes: 19 | label: Describe your issue 20 | description: Please describe the help you want. 21 | placeholder: My issue is... 22 | value: "I want help!" 23 | validations: 24 | required: true 25 | -------------------------------------------------------------------------------- /Tool/Sources/Workspace/FileSaveWatcher.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class FileSaveWatcher { 4 | let url: URL 5 | var fileHandle: FileHandle? 6 | var source: DispatchSourceFileSystemObject? 7 | var changeHandler: () -> Void = {} 8 | 9 | init(fileURL: URL) { 10 | url = fileURL 11 | startup() 12 | } 13 | 14 | deinit { 15 | source?.cancel() 16 | } 17 | 18 | func startup() { 19 | if let source = source { 20 | source.cancel() 21 | } 22 | 23 | fileHandle = try? FileHandle(forReadingFrom: url) 24 | if let fileHandle = fileHandle { 25 | source = DispatchSource.makeFileSystemObjectSource( 26 | fileDescriptor: fileHandle.fileDescriptor, 27 | eventMask: .link, 28 | queue: .main 29 | ) 30 | 31 | source?.setEventHandler { [weak self] in 32 | self?.changeHandler() 33 | self?.startup() 34 | } 35 | 36 | source?.resume() 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Core/Sources/UserDefaultsObserver/UserDefaultsObserver.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public final class UserDefaultsObserver: NSObject { 4 | public var onChange: (() -> Void)? 5 | private weak var object: NSObject? 6 | private let keyPaths: [String] 7 | 8 | public init( 9 | object: NSObject, 10 | forKeyPaths keyPaths: [String], 11 | context: UnsafeMutableRawPointer? 12 | ) { 13 | self.object = object 14 | self.keyPaths = keyPaths 15 | super.init() 16 | for keyPath in keyPaths { 17 | object.addObserver(self, forKeyPath: keyPath, options: .new, context: context) 18 | } 19 | } 20 | 21 | deinit { 22 | for keyPath in keyPaths { 23 | object?.removeObserver(self, forKeyPath: keyPath) 24 | } 25 | } 26 | 27 | public override func observeValue( 28 | forKeyPath keyPath: String?, 29 | of object: Any?, 30 | change: [NSKeyValueChangeKey: Any]?, 31 | context: UnsafeMutableRawPointer? 32 | ) { 33 | onChange?() 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Tool/Sources/UserDefaultsObserver/UserDefaultsObserver.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public final class UserDefaultsObserver: NSObject { 4 | public var onChange: (() -> Void)? 5 | private weak var object: NSObject? 6 | private let keyPaths: [String] 7 | 8 | public init( 9 | object: NSObject, 10 | forKeyPaths keyPaths: [String], 11 | context: UnsafeMutableRawPointer? 12 | ) { 13 | self.object = object 14 | self.keyPaths = keyPaths 15 | super.init() 16 | for keyPath in keyPaths { 17 | object.addObserver(self, forKeyPath: keyPath, options: .new, context: context) 18 | } 19 | } 20 | 21 | deinit { 22 | for keyPath in keyPaths { 23 | object?.removeObserver(self, forKeyPath: keyPath) 24 | } 25 | } 26 | 27 | public override func observeValue( 28 | forKeyPath keyPath: String?, 29 | of object: Any?, 30 | change: [NSKeyValueChangeKey: Any]?, 31 | context: UnsafeMutableRawPointer? 32 | ) { 33 | onChange?() 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /EditorExtension/AcceptPromptToCodeCommand.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import Foundation 3 | import SuggestionModel 4 | import XcodeKit 5 | 6 | class AcceptPromptToCodeCommand: NSObject, XCSourceEditorCommand, CommandType { 7 | var name: String { "Accept Prompt to Code" } 8 | 9 | func perform( 10 | with invocation: XCSourceEditorCommandInvocation, 11 | completionHandler: @escaping (Error?) -> Void 12 | ) { 13 | Task { 14 | do { 15 | try await (Task(timeout: 7) { 16 | let service = try getService() 17 | if let content = try await service.getPromptToCodeAcceptedCode( 18 | editorContent: .init(invocation) 19 | ) { 20 | invocation.accept(content) 21 | } 22 | completionHandler(nil) 23 | }.value) 24 | } catch is CancellationError { 25 | completionHandler(nil) 26 | } catch { 27 | completionHandler(error) 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Core/Sources/ChatPlugin/Translate.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Preferences 3 | 4 | @MainActor 5 | var translationCache = [String: String]() 6 | 7 | public func translate(text: String, cache: Bool = true) async -> String { 8 | let language = UserDefaults.shared.value(for: \.chatGPTLanguage) 9 | if language.isEmpty { return text } 10 | 11 | let key = "\(language)-\(text)" 12 | if cache, let cached = await translationCache[key] { 13 | return cached 14 | } 15 | 16 | if let translated = try? await askChatGPT( 17 | systemPrompt: """ 18 | You are a translator. Your job is to translate the message into \(language). The reply should only contain the translated content. 19 | User: ###${{some text}}### 20 | Assistant: ${{translated text}} 21 | """, 22 | question: "###\(text)###" 23 | ) { 24 | if cache { 25 | let storeTask = Task { @MainActor in 26 | translationCache[key] = translated 27 | } 28 | _ = await storeTask.result 29 | } 30 | return translated 31 | } 32 | return text 33 | } 34 | 35 | -------------------------------------------------------------------------------- /Core/Sources/ServiceUpdateMigration/ServiceUpdateMigrator.swift: -------------------------------------------------------------------------------- 1 | import Configs 2 | import Foundation 3 | import Preferences 4 | 5 | extension UserDefaultPreferenceKeys { 6 | struct OldMigrationVersion: UserDefaultPreferenceKey { 7 | var defaultValue: String = "150" 8 | let key = "OldMigrationVersion" 9 | } 10 | 11 | var oldMigrationVersion: OldMigrationVersion { .init() } 12 | } 13 | 14 | public struct ServiceUpdateMigrator { 15 | public init() {} 16 | 17 | public func migrate() async throws { 18 | let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "0" 19 | 20 | try await migrate(from: UserDefaults.shared.value(for: \.oldMigrationVersion), to: version) 21 | UserDefaults.shared.set(version, for: \.oldMigrationVersion) 22 | } 23 | 24 | func migrate(from oldVersion: String, to currentVersion: String) async throws { 25 | guard let old = Int(oldVersion), old != 0 else { return } 26 | if old <= 135 { 27 | try migrateFromLowerThanOrEqualToVersion135() 28 | } 29 | if old < 240 { 30 | try migrateTo240() 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tool/Sources/ChatTab/ChatTabItem.swift: -------------------------------------------------------------------------------- 1 | import ComposableArchitecture 2 | import Foundation 3 | 4 | public struct AnyChatTabBuilder: Equatable { 5 | public static func == (lhs: AnyChatTabBuilder, rhs: AnyChatTabBuilder) -> Bool { 6 | true 7 | } 8 | 9 | public let chatTabBuilder: any ChatTabBuilder 10 | 11 | public init(_ chatTabBuilder: any ChatTabBuilder) { 12 | self.chatTabBuilder = chatTabBuilder 13 | } 14 | } 15 | 16 | public struct ChatTabItem: ReducerProtocol { 17 | public typealias State = ChatTabInfo 18 | 19 | public enum Action: Equatable { 20 | case updateTitle(String) 21 | case openNewTab(AnyChatTabBuilder) 22 | case tabContentUpdated 23 | } 24 | 25 | public init() {} 26 | 27 | public var body: some ReducerProtocol { 28 | Reduce { state, action in 29 | switch action { 30 | case let .updateTitle(title): 31 | state.title = title 32 | return .none 33 | case .openNewTab: 34 | return .none 35 | case .tabContentUpdated: 36 | return .none 37 | } 38 | } 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /Core/Sources/ChatPlugin/CallAIFunction.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import OpenAIService 3 | 4 | /// This is a magic function that can do anything with no-code. See 5 | /// https://github.com/Torantulino/AI-Functions for more info. 6 | func callAIFunction( 7 | function: String, 8 | args: [Any?], 9 | description: String 10 | ) async throws -> String? { 11 | let args = args.map { arg -> String in 12 | if let arg = arg { 13 | return String(describing: arg) 14 | } else { 15 | return "None" 16 | } 17 | } 18 | let argsString = args.joined(separator: ", ") 19 | let configuration = UserPreferenceChatGPTConfiguration() 20 | .overriding(.init(temperature: 0)) 21 | let service = ChatGPTService( 22 | memory: AutoManagedChatGPTMemory( 23 | systemPrompt: "You are now the following python function: ```# \(description)\n\(function)```\n\nOnly respond with your `return` value.", 24 | configuration: configuration, 25 | functionProvider: NoChatGPTFunctionProvider() 26 | ), 27 | configuration: configuration 28 | ) 29 | return try await service.sendAndWait(content: argsString) 30 | } 31 | 32 | -------------------------------------------------------------------------------- /Core/Sources/UpdateChecker/UpdateChecker.swift: -------------------------------------------------------------------------------- 1 | import Sparkle 2 | import Logger 3 | 4 | public final class UpdateChecker { 5 | let updater: SPUUpdater 6 | let hostBundleFound: Bool 7 | 8 | public init(hostBundle: Bundle?) { 9 | if hostBundle == nil { 10 | hostBundleFound = false 11 | Logger.updateChecker.error("Host bundle not found") 12 | } else { 13 | hostBundleFound = true 14 | } 15 | updater = SPUUpdater( 16 | hostBundle: hostBundle ?? Bundle.main, 17 | applicationBundle: Bundle.main, 18 | userDriver: SPUStandardUserDriver(hostBundle: hostBundle ?? Bundle.main, delegate: nil), 19 | delegate: nil 20 | ) 21 | do { 22 | try updater.start() 23 | } catch { 24 | Logger.updateChecker.error(error.localizedDescription) 25 | } 26 | } 27 | 28 | public func checkForUpdates() { 29 | updater.checkForUpdates() 30 | } 31 | 32 | public var automaticallyChecksForUpdates: Bool { 33 | get { updater.automaticallyChecksForUpdates } 34 | set { updater.automaticallyChecksForUpdates = newValue } 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /Tool/Sources/OpenAIService/Configuration/EmbeddingConfiguration.swift: -------------------------------------------------------------------------------- 1 | import AIModel 2 | import Foundation 3 | import Keychain 4 | import Preferences 5 | 6 | public protocol EmbeddingConfiguration { 7 | var model: EmbeddingModel? { get } 8 | var apiKey: String { get } 9 | var maxToken: Int { get } 10 | var dimensions: Int { get } 11 | } 12 | 13 | public extension EmbeddingConfiguration { 14 | var endpoint: String { 15 | model?.endpoint ?? "" 16 | } 17 | 18 | var apiKey: String { 19 | guard let name = model?.info.apiKeyName else { return "" } 20 | return (try? Keychain.apiKey.get(name)) ?? "" 21 | } 22 | 23 | func overriding( 24 | _ overrides: OverridingEmbeddingConfiguration.Overriding 25 | ) -> OverridingEmbeddingConfiguration { 26 | .init(overriding: self, with: overrides) 27 | } 28 | 29 | func overriding( 30 | _ update: (inout OverridingEmbeddingConfiguration.Overriding) -> Void = { _ in } 31 | ) -> OverridingEmbeddingConfiguration { 32 | var overrides = OverridingEmbeddingConfiguration.Overriding() 33 | update(&overrides) 34 | return .init(overriding: self, with: overrides) 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /Core/Sources/FileChangeChecker/FileChangeChecker.swift: -------------------------------------------------------------------------------- 1 | import CryptoKit 2 | import Dispatch 3 | import Foundation 4 | 5 | /// Check that a file is changed. 6 | public actor FileChangeChecker { 7 | let url: URL 8 | var checksum: Data? 9 | 10 | public init(fileURL: URL) async { 11 | url = fileURL 12 | checksum = getChecksum() 13 | } 14 | 15 | public func checkIfChanged() -> Bool { 16 | guard let newChecksum = getChecksum() else { return false } 17 | return newChecksum != checksum 18 | } 19 | 20 | func getChecksum() -> Data? { 21 | let bufferSize = 16 * 1024 22 | guard let file = try? FileHandle(forReadingFrom: url) else { return nil } 23 | defer { try? file.close() } 24 | var md5 = CryptoKit.Insecure.MD5() 25 | while autoreleasepool(invoking: { 26 | let data = file.readData(ofLength: bufferSize) 27 | if !data.isEmpty { 28 | md5.update(data: data) 29 | return true // Continue 30 | } else { 31 | return false // End of file 32 | } 33 | }) {} 34 | 35 | let data = Data(md5.finalize()) 36 | 37 | return data 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Tool/Tests/OpenAIServiceTests/ChatGPTServiceFieldTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import OpenAIService 3 | 4 | final class ChatGPTServiceFieldTests: XCTestCase { 5 | let skip = true 6 | 7 | func test_calling_the_api() async throws { 8 | let service = ChatGPTService() 9 | 10 | if skip { return } 11 | 12 | do { 13 | let stream = try await service.send(content: "Hello") 14 | for try await text in stream { 15 | print(text) 16 | } 17 | } catch { 18 | print("🔴", error.localizedDescription) 19 | } 20 | 21 | XCTFail("🔴 Please reset skip to true.") 22 | } 23 | 24 | func test_calling_the_api_with_function_calling() async throws { 25 | let service = ChatGPTService() 26 | 27 | if skip { return } 28 | 29 | do { 30 | let stream = try await service.send(content: "Hello") 31 | for try await text in stream { 32 | print(text) 33 | } 34 | } catch { 35 | print("🔴", error.localizedDescription) 36 | } 37 | 38 | XCTFail("🔴 Please reset skip to true.") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Core/Tests/ChatServiceTests/ParseScopesTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | @testable import ChatService 4 | 5 | final class ParseScopesTests: XCTestCase { 6 | let parse = DynamicContextController.parseScopes 7 | 8 | func test_parse_single_scope() async throws { 9 | var prompt = "@web hello" 10 | let scopes = parse(&prompt) 11 | XCTAssertEqual(scopes, ["web"]) 12 | XCTAssertEqual(prompt, "hello") 13 | } 14 | 15 | func test_parse_multiple_spaces() async throws { 16 | var prompt = "@web hello" 17 | let scopes = parse(&prompt) 18 | XCTAssertEqual(scopes, ["web"]) 19 | XCTAssertEqual(prompt, "hello") 20 | } 21 | 22 | func test_parse_no_prefix_at_mark() async throws { 23 | var prompt = " @web hello" 24 | let scopes = parse(&prompt) 25 | XCTAssertEqual(scopes, []) 26 | XCTAssertEqual(prompt, prompt) 27 | } 28 | 29 | func test_parse_multiple_scopes() async throws { 30 | var prompt = "@web+file+selection hello" 31 | let scopes = parse(&prompt) 32 | XCTAssertEqual(scopes, ["web", "file", "selection"]) 33 | XCTAssertEqual(prompt, "hello") 34 | } 35 | } 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Core/Sources/PlusFeatureFlag/PlusFeatureFlag.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftUI 3 | 4 | #if canImport(LicenseManagement) 5 | 6 | import LicenseManagement 7 | 8 | #else 9 | 10 | public typealias PlusFeatureFlag = Int 11 | 12 | @dynamicMemberLookup 13 | public struct PlusFeatureFlags { 14 | public subscript(dynamicMember dynamicMember: String) -> PlusFeatureFlag { return 0 } 15 | init() {} 16 | } 17 | 18 | #endif 19 | 20 | public func withFeatureEnabled( 21 | _ flag: KeyPath, 22 | then: () throws -> Void 23 | ) rethrows { 24 | #if canImport(LicenseManagement) 25 | try LicenseManagement.withFeatureEnabled(flag, then: then) 26 | #endif 27 | } 28 | 29 | public func withFeatureEnabled( 30 | _ flag: KeyPath, 31 | then: () async throws -> Void 32 | ) async rethrows { 33 | #if canImport(LicenseManagement) 34 | try await LicenseManagement.withFeatureEnabled(flag, then: then) 35 | #endif 36 | } 37 | 38 | public func isFeatureAvailable(_ flag: KeyPath) -> Bool { 39 | #if canImport(LicenseManagement) 40 | return LicenseManagement.isFeatureAvailable(flag) 41 | #else 42 | return false 43 | #endif 44 | } 45 | 46 | -------------------------------------------------------------------------------- /Tool/Tests/LangChainTests/VectorStoreTests/TemporaryUSearchTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import USearchIndex 3 | import XCTest 4 | 5 | import USearch 6 | 7 | @testable import LangChain 8 | 9 | class TemporaryUSearchTests: XCTestCase { 10 | func test_usearch() async throws { 11 | let index = USearchIndex( 12 | metric: USearchMetric.l2sq, 13 | dimensions: 4, 14 | connectivity: 8, 15 | quantization: USearchScalar.F32 16 | ) 17 | let vectorA: [Float] = [0.3, 0.5, 1.2, 1.4] 18 | let vectorB: [Float] = [0.4, 0.2, 1.2, 1.1] 19 | try await index.clear() 20 | try await index.add(label: 42, vector: vectorA) 21 | try await index.add(label: 43, vector: vectorB) 22 | 23 | let results = try await index.search(vector: vectorA, count: 10) 24 | assert(results[0].label == 42) 25 | } 26 | 27 | func test_setting_data() async throws { 28 | let identifier = "hello-world" 29 | let store = TemporaryUSearch(identifier: identifier) 30 | try await store.set(EmbeddingData.data.map { datum in 31 | .init( 32 | document: .init(pageContent: datum.text, metadata: [:]), 33 | embeddings: datum.embedding 34 | ) 35 | }) 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /Core/Sources/Service/WorkspaceExtension/Workspace+Cleanup.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Workspace 3 | import SuggestionService 4 | 5 | extension Workspace { 6 | @WorkspaceActor 7 | func cleanUp(availableTabs: Set) { 8 | for (fileURL, _) in filespaces { 9 | if isFilespaceExpired(fileURL: fileURL, availableTabs: availableTabs) { 10 | Task { 11 | try await suggestionService?.notifyCloseTextDocument(fileURL: fileURL) 12 | } 13 | openedFileRecoverableStorage.closeFile(fileURL: fileURL) 14 | closeFilespace(fileURL: fileURL) 15 | } 16 | } 17 | } 18 | 19 | func isFilespaceExpired(fileURL: URL, availableTabs: Set) -> Bool { 20 | let filename = fileURL.lastPathComponent 21 | if availableTabs.contains(filename) { return false } 22 | guard let filespace = filespaces[fileURL] else { return true } 23 | return filespace.isExpired 24 | } 25 | 26 | func cancelInFlightRealtimeSuggestionRequests() async { 27 | guard let suggestionService else { return } 28 | await suggestionService.cancelRequest() 29 | } 30 | 31 | func terminateSuggestionService() async { 32 | await suggestionPlugin?.terminateSuggestionService() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tool/Sources/OpenAIService/Configuration/ChatGPTConfiguration.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import AIModel 3 | import Preferences 4 | import Keychain 5 | 6 | public protocol ChatGPTConfiguration { 7 | var model: ChatModel? { get } 8 | var temperature: Double { get } 9 | var apiKey: String { get } 10 | var stop: [String] { get } 11 | var maxTokens: Int { get } 12 | var minimumReplyTokens: Int { get } 13 | var runFunctionsAutomatically: Bool { get } 14 | } 15 | 16 | public extension ChatGPTConfiguration { 17 | var endpoint: String { 18 | model?.endpoint ?? "" 19 | } 20 | 21 | var apiKey: String { 22 | guard let name = model?.info.apiKeyName else { return "" } 23 | return (try? Keychain.apiKey.get(name)) ?? "" 24 | } 25 | 26 | func overriding( 27 | _ overrides: OverridingChatGPTConfiguration.Overriding 28 | ) -> OverridingChatGPTConfiguration { 29 | .init(overriding: self, with: overrides) 30 | } 31 | 32 | func overriding( 33 | _ update: (inout OverridingChatGPTConfiguration.Overriding) -> Void = { _ in } 34 | ) -> OverridingChatGPTConfiguration { 35 | var overrides = OverridingChatGPTConfiguration.Overriding() 36 | update(&overrides) 37 | return .init(overriding: self, with: overrides) 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /Tool/Sources/SharedUIComponents/CopyButton.swift: -------------------------------------------------------------------------------- 1 | import AppKit 2 | import SwiftUI 3 | 4 | public struct CopyButton: View { 5 | public var copy: () -> Void 6 | @State var isCopied = false 7 | 8 | public init(copy: @escaping () -> Void) { 9 | self.copy = copy 10 | } 11 | 12 | public var body: some View { 13 | Button(action: { 14 | withAnimation(.linear(duration: 0.1)) { 15 | isCopied = true 16 | } 17 | copy() 18 | Task { 19 | try await Task.sleep(nanoseconds: 1_000_000_000) 20 | withAnimation(.linear(duration: 0.1)) { 21 | isCopied = false 22 | } 23 | } 24 | }) { 25 | Image(systemName: isCopied ? "checkmark.circle" : "doc.on.doc") 26 | .resizable() 27 | .aspectRatio(contentMode: .fit) 28 | .frame(width: 14, height: 14) 29 | .frame(width: 20, height: 20, alignment: .center) 30 | .foregroundColor(.secondary) 31 | .background( 32 | .regularMaterial, 33 | in: RoundedRectangle(cornerRadius: 4, style: .circular) 34 | ) 35 | .padding(4) 36 | } 37 | .buttonStyle(.borderless) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Tool/Sources/Workspace/OpenedFileRocoverableStorage.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Preferences 3 | 4 | public final class OpenedFileRecoverableStorage { 5 | let projectRootURL: URL 6 | let userDefault = UserDefaults.shared 7 | let key = "OpenedFileRecoverableStorage" 8 | 9 | init(projectRootURL: URL) { 10 | self.projectRootURL = projectRootURL 11 | } 12 | 13 | public func openFile(fileURL: URL) { 14 | var dict = userDefault.dictionary(forKey: key) ?? [:] 15 | var openedFiles = Set(dict[projectRootURL.path] as? [String] ?? []) 16 | openedFiles.insert(fileURL.path) 17 | dict[projectRootURL.path] = Array(openedFiles) 18 | userDefault.set(dict, forKey: key) 19 | } 20 | 21 | public func closeFile(fileURL: URL) { 22 | var dict = userDefault.dictionary(forKey: key) ?? [:] 23 | var openedFiles = dict[projectRootURL.path] as? [String] ?? [] 24 | openedFiles.removeAll(where: { $0 == fileURL.path }) 25 | dict[projectRootURL.path] = openedFiles 26 | userDefault.set(dict, forKey: key) 27 | } 28 | 29 | public var openedFiles: [URL] { 30 | let dict = userDefault.dictionary(forKey: key) ?? [:] 31 | let openedFiles = dict[projectRootURL.path] as? [String] ?? [] 32 | return openedFiles.map { URL(fileURLWithPath: $0) } 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /Core/Sources/Service/SuggestionCommandHandler/SuggestionCommandHandler.swift: -------------------------------------------------------------------------------- 1 | import SuggestionModel 2 | import XPCShared 3 | 4 | protocol SuggestionCommandHandler { 5 | @ServiceActor 6 | func presentSuggestions(editor: EditorContent) async throws -> UpdatedContent? 7 | @ServiceActor 8 | func presentNextSuggestion(editor: EditorContent) async throws -> UpdatedContent? 9 | @ServiceActor 10 | func presentPreviousSuggestion(editor: EditorContent) async throws -> UpdatedContent? 11 | @ServiceActor 12 | func rejectSuggestion(editor: EditorContent) async throws -> UpdatedContent? 13 | @ServiceActor 14 | func acceptSuggestion(editor: EditorContent) async throws -> UpdatedContent? 15 | @ServiceActor 16 | func acceptPromptToCode(editor: EditorContent) async throws -> UpdatedContent? 17 | @ServiceActor 18 | func presentRealtimeSuggestions(editor: EditorContent) async throws -> UpdatedContent? 19 | @ServiceActor 20 | func generateRealtimeSuggestions(editor: EditorContent) async throws -> UpdatedContent? 21 | @ServiceActor 22 | func chatWithSelection(editor: EditorContent) async throws -> UpdatedContent? 23 | @ServiceActor 24 | func promptToCode(editor: EditorContent) async throws -> UpdatedContent? 25 | @ServiceActor 26 | func customCommand(id: String, editor: EditorContent) async throws -> UpdatedContent? 27 | } 28 | -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | --allman false 2 | --beforemarks 3 | --binarygrouping 4,8 4 | --categorymark "MARK: %c" 5 | --classthreshold 0 6 | --closingparen balanced 7 | --commas always 8 | --conflictmarkers reject 9 | --decimalgrouping 3,6 10 | --elseposition same-line 11 | --enumthreshold 0 12 | --exponentcase lowercase 13 | --exponentgrouping disabled 14 | --fractiongrouping disabled 15 | --fragment false 16 | --funcattributes preserve 17 | --guardelse auto 18 | --header ignore 19 | --hexgrouping 4,8 20 | --hexliteralcase uppercase 21 | --ifdef no-indent 22 | --importgrouping testable-bottom 23 | --indent 4 24 | --indentcase false 25 | --lifecycle 26 | --linebreaks lf 27 | --maxwidth 100 28 | --modifierorder 29 | --nospaceoperators ...,..< 30 | --nowrapoperators 31 | --octalgrouping 4,8 32 | --operatorfunc spaced 33 | --patternlet hoist 34 | --ranges spaced 35 | --self remove 36 | --selfrequired 37 | --semicolons inline 38 | --shortoptionals always 39 | --smarttabs enabled 40 | --stripunusedargs unnamed-only 41 | --structthreshold 0 42 | --tabwidth unspecified 43 | --trailingclosures 44 | --trimwhitespace always 45 | --typeattributes preserve 46 | --varattributes preserve 47 | --voidtype void 48 | --wraparguments before-first 49 | --wrapcollections disabled 50 | --wrapparameters before-first 51 | --xcodeindentation disabled 52 | --yodaswap always 53 | 54 | --enable isEmpty 55 | 56 | --exclude Pods,**/Generated 57 | -------------------------------------------------------------------------------- /Tool/Sources/LangChain/Chains/LLMChain.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public class ChatModelChain: Chain { 4 | public typealias Output = ChatMessage 5 | 6 | public internal(set) var chatModel: ChatModel 7 | public internal(set) var promptTemplate: (Input) -> [ChatMessage] 8 | public internal(set) var stops: [String] 9 | 10 | public init( 11 | chatModel: ChatModel, 12 | stops: [String] = [], 13 | promptTemplate: @escaping (Input) -> [ChatMessage] 14 | ) { 15 | self.chatModel = chatModel 16 | self.promptTemplate = promptTemplate 17 | self.stops = stops 18 | } 19 | 20 | public func callLogic( 21 | _ input: Input, 22 | callbackManagers: [CallbackManager] 23 | ) async throws -> Output { 24 | let prompt = promptTemplate(input) 25 | let output = try await chatModel.generate( 26 | prompt: prompt, 27 | stops: stops, 28 | callbackManagers: callbackManagers 29 | ) 30 | return output 31 | } 32 | 33 | public func parseOutput(_ output: Output) -> String { 34 | if let content = output.content { 35 | return content 36 | } else if let functionCall = output.functionCall { 37 | return "\(functionCall.name): \(functionCall.arguments)" 38 | } 39 | 40 | return "" 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tool/Sources/Preferences/Types/ChatGPTModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum ChatGPTModel: String { 4 | case gpt35Turbo = "gpt-3.5-turbo" 5 | case gpt35Turbo16k = "gpt-3.5-turbo-16k" 6 | case gpt4 = "gpt-4" 7 | case gpt432k = "gpt-4-32k" 8 | case gpt40314 = "gpt-4-0314" 9 | case gpt40613 = "gpt-4-0613" 10 | case gpt35Turbo0301 = "gpt-3.5-turbo-0301" 11 | case gpt35Turbo0613 = "gpt-3.5-turbo-0613" 12 | case gpt35Turbo16k0613 = "gpt-3.5-turbo-16k-0613" 13 | case gpt432k0314 = "gpt-4-32k-0314" 14 | case gpt432k0613 = "gpt-4-32k-0613" 15 | } 16 | 17 | public extension ChatGPTModel { 18 | var maxToken: Int { 19 | switch self { 20 | case .gpt4: 21 | return 8192 22 | case .gpt40314: 23 | return 8192 24 | case .gpt432k: 25 | return 32768 26 | case .gpt432k0314: 27 | return 32768 28 | case .gpt35Turbo: 29 | return 4096 30 | case .gpt35Turbo0301: 31 | return 4096 32 | case .gpt35Turbo0613: 33 | return 4096 34 | case .gpt35Turbo16k: 35 | return 16384 36 | case .gpt35Turbo16k0613: 37 | return 16384 38 | case .gpt40613: 39 | return 8192 40 | case .gpt432k0613: 41 | return 32768 42 | } 43 | } 44 | } 45 | 46 | extension ChatGPTModel: CaseIterable {} 47 | -------------------------------------------------------------------------------- /Core/Sources/HostApp/FeatureSettingsView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct FeatureSettingsView: View { 4 | @State var tag = 0 5 | 6 | var body: some View { 7 | SidebarTabView(tag: $tag) { 8 | ScrollView { 9 | SuggestionSettingsView().padding() 10 | } 11 | .sidebarItem( 12 | tag: 0, 13 | title: "Suggestion", 14 | subtitle: "Generate suggestions for your code", 15 | image: "lightbulb" 16 | ) 17 | 18 | // ScrollView { 19 | // ChatSettingsView().padding() 20 | // } 21 | // .sidebarItem( 22 | // tag: 1, 23 | // title: "Chat", 24 | // subtitle: "Chat about your code", 25 | // image: "character.bubble" 26 | // ) 27 | // 28 | // ScrollView { 29 | // PromptToCodeSettingsView().padding() 30 | // } 31 | // .sidebarItem( 32 | // tag: 2, 33 | // title: "Prompt to Code", 34 | // subtitle: "Write code with natural language", 35 | // image: "paintbrush" 36 | // ) 37 | } 38 | } 39 | } 40 | 41 | struct FeatureSettingsView_Previews: PreviewProvider { 42 | static var previews: some View { 43 | FeatureSettingsView() 44 | .frame(width: 800) 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Tool/Tests/SuggestionModelTests/ModificationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | @testable import SuggestionModel 4 | 5 | final class ModificationTests: XCTestCase { 6 | func test_nsmutablearray_deleting_an_element() { 7 | let a = NSMutableArray(array: ["a", "b", "c"]) 8 | a.apply([.deleted(0...0)]) 9 | XCTAssertEqual(a as! [String], ["b", "c"]) 10 | } 11 | 12 | func test_nsmutablearray_deleting_all_element() { 13 | let a = NSMutableArray(array: ["a", "b", "c"]) 14 | a.apply([.deleted(0...2)]) 15 | XCTAssertEqual(a as! [String], []) 16 | } 17 | 18 | func test_nsmutablearray_deleting_too_much_element() { 19 | let a = NSMutableArray(array: ["a", "b", "c"]) 20 | a.apply([.deleted(0...100)]) 21 | XCTAssertEqual(a as! [String], []) 22 | } 23 | 24 | func test_nsmutablearray_inserting_elements() { 25 | let a = NSMutableArray(array: ["a", "b", "c"]) 26 | a.apply([.inserted(0, ["y", "z"])]) 27 | XCTAssertEqual(a as! [String], ["y", "z", "a", "b", "c"]) 28 | a.apply([.inserted(1, ["0", "1"])]) 29 | XCTAssertEqual(a as! [String], ["y", "0", "1", "z", "a", "b", "c"]) 30 | } 31 | 32 | func test_nsmutablearray_inserting_elements_at_index_out_of_range() { 33 | let a = NSMutableArray(array: ["a", "b", "c"]) 34 | a.apply([.inserted(1000, ["z"])]) 35 | XCTAssertEqual(a as! [String], ["a", "b", "c", "z"]) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift: -------------------------------------------------------------------------------- 1 | import ComposableArchitecture 2 | import SwiftUI 3 | 4 | struct BaseURLPicker: View { 5 | let prompt: Text? 6 | let store: StoreOf 7 | 8 | var body: some View { 9 | WithViewStore(store) { viewStore in 10 | TextField("Base URL", text: viewStore.$baseURL, prompt: prompt) 11 | .overlay(alignment: .trailing) { 12 | Picker( 13 | "", 14 | selection: viewStore.$baseURL, 15 | content: { 16 | if !viewStore.state.availableBaseURLs 17 | .contains(viewStore.state.baseURL), 18 | !viewStore.state.baseURL.isEmpty 19 | { 20 | Text("Custom Value").tag(viewStore.state.baseURL) 21 | } 22 | 23 | Text("Empty (Default Value)").tag("") 24 | 25 | ForEach(viewStore.state.availableBaseURLs, id: \.self) { baseURL in 26 | Text(baseURL).tag(baseURL) 27 | } 28 | } 29 | ) 30 | .frame(width: 20) 31 | } 32 | .onAppear { 33 | viewStore.send(.appear) 34 | } 35 | } 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "16x16.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "32x32 1.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "32x32.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "64.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "128x128.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "256x256 1.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "filename" : "256x256.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "filename" : "512x512 1.png", 47 | "idiom" : "mac", 48 | "scale" : "2x", 49 | "size" : "256x256" 50 | }, 51 | { 52 | "filename" : "512x512.png", 53 | "idiom" : "mac", 54 | "scale" : "1x", 55 | "size" : "512x512" 56 | }, 57 | { 58 | "filename" : "1024x1024.png", 59 | "idiom" : "mac", 60 | "scale" : "2x", 61 | "size" : "512x512" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ExtensionService/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "16x16.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "32x32 1.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "32x32.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "64.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "128x128.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "256x256 1.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "filename" : "256x256.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "filename" : "512x512 1.png", 47 | "idiom" : "mac", 48 | "scale" : "2x", 49 | "size" : "256x256" 50 | }, 51 | { 52 | "filename" : "512x512.png", 53 | "idiom" : "mac", 54 | "scale" : "1x", 55 | "size" : "512x512" 56 | }, 57 | { 58 | "filename" : "1024x1024.png", 59 | "idiom" : "mac", 60 | "scale" : "2x", 61 | "size" : "512x512" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Tool/Sources/ChatTab/ChatTabPool.swift: -------------------------------------------------------------------------------- 1 | import ComposableArchitecture 2 | import Dependencies 3 | import Foundation 4 | import SwiftUI 5 | 6 | /// A pool that stores all the available tabs. 7 | public final class ChatTabPool { 8 | public var createStore: (String) -> StoreOf = { id in 9 | .init( 10 | initialState: .init(id: id, title: ""), 11 | reducer: ChatTabItem() 12 | ) 13 | } 14 | 15 | private var pool: [String: any ChatTab] 16 | 17 | public init(_ pool: [String: any ChatTab] = [:]) { 18 | self.pool = pool 19 | } 20 | 21 | public func getTab(of id: String) -> (any ChatTab)? { 22 | pool[id] 23 | } 24 | 25 | public func setTab(_ tab: any ChatTab) { 26 | pool[tab.id] = tab 27 | } 28 | 29 | public func removeTab(of id: String) { 30 | pool.removeValue(forKey: id) 31 | } 32 | } 33 | 34 | public struct ChatTabPoolDependencyKey: DependencyKey { 35 | public static let liveValue = ChatTabPool() 36 | } 37 | 38 | public extension DependencyValues { 39 | var chatTabPool: ChatTabPool { 40 | get { self[ChatTabPoolDependencyKey.self] } 41 | set { self[ChatTabPoolDependencyKey.self] = newValue } 42 | } 43 | } 44 | 45 | public struct ChatTabPoolEnvironmentKey: EnvironmentKey { 46 | public static let defaultValue = ChatTabPool() 47 | } 48 | 49 | public extension EnvironmentValues { 50 | var chatTabPool: ChatTabPool { 51 | get { self[ChatTabPoolEnvironmentKey.self] } 52 | set { self[ChatTabPoolEnvironmentKey.self] = newValue } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tool/Sources/SuggestionModel/Modification.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum Modification: Codable, Equatable { 4 | case deleted(ClosedRange) 5 | case inserted(Int, [String]) 6 | } 7 | 8 | public extension [String] { 9 | mutating func apply(_ modifications: [Modification]) { 10 | for modification in modifications { 11 | switch modification { 12 | case let .deleted(range): 13 | if isEmpty { break } 14 | let removingRange = range.lowerBound..<(range.upperBound + 1) 15 | removeSubrange(removingRange.clamped(to: 0.. Array { 23 | var newArray = self 24 | newArray.apply(modifications) 25 | return newArray 26 | } 27 | } 28 | 29 | public extension NSMutableArray { 30 | func apply(_ modifications: [Modification]) { 31 | for modification in modifications { 32 | switch modification { 33 | case let .deleted(range): 34 | if count == 0 { break } 35 | let newRange = range.clamped(to: 0...(count - 1)) 36 | removeObjects(in: NSRange(newRange)) 37 | case let .inserted(index, strings): 38 | for string in strings.reversed() { 39 | insert(string, at: Swift.min(count, index)) 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Helper/ReloadLaunchAgent.swift: -------------------------------------------------------------------------------- 1 | import ArgumentParser 2 | import Foundation 3 | 4 | struct ReloadLaunchAgent: ParsableCommand { 5 | static var configuration = CommandConfiguration( 6 | abstract: "Reload the launch agent" 7 | ) 8 | 9 | @Option(name: .long, help: "The service identifier of the service.") 10 | var serviceIdentifier: String 11 | 12 | var launchAgentDirURL: URL { 13 | FileManager.default.homeDirectoryForCurrentUser 14 | .appendingPathComponent("Library/LaunchAgents") 15 | } 16 | 17 | var launchAgentPath: String { 18 | launchAgentDirURL.appendingPathComponent("\(serviceIdentifier).plist").path 19 | } 20 | 21 | func run() throws { 22 | try? launchctl("unload", launchAgentPath) 23 | try launchctl("load", launchAgentPath) 24 | } 25 | } 26 | 27 | private func launchctl(_ args: String...) throws { 28 | return try process("/bin/launchctl", args) 29 | } 30 | 31 | private func process(_ launchPath: String, _ args: [String]) throws { 32 | let task = Process() 33 | task.launchPath = launchPath 34 | task.arguments = args 35 | task.environment = [ 36 | "PATH": "/usr/bin", 37 | ] 38 | let outpipe = Pipe() 39 | task.standardOutput = outpipe 40 | try task.run() 41 | task.waitUntilExit() 42 | 43 | struct E: Error, LocalizedError { 44 | var errorDescription: String? 45 | } 46 | 47 | if task.terminationStatus == 0 { 48 | return 49 | } 50 | throw E( 51 | errorDescription: "Failed to restart. Please make sure the launch agent is already loaded." 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /Core/Sources/SuggestionWidget/TabView.swift: -------------------------------------------------------------------------------- 1 | import ComposableArchitecture 2 | import SwiftUI 3 | 4 | struct TabView: View { 5 | let store: StoreOf 6 | 7 | struct State: Equatable { 8 | var chatPanelInASeparateWindow: Bool 9 | var colorScheme: ColorScheme 10 | } 11 | 12 | var body: some View { 13 | WithViewStore( 14 | store, 15 | observe: { 16 | State( 17 | chatPanelInASeparateWindow: $0.chatPanelInASeparateWindow, 18 | colorScheme: $0.colorScheme 19 | ) 20 | } 21 | ) { viewStore in 22 | Button(action: { 23 | viewStore.send(.toggleChatPanelDetachedButtonClicked) 24 | }, label: { 25 | Image(systemName: "ellipsis.bubble.fill") 26 | .frame(width: Style.widgetWidth, height: Style.widgetHeight) 27 | .background( 28 | Color.userChatContentBackground, 29 | in: Circle() 30 | ) 31 | }) 32 | .buttonStyle(.plain) 33 | .opacity(viewStore.chatPanelInASeparateWindow ? 1 : 0) 34 | .preferredColorScheme(viewStore.colorScheme) 35 | .frame(maxWidth: Style.widgetWidth, maxHeight: Style.widgetHeight) 36 | } 37 | } 38 | } 39 | 40 | struct TabView_Preview: PreviewProvider { 41 | static var previews: some View { 42 | VStack { 43 | TabView(store: .init(initialState: .init(), reducer: ChatPanelFeature())) 44 | } 45 | .frame(width: 30) 46 | .background(Color.black) 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /Core/Sources/HostApp/AccountSettings/BingSearchView.swift: -------------------------------------------------------------------------------- 1 | import AppKit 2 | import Client 3 | import OpenAIService 4 | import Preferences 5 | import SuggestionModel 6 | import SwiftUI 7 | 8 | final class BingSearchViewSettings: ObservableObject { 9 | @AppStorage(\.bingSearchSubscriptionKey) var bingSearchSubscriptionKey: String 10 | @AppStorage(\.bingSearchEndpoint) var bingSearchEndpoint: String 11 | init() {} 12 | } 13 | 14 | struct BingSearchView: View { 15 | @Environment(\.openURL) var openURL 16 | @StateObject var settings = BingSearchViewSettings() 17 | 18 | var body: some View { 19 | Form { 20 | Button(action: { 21 | let url = URL(string: "https://www.microsoft.com/bing/apis/bing-web-search-api")! 22 | openURL(url) 23 | }) { 24 | Text("Apply for Subscription Key for Free") 25 | } 26 | 27 | SecureField(text: $settings.bingSearchSubscriptionKey, prompt: Text("")) { 28 | Text("Bing Search Subscription Key") 29 | } 30 | .textFieldStyle(.roundedBorder) 31 | 32 | TextField( 33 | text: $settings.bingSearchEndpoint, 34 | prompt: Text("https://api.bing.microsoft.com/***") 35 | ) { 36 | Text("Bing Search Endpoint") 37 | }.textFieldStyle(.roundedBorder) 38 | } 39 | } 40 | } 41 | 42 | struct BingSearchView_Previews: PreviewProvider { 43 | static var previews: some View { 44 | VStack(alignment: .leading, spacing: 8) { 45 | BingSearchView() 46 | } 47 | .frame(height: 800) 48 | .padding(.all, 8) 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /Core/Sources/ChatService/CustomCommandTemplateProcessor.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SuggestionModel 3 | import XcodeInspector 4 | 5 | struct CustomCommandTemplateProcessor { 6 | func process(_ text: String) -> String { 7 | let info = getEditorInformation() 8 | let editorContent = info.editorContent 9 | let updatedText = text 10 | .replacingOccurrences(of: "{{selected_code}}", with: """ 11 | \(editorContent?.selectedContent.trimmingCharacters(in: .whitespacesAndNewlines) ?? "") 12 | """) 13 | .replacingOccurrences( 14 | of: "{{active_editor_language}}", 15 | with: info.language.rawValue 16 | ) 17 | .replacingOccurrences( 18 | of: "{{active_editor_file_url}}", 19 | with: info.documentURL?.path ?? "" 20 | ) 21 | .replacingOccurrences( 22 | of: "{{active_editor_file_name}}", 23 | with: info.documentURL?.lastPathComponent ?? "" 24 | ) 25 | return updatedText 26 | } 27 | 28 | struct EditorInformation { 29 | let editorContent: SourceEditor.Content? 30 | let language: CodeLanguage 31 | let documentURL: URL? 32 | } 33 | 34 | func getEditorInformation() -> EditorInformation { 35 | let editorContent = XcodeInspector.shared.focusedEditor?.content 36 | let documentURL = XcodeInspector.shared.activeDocumentURL 37 | let language = documentURL.map(languageIdentifierFromFileURL) ?? .plaintext 38 | 39 | return .init( 40 | editorContent: editorContent, 41 | language: language, 42 | documentURL: documentURL 43 | ) 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Core/Tests/ServiceTests/PseudoCommandHandlerFileProcessingTests.swift: -------------------------------------------------------------------------------- 1 | import SuggestionModel 2 | import XCTest 3 | @testable import Service 4 | 5 | class PseudoCommandHandlerFileProcessingTests: XCTestCase { 6 | func test_convert_range_0_0() { 7 | XCTAssertEqual( 8 | PseudoCommandHandler().convertRangeToCursorRange(0...0, in: "\n"), 9 | CursorRange(start: .zero, end: .init(line: 0, character: 0)) 10 | ) 11 | } 12 | 13 | func test_convert_range_same_line() { 14 | XCTAssertEqual( 15 | PseudoCommandHandler().convertRangeToCursorRange(1...5, in: "123456789\n"), 16 | CursorRange(start: .init(line: 0, character: 1), end: .init(line: 0, character: 5)) 17 | ) 18 | } 19 | 20 | func test_convert_range_multiple_line() { 21 | XCTAssertEqual( 22 | PseudoCommandHandler() 23 | .convertRangeToCursorRange(5...25, in: "123456789\n123456789\n123456789\n"), 24 | CursorRange(start: .init(line: 0, character: 5), end: .init(line: 2, character: 5)) 25 | ) 26 | } 27 | 28 | func test_convert_range_all_line() { 29 | XCTAssertEqual( 30 | PseudoCommandHandler() 31 | .convertRangeToCursorRange(0...29, in: "123456789\n123456789\n123456789\n"), 32 | CursorRange(start: .init(line: 0, character: 0), end: .init(line: 2, character: 9)) 33 | ) 34 | } 35 | 36 | func test_convert_range_out_of_range() { 37 | XCTAssertEqual( 38 | PseudoCommandHandler() 39 | .convertRangeToCursorRange(0...70, in: "123456789\n123456789\n123456789\n"), 40 | CursorRange(start: .init(line: 0, character: 0), end: .init(line: 3, character: 0)) 41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /EditorExtension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | APPLICATION_SUPPORT_FOLDER 6 | $(APPLICATION_SUPPORT_FOLDER) 7 | BUNDLE_IDENTIFIER_BASE 8 | $(BUNDLE_IDENTIFIER_BASE) 9 | CFBundleDevelopmentRegion 10 | $(DEVELOPMENT_LANGUAGE) 11 | CFBundleDisplayName 12 | $(EXTENSION_BUNDLE_DISPLAY_NAME) 13 | CFBundleExecutable 14 | $(EXECUTABLE_NAME) 15 | CFBundleIdentifier 16 | $(PRODUCT_BUNDLE_IDENTIFIER) 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundleName 20 | $(EXTENSION_BUNDLE_NAME) 21 | CFBundlePackageType 22 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 23 | CFBundleShortVersionString 24 | $(MARKETING_VERSION) 25 | CFBundleVersion 26 | $(CURRENT_PROJECT_VERSION) 27 | HOST_APP_NAME 28 | $(HOST_APP_NAME) 29 | NSExtension 30 | 31 | NSExtensionAttributes 32 | 33 | XCSourceEditorCommandDefinitions 34 | 35 | XCSourceEditorExtensionPrincipalClass 36 | $(PRODUCT_MODULE_NAME).SourceEditorExtension 37 | 38 | NSExtensionPointIdentifier 39 | com.apple.dt.Xcode.extension.source-editor 40 | 41 | NSHumanReadableCopyright 42 | 43 | TEAM_ID_PREFIX 44 | $(TeamIdentifierPrefix) 45 | 46 | 47 | -------------------------------------------------------------------------------- /Tool/Sources/LangChain/DocumentLoader/TextLoader.swift: -------------------------------------------------------------------------------- 1 | import AppKit 2 | import Foundation 3 | 4 | /// Load a text document from local file. 5 | public struct TextLoader: DocumentLoader { 6 | public enum MetadataKeys { 7 | public static let filename = "filename" 8 | public static let `extension` = "extension" 9 | public static let contentModificationDate = "contentModificationDate" 10 | public static let filePath = "filePath" 11 | } 12 | 13 | let url: URL 14 | let encoding: String.Encoding 15 | let options: [NSAttributedString.DocumentReadingOptionKey: Any] 16 | 17 | public init( 18 | url: URL, 19 | encoding: String.Encoding = .utf8, 20 | options: [NSAttributedString.DocumentReadingOptionKey: Any] = [:] 21 | ) { 22 | self.url = url 23 | self.encoding = encoding 24 | self.options = options 25 | } 26 | 27 | public func load() async throws -> [Document] { 28 | let data = try Data(contentsOf: url) 29 | let attributedString = try NSAttributedString( 30 | data: data, 31 | options: options, 32 | documentAttributes: nil 33 | ) 34 | let modificationDate = try? url.resourceValues(forKeys: [.contentModificationDateKey]) 35 | .contentModificationDate 36 | return [Document(pageContent: attributedString.string, metadata: [ 37 | MetadataKeys.filename: .string(url.lastPathComponent), 38 | MetadataKeys.extension: .string(url.pathExtension), 39 | MetadataKeys.contentModificationDate: .number( 40 | (modificationDate ?? Date()).timeIntervalSince1970 41 | ), 42 | MetadataKeys.filePath: .string(url.path), 43 | ])] 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLSelection.swift: -------------------------------------------------------------------------------- 1 | import ComposableArchitecture 2 | import Foundation 3 | import Preferences 4 | import SwiftUI 5 | 6 | struct BaseURLSelection: ReducerProtocol { 7 | struct State: Equatable { 8 | @BindingState var baseURL: String = "" 9 | var availableBaseURLs: [String] = [] 10 | } 11 | 12 | enum Action: Equatable, BindableAction { 13 | case appear 14 | case refreshAvailableBaseURLNames 15 | case binding(BindingAction) 16 | } 17 | 18 | @Dependency(\.toast) var toast 19 | @Dependency(\.userDefaults) var userDefaults 20 | 21 | var body: some ReducerProtocol { 22 | BindingReducer() 23 | 24 | Reduce { state, action in 25 | switch action { 26 | case .appear: 27 | return .run { send in 28 | await send(.refreshAvailableBaseURLNames) 29 | } 30 | 31 | case .refreshAvailableBaseURLNames: 32 | let chatModels = userDefaults.value(for: \.chatModels) 33 | let embeddingModels = userDefaults.value(for: \.embeddingModels) 34 | var allBaseURLs = Set( 35 | chatModels.map(\.info.baseURL) 36 | .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } 37 | + embeddingModels.map(\.info.baseURL) 38 | .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } 39 | ) 40 | allBaseURLs.remove("") 41 | state.availableBaseURLs = Array(allBaseURLs).sorted() 42 | return .none 43 | 44 | case .binding: 45 | return .none 46 | } 47 | } 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /Core/Sources/PromptToCodeService/PreviewPromptToCodeService.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SuggestionModel 3 | 4 | public final class PreviewPromptToCodeService: PromptToCodeServiceType { 5 | public init() {} 6 | 7 | public func modifyCode( 8 | code: String, 9 | requirement: String, 10 | source: PromptToCodeSource, 11 | isDetached: Bool, 12 | extraSystemPrompt: String?, 13 | generateDescriptionRequirement: Bool? 14 | ) async throws -> AsyncThrowingStream<(code: String, description: String), Error> { 15 | return AsyncThrowingStream { continuation in 16 | Task { 17 | let code = """ 18 | struct Cat { 19 | var name: String 20 | } 21 | 22 | print("Hello world!") 23 | """ 24 | let description = "I have created a struct `Cat`." 25 | var resultCode = "" 26 | var resultDescription = "" 27 | do { 28 | for character in code { 29 | try await Task.sleep(nanoseconds: 50_000_000) 30 | resultCode.append(character) 31 | continuation.yield((resultCode, resultDescription)) 32 | } 33 | for character in description { 34 | try await Task.sleep(nanoseconds: 50_000_000) 35 | resultDescription.append(character) 36 | continuation.yield((resultCode, resultDescription)) 37 | } 38 | continuation.finish() 39 | } catch { 40 | continuation.finish(throwing: error) 41 | } 42 | } 43 | } 44 | } 45 | 46 | public func stopResponding() {} 47 | } 48 | 49 | -------------------------------------------------------------------------------- /Core/Sources/HostApp/AccountSettings/APIKeyManagement/APIKeySubmission.swift: -------------------------------------------------------------------------------- 1 | import ComposableArchitecture 2 | import Foundation 3 | 4 | struct APIKeySubmission: ReducerProtocol { 5 | struct State: Equatable { 6 | @BindingState var name: String = "" 7 | @BindingState var key: String = "" 8 | } 9 | 10 | enum Action: Equatable, BindableAction { 11 | case binding(BindingAction) 12 | case saveButtonClicked 13 | case cancelButtonClicked 14 | case saveFinished 15 | } 16 | 17 | @Dependency(\.toast) var toast 18 | @Dependency(\.apiKeyKeychain) var keychain 19 | 20 | enum E: Error, LocalizedError { 21 | case nameIsEmpty 22 | case keyIsEmpty 23 | } 24 | 25 | var body: some ReducerProtocol { 26 | BindingReducer() 27 | 28 | Reduce { state, action in 29 | switch action { 30 | case .saveButtonClicked: 31 | do { 32 | guard !state.name.isEmpty else { throw E.nameIsEmpty } 33 | guard !state.key.isEmpty else { throw E.keyIsEmpty } 34 | 35 | try keychain.update( 36 | state.key, 37 | key: state.name.trimmingCharacters(in: .whitespacesAndNewlines) 38 | ) 39 | return .run { send in 40 | await send(.saveFinished) 41 | } 42 | } catch { 43 | toast(error.localizedDescription, .error) 44 | return .none 45 | } 46 | 47 | case .cancelButtonClicked: 48 | return .none 49 | 50 | case .saveFinished: 51 | return .none 52 | 53 | case .binding: 54 | return .none 55 | } 56 | } 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /Core/Sources/ServiceUpdateMigration/MigrateTo135.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import GitHubCopilotService 3 | 4 | func migrateFromLowerThanOrEqualToVersion135() throws { 5 | // 0. Create the application support folder if it doesn't exist 6 | 7 | let urls = try GitHubCopilotBaseService.createFoldersIfNeeded() 8 | 9 | // 1. Move the undefined folder in application support into a sub folder called `GitHub 10 | // Copilot/support` 11 | 12 | let undefinedFolderURL = urls.applicationSupportURL.appendingPathComponent("undefined") 13 | var isUndefinedADirectory: ObjCBool = false 14 | let isUndefinedExisted = FileManager.default.fileExists( 15 | atPath: undefinedFolderURL.path, 16 | isDirectory: &isUndefinedADirectory 17 | ) 18 | if isUndefinedExisted, isUndefinedADirectory.boolValue { 19 | try FileManager.default.moveItem( 20 | at: undefinedFolderURL, 21 | to: urls.supportURL.appendingPathComponent("undefined") 22 | ) 23 | } 24 | 25 | // 2. Copy the GitHub copilot language service to `GitHub Copilot/executable` 26 | 27 | let copilotFolderURL = urls.executableURL.appendingPathComponent("copilot") 28 | var copilotIsFolder: ObjCBool = false 29 | let executable = Bundle.main.resourceURL?.appendingPathComponent("copilot") 30 | if let executable, 31 | FileManager.default.fileExists(atPath: executable.path, isDirectory: &copilotIsFolder), 32 | !FileManager.default.fileExists(atPath: copilotFolderURL.path) 33 | { 34 | try FileManager.default.copyItem( 35 | at: executable, 36 | to: urls.executableURL.appendingPathComponent("copilot") 37 | ) 38 | } 39 | 40 | // 3. Use chmod to change the permission of the executable to 755 41 | 42 | try FileManager.default.setAttributes( 43 | [.posixPermissions: 0o755], 44 | ofItemAtPath: copilotFolderURL.path 45 | ) 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Core/Sources/HostApp/AccountSettings/APIKeyManagement/APIKeyPicker.swift: -------------------------------------------------------------------------------- 1 | import ComposableArchitecture 2 | import SwiftUI 3 | 4 | struct APIKeyPicker: View { 5 | let store: StoreOf 6 | 7 | var body: some View { 8 | WithViewStore(store) { viewStore in 9 | HStack { 10 | Picker( 11 | selection: viewStore.$apiKeyName, 12 | content: { 13 | Text("No API Key").tag("") 14 | if viewStore.state.availableAPIKeyNames.isEmpty { 15 | Text("No API key found, please add a new one →") 16 | } 17 | 18 | if !viewStore.state.availableAPIKeyNames.contains(viewStore.state.apiKeyName), 19 | !viewStore.state.apiKeyName.isEmpty { 20 | Text("Key not found: \(viewStore.state.apiKeyName)") 21 | .tag(viewStore.state.apiKeyName) 22 | } 23 | 24 | ForEach(viewStore.state.availableAPIKeyNames, id: \.self) { name in 25 | Text(name).tag(name) 26 | } 27 | 28 | }, 29 | label: { Text("API Key") } 30 | ) 31 | 32 | Button(action: { store.send(.manageAPIKeysButtonClicked) }) { 33 | Text(Image(systemName: "key")) 34 | } 35 | }.sheet(isPresented: viewStore.$isAPIKeyManagementPresented) { 36 | APIKeyManagementView(store: store.scope( 37 | state: \.apiKeyManagement, 38 | action: APIKeySelection.Action.apiKeyManagement 39 | )) 40 | } 41 | } 42 | .onAppear { 43 | store.send(.appear) 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Core/Sources/HostApp/AccountSettings/APIKeyManagement/APIKeySelection.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftUI 3 | import ComposableArchitecture 4 | 5 | struct APIKeySelection: ReducerProtocol { 6 | struct State: Equatable { 7 | @BindingState var apiKeyName: String = "" 8 | var availableAPIKeyNames: [String] { 9 | apiKeyManagement.availableAPIKeyNames 10 | } 11 | var apiKeyManagement: APIKeyManagement.State = .init() 12 | @BindingState var isAPIKeyManagementPresented: Bool = false 13 | } 14 | 15 | enum Action: Equatable, BindableAction { 16 | case appear 17 | case manageAPIKeysButtonClicked 18 | 19 | case binding(BindingAction) 20 | case apiKeyManagement(APIKeyManagement.Action) 21 | } 22 | 23 | @Dependency(\.toast) var toast 24 | @Dependency(\.apiKeyKeychain) var keychain 25 | 26 | var body: some ReducerProtocol { 27 | BindingReducer() 28 | 29 | Scope(state: \.apiKeyManagement, action: /Action.apiKeyManagement) { 30 | APIKeyManagement() 31 | } 32 | 33 | Reduce { state, action in 34 | switch action { 35 | case .appear: 36 | return .run { send in 37 | await send(.apiKeyManagement(.refreshAvailableAPIKeyNames)) 38 | } 39 | 40 | case .manageAPIKeysButtonClicked: 41 | state.isAPIKeyManagementPresented = true 42 | return .none 43 | 44 | case .binding: 45 | return .none 46 | 47 | case .apiKeyManagement(.closeButtonClicked): 48 | state.isAPIKeyManagementPresented = false 49 | return .none 50 | 51 | case .apiKeyManagement: 52 | return .none 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Core/Sources/ChatContextCollectors/ActiveDocumentChatContextCollector/Functions/MoveToFocusedCodeFunction.swift: -------------------------------------------------------------------------------- 1 | import ASTParser 2 | import Foundation 3 | import OpenAIService 4 | import SuggestionModel 5 | 6 | struct MoveToFocusedCodeFunction: ChatGPTFunction { 7 | typealias Arguments = NoArguments 8 | 9 | struct Result: ChatGPTFunctionResult { 10 | var range: CursorRange 11 | 12 | var botReadableContent: String { 13 | "Editing Document Context is updated to display code at \(range)." 14 | } 15 | } 16 | 17 | struct E: Error, LocalizedError { 18 | var errorDescription: String? 19 | } 20 | 21 | var name: String { 22 | "moveToFocusedCode" 23 | } 24 | 25 | var description: String { 26 | "Move editing document context to the selected or focused code" 27 | } 28 | 29 | weak var contextCollector: ActiveDocumentChatContextCollector? 30 | 31 | init(contextCollector: ActiveDocumentChatContextCollector) { 32 | self.contextCollector = contextCollector 33 | } 34 | 35 | func prepare(reportProgress: @escaping (String) async -> Void) async { 36 | await reportProgress("Finding the focused code..") 37 | } 38 | 39 | func call( 40 | arguments: Arguments, 41 | reportProgress: @escaping (String) async -> Void 42 | ) async throws -> Result { 43 | await reportProgress("Finding the focused code..") 44 | contextCollector?.activeDocumentContext?.moveToFocusedCode() 45 | guard let newContext = contextCollector?.activeDocumentContext?.focusedContext else { 46 | let progress = "Failed to move to focused code." 47 | await reportProgress(progress) 48 | throw E(errorDescription: progress) 49 | } 50 | let progress = "Looking at \(newContext.codeRange)." 51 | await reportProgress(progress) 52 | return .init(range: newContext.codeRange) 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /Tool/Sources/LangChain/ChatModel/OpenAIChat.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import OpenAIService 3 | 4 | public struct OpenAIChat: ChatModel { 5 | public var configuration: ChatGPTConfiguration 6 | public var memory: ChatGPTMemory? 7 | public var functionProvider: ChatGPTFunctionProvider 8 | public var stream: Bool 9 | 10 | public init( 11 | configuration: ChatGPTConfiguration = UserPreferenceChatGPTConfiguration(), 12 | memory: ChatGPTMemory? = ConversationChatGPTMemory(systemPrompt: ""), 13 | functionProvider: ChatGPTFunctionProvider = NoChatGPTFunctionProvider(), 14 | stream: Bool 15 | ) { 16 | self.configuration = configuration 17 | self.memory = memory 18 | self.functionProvider = functionProvider 19 | self.stream = stream 20 | } 21 | 22 | public func generate( 23 | prompt: [ChatMessage], 24 | stops: [String], 25 | callbackManagers: [CallbackManager] 26 | ) async throws -> ChatMessage { 27 | let memory = memory ?? EmptyChatGPTMemory() 28 | 29 | let service = ChatGPTService( 30 | memory: memory, 31 | configuration: configuration, 32 | functionProvider: functionProvider 33 | ) 34 | for message in prompt { 35 | await memory.appendMessage(message) 36 | } 37 | 38 | if stream { 39 | let stream = try await service.send(content: "") 40 | var message = "" 41 | for try await trunk in stream { 42 | message.append(trunk) 43 | callbackManagers.send(CallbackEvents.LLMDidProduceNewToken(info: trunk)) 44 | } 45 | return await memory.messages.last ?? .init(role: .assistant, content: "") 46 | } else { 47 | let _ = try await service.sendAndWait(content: "") 48 | return await memory.messages.last ?? .init(role: .assistant, content: "") 49 | } 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Core/Sources/SuggestionWidget/FeatureReducers/SharedPanelFeature.swift: -------------------------------------------------------------------------------- 1 | import ComposableArchitecture 2 | import Environment 3 | import Preferences 4 | import SwiftUI 5 | 6 | public struct SharedPanelFeature: ReducerProtocol { 7 | public struct Content: Equatable { 8 | public var promptToCodeGroup = PromptToCodeGroup.State() 9 | var suggestion: SuggestionProvider? 10 | public var promptToCode: PromptToCode.State? { promptToCodeGroup.activePromptToCode } 11 | var error: String? 12 | } 13 | 14 | public struct State: Equatable { 15 | var content: Content = .init() 16 | var colorScheme: ColorScheme = .light 17 | var alignTopToAnchor = false 18 | var isPanelDisplayed: Bool = false 19 | var isEmpty: Bool { 20 | if content.error != nil { return false } 21 | if content.promptToCode != nil { return false } 22 | if content.suggestion != nil, 23 | UserDefaults.shared 24 | .value(for: \.suggestionPresentationMode) == .floatingWidget { return false } 25 | return true 26 | } 27 | 28 | var opacity: Double { 29 | guard isPanelDisplayed else { return 0 } 30 | guard !isEmpty else { return 0 } 31 | return 1 32 | } 33 | } 34 | 35 | public enum Action: Equatable { 36 | case errorMessageCloseButtonTapped 37 | case promptToCodeGroup(PromptToCodeGroup.Action) 38 | } 39 | 40 | public var body: some ReducerProtocol { 41 | Scope(state: \.content.promptToCodeGroup, action: /Action.promptToCodeGroup) { 42 | PromptToCodeGroup() 43 | } 44 | 45 | Reduce { state, action in 46 | switch action { 47 | case .errorMessageCloseButtonTapped: 48 | state.content.error = nil 49 | return .none 50 | case .promptToCodeGroup: 51 | return .none 52 | } 53 | } 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /Playground.playground/Pages/WebScrapper.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | import AppKit 2 | import LangChain 3 | import PlaygroundSupport 4 | import SwiftUI 5 | 6 | struct ScrapperForm: View { 7 | @State var webDocuments: [Document] = [] 8 | @State var isProcessing: Bool = false 9 | @State var url: String = "https://developer.apple.com/documentation/swift/applying-macros" 10 | 11 | var body: some View { 12 | Form { 13 | Section(header: Text("Input")) { 14 | TextField("URL", text: $url) 15 | Button("Scrap") { 16 | Task { 17 | do { 18 | try await scrap() 19 | } catch { 20 | webDocuments = 21 | [.init(pageContent: error.localizedDescription, metadata: [:])] 22 | } 23 | } 24 | } 25 | .disabled(isProcessing) 26 | } 27 | Section(header: Text("Web Content")) { 28 | ForEach(webDocuments, id: \.pageContent) { document in 29 | VStack(alignment: .leading) { 30 | Text(document.pageContent) 31 | .font(.body) 32 | } 33 | Divider() 34 | } 35 | } 36 | } 37 | .formStyle(.grouped) 38 | } 39 | 40 | func scrap() async throws { 41 | webDocuments = [] 42 | isProcessing = true 43 | defer { isProcessing = false } 44 | guard let url = URL(string: url) else { return } 45 | let webLoader = WebLoader(urls: [url]) 46 | webDocuments = try await webLoader.load() 47 | } 48 | } 49 | 50 | let hostingView = NSHostingController( 51 | rootView: ScrapperForm() 52 | .frame(width: 600, height: 800) 53 | ) 54 | 55 | PlaygroundPage.current.needsIndefiniteExecution = true 56 | PlaygroundPage.current.liveView = hostingView 57 | 58 | -------------------------------------------------------------------------------- /Core/Sources/Client/XPCService.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Logger 3 | import os.log 4 | import XPCShared 5 | 6 | let shared = XPCService() 7 | 8 | public func getService() throws -> AsyncXPCService { 9 | if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" { 10 | struct RunningInPreview: Error {} 11 | throw RunningInPreview() 12 | } 13 | return AsyncXPCService(service: shared) 14 | } 15 | 16 | class XPCService { 17 | private var isInvalidated = false 18 | private lazy var _connection: NSXPCConnection = buildConnection() 19 | 20 | var connection: NSXPCConnection { 21 | if isInvalidated { 22 | _connection.invalidationHandler = {} 23 | _connection.interruptionHandler = {} 24 | isInvalidated = false 25 | _connection.invalidate() 26 | rebuildConnection() 27 | } 28 | return _connection 29 | } 30 | 31 | private func buildConnection() -> NSXPCConnection { 32 | let connection = NSXPCConnection( 33 | machServiceName: Bundle(for: XPCService.self) 34 | .object(forInfoDictionaryKey: "BUNDLE_IDENTIFIER_BASE") as! String + 35 | ".ExtensionService" 36 | ) 37 | connection.remoteObjectInterface = 38 | NSXPCInterface(with: XPCServiceProtocol.self) 39 | connection.invalidationHandler = { [weak self] in 40 | Logger.client.info("XPCService Invalidated") 41 | self?.isInvalidated = true 42 | } 43 | connection.interruptionHandler = { [weak self] in 44 | Logger.client.info("XPCService interrupted") 45 | self?.isInvalidated = true 46 | } 47 | connection.resume() 48 | return connection 49 | } 50 | 51 | func rebuildConnection() { 52 | _connection = buildConnection() 53 | } 54 | 55 | deinit { 56 | _connection.invalidationHandler = {} 57 | _connection.interruptionHandler = {} 58 | _connection.invalidate() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Tool/Tests/LangChainTests/TextSplitterTests/TextSplitterTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | @testable import LangChain 4 | 5 | final class TextSplitterTests: XCTestCase { 6 | struct Splitter: TextSplitter { 7 | var chunkSize: Int 8 | var chunkOverlap: Int 9 | var lengthFunction: (String) -> Int = { $0.count } 10 | func split(text: String) async throws -> [String] { 11 | [] 12 | } 13 | } 14 | 15 | func test_split_text_with_text_separator() async throws { 16 | let splitter = Splitter( 17 | chunkSize: 1, 18 | chunkOverlap: 1 19 | ) 20 | 21 | let result = splitter.split( 22 | text: "Madam Speaker, Madam Vice President, our First", 23 | separator: " " 24 | ) 25 | 26 | XCTAssertEqual( 27 | result, 28 | ["Madam", " Speaker,", " Madam", " Vice", " President,", " our", " First"] 29 | ) 30 | } 31 | 32 | func test_split_text_with_regex_separator() async throws { 33 | let splitter = Splitter( 34 | chunkSize: 1, 35 | chunkOverlap: 1 36 | ) 37 | 38 | let result = splitter.split( 39 | text: "Madam Speaker, Madam Vice President, our First", 40 | separator: "\\s\\w\\w\\w\\w\\s" // split at " Vice " 41 | ) 42 | 43 | XCTAssertEqual( 44 | result, 45 | ["Madam Speaker, Madam", " Vice President, our First"] 46 | ) 47 | } 48 | 49 | func test_merge_splits() async throws { 50 | let splitter = Splitter( 51 | chunkSize: 15, 52 | chunkOverlap: 5 53 | ) 54 | 55 | let result = splitter.mergeSplits( 56 | ["Madam", " Speaker,", " Madam", " Vice", " President,", " our", " First"] 57 | ) 58 | 59 | XCTAssertEqual( 60 | result, 61 | ["Madam Speaker,", "Madam Vice", "President, our", "our First"] 62 | ) 63 | XCTAssertTrue(result.allSatisfy { $0.count <= 15 }) 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /Core/Tests/ServiceTests/Environment.swift: -------------------------------------------------------------------------------- 1 | import AppKit 2 | import Client 3 | import Environment 4 | import Foundation 5 | import GitHubCopilotService 6 | import SuggestionModel 7 | import Workspace 8 | import XCTest 9 | import XPCShared 10 | 11 | @testable import Service 12 | 13 | func completion(text: String, range: CursorRange, uuid: String = "") -> CodeSuggestion { 14 | .init(text: text, position: range.start, uuid: uuid, range: range, displayText: text) 15 | } 16 | 17 | class MockSuggestionService: GitHubCopilotSuggestionServiceType { 18 | func terminate() async { 19 | fatalError() 20 | } 21 | 22 | func cancelRequest() async { 23 | fatalError() 24 | } 25 | 26 | func notifyOpenTextDocument(fileURL: URL, content: String) async throws { 27 | fatalError() 28 | } 29 | 30 | func notifyChangeTextDocument(fileURL: URL, content: String) async throws { 31 | fatalError() 32 | } 33 | 34 | func notifyCloseTextDocument(fileURL: URL) async throws { 35 | fatalError() 36 | } 37 | 38 | func notifySaveTextDocument(fileURL: URL) async throws { 39 | fatalError() 40 | } 41 | 42 | var completions = [CodeSuggestion]() 43 | var accepted: String? 44 | var rejected: [String] = [] 45 | 46 | init(completions: [CodeSuggestion]) { 47 | self.completions = completions 48 | } 49 | 50 | func getCompletions( 51 | fileURL: URL, 52 | content: String, 53 | cursorPosition: SuggestionModel.CursorPosition, 54 | tabSize: Int, 55 | indentSize: Int, 56 | usesTabsForIndentation: Bool, 57 | ignoreSpaceOnlySuggestions: Bool, 58 | ignoreTrailingNewLinesAndSpaces: Bool 59 | ) async throws -> [SuggestionModel.CodeSuggestion] { 60 | completions 61 | } 62 | 63 | func notifyAccepted(_ completion: CodeSuggestion) async { 64 | accepted = completion.uuid 65 | } 66 | 67 | func notifyRejected(_ completions: [CodeSuggestion]) async { 68 | rejected = completions.map(\.uuid) 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /Core/Sources/Service/SuggestionPresenter/PresentInWindowSuggestionPresenter.swift: -------------------------------------------------------------------------------- 1 | import ChatService 2 | import Foundation 3 | import OpenAIService 4 | import SuggestionModel 5 | import SuggestionWidget 6 | 7 | struct PresentInWindowSuggestionPresenter { 8 | func presentSuggestion(fileURL: URL) { 9 | Task { @MainActor in 10 | let controller = Service.shared.guiController.widgetController 11 | controller.suggestCode() 12 | } 13 | } 14 | 15 | func discardSuggestion(fileURL: URL) { 16 | Task { @MainActor in 17 | let controller = Service.shared.guiController.widgetController 18 | controller.discardSuggestion() 19 | } 20 | } 21 | 22 | func markAsProcessing(_ isProcessing: Bool) { 23 | Task { @MainActor in 24 | let controller = Service.shared.guiController.widgetController 25 | controller.markAsProcessing(isProcessing) 26 | } 27 | } 28 | 29 | func presentError(_ error: Error) { 30 | if error is CancellationError { return } 31 | if let urlError = error as? URLError, urlError.code == URLError.cancelled { return } 32 | Task { @MainActor in 33 | let controller = Service.shared.guiController.widgetController 34 | controller.presentError(error.localizedDescription) 35 | } 36 | } 37 | 38 | func presentErrorMessage(_ message: String) { 39 | Task { @MainActor in 40 | let controller = Service.shared.guiController.widgetController 41 | controller.presentError(message) 42 | } 43 | } 44 | 45 | func closeChatRoom(fileURL: URL) { 46 | Task { @MainActor in 47 | let controller = Service.shared.guiController.widgetController 48 | controller.closeChatRoom() 49 | } 50 | } 51 | 52 | func presentChatRoom(fileURL: URL) { 53 | Task { @MainActor in 54 | let controller = Service.shared.guiController.widgetController 55 | controller.presentChatRoom() 56 | } 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /Core/Sources/ChatService/ContextAwareAutoManagedChatGPTMemory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import OpenAIService 3 | 4 | public final class ContextAwareAutoManagedChatGPTMemory: ChatGPTMemory { 5 | private let memory: AutoManagedChatGPTMemory 6 | let contextController: DynamicContextController 7 | let functionProvider: ChatFunctionProvider 8 | weak var chatService: ChatService? 9 | 10 | public var messages: [ChatMessage] { 11 | get async { await memory.messages } 12 | } 13 | 14 | public var remainingTokens: Int? { 15 | get async { await memory.remainingTokens } 16 | } 17 | 18 | public var history: [ChatMessage] { 19 | get async { await memory.history } 20 | } 21 | 22 | func observeHistoryChange(_ observer: @escaping () -> Void) { 23 | memory.observeHistoryChange(observer) 24 | } 25 | 26 | init( 27 | configuration: ChatGPTConfiguration, 28 | functionProvider: ChatFunctionProvider 29 | ) { 30 | memory = AutoManagedChatGPTMemory( 31 | systemPrompt: "", 32 | configuration: configuration, 33 | functionProvider: functionProvider 34 | ) 35 | contextController = DynamicContextController( 36 | memory: memory, 37 | functionProvider: functionProvider, 38 | configuration: configuration, 39 | contextCollectors: allContextCollectors 40 | ) 41 | self.functionProvider = functionProvider 42 | } 43 | 44 | public func mutateHistory(_ update: (inout [ChatMessage]) -> Void) async { 45 | await memory.mutateHistory(update) 46 | } 47 | 48 | public func refresh() async { 49 | let content = (await memory.history) 50 | .last(where: { $0.role == .user || $0.role == .function })?.content 51 | try? await contextController.updatePromptToMatchContent(systemPrompt: """ 52 | \(chatService?.systemPrompt ?? "") 53 | \(chatService?.extraSystemPrompt ?? "") 54 | """, content: content ?? "") 55 | await memory.refresh() 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /Core/Sources/ChatContextCollectors/ActiveDocumentChatContextCollector/Functions/ExpandFocusRangeFunction.swift: -------------------------------------------------------------------------------- 1 | import ASTParser 2 | import Foundation 3 | import OpenAIService 4 | import SuggestionModel 5 | 6 | struct ExpandFocusRangeFunction: ChatGPTFunction { 7 | struct Arguments: Codable {} 8 | 9 | struct Result: ChatGPTFunctionResult { 10 | var range: CursorRange 11 | 12 | var botReadableContent: String { 13 | "Editing Document Context is updated to display code at \(range)." 14 | } 15 | } 16 | 17 | struct E: Error, LocalizedError { 18 | var errorDescription: String? 19 | } 20 | 21 | var name: String { 22 | "expandFocusRange" 23 | } 24 | 25 | var description: String { 26 | "Call when Editing Document Context provides too little context to answer a question." 27 | } 28 | 29 | var argumentSchema: JSONSchemaValue { [ 30 | .type: "object", 31 | .properties: [:], 32 | ] } 33 | 34 | weak var contextCollector: ActiveDocumentChatContextCollector? 35 | 36 | init(contextCollector: ActiveDocumentChatContextCollector) { 37 | self.contextCollector = contextCollector 38 | } 39 | 40 | func prepare(reportProgress: @escaping (String) async -> Void) async { 41 | await reportProgress("Finding the focused code..") 42 | } 43 | 44 | func call( 45 | arguments: Arguments, 46 | reportProgress: @escaping (String) async -> Void 47 | ) async throws -> Result { 48 | await reportProgress("Finding the focused code..") 49 | contextCollector?.activeDocumentContext?.expandFocusedRangeToContextRange() 50 | guard let newContext = contextCollector?.activeDocumentContext?.focusedContext else { 51 | let progress = "Failed to expand focused code." 52 | await reportProgress(progress) 53 | throw E(errorDescription: progress) 54 | } 55 | let progress = "Looking at \(newContext.codeRange)." 56 | await reportProgress(progress) 57 | return .init(range: newContext.codeRange) 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /Tool/Sources/SharedUIComponents/CustomScrollView.swift: -------------------------------------------------------------------------------- 1 | import AppKit 2 | import Combine 3 | import Preferences 4 | import SwiftUI 5 | 6 | public struct CustomScrollViewHeightPreferenceKey: SwiftUI.PreferenceKey { 7 | public static var defaultValue: Double = 0 8 | public static func reduce(value: inout Double, nextValue: () -> Double) { 9 | value = nextValue() + value 10 | } 11 | } 12 | 13 | public struct CustomScrollViewUpdateHeightModifier: ViewModifier { 14 | public func body(content: Content) -> some View { 15 | content 16 | .background { 17 | GeometryReader { proxy in 18 | Color.clear 19 | .preference( 20 | key: CustomScrollViewHeightPreferenceKey.self, 21 | value: proxy.size.height 22 | ) 23 | } 24 | } 25 | } 26 | } 27 | 28 | /// Used to workaround a SwiftUI bug. https://github.com/intitni/CopilotForXcode/issues/122 29 | public struct CustomScrollView: View { 30 | @ViewBuilder var content: () -> Content 31 | @State var height: Double = 10 32 | @AppStorage(\.useCustomScrollViewWorkaround) var useNSScrollViewWrapper 33 | 34 | public init(content: @escaping () -> Content) { 35 | self.content = content 36 | } 37 | 38 | public var body: some View { 39 | if useNSScrollViewWrapper { 40 | List { 41 | content() 42 | .listRowInsets(EdgeInsets(top: 0, leading: -8, bottom: 0, trailing: -8)) 43 | .modifier(CustomScrollViewUpdateHeightModifier()) 44 | } 45 | .listStyle(.plain) 46 | .frame(idealHeight: max(10, height)) 47 | .onPreferenceChange(CustomScrollViewHeightPreferenceKey.self) { newHeight in 48 | Task { @MainActor in 49 | height = newHeight 50 | } 51 | } 52 | } else { 53 | ScrollView { 54 | content() 55 | } 56 | } 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /Core/Sources/Service/Service.swift: -------------------------------------------------------------------------------- 1 | import Dependencies 2 | import Foundation 3 | import Workspace 4 | 5 | #if canImport(KeyBindingManager) 6 | import EnhancedWorkspace 7 | import KeyBindingManager 8 | #endif 9 | 10 | @globalActor public enum ServiceActor { 11 | public actor TheActor {} 12 | public static let shared = TheActor() 13 | } 14 | 15 | /// The running extension service. 16 | public final class Service { 17 | public static let shared = Service() 18 | 19 | @WorkspaceActor 20 | let workspacePool: WorkspacePool 21 | @MainActor 22 | public let guiController = GraphicalUserInterfaceController() 23 | public let realtimeSuggestionController = RealtimeSuggestionController() 24 | public let scheduledCleaner: ScheduledCleaner 25 | #if canImport(KeyBindingManager) 26 | let keyBindingManager: KeyBindingManager 27 | #endif 28 | 29 | private init() { 30 | @Dependency(\.workspacePool) var workspacePool 31 | 32 | scheduledCleaner = .init(workspacePool: workspacePool, guiController: guiController) 33 | #if canImport(KeyBindingManager) 34 | keyBindingManager = .init( 35 | workspacePool: workspacePool, 36 | acceptSuggestion: { 37 | Task { 38 | await PseudoCommandHandler().acceptSuggestion() 39 | } 40 | } 41 | ) 42 | #endif 43 | 44 | workspacePool.registerPlugin { SuggestionServiceWorkspacePlugin(workspace: $0) } 45 | #if canImport(EnhancedWorkspace) 46 | if !UserDefaults.shared.value(for: \.disableEnhancedWorkspace) { 47 | workspacePool.registerPlugin { EnhancedWorkspacePlugin(workspace: $0) } 48 | } 49 | #endif 50 | 51 | self.workspacePool = workspacePool 52 | } 53 | 54 | @MainActor 55 | public func start() { 56 | scheduledCleaner.start() 57 | realtimeSuggestionController.start() 58 | guiController.start() 59 | #if canImport(KeyBindingManager) 60 | keyBindingManager.start() 61 | #endif 62 | DependencyUpdater().update() 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | title: "[Bug]: " 4 | labels: ["bug"] 5 | assignees: 6 | - intitni 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | Thanks for taking the time to fill out this bug report! 12 | - type: checkboxes 13 | id: before-reporting 14 | attributes: 15 | label: Before Reporting 16 | description: Before reporting the bug, we suggestion that you first refer to the [FAQ](https://github.com/intitni/CopilotForXcode/wiki/Frequently-Asked-Questions) to check if it may address your issue. And search for existing issues to avoid duplication. 17 | options: 18 | - label: I have checked FAQ, and there is no solution to my issue 19 | required: true 20 | - label: I have searched the existing issues, and there is no existing issue for my issue 21 | required: true 22 | - type: textarea 23 | id: what-happened 24 | attributes: 25 | label: What happened? 26 | description: Also tell us, what did you expect to happen? 27 | placeholder: Tell us what you see! 28 | value: "A bug happened!" 29 | validations: 30 | required: true 31 | - type: textarea 32 | id: reproduce 33 | attributes: 34 | label: How to reproduce the bug. 35 | description: If possible, please provide the steps to reproduce the bug. 36 | placeholder: "1. *****\n2.*****" 37 | value: "It just happens!" 38 | - type: textarea 39 | id: logs 40 | attributes: 41 | label: Relevant log output 42 | description: If it's a crash, please provide the crash report. You can find it in the Console.app. 43 | render: shell 44 | - type: input 45 | id: mac-version 46 | attributes: 47 | label: macOS version 48 | - type: input 49 | id: xcode-version 50 | attributes: 51 | label: Xcode version 52 | - type: input 53 | id: copilot-for-xcode-version 54 | attributes: 55 | label: Copilot for Xcode version 56 | - type: input 57 | id: node-version 58 | attributes: 59 | label: Node version 60 | 61 | -------------------------------------------------------------------------------- /Core/Sources/HostApp/CustomCommandSettings/CustomCommand.swift: -------------------------------------------------------------------------------- 1 | import ComposableArchitecture 2 | import Foundation 3 | import PlusFeatureFlag 4 | import Preferences 5 | import SwiftUI 6 | import Toast 7 | 8 | struct CustomCommandFeature: ReducerProtocol { 9 | struct State: Equatable { 10 | var editCustomCommand: EditCustomCommand.State? 11 | } 12 | 13 | let settings: CustomCommandView.Settings 14 | 15 | enum Action: Equatable { 16 | case createNewCommand 17 | case editCommand(CustomCommand) 18 | case editCustomCommand(EditCustomCommand.Action) 19 | case deleteCommand(CustomCommand) 20 | } 21 | 22 | @Dependency(\.toast) var toast 23 | 24 | var body: some ReducerProtocol { 25 | Reduce { state, action in 26 | switch action { 27 | case .createNewCommand: 28 | if !isFeatureAvailable(\.unlimitedCustomCommands), 29 | settings.customCommands.count >= 10 30 | { 31 | toast("Upgrade to Plus to add more commands", .info) 32 | return .none 33 | } 34 | state.editCustomCommand = EditCustomCommand.State(nil) 35 | return .none 36 | case let .editCommand(command): 37 | state.editCustomCommand = EditCustomCommand.State(command) 38 | return .none 39 | case .editCustomCommand(.close): 40 | state.editCustomCommand = nil 41 | return .none 42 | case let .deleteCommand(command): 43 | settings.customCommands.removeAll( 44 | where: { $0.id == command.id } 45 | ) 46 | if state.editCustomCommand?.commandId == command.id { 47 | state.editCustomCommand = nil 48 | } 49 | return .none 50 | case .editCustomCommand: 51 | return .none 52 | } 53 | }.ifLet(\.editCustomCommand, action: /Action.editCustomCommand) { 54 | EditCustomCommand(settings: settings) 55 | } 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /Core/Sources/ChatContextCollectors/WebChatContextCollector/WebChatContextCollector.swift: -------------------------------------------------------------------------------- 1 | import ChatContextCollector 2 | import Foundation 3 | import OpenAIService 4 | 5 | public final class WebChatContextCollector: ChatContextCollector { 6 | var recentLinks = [String]() 7 | 8 | public init() {} 9 | 10 | public func generateContext( 11 | history: [ChatMessage], 12 | scopes: Set, 13 | content: String, 14 | configuration: ChatGPTConfiguration 15 | ) -> ChatContext? { 16 | guard scopes.contains("web") || scopes.contains("w") else { return nil } 17 | let links = Self.detectLinks(from: history) + Self.detectLinks(from: content) 18 | let functions: [(any ChatGPTFunction)?] = [ 19 | SearchFunction(maxTokens: configuration.maxTokens), 20 | // allow this function only when there is a link in the memory. 21 | links.isEmpty ? nil : QueryWebsiteFunction(), 22 | ] 23 | return .init( 24 | systemPrompt: "You prefer to answer questions with latest content on the internet.", 25 | functions: functions.compactMap { $0 } 26 | ) 27 | } 28 | } 29 | 30 | extension WebChatContextCollector { 31 | static func detectLinks(from messages: [ChatMessage]) -> [String] { 32 | return messages.lazy 33 | .compactMap { 34 | $0.content ?? $0.functionCall?.arguments 35 | } 36 | .map(detectLinks(from:)) 37 | .flatMap { $0 } 38 | } 39 | 40 | static func detectLinks(from content: String) -> [String] { 41 | var links = [String]() 42 | let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) 43 | let matches = detector?.matches( 44 | in: content, 45 | options: [], 46 | range: NSRange(content.startIndex..., in: content) 47 | ) 48 | 49 | for match in matches ?? [] { 50 | guard let range = Range(match.range, in: content) else { continue } 51 | let url = content[range] 52 | links.append(String(url)) 53 | } 54 | return links 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /Tool/Sources/Toast/Toast.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftUI 3 | import Dependencies 4 | 5 | public enum ToastType { 6 | case info 7 | case warning 8 | case error 9 | } 10 | 11 | public struct ToastKey: EnvironmentKey { 12 | public static var defaultValue: (String, ToastType) -> Void = { _, _ in } 13 | } 14 | 15 | public extension EnvironmentValues { 16 | var toast: (String, ToastType) -> Void { 17 | get { self[ToastKey.self] } 18 | set { self[ToastKey.self] = newValue } 19 | } 20 | } 21 | 22 | public struct ToastControllerDependencyKey: DependencyKey { 23 | public static let liveValue = ToastController(messages: []) 24 | } 25 | 26 | public extension DependencyValues { 27 | var toastController: ToastController { 28 | get { self[ToastControllerDependencyKey.self] } 29 | set { self[ToastControllerDependencyKey.self] = newValue } 30 | } 31 | 32 | var toast: (String, ToastType) -> Void { 33 | get { toastController.toast } 34 | } 35 | } 36 | 37 | public class ToastController: ObservableObject { 38 | public struct Message: Identifiable { 39 | public var id: UUID 40 | public var type: ToastType 41 | public var content: Text 42 | public init(id: UUID, type: ToastType, content: Text) { 43 | self.id = id 44 | self.type = type 45 | self.content = content 46 | } 47 | } 48 | 49 | @Published public var messages: [Message] = [] 50 | 51 | public init(messages: [Message]) { 52 | self.messages = messages 53 | } 54 | 55 | public func toast(content: String, type: ToastType) { 56 | let id = UUID() 57 | let message = Message(id: id, type: type, content: Text(content)) 58 | 59 | Task { @MainActor in 60 | withAnimation(.easeInOut(duration: 0.2)) { 61 | messages.append(message) 62 | messages = messages.suffix(3) 63 | } 64 | try await Task.sleep(nanoseconds: 4_000_000_000) 65 | withAnimation(.easeInOut(duration: 0.2)) { 66 | messages.removeAll { $0.id == id } 67 | } 68 | } 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /Core/Sources/CodeiumService/OpendDocumentPool.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | private let maxSize: Int = 1_000_000 // Byte 4 | 5 | actor OpenedDocumentPool { 6 | var openedDocuments = [URL: OpenedDocument]() 7 | 8 | func getOtherDocuments(exceptURL: URL) -> [OpenedDocument] { 9 | let ordered = openedDocuments.values.sorted { $0.updateTime > $1.updateTime } 10 | var documents = [OpenedDocument]() 11 | var size = 0 12 | for document in ordered where document.url != exceptURL { 13 | size += document.size 14 | if size > maxSize { 15 | break 16 | } 17 | documents.append(document) 18 | } 19 | 20 | return documents 21 | } 22 | 23 | func openDocument(url: URL, relativePath: String, content: String) { 24 | let document = OpenedDocument(url: url, relativePath: relativePath, content: content) 25 | openedDocuments[url] = document 26 | } 27 | 28 | func updateDocument(url: URL, relativePath: String, content: String) { 29 | if let document = openedDocuments[url] { 30 | document.update(content: content) 31 | } else { 32 | openDocument(url: url, relativePath: relativePath, content: content) 33 | } 34 | } 35 | 36 | func closeDocument(url: URL) { 37 | openedDocuments[url] = nil 38 | } 39 | } 40 | 41 | final class OpenedDocument { 42 | var url: URL 43 | var relativePath: String 44 | var updateTime: Date 45 | var content: String 46 | var size: Int 47 | 48 | public init(url: URL, relativePath: String, content: String) { 49 | self.url = url 50 | self.relativePath = relativePath 51 | updateTime = Date() 52 | size = content.utf8.count 53 | if size > maxSize { 54 | self.content = "" 55 | } else { 56 | self.content = content 57 | } 58 | } 59 | 60 | func update(content: String) { 61 | updateTime = Date() 62 | size = content.utf8.count 63 | if size > maxSize { 64 | self.content = "" 65 | } else { 66 | self.content = content 67 | } 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /Tool/Sources/ASTParser/TreeCursor.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftTreeSitter 3 | 4 | extension TreeCursor { 5 | /// Deep first search nodes. 6 | /// - Parameter skipChildren: Check if children of a `Node` should be skipped. 7 | func deepFirstSearch( 8 | skipChildren: @escaping (Node) -> Bool 9 | ) -> CursorDeepFirstSearchSequence { 10 | return CursorDeepFirstSearchSequence(cursor: self, skipChildren: skipChildren) 11 | } 12 | } 13 | 14 | // MARK: - Search 15 | 16 | protocol Cursor { 17 | associatedtype Node 18 | var currentNode: Node? { get } 19 | func goToFirstChild() -> Bool 20 | func goToNextSibling() -> Bool 21 | func goToParent() -> Bool 22 | } 23 | 24 | extension TreeCursor: Cursor { 25 | func goToNextSibling() -> Bool { 26 | gotoNextSibling() 27 | } 28 | 29 | func goToParent() -> Bool { 30 | gotoParent() 31 | } 32 | } 33 | 34 | struct CursorDeepFirstSearchSequence: Sequence { 35 | let cursor: C 36 | let skipChildren: (C.Node) -> Bool 37 | 38 | func makeIterator() -> CursorDeepFirstSearchIterator { 39 | return CursorDeepFirstSearchIterator( 40 | cursor: cursor, 41 | skipChildren: skipChildren 42 | ) 43 | } 44 | 45 | struct CursorDeepFirstSearchIterator: IteratorProtocol { 46 | let cursor: C 47 | let skipChildren: (C.Node) -> Bool 48 | var isEnded = false 49 | 50 | mutating func next() -> C.Node? { 51 | guard !isEnded else { return nil } 52 | let currentNode = cursor.currentNode 53 | let hasChild = { 54 | guard let n = currentNode else { return false } 55 | if skipChildren(n) { return false } 56 | return cursor.goToFirstChild() 57 | }() 58 | if !hasChild { 59 | while !cursor.goToNextSibling() { 60 | if !cursor.goToParent() { 61 | isEnded = true 62 | break 63 | } 64 | } 65 | } 66 | 67 | return currentNode 68 | } 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /Tool/Sources/SuggestionModel/ExportedFromLSP.swift: -------------------------------------------------------------------------------- 1 | import LanguageServerProtocol 2 | 3 | public typealias CursorPosition = LanguageServerProtocol.Position 4 | 5 | public extension CursorPosition { 6 | static let zero = CursorPosition(line: 0, character: 0) 7 | static var outOfScope: CursorPosition { .init(line: -1, character: -1) } 8 | 9 | var readableText: String { 10 | return "[\(line + 1), \(character)]" 11 | } 12 | } 13 | 14 | public struct CursorRange: Codable, Hashable, Sendable, Equatable, CustomStringConvertible { 15 | public static let zero = CursorRange(start: .zero, end: .zero) 16 | 17 | public var start: CursorPosition 18 | public var end: CursorPosition 19 | 20 | public init(start: Position, end: Position) { 21 | self.start = start 22 | self.end = end 23 | } 24 | 25 | public init(startPair: (Int, Int), endPair: (Int, Int)) { 26 | start = CursorPosition(startPair) 27 | end = CursorPosition(endPair) 28 | } 29 | 30 | public func contains(_ position: CursorPosition) -> Bool { 31 | return position >= start && position <= end 32 | } 33 | 34 | public func contains(_ range: CursorRange) -> Bool { 35 | return range.start >= start && range.end <= end 36 | } 37 | 38 | public func strictlyContains(_ range: CursorRange) -> Bool { 39 | return range.start > start && range.end < end 40 | } 41 | 42 | public func intersects(_ other: LSPRange) -> Bool { 43 | return contains(other.start) || contains(other.end) 44 | } 45 | 46 | public var isEmpty: Bool { 47 | return start == end 48 | } 49 | 50 | public static func == (lhs: CursorRange, rhs: CursorRange) -> Bool { 51 | return lhs.start == rhs.start && lhs.end == rhs.end 52 | } 53 | 54 | public var description: String { 55 | return "\(start.readableText) - \(end.readableText)" 56 | } 57 | } 58 | 59 | public extension CursorRange { 60 | static var outOfScope: CursorRange { .init(start: .outOfScope, end: .outOfScope) } 61 | static func cursor(_ position: CursorPosition) -> CursorRange { 62 | return .init(start: position, end: position) 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /Core/Sources/HostApp/ServiceView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import ComposableArchitecture 3 | 4 | struct ServiceView: View { 5 | let store: StoreOf 6 | @State var tag = 1 7 | 8 | var body: some View { 9 | SidebarTabView(tag: $tag) { 10 | // ScrollView { 11 | // CopilotView().padding() 12 | // }.sidebarItem( 13 | // tag: 0, 14 | // title: "GitHub Copilot", 15 | // subtitle: "Suggestion", 16 | // image: "globe" 17 | // ) 18 | // 19 | ScrollView { 20 | CodeiumView().padding() 21 | }.sidebarItem( 22 | tag: 1, 23 | title: "Codeium", 24 | subtitle: "Suggestion", 25 | image: "globe" 26 | ) 27 | 28 | // ChatModelManagementView(store: store.scope( 29 | // state: \.chatModelManagement, 30 | // action: HostApp.Action.chatModelManagement 31 | // )).sidebarItem( 32 | // tag: 2, 33 | // title: "Chat Models", 34 | // subtitle: "Chat, Prompt to Code", 35 | // image: "globe" 36 | // ) 37 | // 38 | // EmbeddingModelManagementView(store: store.scope( 39 | // state: \.embeddingModelManagement, 40 | // action: HostApp.Action.embeddingModelManagement 41 | // )).sidebarItem( 42 | // tag: 3, 43 | // title: "Embedding Models", 44 | // subtitle: "Chat, Prompt to Code", 45 | // image: "globe" 46 | // ) 47 | // 48 | // ScrollView { 49 | // BingSearchView().padding() 50 | // }.sidebarItem( 51 | // tag: 4, 52 | // title: "Bing Search", 53 | // subtitle: "Search Chat Plugin", 54 | // image: "globe" 55 | // ) 56 | } 57 | } 58 | } 59 | 60 | struct AccountView_Previews: PreviewProvider { 61 | static var previews: some View { 62 | ServiceView(store: .init(initialState: .init(), reducer: HostApp())) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Core/Sources/SuggestionWidget/Providers/SuggestionProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftUI 3 | 4 | public final class SuggestionProvider: ObservableObject, Equatable { 5 | public static func == (lhs: SuggestionProvider, rhs: SuggestionProvider) -> Bool { 6 | lhs.code == rhs.code && lhs.language == rhs.language 7 | } 8 | 9 | @Published public var code: String = "" 10 | @Published public var language: String = "" 11 | @Published public var startLineIndex: Int = 0 12 | @Published public var suggestionCount: Int = 0 13 | @Published public var currentSuggestionIndex: Int = 0 14 | @Published public var commonPrecedingSpaceCount = 0 15 | 16 | public var onSelectPreviousSuggestionTapped: () -> Void 17 | public var onSelectNextSuggestionTapped: () -> Void 18 | public var onRejectSuggestionTapped: () -> Void 19 | public var onAcceptSuggestionTapped: () -> Void 20 | 21 | public init( 22 | code: String = "", 23 | language: String = "", 24 | startLineIndex: Int = 0, 25 | suggestionCount: Int = 0, 26 | currentSuggestionIndex: Int = 0, 27 | onSelectPreviousSuggestionTapped: @escaping () -> Void = {}, 28 | onSelectNextSuggestionTapped: @escaping () -> Void = {}, 29 | onRejectSuggestionTapped: @escaping () -> Void = {}, 30 | onAcceptSuggestionTapped: @escaping () -> Void = {} 31 | ) { 32 | self.code = code 33 | self.language = language 34 | self.startLineIndex = startLineIndex 35 | self.suggestionCount = suggestionCount 36 | self.currentSuggestionIndex = currentSuggestionIndex 37 | self.onSelectPreviousSuggestionTapped = onSelectPreviousSuggestionTapped 38 | self.onSelectNextSuggestionTapped = onSelectNextSuggestionTapped 39 | self.onRejectSuggestionTapped = onRejectSuggestionTapped 40 | self.onAcceptSuggestionTapped = onAcceptSuggestionTapped 41 | } 42 | 43 | func selectPreviousSuggestion() { onSelectPreviousSuggestionTapped() } 44 | func selectNextSuggestion() { onSelectNextSuggestionTapped() } 45 | func rejectSuggestion() { onRejectSuggestionTapped() } 46 | func acceptSuggestion() { onAcceptSuggestionTapped() } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /Tool/Sources/Logger/Logger.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import os.log 3 | 4 | enum LogLevel: String { 5 | case debug 6 | case info 7 | case error 8 | } 9 | 10 | public final class Logger { 11 | private let subsystem: String 12 | private let category: String 13 | private let osLog: OSLog 14 | 15 | public static let service = Logger(category: "Service") 16 | public static let ui = Logger(category: "UI") 17 | public static let client = Logger(category: "Client") 18 | public static let updateChecker = Logger(category: "UpdateChecker") 19 | public static let gitHubCopilot = Logger(category: "GitHubCopilot") 20 | public static let codeium = Logger(category: "Codeium") 21 | public static let langchain = Logger(category: "LangChain") 22 | #if DEBUG 23 | /// Use a temp logger to log something temporary. I won't be available in release builds. 24 | public static let temp = Logger(category: "Temp") 25 | #endif 26 | 27 | public init(subsystem: String = "com.intii.CodeiumForXcode", category: String) { 28 | self.subsystem = subsystem 29 | self.category = category 30 | osLog = OSLog(subsystem: subsystem, category: category) 31 | } 32 | 33 | func log(level: LogLevel, message: String) { 34 | let osLogType: OSLogType 35 | switch level { 36 | case .debug: 37 | osLogType = .debug 38 | case .info: 39 | osLogType = .info 40 | case .error: 41 | osLogType = .error 42 | } 43 | 44 | os_log("%{public}@", log: osLog, type: osLogType, message as CVarArg) 45 | } 46 | 47 | public func debug(_ message: String) { 48 | log(level: .debug, message: message) 49 | } 50 | 51 | public func info(_ message: String) { 52 | log(level: .info, message: message) 53 | } 54 | 55 | public func error(_ message: String) { 56 | log(level: .error, message: message) 57 | } 58 | 59 | public func error(_ error: Error) { 60 | log(level: .error, message: error.localizedDescription) 61 | } 62 | 63 | public func signpost(_ type: OSSignpostType, name: StaticString) { 64 | os_signpost(type, log: osLog, name: name) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Core/Sources/PromptToCodeService/PromptToCodeServiceType.swift: -------------------------------------------------------------------------------- 1 | import Dependencies 2 | import Foundation 3 | import SuggestionModel 4 | 5 | public protocol PromptToCodeServiceType { 6 | func modifyCode( 7 | code: String, 8 | requirement: String, 9 | source: PromptToCodeSource, 10 | isDetached: Bool, 11 | extraSystemPrompt: String?, 12 | generateDescriptionRequirement: Bool? 13 | ) async throws -> AsyncThrowingStream<(code: String, description: String), Error> 14 | 15 | func stopResponding() 16 | } 17 | 18 | public struct PromptToCodeSource { 19 | public var language: CodeLanguage 20 | public var documentURL: URL 21 | public var projectRootURL: URL 22 | public var allCode: String 23 | public var range: CursorRange 24 | 25 | public init( 26 | language: CodeLanguage, 27 | documentURL: URL, 28 | projectRootURL: URL, 29 | allCode: String, 30 | range: CursorRange 31 | ) { 32 | self.language = language 33 | self.documentURL = documentURL 34 | self.projectRootURL = projectRootURL 35 | self.allCode = allCode 36 | self.range = range 37 | } 38 | } 39 | 40 | public struct PromptToCodeServiceDependencyKey: DependencyKey { 41 | public static let liveValue: PromptToCodeServiceType = PreviewPromptToCodeService() 42 | public static let previewValue: PromptToCodeServiceType = PreviewPromptToCodeService() 43 | } 44 | 45 | public struct PromptToCodeServiceFactoryDependencyKey: DependencyKey { 46 | public static let liveValue: () -> PromptToCodeServiceType = { OpenAIPromptToCodeService() } 47 | public static let previewValue: () -> PromptToCodeServiceType = { PreviewPromptToCodeService() } 48 | } 49 | 50 | public extension DependencyValues { 51 | var promptToCodeService: PromptToCodeServiceType { 52 | get { self[PromptToCodeServiceDependencyKey.self] } 53 | set { self[PromptToCodeServiceDependencyKey.self] = newValue } 54 | } 55 | 56 | var promptToCodeServiceFactory: () -> PromptToCodeServiceType { 57 | get { self[PromptToCodeServiceFactoryDependencyKey.self] } 58 | set { self[PromptToCodeServiceFactoryDependencyKey.self] = newValue } 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /Core/Tests/PromptToCodeServiceTests/ExtractCodeFromChatGPTTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import PromptToCodeService 3 | 4 | final class ExtractCodeFromChatGPTTests: XCTestCase { 5 | func test_extract_from_no_code_block() { 6 | let api = OpenAIPromptToCodeService() 7 | let result = api.extractCodeAndDescription(from: """ 8 | hello world! 9 | """) 10 | 11 | XCTAssertEqual(result.code, "") 12 | XCTAssertEqual(result.description, "") 13 | } 14 | 15 | func test_extract_from_incomplete_code_block() { 16 | let api = OpenAIPromptToCodeService() 17 | let result = api.extractCodeAndDescription(from: """ 18 | ```swift 19 | func foo() {} 20 | """) 21 | 22 | XCTAssertEqual(result.code, "func foo() {}") 23 | XCTAssertEqual(result.description, "") 24 | } 25 | 26 | func test_extract_from_complete_code_block() { 27 | let api = OpenAIPromptToCodeService() 28 | let result = api.extractCodeAndDescription(from: """ 29 | ```swift 30 | func foo() {} 31 | 32 | func bar() {} 33 | ``` 34 | 35 | Description 36 | """) 37 | 38 | XCTAssertEqual(result.code, "func foo() {}\n\nfunc bar() {}") 39 | XCTAssertEqual(result.description, "Description") 40 | } 41 | 42 | func test_extract_from_incomplete_code_block_without_language() { 43 | let api = OpenAIPromptToCodeService() 44 | let result = api.extractCodeAndDescription(from: """ 45 | ``` 46 | func foo() {} 47 | """) 48 | 49 | XCTAssertEqual(result.code, "func foo() {}") 50 | XCTAssertEqual(result.description, "") 51 | } 52 | 53 | func test_extract_from_code_block_without_language() { 54 | let api = OpenAIPromptToCodeService() 55 | let result = api.extractCodeAndDescription(from: """ 56 | ``` 57 | func foo() {} 58 | 59 | func bar() {} 60 | ``` 61 | 62 | Description 63 | """) 64 | 65 | XCTAssertEqual(result.code, "func foo() {}\n\nfunc bar() {}") 66 | XCTAssertEqual(result.description, "Description") 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Core/Tests/ServiceTests/ExtractSelectedCodeTests.swift: -------------------------------------------------------------------------------- 1 | import SuggestionModel 2 | import XCTest 3 | @testable import Service 4 | @testable import XPCShared 5 | 6 | class ExtractSelectedCodeTests: XCTestCase { 7 | func test_empty_selection() { 8 | let selection = EditorContent.Selection( 9 | start: CursorPosition(line: 0, character: 0), 10 | end: CursorPosition(line: 0, character: 0) 11 | ) 12 | let lines = ["let foo = 1\n", "let bar = 2\n"] 13 | let result = selectedCode(in: selection, for: lines) 14 | XCTAssertEqual(result, "") 15 | } 16 | 17 | func test_single_line_selection() { 18 | let selection = EditorContent.Selection( 19 | start: CursorPosition(line: 0, character: 4), 20 | end: CursorPosition(line: 0, character: 10) 21 | ) 22 | let lines = ["let foo = 1\n", "let bar = 2\n"] 23 | let result = selectedCode(in: selection, for: lines) 24 | XCTAssertEqual(result, "foo = ") 25 | } 26 | 27 | func test_single_line_selection_at_line_end() { 28 | let selection = EditorContent.Selection( 29 | start: CursorPosition(line: 0, character: 8), 30 | end: CursorPosition(line: 0, character: 11) 31 | ) 32 | let lines = ["let foo = 1\n", "let bar = 2\n"] 33 | let result = selectedCode(in: selection, for: lines) 34 | XCTAssertEqual(result, "= 1") 35 | } 36 | 37 | func test_multi_line_selection() { 38 | let selection = EditorContent.Selection( 39 | start: CursorPosition(line: 0, character: 4), 40 | end: CursorPosition(line: 1, character: 11) 41 | ) 42 | let lines = ["let foo = 1\n", "let bar = 2\n", "let baz = 3\n"] 43 | let result = selectedCode(in: selection, for: lines) 44 | XCTAssertEqual(result, "foo = 1\nlet bar = 2") 45 | } 46 | 47 | func test_invalid_selection() { 48 | let selection = EditorContent.Selection( 49 | start: CursorPosition(line: 1, character: 4), 50 | end: CursorPosition(line: 0, character: 10) 51 | ) 52 | let lines = ["let foo = 1", "let bar = 2"] 53 | let result = selectedCode(in: selection, for: lines) 54 | XCTAssertEqual(result, "") 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Core/Sources/XPCShared/XPCServiceProtocol.swift: -------------------------------------------------------------------------------- 1 | import SuggestionModel 2 | import Foundation 3 | 4 | @objc(XPCServiceProtocol) 5 | public protocol XPCServiceProtocol { 6 | func getSuggestedCode( 7 | editorContent: Data, 8 | withReply reply: @escaping (_ updatedContent: Data?, Error?) -> Void 9 | ) 10 | func getNextSuggestedCode( 11 | editorContent: Data, 12 | withReply reply: @escaping (_ updatedContent: Data?, Error?) -> Void 13 | ) 14 | func getPreviousSuggestedCode( 15 | editorContent: Data, 16 | withReply reply: @escaping (_ updatedContent: Data?, Error?) -> Void 17 | ) 18 | func getSuggestionAcceptedCode( 19 | editorContent: Data, 20 | withReply reply: @escaping (_ updatedContent: Data?, Error?) -> Void 21 | ) 22 | func getSuggestionRejectedCode( 23 | editorContent: Data, 24 | withReply reply: @escaping (_ updatedContent: Data?, Error?) -> Void 25 | ) 26 | func getRealtimeSuggestedCode( 27 | editorContent: Data, 28 | withReply reply: @escaping (Data?, Error?) -> Void 29 | ) 30 | func getPromptToCodeAcceptedCode( 31 | editorContent: Data, 32 | withReply reply: @escaping (_ updatedContent: Data?, Error?) -> Void 33 | ) 34 | func chatWithSelection( 35 | editorContent: Data, 36 | withReply reply: @escaping (Data?, Error?) -> Void 37 | ) 38 | func promptToCode( 39 | editorContent: Data, 40 | withReply reply: @escaping (Data?, Error?) -> Void 41 | ) 42 | func customCommand( 43 | id: String, 44 | editorContent: Data, 45 | withReply reply: @escaping (Data?, Error?) -> Void 46 | ) 47 | 48 | func toggleRealtimeSuggestion(withReply reply: @escaping (Error?) -> Void) 49 | 50 | func prefetchRealtimeSuggestions( 51 | editorContent: Data, 52 | withReply reply: @escaping () -> Void 53 | ) 54 | 55 | func getXPCServiceVersion(withReply reply: @escaping (String, String) -> Void) 56 | func getXPCServiceAccessibilityPermission(withReply reply: @escaping (Bool) -> Void) 57 | func postNotification(name: String, withReply reply: @escaping () -> Void) 58 | func performAction(name: String, arguments: String, withReply reply: @escaping (String) -> Void) 59 | } 60 | -------------------------------------------------------------------------------- /Core/Sources/ChatContextCollectors/ActiveDocumentChatContextCollector/Functions/MoveToCodeAroundLineFunction.swift: -------------------------------------------------------------------------------- 1 | import ASTParser 2 | import Foundation 3 | import OpenAIService 4 | import SuggestionModel 5 | 6 | struct MoveToCodeAroundLineFunction: ChatGPTFunction { 7 | struct Arguments: Codable { 8 | var line: Int 9 | } 10 | 11 | struct Result: ChatGPTFunctionResult { 12 | var range: CursorRange 13 | 14 | var botReadableContent: String { 15 | "Editing Document Context is updated to display code at \(range)." 16 | } 17 | } 18 | 19 | struct E: Error, LocalizedError { 20 | var errorDescription: String? 21 | } 22 | 23 | var name: String { 24 | "getCodeAtLine" 25 | } 26 | 27 | var description: String { 28 | "Get the code at the given line, so you can answer the question about the code at that line." 29 | } 30 | 31 | var argumentSchema: JSONSchemaValue { [ 32 | .type: "object", 33 | .properties: [ 34 | "line": [ 35 | .type: "number", 36 | .description: "The line number in the file", 37 | ], 38 | ], 39 | .required: ["line"], 40 | ] } 41 | 42 | weak var contextCollector: ActiveDocumentChatContextCollector? 43 | 44 | init(contextCollector: ActiveDocumentChatContextCollector) { 45 | self.contextCollector = contextCollector 46 | } 47 | 48 | func prepare(reportProgress: @escaping (String) async -> Void) async { 49 | await reportProgress("Finding code around..") 50 | } 51 | 52 | func call( 53 | arguments: Arguments, 54 | reportProgress: @escaping (String) async -> Void 55 | ) async throws -> Result { 56 | await reportProgress("Finding code around line \(arguments.line)..") 57 | contextCollector?.activeDocumentContext?.moveToCodeAroundLine(arguments.line) 58 | guard let newContext = contextCollector?.activeDocumentContext?.focusedContext else { 59 | let progress = "Failed to move to focused code." 60 | await reportProgress(progress) 61 | throw E(errorDescription: progress) 62 | } 63 | let progress = "Looking at \(newContext.codeRange)" 64 | await reportProgress(progress) 65 | return .init(range: newContext.codeRange) 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /EditorExtension/AcceptSuggestionCommand.swift: -------------------------------------------------------------------------------- 1 | import Client 2 | import SuggestionModel 3 | import Foundation 4 | import XcodeKit 5 | import XPCShared 6 | 7 | class AcceptSuggestionCommand: NSObject, XCSourceEditorCommand, CommandType { 8 | var name: String { "Accept Suggestion" } 9 | 10 | func perform( 11 | with invocation: XCSourceEditorCommandInvocation, 12 | completionHandler: @escaping (Error?) -> Void 13 | ) { 14 | Task { 15 | do { 16 | try await (Task(timeout: 7) { 17 | let service = try getService() 18 | if let content = try await service.getSuggestionAcceptedCode( 19 | editorContent: .init(invocation) 20 | ) { 21 | invocation.accept(content) 22 | } 23 | completionHandler(nil) 24 | }.value) 25 | } catch is CancellationError { 26 | completionHandler(nil) 27 | } catch { 28 | completionHandler(error) 29 | } 30 | } 31 | } 32 | } 33 | 34 | /// https://gist.github.com/swhitty/9be89dfe97dbb55c6ef0f916273bbb97 35 | extension Task where Failure == Error { 36 | // Start a new Task with a timeout. If the timeout expires before the operation is 37 | // completed then the task is cancelled and an error is thrown. 38 | init( 39 | priority: TaskPriority? = nil, 40 | timeout: TimeInterval, 41 | operation: @escaping @Sendable () async throws -> Success 42 | ) { 43 | self = Task(priority: priority) { 44 | try await withThrowingTaskGroup(of: Success.self) { group -> Success in 45 | group.addTask(operation: operation) 46 | group.addTask { 47 | try await _Concurrency.Task.sleep(nanoseconds: UInt64(timeout * 1_000_000_000)) 48 | throw TimeoutError() 49 | } 50 | guard let success = try await group.next() else { 51 | throw _Concurrency.CancellationError() 52 | } 53 | group.cancelAll() 54 | return success 55 | } 56 | } 57 | } 58 | } 59 | 60 | private struct TimeoutError: LocalizedError { 61 | var errorDescription: String? = "Task timed out before completion" 62 | } 63 | -------------------------------------------------------------------------------- /Core/Sources/ChatPlugins/MathChatPlugin/MathChatPlugin.swift: -------------------------------------------------------------------------------- 1 | import ChatPlugin 2 | import Environment 3 | import Foundation 4 | import OpenAIService 5 | 6 | /// Use Python to solve math problems. 7 | public actor MathChatPlugin: ChatPlugin { 8 | public static var command: String { "math" } 9 | public nonisolated var name: String { "Math" } 10 | 11 | let chatGPTService: any ChatGPTServiceType 12 | var isCancelled = false 13 | weak var delegate: ChatPluginDelegate? 14 | 15 | public init(inside chatGPTService: any ChatGPTServiceType, delegate: ChatPluginDelegate) { 16 | self.chatGPTService = chatGPTService 17 | self.delegate = delegate 18 | } 19 | 20 | public func send(content: String, originalMessage: String) async { 21 | delegate?.pluginDidStart(self) 22 | delegate?.pluginDidStartResponding(self) 23 | 24 | let id = "\(Self.command)-\(UUID().uuidString)" 25 | var reply = ChatMessage(id: id, role: .assistant, content: "") 26 | 27 | await chatGPTService.memory.mutateHistory { history in 28 | history.append(.init(role: .user, content: originalMessage)) 29 | } 30 | 31 | do { 32 | let result = try await solveMathProblem(content) 33 | let formattedResult = "Answer: \(result)" 34 | if !isCancelled { 35 | await chatGPTService.memory.mutateHistory { history in 36 | if history.last?.id == id { 37 | history.removeLast() 38 | } 39 | reply.content = formattedResult 40 | history.append(reply) 41 | } 42 | } 43 | } catch { 44 | if !isCancelled { 45 | await chatGPTService.memory.mutateHistory { history in 46 | if history.last?.id == id { 47 | history.removeLast() 48 | } 49 | reply.content = error.localizedDescription 50 | history.append(reply) 51 | } 52 | } 53 | } 54 | 55 | delegate?.pluginDidEndResponding(self) 56 | delegate?.pluginDidEnd(self) 57 | } 58 | 59 | public func cancel() async { 60 | isCancelled = true 61 | } 62 | 63 | public func stopResponding() async { 64 | isCancelled = true 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /Core/Sources/CodeiumService/CodeiumAuthService.swift: -------------------------------------------------------------------------------- 1 | import Configs 2 | import Foundation 3 | import Keychain 4 | 5 | public final class CodeiumAuthService { 6 | public init() {} 7 | let codeiumKeyKey = "codeiumAuthKey" 8 | let keychain = Keychain() 9 | 10 | var key: String? { try? keychain.get(codeiumKeyKey) } 11 | 12 | public var isSignedIn: Bool { return key != nil } 13 | 14 | public func signIn(token: String) async throws { 15 | let key = try await generate(token: token) 16 | try keychain.update(key, key: codeiumKeyKey) 17 | } 18 | 19 | public func signOut() async throws { 20 | try keychain.remove(codeiumKeyKey) 21 | } 22 | 23 | struct GenerateKeyRequestBody: Codable { 24 | var firebase_id_token: String 25 | } 26 | 27 | struct GenerateKeyResponseBody: Codable { 28 | var api_key: String 29 | } 30 | 31 | struct GenerateKeyErrorResponseBody: Codable, Error, LocalizedError { 32 | var detail: String 33 | var errorDescription: String? { detail } 34 | } 35 | 36 | func generate(token: String) async throws -> String { 37 | var registerUserUrl = URL(string: "https://api.codeium.com/register_user/") 38 | let apiUrl = UserDefaults.shared.value(for: \.codeiumApiUrl) 39 | if UserDefaults.shared.value(for: \.codeiumEnterpriseMode), apiUrl != "" { 40 | registerUserUrl = 41 | URL(string: apiUrl + "/exa.api_server_pb.ApiServerService/RegisterUser") 42 | } 43 | 44 | var request = URLRequest(url: registerUserUrl!) 45 | request.httpMethod = "POST" 46 | request.addValue("application/json", forHTTPHeaderField: "Content-Type") 47 | let requestBody = GenerateKeyRequestBody(firebase_id_token: token) 48 | let requestData = try JSONEncoder().encode(requestBody) 49 | request.httpBody = requestData 50 | let (data, _) = try await URLSession.shared.data(for: request) 51 | do { 52 | let response = try JSONDecoder().decode(GenerateKeyResponseBody.self, from: data) 53 | return response.api_key 54 | } catch { 55 | if let response = try? JSONDecoder() 56 | .decode(GenerateKeyErrorResponseBody.self, from: data) 57 | { 58 | throw response 59 | } 60 | throw error 61 | } 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /Tool/Sources/OpenAIService/Models.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct Cancellable { 4 | let cancel: () -> Void 5 | func callAsFunction() { 6 | cancel() 7 | } 8 | } 9 | 10 | public struct ChatMessage: Equatable, Codable { 11 | public enum Role: String, Codable, Equatable { 12 | case system 13 | case user 14 | case assistant 15 | case function 16 | } 17 | 18 | public struct FunctionCall: Codable, Equatable { 19 | public var name: String 20 | public var arguments: String 21 | public init(name: String, arguments: String) { 22 | self.name = name 23 | self.arguments = arguments 24 | } 25 | } 26 | 27 | /// The role of a message. 28 | public var role: Role 29 | 30 | /// The content of the message, either the chat message, or a result of a function call. 31 | public var content: String? { 32 | didSet { tokensCount = nil } 33 | } 34 | 35 | /// A function call from the bot. 36 | public var functionCall: FunctionCall? { 37 | didSet { tokensCount = nil } 38 | } 39 | 40 | /// The function name of a reply to a function call. 41 | public var name: String? { 42 | didSet { tokensCount = nil } 43 | } 44 | 45 | /// The summary of a message that is used for display. 46 | public var summary: String? 47 | 48 | /// The id of the message. 49 | public var id: String 50 | 51 | /// The number of tokens of this message. 52 | var tokensCount: Int? 53 | 54 | /// Is the message considered empty. 55 | var isEmpty: Bool { 56 | if let content, !content.isEmpty { return false } 57 | if let functionCall, !functionCall.name.isEmpty { return false } 58 | if let name, !name.isEmpty { return false } 59 | return true 60 | } 61 | 62 | public init( 63 | id: String = UUID().uuidString, 64 | role: Role, 65 | content: String?, 66 | name: String? = nil, 67 | functionCall: FunctionCall? = nil, 68 | summary: String? = nil, 69 | tokenCount: Int? = nil 70 | ) { 71 | self.role = role 72 | self.content = content 73 | self.name = name 74 | self.functionCall = functionCall 75 | self.summary = summary 76 | self.id = id 77 | tokensCount = tokenCount 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /Tool/Sources/LangChain/Chains/CombineAnswersChain.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Logger 3 | import OpenAIService 4 | 5 | public class CombineAnswersChain: Chain { 6 | public struct Input: Decodable { 7 | public var question: String 8 | public var answers: [String] 9 | public init(question: String, answers: [String]) { 10 | self.question = question 11 | self.answers = answers 12 | } 13 | } 14 | 15 | public typealias Output = String 16 | public let chatModelChain: ChatModelChain 17 | 18 | public init( 19 | configuration: ChatGPTConfiguration = UserPreferenceChatGPTConfiguration(), 20 | extraInstructions: String = "" 21 | ) { 22 | chatModelChain = .init( 23 | chatModel: OpenAIChat( 24 | configuration: configuration.overriding { 25 | $0.runFunctionsAutomatically = false 26 | }, 27 | memory: nil, 28 | stream: false 29 | ), 30 | stops: ["Observation:"], 31 | promptTemplate: { input in 32 | [ 33 | .init( 34 | role: .system, 35 | content: """ 36 | You are a helpful assistant. 37 | Your job is to combine multiple answers from different sources to one question. 38 | \(extraInstructions) 39 | """ 40 | ), 41 | .init(role: .user, content: """ 42 | Question: \(input.question) 43 | 44 | Answers: 45 | \(input.answers.joined(separator: "\n\(String(repeating: "-", count: 32))\n")) 46 | 47 | What is the combined answer? 48 | """), 49 | ] 50 | } 51 | ) 52 | } 53 | 54 | public func callLogic( 55 | _ input: Input, 56 | callbackManagers: [CallbackManager] 57 | ) async throws -> String { 58 | let output = try await chatModelChain.call(input, callbackManagers: callbackManagers) 59 | return await parseOutput(output) 60 | } 61 | 62 | public func parseOutput(_ message: ChatMessage) async -> String { 63 | return message.content ?? "No answer." 64 | } 65 | 66 | public func parseOutput(_ output: String) -> String { 67 | output 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /Tool/Sources/Preferences/Types/CustomCommand.swift: -------------------------------------------------------------------------------- 1 | import CryptoKit 2 | import Foundation 3 | 4 | public struct CustomCommand: Codable, Equatable { 5 | /// The custom command feature. 6 | /// 7 | /// Keep everything optional so nothing will break when the format changes. 8 | public enum Feature: Codable, Equatable { 9 | /// Prompt to code. 10 | case promptToCode( 11 | extraSystemPrompt: String?, 12 | prompt: String?, 13 | continuousMode: Bool?, 14 | generateDescription: Bool? 15 | ) 16 | /// Send message. 17 | case chatWithSelection( 18 | extraSystemPrompt: String?, 19 | prompt: String?, 20 | useExtraSystemPrompt: Bool? 21 | ) 22 | /// Custom chat. 23 | case customChat(systemPrompt: String?, prompt: String?) 24 | /// Single round dialog. 25 | case singleRoundDialog( 26 | systemPrompt: String?, 27 | overwriteSystemPrompt: Bool?, 28 | prompt: String?, 29 | receiveReplyInNotification: Bool? 30 | ) 31 | } 32 | 33 | public var id: String { commandId ?? legacyId } 34 | public var commandId: String? 35 | public var name: String 36 | public var feature: Feature 37 | 38 | public init(commandId: String, name: String, feature: Feature) { 39 | self.commandId = commandId 40 | self.name = name 41 | self.feature = feature 42 | } 43 | 44 | public init(from decoder: Decoder) throws { 45 | let container = try decoder.container(keyedBy: CodingKeys.self) 46 | commandId = try container.decodeIfPresent(String.self, forKey: .commandId) 47 | name = try container.decode(String.self, forKey: .name) 48 | feature = (try? container 49 | .decode(CustomCommand.Feature.self, forKey: .feature)) ?? .chatWithSelection( 50 | extraSystemPrompt: "", 51 | prompt: "", 52 | useExtraSystemPrompt: false 53 | ) 54 | } 55 | 56 | var legacyId: String { 57 | name.sha1HexString 58 | } 59 | } 60 | 61 | private extension Digest { 62 | var bytes: [UInt8] { Array(makeIterator()) } 63 | var data: Data { Data(bytes) } 64 | 65 | var hexStr: String { 66 | bytes.map { String(format: "%02X", $0) }.joined() 67 | } 68 | } 69 | 70 | private extension String { 71 | var sha1HexString: String { 72 | Insecure.SHA1.hash(data: data(using: .utf8) ?? Data()).hexStr 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /Core/.swiftpm/xcode/xcshareddata/xcschemes/Core-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 42 | 43 | 49 | 50 | 56 | 57 | 58 | 59 | 61 | 62 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Tool/.swiftpm/xcode/xcshareddata/xcschemes/Tool-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 42 | 43 | 49 | 50 | 56 | 57 | 58 | 59 | 61 | 62 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Tool/Sources/LangChain/Callback.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol CallbackEvent { 4 | associatedtype Info 5 | var info: Info { get } 6 | } 7 | 8 | public struct CallbackEvents { 9 | public struct UnTypedEvent: CallbackEvent { 10 | public var info: String 11 | public init(info: String) { 12 | self.info = info 13 | } 14 | } 15 | 16 | public var untyped: UnTypedEvent.Type { UnTypedEvent.self } 17 | 18 | private init() {} 19 | } 20 | 21 | public struct CallbackManager { 22 | struct Observer { 23 | let handler: (Event.Info) -> Void 24 | } 25 | 26 | fileprivate var observers = [Any]() 27 | 28 | public init() {} 29 | 30 | public init(observers: (inout CallbackManager) -> Void) { 31 | var manager = CallbackManager() 32 | observers(&manager) 33 | self = manager 34 | } 35 | 36 | public mutating func on( 37 | _: Event.Type = Event.self, 38 | _ handler: @escaping (Event.Info) -> Void 39 | ) { 40 | observers.append(Observer(handler: handler)) 41 | } 42 | 43 | public mutating func on( 44 | _: KeyPath, 45 | _ handler: @escaping (Event.Info) -> Void 46 | ) { 47 | observers.append(Observer(handler: handler)) 48 | } 49 | 50 | public func send(_ event: Event) { 51 | for case let observer as Observer in observers { 52 | observer.handler(event.info) 53 | } 54 | } 55 | 56 | func send( 57 | _: KeyPath, 58 | _ info: Event.Info 59 | ) { 60 | for case let observer as Observer in observers { 61 | observer.handler(info) 62 | } 63 | } 64 | 65 | public func send(_ string: String) { 66 | for case let observer as Observer in observers { 67 | observer.handler(string) 68 | } 69 | } 70 | } 71 | 72 | public extension [CallbackManager] { 73 | func send(_ event: Event) { 74 | for cb in self { cb.send(event) } 75 | } 76 | 77 | func send( 78 | _ keyPath: KeyPath, 79 | _ info: Event.Info 80 | ) { 81 | for cb in self { cb.send(keyPath, info) } 82 | } 83 | 84 | func send(_ event: String) { 85 | for cb in self { cb.send(event) } 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /Tool/Sources/ASTParser/DumpSyntaxTree.swift: -------------------------------------------------------------------------------- 1 | import SwiftTreeSitter 2 | 3 | public extension ASTTree { 4 | /// Dumps the syntax tree as a string, for debugging purposes. 5 | func dump() -> String { 6 | guard let tree, let root = tree.rootNode else { return "" } 7 | var result = "" 8 | 9 | let appendNode: (_ level: Int, _ node: Node) -> Void = { level, node in 10 | let range = node.pointRange 11 | let lowerBoundL = range.lowerBound.row 12 | let lowerBoundC = range.lowerBound.column / 2 13 | let upperBoundL = range.upperBound.row 14 | let upperBoundC = range.upperBound.column / 2 15 | let line = 16 | "\(String(repeating: " ", count: level))\(node.nodeType ?? "N/A") [\(lowerBoundL), \(lowerBoundC)] - [\(upperBoundL), \(upperBoundC)]" 17 | result += line + "\n" 18 | } 19 | 20 | guard let node = root.descendant(in: root.byteRange) else { return result } 21 | 22 | appendNode(0, node) 23 | 24 | let cursor = node.treeCursor 25 | let level = 0 26 | 27 | if cursor.goToFirstChild(for: node.byteRange.lowerBound) == false { 28 | return result 29 | } 30 | 31 | cursor.enumerateCurrentAndDescendents(level: level + 1) { level, node in 32 | appendNode(level, node) 33 | } 34 | 35 | while cursor.goToNextSibling() { 36 | guard let node = cursor.currentNode else { 37 | assertionFailure("no current node when gotoNextSibling succeeded") 38 | break 39 | } 40 | 41 | // once we are past the interesting range, stop 42 | if node.byteRange.lowerBound > root.byteRange.upperBound { 43 | break 44 | } 45 | 46 | cursor.enumerateCurrentAndDescendents(level: level + 1) { level, node in 47 | appendNode(level, node) 48 | } 49 | } 50 | 51 | return result 52 | } 53 | } 54 | 55 | private extension TreeCursor { 56 | func enumerateCurrentAndDescendents(level: Int, block: (Int, Node) throws -> Void) rethrows { 57 | if let node = currentNode { 58 | try block(level, node) 59 | } 60 | 61 | if goToFirstChild() == false { 62 | return 63 | } 64 | 65 | try enumerateCurrentAndDescendents(level: level + 1, block: block) 66 | 67 | while goToNextSibling() { 68 | try enumerateCurrentAndDescendents(level: level + 1, block: block) 69 | } 70 | 71 | let success = gotoParent() 72 | 73 | assert(success) 74 | } 75 | } 76 | 77 | --------------------------------------------------------------------------------