Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
f6a1abe
Merge tag '0.30.4' into develop
intitni Feb 17, 2024
2d2e337
Remove warnings
intitni Feb 18, 2024
ecc4e9d
Merge tag '0.30.5.beta.1' into develop
intitni Feb 20, 2024
769eafd
Replace AsyncPassthroughSubject from AsyncExtension
intitni Feb 20, 2024
65c1e31
Merge branch 'feature/fix-deadlock' into develop
intitni Feb 20, 2024
021abbb
Update the default chat prompt
intitni Feb 20, 2024
cd4e807
Update Azure OpenAI API version
intitni Feb 20, 2024
b0a740f
Fix that the active application can be incorrect after debouncing
intitni Feb 20, 2024
5900121
Add timeout for UI elements
intitni Feb 20, 2024
3c3bacb
Bump version to 0.30.5
intitni Feb 20, 2024
dbbb30c
Update CopilotForXcodeKit to 0.3.0
intitni Feb 21, 2024
8325c3e
Support GitHub Copilot enterprise
intitni Feb 20, 2024
af2f668
Support Copilot.vim 1.17.0 to 1.19.x
intitni Feb 20, 2024
8fea60a
Fix auth provider url position
intitni Feb 21, 2024
3ad7e55
Bump Copilot.vim to 1.19.2
intitni Feb 21, 2024
65043e0
Replace miss-used NSWorkspace notification center
intitni Feb 21, 2024
079ad69
Support refreshing configuration
intitni Feb 21, 2024
e09d808
Merge branch 'feature/github-copilot-enterprise' into develop
intitni Feb 21, 2024
d609ddb
Update realtime suggestion debounce to minimum 0.25
intitni Feb 21, 2024
d7bcfcd
Discard suggestion when the focused editor element is changed
intitni Feb 21, 2024
ce0b20b
Update locations immediately in some situations
intitni Feb 21, 2024
c517f66
Remove the notification center thing
intitni Feb 21, 2024
6b4e6f8
Fix that the suggestion panel counld dance around when the completion…
intitni Feb 21, 2024
93bc361
Merge branch 'feature/tweak-about-widget-location-update' into develop
intitni Feb 21, 2024
5944902
Bump build number
intitni Feb 21, 2024
321236a
Ignore .applicationDeactivated because it's handled somewhere else
intitni Feb 21, 2024
efe5e20
Remove the useless await
intitni Feb 21, 2024
f9c30c6
Adjust interval
intitni Feb 21, 2024
5e86b93
Move the state retrieval to after the debouncing
intitni Feb 21, 2024
c1f506b
Bump version
intitni Feb 21, 2024
ec0da25
Merge tag '0.30.5.beta.2' into develop
intitni Feb 21, 2024
0266337
Make XcodeInspector a bit more thread safe
intitni Feb 21, 2024
9bee878
Access XcodeInspector thru actor
intitni Feb 21, 2024
b1dddcc
Update
intitni Feb 22, 2024
a03221f
Support opening ExtensionManager from menu item
intitni Feb 22, 2024
46fff5d
Merge branch 'feature/xcode-inspector-thread-safe-trial' into develop
intitni Feb 22, 2024
a82942d
Update build number
intitni Feb 22, 2024
9c7e86a
Access XcodeInspector state through actor
intitni Feb 22, 2024
e864847
Update
intitni Feb 22, 2024
d586dad
Update appcast.xml
intitni Feb 22, 2024
365050d
Merge branch 'release/0.30.5'
intitni Feb 22, 2024
f8394dc
Update README.md
intitni Feb 22, 2024
38faffe
Update README.md
intitni Feb 22, 2024
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
4 changes: 2 additions & 2 deletions Core/Sources/ChatPlugin/TerminalChatPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ public actor TerminalChatPlugin: ChatPlugin {
}

