├── 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 |
--------------------------------------------------------------------------------