Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ public final class TerminalChatPlugin: ChatPlugin {
}

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

var environment = [String: String]()
if let fileURL {
Expand Down
8 changes: 8 additions & 0 deletions Core/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,14 @@ extension [Target.Dependency] {
}
return self
}

func proCore(_ targetNames: [String]) -> [Target.Dependency] {
if isProIncluded {
return self + targetNames
.map { Target.Dependency.product(name: $0, package: "ProCore") }
}
return self
}
}

extension [Package.Dependency] {
Expand Down
43 changes: 30 additions & 13 deletions Core/Sources/KeyBindingManager/TabToAcceptSuggestion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -277,30 +277,47 @@ extension TabToAcceptSuggestion {
}
}

import Combine

private class ThreadSafeAccessToXcodeInspector {
static let shared = ThreadSafeAccessToXcodeInspector()

private(set) var activeDocumentURL: URL?
private(set) var activeXcode: AppInstanceInspector?
private(set) var focusedEditor: SourceEditor?
private var cancellable: Set<AnyCancellable> = []

init() {
let inspector = XcodeInspector.shared
Task { [weak self] in
for await _ in NotificationCenter.default
.notifications(named: .activeDocumentURLDidChange)
{
guard let self else { return }
self.activeDocumentURL = await XcodeInspector.shared.activeDocumentURL
}
}

inspector.$activeDocumentURL.receive(on: DispatchQueue.main).sink { [weak self] newValue in
self?.activeDocumentURL = newValue
}.store(in: &cancellable)
Task { [weak self] in
for await _ in NotificationCenter.default
.notifications(named: .activeXcodeDidChange)
{
guard let self else { return }
self.activeXcode = await XcodeInspector.shared.activeXcode
}
}

inspector.$activeXcode.receive(on: DispatchQueue.main).sink { [weak self] newValue in
self?.activeXcode = newValue
}.store(in: &cancellable)
Task { [weak self] in
for await _ in NotificationCenter.default
.notifications(named: .focusedEditorDidChange)
{
guard let self else { return }
self.focusedEditor = await XcodeInspector.shared.focusedEditor
}
}

inspector.$focusedEditor.receive(on: DispatchQueue.main).sink { [weak self] newValue in
self?.focusedEditor = newValue
}.store(in: &cancellable)
// Initialize current values
Task {
activeDocumentURL = await XcodeInspector.shared.activeDocumentURL
activeXcode = await XcodeInspector.shared.activeApplication
focusedEditor = await XcodeInspector.shared.focusedEditor
}
}
}

4 changes: 2 additions & 2 deletions Core/Sources/LegacyChatPlugin/TerminalChatPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ public actor TerminalChatPlugin: LegacyChatPlugin {
}

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