do {
let fileURL = XcodeInspector.shared.realtimeActiveDocumentURL
let projectURL = XcodeInspector.shared.realtimeActiveProjectURL
let fileURL = await XcodeInspector.shared.safe.realtimeActiveDocumentURL
let projectURL = await XcodeInspector.shared.safe.realtimeActiveProjectURL

var environment = [String: String]()
if let fileURL {
Expand Down
18 changes: 12 additions & 6 deletions Core/Sources/ChatService/ChatService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,14 @@ public final class ChatService: ObservableObject {
guard let info else { return }

let templateProcessor = CustomCommandTemplateProcessor()
mutateSystemPrompt(info.specifiedSystemPrompt.map(templateProcessor.process))
mutateExtraSystemPrompt(info.extraSystemPrompt.map(templateProcessor.process) ?? "")
if let specifiedSystemPrompt = info.specifiedSystemPrompt {
await mutateSystemPrompt(templateProcessor.process(specifiedSystemPrompt))
}
if let extraSystemPrompt = info.extraSystemPrompt {
await mutateExtraSystemPrompt(templateProcessor.process(extraSystemPrompt))
} else {
mutateExtraSystemPrompt("")
}

let customCommandPrefix = {
if let name = info.name { return "[\(name)] " }
Expand Down Expand Up @@ -250,9 +256,9 @@ public final class ChatService: ObservableObject {
let templateProcessor = CustomCommandTemplateProcessor()
if let systemPrompt {
if overwriteSystemPrompt {
mutateSystemPrompt(templateProcessor.process(systemPrompt))
await mutateSystemPrompt(templateProcessor.process(systemPrompt))
} else {
mutateExtraSystemPrompt(templateProcessor.process(systemPrompt))
await mutateExtraSystemPrompt(templateProcessor.process(systemPrompt))
}
}
return try await sendAndWait(content: templateProcessor.process(prompt))
Expand All @@ -265,10 +271,10 @@ public final class ChatService: ObservableObject {
) async throws -> String {
let templateProcessor = CustomCommandTemplateProcessor()
if let systemPrompt {
mutateSystemPrompt(templateProcessor.process(systemPrompt))
await mutateSystemPrompt(templateProcessor.process(systemPrompt))
}
if let extraSystemPrompt {
mutateExtraSystemPrompt(templateProcessor.process(extraSystemPrompt))
await mutateExtraSystemPrompt(templateProcessor.process(extraSystemPrompt))
}
return try await sendAndWait(content: templateProcessor.process(prompt))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public final class ContextAwareAutoManagedChatGPTMemory: ChatGPTMemory {
systemPrompt: """
\(chatService?.systemPrompt ?? "")
\(chatService?.extraSystemPrompt ?? "")
""",
""".trimmingCharacters(in: .whitespacesAndNewlines),
content: content ?? ""
)
return await memory.generatePrompt()
Expand Down
10 changes: 5 additions & 5 deletions Core/Sources/ChatService/CustomCommandTemplateProcessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import XcodeInspector
public struct CustomCommandTemplateProcessor {
public init() {}

public func process(_ text: String) -> String {
let info = getEditorInformation()
public func process(_ text: String) async -> String {
let info = await getEditorInformation()
let editorContent = info.editorContent
let updatedText = text
.replacingOccurrences(of: "{{selected_code}}", with: """
Expand Down Expand Up @@ -38,9 +38,9 @@ public struct CustomCommandTemplateProcessor {
let documentURL: URL?
}

func getEditorInformation() -> EditorInformation {
let editorContent = XcodeInspector.shared.focusedEditor?.getContent()
let documentURL = XcodeInspector.shared.activeDocumentURL
func getEditorInformation() async -> EditorInformation {
let editorContent = await XcodeInspector.shared.safe.focusedEditor?.getContent()
let documentURL = await XcodeInspector.shared.safe.activeDocumentURL
let language = documentURL.map(languageIdentifierFromFileURL) ?? .plaintext

return .init(
Expand Down
2 changes: 1 addition & 1 deletion Core/Sources/ChatService/DynamicContextController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ final class DynamicContextController {
let contextualSystemPrompt = """
\(language.isEmpty ? "" : "You must always reply in \(language)")
\(systemPrompt)
"""
""".trimmingCharacters(in: .whitespacesAndNewlines)
await memory.mutateSystemPrompt(contextualSystemPrompt)
await memory.mutateContextSystemPrompt(contextSystemPrompt)
await memory.mutateRetrievedContent(retrievedContent.map(\.document))
Expand Down
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
33 changes: 17 additions & 16 deletions Core/Sources/PromptToCodeService/OpenAIPromptToCodeService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,23 @@ public final class OpenAIPromptToCodeService: PromptToCodeServiceType {
return userPreferredLanguage.isEmpty ? "" : " in \(userPreferredLanguage)"
}()

let editor: EditorInformation = XcodeInspector.shared.getFocusedEditorContent() ?? .init(
editorContent: .init(
content: source.content,
lines: source.lines,
selections: [source.range],
cursorPosition: .outOfScope,
lineAnnotations: []
),
selectedContent: code,
selectedLines: [],
documentURL: source.documentURL,
workspaceURL: source.projectRootURL,
projectRootURL: source.projectRootURL,
relativePath: "",
language: source.language
)
let editor: EditorInformation = await XcodeInspector.shared.getFocusedEditorContent()
?? .init(
editorContent: .init(
content: source.content,
lines: source.lines,
selections: [source.range],
cursorPosition: .outOfScope,
lineAnnotations: []
),
selectedContent: code,
selectedLines: [],
documentURL: source.documentURL,
workspaceURL: source.projectRootURL,
projectRootURL: source.projectRootURL,
relativePath: "",
language: source.language
)

let rule: String = {
func generateDescription(index: Int) -> String {
Expand Down
24 changes: 14 additions & 10 deletions Core/Sources/Service/RealtimeSuggestionController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ public actor RealtimeSuggestionController {
private func handleFocusElementChange(_ sourceEditor: SourceEditor) {
Task { // Notify suggestion service for open file.
try await Task.sleep(nanoseconds: 500_000_000)
guard let fileURL = XcodeInspector.shared.realtimeActiveDocumentURL else { return }
guard let fileURL = await XcodeInspector.shared.safe.realtimeActiveDocumentURL
else { return }
_ = try await Service.shared.workspacePool
.fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL)
}
Expand All @@ -58,15 +59,16 @@ public actor RealtimeSuggestionController {
editorObservationTask = nil

editorObservationTask = Task { [weak self] in
if let fileURL = XcodeInspector.shared.realtimeActiveDocumentURL {
if let fileURL = await XcodeInspector.shared.safe.realtimeActiveDocumentURL {
await PseudoCommandHandler().invalidateRealtimeSuggestionsIfNeeded(
fileURL: fileURL,
sourceEditor: sourceEditor
)
}

let valueChange = notificationsFromEditor.filter { $0.kind == .valueChanged }
let selectedTextChanged = notificationsFromEditor
let valueChange = await notificationsFromEditor.notifications()
.filter { $0.kind == .valueChanged }
let selectedTextChanged = await notificationsFromEditor.notifications()
.filter { $0.kind == .selectedTextChanged }

await withTaskGroup(of: Void.self) { [weak self] group in
Expand All @@ -92,7 +94,8 @@ public actor RealtimeSuggestionController {
}
group.addTask {
let handler = {
guard let fileURL = XcodeInspector.shared.activeDocumentURL else { return }
guard let fileURL = await XcodeInspector.shared.safe.activeDocumentURL
else { return }
await PseudoCommandHandler().invalidateRealtimeSuggestionsIfNeeded(
fileURL: fileURL,
sourceEditor: sourceEditor
Expand All @@ -118,7 +121,8 @@ public actor RealtimeSuggestionController {

Task { @WorkspaceActor in // Get cache ready for real-time suggestions.
guard UserDefaults.shared.value(for: \.preCacheOnFileOpen) else { return }
guard let fileURL = XcodeInspector.shared.realtimeActiveDocumentURL else { return }
guard let fileURL = await XcodeInspector.shared.safe.realtimeActiveDocumentURL
else { return }
let (_, filespace) = try await Service.shared.workspacePool
.fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL)

Expand All @@ -127,7 +131,7 @@ public actor RealtimeSuggestionController {
// avoid the command get called twice
filespace.codeMetadata.uti = ""
do {
try await XcodeInspector.shared.latestActiveXcode?
try await XcodeInspector.shared.safe.latestActiveXcode?
.triggerCopilotCommand(name: "Real-time Suggestions")
} catch {
if filespace.codeMetadata.uti?.isEmpty ?? true {
Expand All @@ -141,7 +145,7 @@ public actor RealtimeSuggestionController {
func triggerPrefetchDebounced(force: Bool = false) {
inflightPrefetchTask = Task(priority: .utility) { @WorkspaceActor in
try? await Task.sleep(nanoseconds: UInt64(
max(UserDefaults.shared.value(for: \.realtimeSuggestionDebounce), 0.15)
max(UserDefaults.shared.value(for: \.realtimeSuggestionDebounce), 0.25)
* 1_000_000_000
))

Expand All @@ -151,7 +155,7 @@ public actor RealtimeSuggestionController {
else { return }

if UserDefaults.shared.value(for: \.disableSuggestionFeatureGlobally),
let fileURL = XcodeInspector.shared.activeDocumentURL,
let fileURL = await XcodeInspector.shared.safe.activeDocumentURL,
let (workspace, _) = try? await Service.shared.workspacePool
.fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL)
{
Expand Down Expand Up @@ -188,7 +192,7 @@ public actor RealtimeSuggestionController {
}

func notifyEditingFileChange(editor: AXUIElement) async {
guard let fileURL = XcodeInspector.shared.activeDocumentURL,
guard let fileURL = await XcodeInspector.shared.safe.activeDocumentURL,
let (workspace, _) = try? await Service.shared.workspacePool
.fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL)
else { return }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ struct PseudoCommandHandler {
}
}() else {
do {
try await XcodeInspector.shared.latestActiveXcode?
try await XcodeInspector.shared.safe.latestActiveXcode?
.triggerCopilotCommand(name: command.name)
} catch {
let presenter = PresentInWindowSuggestionPresenter()
Expand All @@ -183,7 +183,7 @@ struct PseudoCommandHandler {
throw CancellationError()
}
do {
try await XcodeInspector.shared.latestActiveXcode?
try await XcodeInspector.shared.safe.latestActiveXcode?
.triggerCopilotCommand(name: "Accept Prompt to Code")
} catch {
let last = Self.lastTimeCommandFailedToTriggerWithAccessibilityAPI
Expand Down Expand Up @@ -238,7 +238,7 @@ struct PseudoCommandHandler {
throw CancellationError()
}
do {
try await XcodeInspector.shared.latestActiveXcode?
try await XcodeInspector.shared.safe.latestActiveXcode?
.triggerCopilotCommand(name: "Accept Suggestion")
} catch {
let last = Self.lastTimeCommandFailedToTriggerWithAccessibilityAPI
Expand Down Expand Up @@ -288,7 +288,7 @@ struct PseudoCommandHandler {
}

func dismissSuggestion() async {
guard let documentURL = XcodeInspector.shared.activeDocumentURL else { return }
guard let documentURL = await XcodeInspector.shared.safe.activeDocumentURL else { return }
guard let (_, filespace) = try? await Service.shared.workspacePool
.fetchOrCreateWorkspaceAndFilespace(fileURL: documentURL) else { return }

Expand Down Expand Up @@ -377,14 +377,14 @@ extension PseudoCommandHandler {
return (content, split, [range], range.start)
}

func getFileURL() -> URL? {
XcodeInspector.shared.realtimeActiveDocumentURL
func getFileURL() async -> URL? {
await XcodeInspector.shared.safe.realtimeActiveDocumentURL
}

@WorkspaceActor
func getFilespace() async -> Filespace? {
guard
let fileURL = getFileURL(),
let fileURL = await getFileURL(),
let (_, filespace) = try? await Service.shared.workspacePool
.fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL)
else { return nil }
Expand All @@ -394,7 +394,10 @@ extension PseudoCommandHandler {
@WorkspaceActor
func getEditorContent(sourceEditor: SourceEditor?) async -> EditorContent? {
guard let filespace = await getFilespace(),
let sourceEditor = sourceEditor ?? XcodeInspector.shared.focusedEditor
let sourceEditor = await {
if let sourceEditor { sourceEditor }
else { await XcodeInspector.shared.safe.focusedEditor }
}()
else { return nil }
if Task.isCancelled { return nil }
let content = sourceEditor.getContent()
Expand Down
Loading