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 @@ -28,6 +28,7 @@ struct PresentInWindowSuggestionPresenter {

func presentError(_ error: Error) {
if error is CancellationError { return }
if let urlError = error as? URLError, urlError.code == URLError.cancelled { return }
Task { @MainActor in
let controller = GraphicalUserInterfaceController.shared.suggestionWidget
controller.presentError(error.localizedDescription)
Expand Down
19 changes: 11 additions & 8 deletions Core/Sources/SuggestionWidget/SuggestionWidgetController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,9 @@ public final class SuggestionWidgetController: NSObject {

public extension SuggestionWidgetController {
func suggestCode(fileURL: URL) {
widgetViewModel.isProcessing = false
Task {
markAsProcessing(true)
defer { markAsProcessing(false) }
if let suggestion = await dataSource?.suggestionForFile(at: fileURL) {
suggestionPanelViewModel.content = .suggestion(suggestion)
suggestionPanelViewModel.isPanelDisplayed = true
Expand All @@ -285,25 +286,28 @@ public extension SuggestionWidgetController {
}

func discardSuggestion(fileURL: URL) {
widgetViewModel.isProcessing = false
Task {
await updateContentForActiveEditor(fileURL: fileURL)
}
}

func markAsProcessing(_ isProcessing: Bool) {
widgetViewModel.isProcessing = isProcessing
if isProcessing {
widgetViewModel.markIsProcessing()
} else {
widgetViewModel.endIsProcessing()
}
}

func presentError(_ errorDescription: String) {
suggestionPanelViewModel.content = .error(errorDescription)
suggestionPanelViewModel.isPanelDisplayed = true
widgetViewModel.isProcessing = false
}

func presentChatRoom(fileURL: URL) {
widgetViewModel.isProcessing = false
Task {
markAsProcessing(true)
defer { markAsProcessing(false) }
if let chat = await dataSource?.chatForFile(at: fileURL) {
chatWindowViewModel.chat = chat
chatWindowViewModel.isPanelDisplayed = true
Expand Down Expand Up @@ -343,15 +347,15 @@ public extension SuggestionWidgetController {
}

func closeChatRoom(fileURL: URL) {
widgetViewModel.isProcessing = false
Task {
await updateContentForActiveEditor(fileURL: fileURL)
}
}

func presentPromptToCode(fileURL: URL) {
widgetViewModel.isProcessing = false
Task {
markAsProcessing(true)
defer { markAsProcessing(false) }
if let provider = await dataSource?.promptToCodeForFile(at: fileURL) {
suggestionPanelViewModel.content = .promptToCode(provider)
suggestionPanelViewModel.isPanelDisplayed = true
Expand All @@ -367,7 +371,6 @@ public extension SuggestionWidgetController {
}

func discardPromptToCode(fileURL: URL) {
widgetViewModel.isProcessing = false
Task {
await updateContentForActiveEditor(fileURL: fileURL)
}
Expand Down
33 changes: 32 additions & 1 deletion Core/Sources/SuggestionWidget/WidgetView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,40 @@ import SwiftUI

@MainActor
final class WidgetViewModel: ObservableObject {
@Published var isProcessing: Bool
struct IsProcessingCounter {
var expirationDate: TimeInterval
}

private var isProcessingCounters = [IsProcessingCounter]()
private var cleanupIsProcessingCounterTask: Task<Void, Error>?
@Published private(set) var isProcessing: Bool
@Published var currentFileURL: URL?

func markIsProcessing(date: Date = Date()) {
let deadline = date.timeIntervalSince1970 + 20
isProcessingCounters.append(IsProcessingCounter(expirationDate: deadline))
isProcessing = true

cleanupIsProcessingCounterTask?.cancel()
cleanupIsProcessingCounterTask = Task { [weak self] in
try await Task.sleep(nanoseconds: 20 * 1_000_000_000)
try Task.checkCancellation()
Task { @MainActor [weak self] in
guard let self else { return }
isProcessingCounters.removeAll()
isProcessing = false
}
}
}

func endIsProcessing(date: Date = Date()) {
if !isProcessingCounters.isEmpty {
isProcessingCounters.removeFirst()
}
isProcessingCounters.removeAll(where: { $0.expirationDate < date.timeIntervalSince1970 })
isProcessing = !isProcessingCounters.isEmpty
}

init(isProcessing: Bool = false) {
self.isProcessing = isProcessing
}
Expand Down
4 changes: 2 additions & 2 deletions Version.xcconfig
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
APP_VERSION = 0.17.0
APP_BUILD = 170
APP_VERSION = 0.17.1
APP_BUILD = 171
12 changes: 12 additions & 0 deletions appcast.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
<channel>
<title>Copilot for Xcode</title>

<item>
<title>0.17.1</title>
<pubDate>Wed, 31 May 2023 12:30:21 +0800</pubDate>
<sparkle:version>171</sparkle:version>
<sparkle:shortVersionString>0.17.1</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>12.0</sparkle:minimumSystemVersion>
<sparkle:releaseNotesLink>
https://github.com/intitni/CopilotForXcode/releases/tag/0.17.1
</sparkle:releaseNotesLink>
<enclosure url="https://github.com/intitni/CopilotForXcode/releases/download/0.17.1/Copilot.for.Xcode.app.zip" length="19078871" type="application/octet-stream" sparkle:edSignature="YCA19m2Ct8oM45VQXGWqYDL3OLdAL4dT9ZOJinc2w8VExVy/21ei27x9+qA8/7F/+njC3vpdJ/8K3fu3PYDSDw=="/>
</item>

<item>
<title>0.17.0</title>
<pubDate>Sat, 27 May 2023 15:33:25 +0800</pubDate>
Expand Down