var environment = [String: String]()
if let fileURL {
Expand Down
36 changes: 22 additions & 14 deletions Core/Sources/Service/GlobalShortcutManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extension KeyboardShortcuts.Name {
@MainActor
final class GlobalShortcutManager {
let guiController: GraphicalUserInterfaceController
private var cancellable = Set<AnyCancellable>()
private var activeAppChangeTask: Task<Void, Error>?

nonisolated init(guiController: GraphicalUserInterfaceController) {
self.guiController = guiController
Expand All @@ -34,22 +34,30 @@ final class GlobalShortcutManager {
}
}

XcodeInspector.shared.$activeApplication.sink { app in
if !UserDefaults.shared.value(for: \.showHideWidgetShortcutGlobally) {
let shouldBeEnabled = if let app, app.isXcode || app.isExtensionService {
true
activeAppChangeTask?.cancel()
activeAppChangeTask = Task.detached { [weak self] in
let notifications = NotificationCenter.default
.notifications(named: .activeApplicationDidChange)
for await _ in notifications {
guard let self else { return }
try Task.checkCancellation()
if !UserDefaults.shared.value(for: \.showHideWidgetShortcutGlobally) {
let app = await XcodeInspector.shared.activeApplication
let shouldBeEnabled = if let app, app.isXcode || app.isExtensionService {
true
} else {
false
}
if shouldBeEnabled {
await self.setupShortcutIfNeeded()
} else {
await self.removeShortcutIfNeeded()
}
} else {
false
await self.setupShortcutIfNeeded()
}
if shouldBeEnabled {
self.setupShortcutIfNeeded()
} else {
self.removeShortcutIfNeeded()
}
} else {
self.setupShortcutIfNeeded()
}
}.store(in: &cancellable)
}
}

func setupShortcutIfNeeded() {
Expand Down
34 changes: 17 additions & 17 deletions Core/Sources/Service/RealtimeSuggestionController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import ActiveApplicationMonitor
import AppKit
import AsyncAlgorithms
import AXExtension
import Combine
import Foundation
import Logger
import Preferences
Expand All @@ -11,15 +10,14 @@ import Workspace
import XcodeInspector

public actor RealtimeSuggestionController {
private var cancellable: Set<AnyCancellable> = []
private var xcodeChangeObservationTask: Task<Void, Error>?
private var inflightPrefetchTask: Task<Void, Error>?
private var editorObservationTask: Task<Void, Error>?
private var sourceEditor: SourceEditor?

init() {}

deinit {
cancellable.forEach { $0.cancel() }
inflightPrefetchTask?.cancel()
editorObservationTask?.cancel()
}
Expand All @@ -30,16 +28,18 @@ public actor RealtimeSuggestionController {
}

private func observeXcodeChange() {
cancellable.forEach { $0.cancel() }
xcodeChangeObservationTask?.cancel()

XcodeInspector.shared.$focusedEditor
.sink { [weak self] editor in
xcodeChangeObservationTask = Task { [weak self] in
for await _ in NotificationCenter.default
.notifications(named: .focusedEditorDidChange)
{
guard let self else { return }
Task {
guard let editor else { return }
await self.handleFocusElementChange(editor)
}
}.store(in: &cancellable)
try Task.checkCancellation()
guard let editor = await XcodeInspector.shared.focusedEditor else { continue }
await self.handleFocusElementChange(editor)
}
}
}

private func handleFocusElementChange(_ sourceEditor: SourceEditor) {
Expand All @@ -51,7 +51,7 @@ public actor RealtimeSuggestionController {
editorObservationTask = nil

editorObservationTask = Task { [weak self] in
if let fileURL = await XcodeInspector.shared.safe.realtimeActiveDocumentURL {
if let fileURL = XcodeInspector.shared.realtimeActiveDocumentURL {
await PseudoCommandHandler().invalidateRealtimeSuggestionsIfNeeded(
fileURL: fileURL,
sourceEditor: sourceEditor
Expand Down Expand Up @@ -86,7 +86,7 @@ public actor RealtimeSuggestionController {
}
group.addTask {
let handler = {
guard let fileURL = await XcodeInspector.shared.safe.activeDocumentURL
guard let fileURL = await XcodeInspector.shared.activeDocumentURL
else { return }
await PseudoCommandHandler().invalidateRealtimeSuggestionsIfNeeded(
fileURL: fileURL,
Expand All @@ -113,7 +113,7 @@ public actor RealtimeSuggestionController {

Task { @WorkspaceActor in // Get cache ready for real-time suggestions.
guard UserDefaults.shared.value(for: \.preCacheOnFileOpen) else { return }
guard let fileURL = await XcodeInspector.shared.safe.realtimeActiveDocumentURL
guard let fileURL = XcodeInspector.shared.realtimeActiveDocumentURL
else { return }
let (_, filespace) = try await Service.shared.workspacePool
.fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL)
Expand All @@ -123,7 +123,7 @@ public actor RealtimeSuggestionController {
// avoid the command get called twice
filespace.codeMetadata.uti = ""
do {
try await XcodeInspector.shared.safe.latestActiveXcode?
try await XcodeInspector.shared.latestActiveXcode?
.triggerCopilotCommand(name: "Prepare for Real-time Suggestions")
} catch {
if filespace.codeMetadata.uti?.isEmpty ?? true {
Expand All @@ -147,7 +147,7 @@ public actor RealtimeSuggestionController {
else { return }

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

func notifyEditingFileChange(editor: AXUIElement) async {
guard let fileURL = await XcodeInspector.shared.safe.activeDocumentURL,
guard let fileURL = await XcodeInspector.shared.activeDocumentURL,
let (workspace, _) = try? await Service.shared.workspacePool
.fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL)
else { return }
Expand Down
2 changes: 1 addition & 1 deletion Core/Sources/Service/ScheduledCleaner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public final class ScheduledCleaner {
func cleanUp() async {
guard let service else { return }

let workspaceInfos = XcodeInspector.shared.xcodes.reduce(
let workspaceInfos = await XcodeInspector.shared.xcodes.reduce(
into: [
XcodeAppInstanceInspector.WorkspaceIdentifier:
XcodeAppInstanceInspector.WorkspaceInfo
Expand Down
28 changes: 15 additions & 13 deletions Core/Sources/Service/Service.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,20 @@ public final class Service {
globalShortcutManager.start()
keyBindingManager.start()

Task {
await XcodeInspector.shared.safe.$activeDocumentURL
.removeDuplicates()
.filter { $0 != .init(fileURLWithPath: "/") }
.compactMap { $0 }
.sink { fileURL in
Task {
@Dependency(\.workspacePool) var workspacePool
return try await workspacePool
.fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL)
}
}.store(in: &cancellable)
Task.detached { [weak self] in
let notifications = NotificationCenter.default
.notifications(named: .activeDocumentURLDidChange)
var previousURL: URL?
for await _ in notifications {
guard self != nil else { return }
let url = await XcodeInspector.shared.activeDocumentURL
if let url, url != previousURL, url != .init(fileURLWithPath: "/") {
previousURL = url
@Dependency(\.workspacePool) var workspacePool
_ = try await workspacePool
.fetchOrCreateWorkspaceAndFilespace(fileURL: url)
}
}
}
}

Expand Down Expand Up @@ -158,7 +160,7 @@ public extension Service {
}
}
}

try ExtensionServiceRequests.GetSuggestionLineAcceptedCode.handle(
endpoint: endpoint,
requestBody: requestBody,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ struct PseudoCommandHandler: CommandHandler {
}
}() else {
do {
try await XcodeInspector.shared.safe.latestActiveXcode?
try await XcodeInspector.shared.latestActiveXcode?
.triggerCopilotCommand(name: command.name)
} catch {
let presenter = PresentInWindowSuggestionPresenter()
Expand All @@ -211,11 +211,11 @@ struct PseudoCommandHandler: CommandHandler {
throw CancellationError()
}
do {
try await XcodeInspector.shared.safe.latestActiveXcode?
try await XcodeInspector.shared.latestActiveXcode?
.triggerCopilotCommand(name: "Accept Modification")
} catch {
do {
try await XcodeInspector.shared.safe.latestActiveXcode?
try await XcodeInspector.shared.latestActiveXcode?
.triggerCopilotCommand(name: "Accept Prompt to Code")
} catch {
let last = Self.lastTimeCommandFailedToTriggerWithAccessibilityAPI
Expand Down Expand Up @@ -288,7 +288,7 @@ struct PseudoCommandHandler: CommandHandler {
throw CancellationError()
}
do {
try await XcodeInspector.shared.safe.latestActiveXcode?
try await XcodeInspector.shared.latestActiveXcode?
.triggerCopilotCommand(name: "Accept Suggestion Line")
} catch {
let last = Self.lastTimeCommandFailedToTriggerWithAccessibilityAPI
Expand Down Expand Up @@ -350,7 +350,7 @@ struct PseudoCommandHandler: CommandHandler {
throw CancellationError()
}
do {
try await XcodeInspector.shared.safe.latestActiveXcode?
try await XcodeInspector.shared.latestActiveXcode?
.triggerCopilotCommand(name: "Accept Suggestion")
} catch {
let last = Self.lastTimeCommandFailedToTriggerWithAccessibilityAPI
Expand Down Expand Up @@ -407,7 +407,7 @@ struct PseudoCommandHandler: CommandHandler {
}

func dismissSuggestion() async {
guard let documentURL = await XcodeInspector.shared.safe.activeDocumentURL else { return }
guard let documentURL = await XcodeInspector.shared.activeDocumentURL else { return }
PresentInWindowSuggestionPresenter().discardSuggestion(fileURL: documentURL)
guard let (_, filespace) = try? await Service.shared.workspacePool
.fetchOrCreateWorkspaceAndFilespace(fileURL: documentURL) else { return }
Expand Down Expand Up @@ -670,7 +670,7 @@ extension PseudoCommandHandler {
}

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

@WorkspaceActor
Expand All @@ -688,7 +688,7 @@ extension PseudoCommandHandler {
guard let filespace = await getFilespace(),
let sourceEditor = await {
if let sourceEditor { sourceEditor }
else { await XcodeInspector.shared.safe.focusedEditor }
else { await XcodeInspector.shared.focusedEditor }
}()
else { return nil }
if Task.isCancelled { return nil }
Expand All @@ -713,7 +713,7 @@ extension PseudoCommandHandler {
}

func handleAcceptSuggestionLineCommand(editor: EditorContent) async throws -> CodeSuggestion? {
guard let fileURL = await XcodeInspector.shared.safe.realtimeActiveDocumentURL
guard let _ = XcodeInspector.shared.realtimeActiveDocumentURL
else { return nil }

return try await acceptSuggestionLineInGroup(
Expand All @@ -726,7 +726,7 @@ extension PseudoCommandHandler {
atIndex index: Int?,
editor: EditorContent
) async throws -> CodeSuggestion? {
guard let fileURL = await XcodeInspector.shared.safe.realtimeActiveDocumentURL
guard let fileURL = XcodeInspector.shared.realtimeActiveDocumentURL
else { return nil }
let (workspace, _) = try await Service.shared.workspacePool
.fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL)
Expand Down
Loading