CopilotForXcodeKit

CopilotforXcodeKit 是一个 Swift 包,允许您为 Copilot for Xcode 构建扩展。

路线图

构建扩展

1. 创建扩展目标

创建一个通用扩展目标。该扩展应面向 macOS 13+。它可以被沙盒化。

在目标的 info.plist 文件中,将 ExtensionPointIdentifier 设置为 com.intii.CopilotForXcode.ExtensionService.Extension

<key>EXAppExtensionAttributes</key>
<dict>
    <key>EXExtensionPointIdentifier</key>
    <string>com.intii.CopilotForXcode.ExtensionService.Extension</string>
</dict>

2. 添加 CopilotForXcodeKit 作为依赖

在 Package.swift 中或通过 Xcode 中的 UI 添加此仓库作为依赖项。

3. 实现扩展

创建一个符合 CopilotForXcodeExtension 协议的类,并使用 @main 标记它。

import CopilotForXcodeKit

@main
class Extension: CopilotForXcodeExtension {
    ...
}

3.1 通过全局连接通信

当扩展在 Copilot for Xcode 中开启时,将建立连接。connectionDidActivate(connectedTo:) 将被调用,并且 host 将被设置。

一旦 host 被设置,您就可以使用它与 Copilot for Xcode 进行通信。

class Extension: CopilotForXcodeExtension {
    var host: HostServer?
    
    func connectionDidActivate(connectedTo host: HostServer) {
        Task {
            try await host.toast("Connected to Example Extension")
        }
    }
    
    ...
}

Copilot for Xcode 会偶尔与您的扩展通信,以提供有关 Xcode 的更新。您可以实现观察者方法,例如 workspace(_:didOpenFileAt:) 来接收这些信息。

3.2 提供配置界面

要提供用户可以从扩展列表中访问的配置界面,您必须

struct SceneConfiguration: CopilotForXcodeExtensionSceneConfiguration {
    var configurationScene: ConfigurationScene<ConfigurationView>? {
        .init { viewModel in
            ConfigurationView(model: viewModel)
        } onConnection: { _ in
            return true
        }
    }
}

ConfigurationView 在配置界面打开时被实例化,并且连接在此刻建立。

然后,您可以使用视图模型的 host 属性与 Copilot for Xcode 通信。

struct ConfigurationView: View {
    @ObservedObject var model: CopilotForXcodeSceneModel
    var body: some View {
        Button("Toast") {
            Task { @MainActor in
                try? await model.host?.toast("Hello")
            }
        }
    }
}

3.3 提供建议服务

要在您的扩展中启用建议服务,您必须

class SuggestionService: SuggestionServiceType {
    var configuration: SuggestionServiceConfiguration {
        .init(acceptsRelevantCodeSnippets: true)
    }

    func getSuggestions(
        _ request: SuggestionRequest,
        workspace: WorkspaceInfo
    ) async throws -> [CodeSuggestion] {
        [
            .init(
                id: UUID().uuidString,
                text: "Hello World",
                position: request.cursorPosition,
                range: .init(start: request.cursorPosition, end: request.cursorPosition)
            ),
        ]
    }
    
    ...
}

如果您需要为不同的工作区维护多个建议服务,例如使用语言服务器的工作区,您将负责单独管理每个服务的生命周期。

4. 调试扩展

要调试扩展,您有两种选择:直接在 Xcode 中运行它或只是构建它。这两种方法之间没有实际区别,因为 Xcode 不会自动附加到扩展的进程(可能是一个 bug)。

无论哪种方式,一旦扩展构建完成,它将在 Copilot for Xcode 中可用。然后您需要启用该扩展

之后,扩展进程将开始运行。您可以从 Xcode 的“调试”菜单附加到它。

建议给调试版本一个不同的 Bundle Identifier,以防止与发布版本发生冲突。

从 Xcode 运行扩展时,系统将提示您选择目标应用程序。请选择“Copilot for Xcode.app”。