Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support GitHub Copilot enterprise #441

Merged
merged 6 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions Core/Sources/HostApp/AccountSettings/GitHubCopilotView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct GitHubCopilotView: View {
@AppStorage(\.gitHubCopilotProxyUsername) var gitHubCopilotProxyUsername
@AppStorage(\.gitHubCopilotProxyPassword) var gitHubCopilotProxyPassword
@AppStorage(\.gitHubCopilotUseStrictSSL) var gitHubCopilotUseStrictSSL
@AppStorage(\.gitHubCopilotEnterpriseURI) var gitHubCopilotEnterpriseURI
@AppStorage(\.gitHubCopilotIgnoreTrailingNewLines)
var gitHubCopilotIgnoreTrailingNewLines
@AppStorage(\.disableGitHubCopilotSettingsAutoRefreshOnAppear)
Expand Down Expand Up @@ -157,15 +158,15 @@ struct GitHubCopilotView: View {
) {
Text("Path to Node (v18+)")
}

Text(
"Provide the path to the executable if it can't be found by the app, shim executable is not supported"
)
.lineLimit(10)
.foregroundColor(.secondary)
.font(.callout)
.dynamicHeightTextInFormWorkaround()

Picker(selection: $settings.runNodeWith) {
ForEach(NodeRunner.allCases, id: \.rawValue) { runner in
switch runner {
Expand All @@ -180,7 +181,7 @@ struct GitHubCopilotView: View {
} label: {
Text("Run Node with")
}

Group {
switch settings.runNodeWith {
case .env:
Expand Down Expand Up @@ -257,6 +258,10 @@ struct GitHubCopilotView: View {
}
.opacity(isRunningAction ? 0.8 : 1)
.disabled(isRunningAction)

Button("Refresh Configuration for Enterprise and Proxy") {
refreshConfiguration()
}
}

SettingsDivider("Advanced")
Expand All @@ -276,6 +281,17 @@ struct GitHubCopilotView: View {
Toggle("Verbose Log", isOn: $settings.gitHubCopilotVerboseLog)
}

SettingsDivider("Enterprise")

Form {
TextField(
text: $settings.gitHubCopilotEnterpriseURI,
prompt: Text("Leave it blank if non is available.")
) {
Text("Auth Provider URL")
}
}

SettingsDivider("Proxy")

Form {
Expand Down Expand Up @@ -403,6 +419,25 @@ struct GitHubCopilotView: View {
}
}
}

func refreshConfiguration() {
NotificationCenter.default.post(
name: .gitHubCopilotShouldRefreshEditorInformation,
object: nil
)

Task {
let service = try getService()
do {
try await service.postNotification(
name: Notification.Name
.gitHubCopilotShouldRefreshEditorInformation.rawValue
)
} catch {
toast(error.localizedDescription, .error)
}
}
}
}

struct ActivityIndicatorView: NSViewRepresentable {
Expand Down
2 changes: 1 addition & 1 deletion Core/Sources/Service/XPCService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public class XPCService: NSObject, XPCServiceProtocol {

public func postNotification(name: String, withReply reply: @escaping () -> Void) {
reply()
NSWorkspace.shared.notificationCenter.post(name: .init(name), object: nil)
NotificationCenter.default.post(name: .init(name), object: nil)
}

// MARK: - Requests
Expand Down
2 changes: 1 addition & 1 deletion Pro
Submodule Pro updated from 9c1132 to 528831
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public actor AsyncPassthroughSubject<Element> {
AsyncStream { [weak self, name] continuation in
let task = Task { [weak self] in
await self?.storeContinuation(continuation)
let notifications = NSWorkspace.shared.notificationCenter.notifications(named: name)
let notifications = NotificationCenter.default.notifications(named: name)
.compactMap {
$0.object as? Element
}
Expand All @@ -46,7 +46,7 @@ public actor AsyncPassthroughSubject<Element> {
}

func _send(_ element: Element) {
NSWorkspace.shared.notificationCenter.post(name: name, object: element)
NotificationCenter.default.post(name: name, object: element)
}

func storeContinuation(_ continuation: AsyncStream<Element>.Continuation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public struct GitHubCopilotInstallationManager {
return URL(string: link)!
}

static let latestSupportedVersion = "1.17.0"
static let latestSupportedVersion = "1.19.2"

public init() {}

Expand Down
73 changes: 57 additions & 16 deletions Tool/Sources/GitHubCopilotService/GitHubCopilotRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ public struct GitHubCopilotCodeSuggestion: Codable, Equatable {
public var displayText: String
}


enum GitHubCopilotRequest {
struct SetEditorInfo: GitHubCopilotRequestType {
struct Response: Codable {}
Expand Down Expand Up @@ -81,22 +80,58 @@ enum GitHubCopilotRequest {
}
}

var request: ClientRequest {
if let networkProxy {
return .custom("setEditorInfo", .hash([
"editorInfo": .hash([
"name": "Xcode",
"version": "",
]),
"editorPluginInfo": .hash([
"name": "Copilot for Xcode",
"version": "",
]),
"networkProxy": networkProxy,
]))
var http: JSONValue? {
var dict: [String: JSONValue] = [:]
let host = UserDefaults.shared.value(for: \.gitHubCopilotProxyHost)
if host.isEmpty { return nil }
var port = UserDefaults.shared.value(for: \.gitHubCopilotProxyPort)
if port.isEmpty { port = "80" }
let username = UserDefaults.shared.value(for: \.gitHubCopilotProxyUsername)
let password = UserDefaults.shared.value(for: \.gitHubCopilotProxyPassword)
let strictSSL = UserDefaults.shared.value(for: \.gitHubCopilotUseStrictSSL)

let url = if !username.isEmpty {
"http://\(username):\(password)@\(host):\(port)"
} else {
"http://\(host):\(port)"
}

return .custom("setEditorInfo", .hash([
dict["proxy"] = .string(url)
dict["proxyStrictSSL"] = .bool(strictSSL)

if dict.isEmpty { return nil }

return .hash(dict)
}

var editorConfiguration: JSONValue? {
var dict: [String: JSONValue] = [:]
dict["http"] = http

let enterpriseURI = UserDefaults.shared.value(for: \.gitHubCopilotEnterpriseURI)
if !enterpriseURI.isEmpty {
dict["github-enterprise"] = .hash([
"uri": .string(enterpriseURI),
])
}

if dict.isEmpty { return nil }
return .hash(dict)
}

var authProvider: JSONValue? {
var dict: [String: JSONValue] = [:]
let enterpriseURI = UserDefaults.shared.value(for: \.gitHubCopilotEnterpriseURI)
if !enterpriseURI.isEmpty {
dict["url"] = .string(enterpriseURI)
}

if dict.isEmpty { return nil }
return .hash(dict)
}

var request: ClientRequest {
var dict: [String: JSONValue] = [
"editorInfo": .hash([
"name": "Xcode",
"version": "",
Expand All @@ -105,7 +140,13 @@ enum GitHubCopilotRequest {
"name": "Copilot for Xcode",
"version": "",
]),
]))
]

dict["editorConfiguration"] = editorConfiguration
dict["authProvider"] = authProvider
dict["networkProxy"] = networkProxy

return .custom("setEditorInfo", .hash(dict))
}
}

Expand Down
18 changes: 16 additions & 2 deletions Tool/Sources/GitHubCopilotService/GitHubCopilotService.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import AppKit
import Foundation
import LanguageClient
import LanguageServerProtocol
Expand Down Expand Up @@ -51,6 +52,11 @@ enum GitHubCopilotError: Error, LocalizedError {
}
}

public extension Notification.Name {
static let gitHubCopilotShouldRefreshEditorInformation = Notification
.Name("com.intii.CopilotForXcode.GitHubCopilotShouldRefreshEditorInformation")
}

public class GitHubCopilotBaseService {
let projectRootURL: URL
var server: GitHubCopilotLSP
Expand Down Expand Up @@ -160,8 +166,16 @@ public class GitHubCopilotBaseService {
self.server = server
localProcessServer = localServer

Task {
try await server.sendRequest(GitHubCopilotRequest.SetEditorInfo())
Task { [weak self] in
_ = try? await server.sendRequest(GitHubCopilotRequest.SetEditorInfo())

for await notification in NotificationCenter.default
.notifications(named: .gitHubCopilotShouldRefreshEditorInformation)
{
print("Yes!")
guard let self else { return }
_ = try? await server.sendRequest(GitHubCopilotRequest.SetEditorInfo())
}
}
}

Expand Down
10 changes: 5 additions & 5 deletions Tool/Sources/OpenAIService/Debug/Debug.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ enum Debugger {
static func didSendRequestBody(body: CompletionRequestBody) {
do {
let json = try JSONEncoder().encode(body)
let center = NSWorkspace.shared.notificationCenter
let center = NotificationCenter.default
center.post(
name: .init("ServiceDebugger.ChatRequestDebug.requestSent"),
object: nil,
Expand All @@ -24,7 +24,7 @@ enum Debugger {
}

static func didReceiveFunction(name: String, arguments: String) {
let center = NSWorkspace.shared.notificationCenter
let center = NotificationCenter.default
center.post(
name: .init("ServiceDebugger.ChatRequestDebug.receivedFunctionCall"),
object: nil,
Expand All @@ -37,7 +37,7 @@ enum Debugger {
}

static func didReceiveFunctionResult(result: String) {
let center = NSWorkspace.shared.notificationCenter
let center = NotificationCenter.default
center.post(
name: .init("ServiceDebugger.ChatRequestDebug.receivedFunctionResult"),
object: nil,
Expand All @@ -49,7 +49,7 @@ enum Debugger {
}

static func didReceiveResponse(content: String) {
let center = NSWorkspace.shared.notificationCenter
let center = NotificationCenter.default
center.post(
name: .init("ServiceDebugger.ChatRequestDebug.responseReceived"),
object: nil,
Expand All @@ -61,7 +61,7 @@ enum Debugger {
}

static func didFinish() {
let center = NSWorkspace.shared.notificationCenter
let center = NotificationCenter.default
center.post(
name: .init("ServiceDebugger.ChatRequestDebug.finished"),
object: nil,
Expand Down
4 changes: 4 additions & 0 deletions Tool/Sources/Preferences/Keys.swift
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ public extension UserDefaultPreferenceKeys {
var gitHubCopilotProxyPort: PreferenceKey<String> {
.init(defaultValue: "", key: "GitHubCopilotProxyPort")
}

var gitHubCopilotEnterpriseURI: PreferenceKey<String> {
.init(defaultValue: "", key: "GitHubCopilotEnterpriseURI")
}

var gitHubCopilotUseStrictSSL: PreferenceKey<Bool> {
.init(defaultValue: true, key: "GitHubCopilotUseStrictSSL")
Expand Down
6 changes: 3 additions & 3 deletions Tool/Sources/XcodeInspector/XcodeInspector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ public final class XcodeInspector: ObservableObject {
}

group.addTask { [weak self] in // malfunctioning
let sequence = NSWorkspace.shared.notificationCenter
let sequence = NotificationCenter.default
.notifications(named: .accessibilityAPIMalfunctioning)
for await notification in sequence {
try Task.checkCancellation()
Expand Down Expand Up @@ -347,15 +347,15 @@ public final class XcodeInspector: ObservableObject {
else { return }

if let editor = focusedEditor, !editor.element.isSourceEditor {
NSWorkspace.shared.notificationCenter.post(
NotificationCenter.default.post(
name: .accessibilityAPIMalfunctioning,
object: "Source Editor Element Corrupted: \(source)"
)
} else if let element = activeXcode?.appElement.focusedElement {
if element.description != focusedElement?.description ||
element.role != focusedElement?.role
{
NSWorkspace.shared.notificationCenter.post(
NotificationCenter.default.post(
name: .accessibilityAPIMalfunctioning,
object: "Element Inconsistency: \(source)"
)
Expand Down