diff --git a/.github/workflows/close_inactive_issues.yml b/.github/workflows/close_inactive_issues.yml index 6be38831..1fae3c1f 100644 --- a/.github/workflows/close_inactive_issues.yml +++ b/.github/workflows/close_inactive_issues.yml @@ -15,7 +15,7 @@ jobs: days-before-issue-stale: 30 days-before-issue-close: 14 stale-issue-label: "stale" - exempt-issue-labels: "low priority, help wanted, planned" + exempt-issue-labels: "low priority, help wanted, planned, investigating, blocked" stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." days-before-pr-stale: -1 diff --git a/Core/Sources/ChatGPTChatTab/Chat.swift b/Core/Sources/ChatGPTChatTab/Chat.swift index d5e32804..ee9b9dd0 100644 --- a/Core/Sources/ChatGPTChatTab/Chat.swift +++ b/Core/Sources/ChatGPTChatTab/Chat.swift @@ -14,16 +14,26 @@ public struct DisplayedChatMessage: Equatable { } public struct Reference: Equatable { + public typealias Kind = ChatMessage.Reference.Kind + public var title: String public var subtitle: String public var uri: String public var startLine: Int? - - public init(title: String, subtitle: String, uri: String, startLine: Int?) { + public var kind: Kind + + public init( + title: String, + subtitle: String, + uri: String, + startLine: Int?, + kind: Kind + ) { self.title = title self.subtitle = subtitle self.uri = uri self.startLine = startLine + self.kind = kind } } @@ -304,7 +314,8 @@ struct Chat: ReducerProtocol { title: $0.title, subtitle: $0.subTitle, uri: $0.uri, - startLine: $0.startLine + startLine: $0.startLine, + kind: $0.kind ) } ) diff --git a/Core/Sources/ChatGPTChatTab/ChatPanel.swift b/Core/Sources/ChatGPTChatTab/ChatPanel.swift index adaa78b0..7a729bfd 100644 --- a/Core/Sources/ChatGPTChatTab/ChatPanel.swift +++ b/Core/Sources/ChatGPTChatTab/ChatPanel.swift @@ -440,7 +440,8 @@ struct ChatPanel_Preview: PreviewProvider { title: "Hello Hello Hello Hello", subtitle: "Hi Hi Hi Hi", uri: "https://google.com", - startLine: nil + startLine: nil, + kind: .class ), ] ), diff --git a/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift b/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift index 1bf4c316..c8efac88 100644 --- a/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift +++ b/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift @@ -108,17 +108,20 @@ struct ReferenceList: View { chat.send(.referenceClicked(reference)) }) { HStack(spacing: 8) { + ReferenceIcon(kind: reference.kind) + .layoutPriority(2) Text(reference.title) .truncationMode(.middle) .lineLimit(1) + .layoutPriority(1) Text(reference.subtitle) .lineLimit(1) .truncationMode(.middle) - .layoutPriority(/*@START_MENU_TOKEN@*/0/*@END_MENU_TOKEN@*/) .foregroundStyle(.tertiary) + .layoutPriority(0) } - .padding(.vertical, 6) - .padding(.horizontal, 8) + .padding(.vertical, 4) + .padding(.horizontal, 4) .frame(maxWidth: .infinity, alignment: .leading) .overlay { RoundedRectangle(cornerRadius: 4) @@ -134,6 +137,83 @@ struct ReferenceList: View { } } +struct ReferenceIcon: View { + let kind: DisplayedChatMessage.Reference.Kind + + var body: some View { + RoundedRectangle(cornerRadius: 4) + .fill({ + switch kind { + case .class: + Color.purple + case .struct: + Color.purple + case .enum: + Color.purple + case .actor: + Color.purple + case .protocol: + Color.purple + case .extension: + Color.indigo + case .case: + Color.green + case .property: + Color.teal + case .typealias: + Color.orange + case .function: + Color.teal + case .method: + Color.blue + case .text: + Color.gray + case .webpage: + Color.blue + case .other: + Color.gray + } + }()) + .frame(width: 22, height: 22) + .overlay(alignment: .center) { + Group { + switch kind { + case .class: + Text("C") + case .struct: + Text("S") + case .enum: + Text("E") + case .actor: + Text("A") + case .protocol: + Text("Pr") + case .extension: + Text("Ex") + case .case: + Text("K") + case .property: + Text("P") + case .typealias: + Text("T") + case .function: + Text("𝑓") + case .method: + Text("M") + case .text: + Text("Tx") + case .webpage: + Text("Wb") + case .other: + Text("Ot") + } + } + .font(.system(size: 12).monospaced()) + .foregroundColor(.white) + } + } +} + #Preview("Bot Message") { BotMessage( id: "1", @@ -147,7 +227,8 @@ struct ReferenceList: View { title: "ReferenceList", subtitle: "/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift:100", uri: "https://google.com", - startLine: nil + startLine: nil, + kind: .class ), count: 20), chat: .init(initialState: .init(), reducer: Chat(service: .init())) ) @@ -161,37 +242,43 @@ struct ReferenceList: View { title: "ReferenceList", subtitle: "/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift:100", uri: "https://google.com", - startLine: nil + startLine: nil, + kind: .class ), .init( title: "BotMessage.swift:100-102", subtitle: "/Core/Sources/ChatGPTChatTab/Views", uri: "https://google.com", - startLine: nil + startLine: nil, + kind: .struct ), .init( title: "ReferenceList", subtitle: "/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift:100", uri: "https://google.com", - startLine: nil + startLine: nil, + kind: .function ), .init( title: "ReferenceList", subtitle: "/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift:100", uri: "https://google.com", - startLine: nil + startLine: nil, + kind: .case ), .init( title: "ReferenceList", subtitle: "/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift:100", uri: "https://google.com", - startLine: nil + startLine: nil, + kind: .extension ), .init( title: "ReferenceList", subtitle: "/Core/Sources/ChatGPTChatTab/Views/BotMessage.swift:100", uri: "https://google.com", - startLine: nil + startLine: nil, + kind: .webpage ), ], chat: .init(initialState: .init(), reducer: Chat(service: .init()))) } diff --git a/Core/Sources/HostApp/DebugView.swift b/Core/Sources/HostApp/DebugView.swift index 5d93f5e5..e4054dd1 100644 --- a/Core/Sources/HostApp/DebugView.swift +++ b/Core/Sources/HostApp/DebugView.swift @@ -17,6 +17,8 @@ final class DebugSettings: ObservableObject { @AppStorage(\.useUserDefaultsBaseAPIKeychain) var useUserDefaultsBaseAPIKeychain @AppStorage(\.disableEnhancedWorkspace) var disableEnhancedWorkspace @AppStorage(\.disableGitIgnoreCheck) var disableGitIgnoreCheck + @AppStorage(\.disableFileContentManipulationByCheatsheet) + var disableFileContentManipulationByCheatsheet init() {} } @@ -60,19 +62,23 @@ struct DebugSettingsView: View { Toggle(isOn: $settings.useUserDefaultsBaseAPIKeychain) { Text("Store API keys in UserDefaults") } - + Toggle(isOn: $settings.disableEnhancedWorkspace) { Text("Disable enhanced workspace") } - + Toggle(isOn: $settings.disableGitIgnoreCheck) { Text("Disable git ignore check") } - + + Toggle(isOn: $settings.disableFileContentManipulationByCheatsheet) { + Text("Disable file content manipulation by cheatsheet") + } + Button("Reset migration version to 0") { UserDefaults.shared.set(nil, forKey: "OldMigrationVersion") } - + Button("Reset 0.23.0 migration") { UserDefaults.shared.set("239", forKey: "OldMigrationVersion") UserDefaults.shared.set(nil, forKey: "MigrateTo240Finished") @@ -92,4 +98,3 @@ struct DebugSettingsView_Preview: PreviewProvider { } } - diff --git a/Core/Sources/HostApp/FeatureSettings/ChatSettingsView.swift b/Core/Sources/HostApp/FeatureSettings/ChatSettingsView.swift index b33770d0..e2a70d16 100644 --- a/Core/Sources/HostApp/FeatureSettings/ChatSettingsView.swift +++ b/Core/Sources/HostApp/FeatureSettings/ChatSettingsView.swift @@ -404,13 +404,14 @@ struct ChatSettingsView: View { } // MARK: - Preview - -#Preview { - ScrollView { - ChatSettingsView() - .padding() - } - .frame(height: 800) - .environment(\.overrideFeatureFlag, \.never) -} +// +//#Preview { +// ScrollView { +// ChatSettingsView() +// .padding() +// } +// .frame(height: 800) +// .environment(\.overrideFeatureFlag, \.never) +//} +// diff --git a/Core/Sources/SuggestionWidget/FeatureReducers/PanelFeature.swift b/Core/Sources/SuggestionWidget/FeatureReducers/PanelFeature.swift index 4fd43f94..5af9f2ba 100644 --- a/Core/Sources/SuggestionWidget/FeatureReducers/PanelFeature.swift +++ b/Core/Sources/SuggestionWidget/FeatureReducers/PanelFeature.swift @@ -111,7 +111,6 @@ public struct PanelFeature: ReducerProtocol { case .removeDisplayedContent: state.content.error = nil - state.content.promptToCodeGroup.activeDocumentURL = nil state.content.suggestion = nil return .none diff --git a/Core/Sources/SuggestionWidget/FeatureReducers/PromptToCodeGroup.swift b/Core/Sources/SuggestionWidget/FeatureReducers/PromptToCodeGroup.swift index 14d49b56..d689509a 100644 --- a/Core/Sources/SuggestionWidget/FeatureReducers/PromptToCodeGroup.swift +++ b/Core/Sources/SuggestionWidget/FeatureReducers/PromptToCodeGroup.swift @@ -19,7 +19,6 @@ public struct PromptToCodeGroup: ReducerProtocol { return promptToCodes[id: id] } set { - activeDocumentURL = newValue?.id if let id = newValue?.id { promptToCodes[id: id] = newValue } diff --git a/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift b/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift index 3465e422..5409ed95 100644 --- a/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift +++ b/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift @@ -115,7 +115,7 @@ public struct WidgetFeature: ReducerProtocol { case updateColorScheme case updateWindowLocation(animated: Bool) - case updateWindowOpacity + case updateWindowOpacity(immediately: Bool) case updateFocusingDocumentURL case updateWindowOpacityFinished case updateKeyWindow(WindowCanBecomeKey) @@ -131,7 +131,6 @@ public struct WidgetFeature: ReducerProtocol { @Dependency(\.suggestionWidgetUserDefaultsObservers) var userDefaultsObservers @Dependency(\.suggestionWidgetControllerDependency) var suggestionWidgetControllerDependency - @Dependency(\.activeApplicationMonitor) var activeApplicationMonitor @Dependency(\.xcodeInspector) var xcodeInspector @Dependency(\.mainQueue) var mainQueue @Dependency(\.activateThisApp) var activateThisApp @@ -207,7 +206,7 @@ public struct WidgetFeature: ReducerProtocol { let isDetached = state.chatPanelState.chatPanelInASeparateWindow return .run { send in await send(.updateWindowLocation(animated: false)) - await send(.updateWindowOpacity) + await send(.updateWindowOpacity(immediately: false)) if isDetached { Task { @MainActor in windows.chatPanelWindow.alphaValue = 1 @@ -218,7 +217,7 @@ public struct WidgetFeature: ReducerProtocol { let isDetached = state.chatPanelState.chatPanelInASeparateWindow return .run { send in await send(.updateWindowLocation(animated: !isDetached)) - await send(.updateWindowOpacity) + await send(.updateWindowOpacity(immediately: false)) } default: return .none } @@ -237,13 +236,23 @@ public struct WidgetFeature: ReducerProtocol { case .observeActiveApplicationChange: return .run { send in - var previousApp: RunningApplicationInfo? - for await app in activeApplicationMonitor.createInfoStream() { + let stream = AsyncStream { continuation in + let cancellable = xcodeInspector.$activeApplication.sink { newValue in + guard let newValue else { return } + continuation.yield(newValue.runningApplication) + } + continuation.onTermination = { _ in + cancellable.cancel() + } + } + + var previousAppIdentifier: pid_t? + for await app in stream { try Task.checkCancellation() - if app?.processIdentifier != previousApp?.processIdentifier { + if app.processIdentifier != previousAppIdentifier { await send(.updateActiveApplication) } - previousApp = app + previousAppIdentifier = app.processIdentifier } }.cancellable(id: CancelID.observeActiveApplicationChange, cancelInFlight: true) @@ -269,7 +278,7 @@ public struct WidgetFeature: ReducerProtocol { for await _ in stream { try Task.checkCancellation() await send(.updateWindowLocation(animated: false)) - await send(.updateWindowOpacity) + await send(.updateWindowOpacity(immediately: false)) } }.cancellable(id: CancelID.observeCompletionPanelChange, cancelInFlight: true) @@ -279,10 +288,11 @@ public struct WidgetFeature: ReducerProtocol { .notifications(named: NSWorkspace.activeSpaceDidChangeNotification) for await _ in sequence { try Task.checkCancellation() - guard let activeXcode = activeApplicationMonitor.activeXcode - else { continue } + guard let activeXcode = xcodeInspector.activeXcode else { continue } guard await windows.fullscreenDetector.isOnActiveSpace else { continue } - let app = AXUIElementCreateApplication(activeXcode.processIdentifier) + let app = AXUIElementCreateApplication( + activeXcode.runningApplication.processIdentifier + ) if let _ = app.focusedWindow { await windows.orderFront() } @@ -333,13 +343,13 @@ public struct WidgetFeature: ReducerProtocol { }.cancellable(id: CancelID.observeUserDefaults, cancelInFlight: true) case .observeWindowChange: - guard let app = activeApplicationMonitor.activeApplication else { return .none } + guard let app = xcodeInspector.activeApplication else { return .none } guard app.isXcode else { return .none } let documentURL = state.focusingDocumentURL let notifications = AXNotificationStream( - app: app, + app: app.runningApplication, notificationNames: kAXApplicationActivatedNotification, kAXMovedNotification, @@ -382,19 +392,21 @@ public struct WidgetFeature: ReducerProtocol { kAXFocusedWindowChangedNotification, ].contains(notification.name) { await send(.updateWindowLocation(animated: false)) - await send(.updateWindowOpacity) + await send(.updateWindowOpacity(immediately: false)) await send(.observeEditorChange) await send(.panel(.switchToAnotherEditorAndUpdateContent)) } else { await send(.updateWindowLocation(animated: false)) - await send(.updateWindowOpacity) + await send(.updateWindowOpacity(immediately: false)) } } }.cancellable(id: CancelID.observeWindowChange, cancelInFlight: true) case .observeEditorChange: - guard let app = activeApplicationMonitor.activeApplication else { return .none } - let appElement = AXUIElementCreateApplication(app.processIdentifier) + guard let app = xcodeInspector.activeApplication else { return .none } + let appElement = AXUIElementCreateApplication( + app.runningApplication.processIdentifier + ) guard let focusedElement = appElement.focusedElement, focusedElement.description == "Source Editor", let scrollView = focusedElement.parent, @@ -402,12 +414,12 @@ public struct WidgetFeature: ReducerProtocol { else { return .none } let selectionRangeChange = AXNotificationStream( - app: app, + app: app.runningApplication, element: focusedElement, notificationNames: kAXSelectedTextChangedNotification ) let scroll = AXNotificationStream( - app: app, + app: app.runningApplication, element: scrollBar, notificationNames: kAXValueChangedNotification ) @@ -418,37 +430,35 @@ public struct WidgetFeature: ReducerProtocol { selectionRangeChange.debounce(for: Duration.milliseconds(500)), scroll ) { - guard activeApplicationMonitor.latestXcode != nil - else { return } + guard xcodeInspector.latestActiveXcode != nil else { return } try Task.checkCancellation() await send(.updateWindowLocation(animated: false)) - await send(.updateWindowOpacity) + await send(.updateWindowOpacity(immediately: false)) } } else { for await _ in merge(selectionRangeChange, scroll) { - guard activeApplicationMonitor.latestXcode != nil - else { return } + guard xcodeInspector.latestActiveXcode != nil else { return } try Task.checkCancellation() await send(.updateWindowLocation(animated: false)) - await send(.updateWindowOpacity) + await send(.updateWindowOpacity(immediately: false)) } } }.cancellable(id: CancelID.observeEditorChange, cancelInFlight: true) case .updateActiveApplication: - if let app = activeApplicationMonitor.activeApplication, app.isXcode { + if let app = xcodeInspector.activeApplication, app.isXcode { return .run { send in await send(.panel(.switchToAnotherEditorAndUpdateContent)) await send(.updateWindowLocation(animated: false)) - await send(.updateWindowOpacity) + await send(.updateWindowOpacity(immediately: true)) await windows.orderFront() await send(.observeWindowChange) } } return .run { send in await send(.updateWindowLocation(animated: false)) - await send(.updateWindowOpacity) + await send(.updateWindowOpacity(immediately: true)) } case .updateColorScheme: @@ -478,7 +488,6 @@ public struct WidgetFeature: ReducerProtocol { state.focusingDocumentURL = xcodeInspector.realtimeActiveDocumentURL return .none - #warning("TODO: use function instead of action for high rate actions like this") case let .updateWindowLocation(animated): guard let widgetLocation = generateWidgetLocation() else { return .none } state.panelState.sharedPanelState.alignTopToAnchor = widgetLocation @@ -540,18 +549,23 @@ public struct WidgetFeature: ReducerProtocol { } } - case .updateWindowOpacity: + #warning("TODO: control windows in their dedicated reducers.") + case let .updateWindowOpacity(immediately): let isChatPanelDetached = state.chatPanelState.chatPanelInASeparateWindow let hasChat = !state.chatPanelState.chatTabGroup.tabInfo.isEmpty - let shouldDebounce = Date().timeIntervalSince(state.lastUpdateWindowOpacityTime) < 1 + let shouldDebounce = !immediately && + Date().timeIntervalSince(state.lastUpdateWindowOpacityTime) < 1 return .run { send in + let activeApp = xcodeInspector.activeApplication if shouldDebounce { try await mainQueue.sleep(for: .seconds(0.2)) } try Task.checkCancellation() let task = Task { @MainActor in - if let app = activeApplicationMonitor.activeApplication, app.isXcode { - let application = AXUIElementCreateApplication(app.processIdentifier) + if let activeApp, activeApp.isXcode { + let application = AXUIElementCreateApplication( + activeApp.runningApplication.processIdentifier + ) /// We need this to hide the windows when Xcode is minimized. let noFocus = application.focusedWindow == nil windows.sharedPanelWindow.alphaValue = noFocus ? 0 : 1 @@ -564,9 +578,7 @@ public struct WidgetFeature: ReducerProtocol { } else { windows.chatPanelWindow.alphaValue = noFocus ? 0 : 1 } - } else if let app = activeApplicationMonitor.activeApplication, - app.bundleIdentifier == Bundle.main.bundleIdentifier - { + } else if let activeApp, activeApp.isExtensionService { let noFocus = { guard let xcode = xcodeInspector.latestActiveXcode else { return true } @@ -628,7 +640,9 @@ public struct WidgetFeature: ReducerProtocol { } } } +} +extension WidgetFeature { @MainActor func hidePanelWindows() { windows.sharedPanelWindow.alphaValue = 0 diff --git a/Core/Sources/SuggestionWidget/WidgetPositionStrategy.swift b/Core/Sources/SuggestionWidget/WidgetPositionStrategy.swift index 5323ffb1..b46c705c 100644 --- a/Core/Sources/SuggestionWidget/WidgetPositionStrategy.swift +++ b/Core/Sources/SuggestionWidget/WidgetPositionStrategy.swift @@ -312,7 +312,6 @@ enum UpdateLocationStrategy { let caseConsiderCompletionPanel = { (completionPanelRect: CGRect) -> WidgetLocation.PanelLocation? in let completionPanelBelowCursor = completionPanelRect.minY >= selectionFrame.midY - switch (completionPanelBelowCursor, alignPanelTopToAnchor) { case (true, false), (false, true): // case: different position, place the suggestion as it should be @@ -394,6 +393,9 @@ enum UpdateLocationStrategy { var firstLineRange: CFRange = .init() let foundFirstLine = AXValueGetValue(selectedRange, .cfRange, &firstLineRange) firstLineRange.length = 0 + + #warning("FIXME: When selection is too low and out of the screen, the selection range becomes something else.") + if foundFirstLine, let firstLineSelectionRange = AXValueCreate(.cfRange, &firstLineRange), let firstLineRect: AXValue = try? editor.copyParameterizedValue( @@ -411,4 +413,3 @@ enum UpdateLocationStrategy { return selectionFrame } } - diff --git a/Pro b/Pro index a33512ff..a9e9bebd 160000 --- a/Pro +++ b/Pro @@ -1 +1 @@ -Subproject commit a33512ffb46a72b4fee6d89d397560c28f536e79 +Subproject commit a9e9bebd3af1af4fe5b20d2c3df4e436938c2bb6 diff --git a/Tool/Sources/CodeiumService/CodeiumInstallationManager.swift b/Tool/Sources/CodeiumService/CodeiumInstallationManager.swift index 808aeabc..62e6e0b5 100644 --- a/Tool/Sources/CodeiumService/CodeiumInstallationManager.swift +++ b/Tool/Sources/CodeiumService/CodeiumInstallationManager.swift @@ -3,7 +3,7 @@ import Terminal public struct CodeiumInstallationManager { private static var isInstalling = false - static let latestSupportedVersion = "1.4.15" + static let latestSupportedVersion = "1.6.6" public init() {} diff --git a/Tool/Sources/GitHubCopilotService/CopilotLocalProcessServer.swift b/Tool/Sources/GitHubCopilotService/CopilotLocalProcessServer.swift index 2b1a0882..e655a91d 100644 --- a/Tool/Sources/GitHubCopilotService/CopilotLocalProcessServer.swift +++ b/Tool/Sources/GitHubCopilotService/CopilotLocalProcessServer.swift @@ -108,13 +108,13 @@ extension CopilotLocalProcessServer: LanguageServerProtocol.Server { server.sendNotification(notif, completionHandler: completionHandler) } - + /// Cancel ongoing completion requests. public func cancelOngoingTasks() async { guard let server = wrappedServer, process.isRunning else { return } - + let task = Task { @MainActor in for id in self.ongoingCompletionRequestIDs { switch id { @@ -126,7 +126,7 @@ extension CopilotLocalProcessServer: LanguageServerProtocol.Server { } self.ongoingCompletionRequestIDs = [] } - + await task.value } @@ -200,32 +200,44 @@ extension CustomJSONRPCLanguageServer { block: @escaping (Error?) -> Void ) -> Bool { let methodName = anyNotification.method + let debugDescription = { + if let params = anyNotification.params { + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + if let jsonData = try? encoder.encode(params), + let text = String(data: jsonData, encoding: .utf8) + { + return text + } + } + return "N/A" + }() switch methodName { case "window/logMessage": if UserDefaults.shared.value(for: \.gitHubCopilotVerboseLog) { Logger.gitHubCopilot - .info("\(anyNotification.method): \(anyNotification.params.debugDescription)") + .info("\(anyNotification.method): \(debugDescription)") } block(nil) return true case "LogMessage": if UserDefaults.shared.value(for: \.gitHubCopilotVerboseLog) { Logger.gitHubCopilot - .info("\(anyNotification.method): \(anyNotification.params.debugDescription)") + .info("\(anyNotification.method): \(debugDescription)") } - block(nil) + block(nil)// return true case "statusNotification": if UserDefaults.shared.value(for: \.gitHubCopilotVerboseLog) { Logger.gitHubCopilot - .info("\(anyNotification.method): \(anyNotification.params.debugDescription)") + .info("\(anyNotification.method): \(debugDescription)") } block(nil) return true case "featureFlagsNotification": if UserDefaults.shared.value(for: \.gitHubCopilotVerboseLog) { Logger.gitHubCopilot - .info("\(anyNotification.method): \(anyNotification.params.debugDescription)") + .info("\(anyNotification.method): \(debugDescription)") } block(nil) return true diff --git a/Tool/Sources/GitHubCopilotService/GitHubCopilotInstallationManager.swift b/Tool/Sources/GitHubCopilotService/GitHubCopilotInstallationManager.swift index 051a3425..c706f81f 100644 --- a/Tool/Sources/GitHubCopilotService/GitHubCopilotInstallationManager.swift +++ b/Tool/Sources/GitHubCopilotService/GitHubCopilotInstallationManager.swift @@ -10,7 +10,7 @@ public struct GitHubCopilotInstallationManager { return URL(string: link)! } - static let latestSupportedVersion = "1.11.4" + static let latestSupportedVersion = "1.12.1" public init() {} diff --git a/Tool/Sources/Logger/Logger.swift b/Tool/Sources/Logger/Logger.swift index 8b740ddc..00b1d6eb 100644 --- a/Tool/Sources/Logger/Logger.swift +++ b/Tool/Sources/Logger/Logger.swift @@ -20,6 +20,7 @@ public final class Logger { public static let codeium = Logger(category: "Codeium") public static let langchain = Logger(category: "LangChain") public static let retrieval = Logger(category: "Retrieval") + public static let license = Logger(category: "License") #if DEBUG /// Use a temp logger to log something temporary. I won't be available in release builds. public static let temp = Logger(category: "Temp") diff --git a/Tool/Sources/OpenAIService/Models.swift b/Tool/Sources/OpenAIService/Models.swift index ea19c52f..8ff25b96 100644 --- a/Tool/Sources/OpenAIService/Models.swift +++ b/Tool/Sources/OpenAIService/Models.swift @@ -28,12 +28,31 @@ public struct ChatMessage: Equatable, Codable { } public struct Reference: Codable, Equatable { + public enum Kind: String, Codable { + case `class` + case `struct` + case `enum` + case `actor` + case `protocol` + case `extension` + case `case` + case property + case `typealias` + case function + case method + case text + case webpage + case other + } + public var title: String public var subTitle: String public var uri: String public var content: String public var startLine: Int? public var endLine: Int? + @FallbackDecoding + public var kind: Kind public init( title: String, @@ -41,7 +60,8 @@ public struct ChatMessage: Equatable, Codable { content: String, uri: String, startLine: Int?, - endLine: Int? + endLine: Int?, + kind: Kind ) { self.title = title self.subTitle = subTitle @@ -49,6 +69,7 @@ public struct ChatMessage: Equatable, Codable { self.uri = uri self.startLine = startLine self.endLine = endLine + self.kind = kind } } @@ -112,3 +133,7 @@ public struct ChatMessage: Equatable, Codable { } } +public struct ReferenceKindFallback: FallbackValueProvider { + public static var defaultValue: ChatMessage.Reference.Kind { .other } +} + diff --git a/Tool/Sources/Preferences/Keys.swift b/Tool/Sources/Preferences/Keys.swift index cae7c756..d1808434 100644 --- a/Tool/Sources/Preferences/Keys.swift +++ b/Tool/Sources/Preferences/Keys.swift @@ -550,6 +550,10 @@ public extension UserDefaultPreferenceKeys { var disableGitIgnoreCheck: FeatureFlag { .init(defaultValue: false, key: "FeatureFlag-DisableGitIgnoreCheck") } + + var disableFileContentManipulationByCheatsheet: FeatureFlag { + .init(defaultValue: true, key: "FeatureFlag-DisableFileContentManipulationByCheatsheet") + } var disableEnhancedWorkspace: FeatureFlag { .init( diff --git a/Tool/Sources/Workspace/Filespace.swift b/Tool/Sources/Workspace/Filespace.swift index 30553332..685fd134 100644 --- a/Tool/Sources/Workspace/Filespace.swift +++ b/Tool/Sources/Workspace/Filespace.swift @@ -88,7 +88,9 @@ public final class Filespace { // MARK: Git Ignore + @WorkspaceActor private var gitIgnoreStatus: GitIgnoreStatus? + @WorkspaceActor public var isGitIgnored: Bool { get async { @Dependency(\.gitIgnoredChecker) var gitIgnoredChecker diff --git a/Tool/Sources/WorkspaceSuggestionService/SuggestionWorkspacePlugin.swift b/Tool/Sources/WorkspaceSuggestionService/SuggestionWorkspacePlugin.swift index 7d17b684..e9147a79 100644 --- a/Tool/Sources/WorkspaceSuggestionService/SuggestionWorkspacePlugin.swift +++ b/Tool/Sources/WorkspaceSuggestionService/SuggestionWorkspacePlugin.swift @@ -88,6 +88,7 @@ public final class SuggestionServiceWorkspacePlugin: WorkspacePlugin { workspace?.refreshUpdateTime() workspace?.openedFileRecoverableStorage.openFile(fileURL: filespace.fileURL) Task { + guard !(await filespace.isGitIgnored) else { return } // check if file size is larger than 15MB, if so, return immediately if let attrs = try? FileManager.default .attributesOfItem(atPath: filespace.fileURL.path), @@ -106,6 +107,7 @@ public final class SuggestionServiceWorkspacePlugin: WorkspacePlugin { filespace.refreshUpdateTime() workspace?.refreshUpdateTime() Task { + guard !(await filespace.isGitIgnored) else { return } try await suggestionService?.notifyChangeTextDocument( fileURL: filespace.fileURL, content: content @@ -117,6 +119,7 @@ public final class SuggestionServiceWorkspacePlugin: WorkspacePlugin { filespace.refreshUpdateTime() workspace?.refreshUpdateTime() Task { + guard !(await filespace.isGitIgnored) else { return } try await suggestionService?.notifySaveTextDocument(fileURL: filespace.fileURL) } } diff --git a/Version.xcconfig b/Version.xcconfig index fc9e5829..e5fad3e5 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -1,3 +1,3 @@ -APP_VERSION = 0.28.2 -APP_BUILD = 292 +APP_VERSION = 0.28.3 +APP_BUILD = 293 diff --git a/appcast.xml b/appcast.xml index b2b9b2f7..18d180d6 100644 --- a/appcast.xml +++ b/appcast.xml @@ -3,6 +3,18 @@ Copilot for Xcode + + 0.28.3 + Tue, 12 Dec 2023 14:29:58 +0800 + 293 + 0.28.3 + 12.0 + + https://github.com/intitni/CopilotForXcode/releases/tag/0.28.3 + + + + 0.28.2 Mon, 04 Dec 2023 23:11:26 +0800