Skip to content

Commit e4a3225

Browse files
committed
Taking the actual opened tabs into account when cleaning up filespaces
1 parent 14d802f commit e4a3225

File tree

3 files changed

+75
-6
lines changed

3 files changed

+75
-6
lines changed

Core/Sources/AXExtension/AXUIElement.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,16 @@ public extension AXUIElement {
2828
try? copyValue(key: kAXDocumentAttribute)
2929
}
3030

31+
/// Label in Accessibility Inspector.
3132
var description: String {
3233
(try? copyValue(key: kAXDescriptionAttribute)) ?? ""
3334
}
3435

36+
/// Type in Accessibility Inspector.
37+
var roleDescription: String {
38+
(try? copyValue(key: kAXRoleDescriptionAttribute)) ?? ""
39+
}
40+
3541
var label: String {
3642
(try? copyValue(key: kAXLabelValueAttribute)) ?? ""
3743
}
@@ -158,6 +164,29 @@ public extension AXUIElement {
158164
}
159165
return nil
160166
}
167+
168+
func children(where match: (AXUIElement) -> Bool) -> [AXUIElement] {
169+
var all = [AXUIElement]()
170+
for child in children {
171+
if match(child) { all.append(child) }
172+
}
173+
for child in children {
174+
all.append(contentsOf: child.children(where: match))
175+
}
176+
return all
177+
}
178+
179+
func firstChild(where match: (AXUIElement) -> Bool) -> AXUIElement? {
180+
for child in children {
181+
if match(child) { return child }
182+
}
183+
for child in children {
184+
if let target = child.firstChild(where: match) {
185+
return target
186+
}
187+
}
188+
return nil
189+
}
161190

162191
func visibleChild(identifier: String) -> AXUIElement? {
163192
for child in visibleChildren {
Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import ActiveApplicationMonitor
2+
import AppKit
3+
import AXExtension
14
import Foundation
25

36
public final class ScheduledCleaner {
@@ -6,22 +9,47 @@ public final class ScheduledCleaner {
69
Task { @ServiceActor in
710
while !Task.isCancelled {
811
try await Task.sleep(nanoseconds: 2 * 60 * 60 * 1_000_000_000)
12+
let availableTabs = findAvailableOpenedTabs()
913
for (url, workspace) in workspaces {
1014
if workspace.isExpired {
1115
workspaces[url] = nil
1216
} else {
1317
// cleanup chats for unused files
1418
let filespaces = workspace.filespaces
15-
for (url, filespace) in filespaces {
16-
if filespace.isExpired {
19+
for (url, _) in filespaces {
20+
if workspace.isFilespaceExpired(
21+
fileURL: url,
22+
availableTabs: availableTabs
23+
) {
1724
WidgetDataSource.shared.cleanup(for: url)
1825
}
1926
}
2027
// cleanup workspace
21-
workspace.cleanUp()
28+
workspace.cleanUp(availableTabs: availableTabs)
2229
}
2330
}
2431
}
2532
}
2633
}
34+
35+
func findAvailableOpenedTabs() -> Set<String> {
36+
guard let xcode = ActiveApplicationMonitor.latestXcode else { return [] }
37+
let app = AXUIElementCreateApplication(xcode.processIdentifier)
38+
let windows = app.windows.filter { $0.identifier == "Xcode.WorkspaceWindow" }
39+
guard !windows.isEmpty else { return [] }
40+
var allTabs = Set<String>()
41+
for window in windows {
42+
guard let editArea = window.firstChild(where: { $0.description == "editor area" })
43+
else { continue }
44+
let tabBars = editArea.children { $0.description == "tab bar" }
45+
for tabBar in tabBars {
46+
let tabs = tabBar.children { $0.roleDescription == "tab" }
47+
for tab in tabs {
48+
allTabs.insert(tab.title)
49+
}
50+
}
51+
}
52+
return allTabs
53+
}
2754
}
55+

Core/Sources/Service/Workspace.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ extension Workspace {
325325
}
326326

327327
func notifyOpenFile(filespace: Filespace) {
328+
lastTriggerDate = Environment.now()
328329
Task {
329330
try await copilotSuggestionService?.notifyOpenTextDocument(
330331
fileURL: filespace.fileURL,
@@ -334,6 +335,8 @@ extension Workspace {
334335
}
335336

336337
func notifyUpdateFile(filespace: Filespace, content: String) {
338+
filespace.refreshUpdateTime()
339+
lastTriggerDate = Environment.now()
337340
Task {
338341
try await copilotSuggestionService?.notifyChangeTextDocument(
339342
fileURL: filespace.fileURL,
@@ -343,23 +346,32 @@ extension Workspace {
343346
}
344347

345348
func notifySaveFile(filespace: Filespace) {
349+
filespace.refreshUpdateTime()
350+
lastTriggerDate = Environment.now()
346351
Task {
347352
try await copilotSuggestionService?.notifySaveTextDocument(fileURL: filespace.fileURL)
348353
}
349354
}
350355
}
351356

352357
extension Workspace {
353-
func cleanUp() {
354-
for (fileURL, filespace) in filespaces {
355-
if filespace.isExpired {
358+
func cleanUp(availableTabs: Set<String>) {
359+
for (fileURL, _) in filespaces {
360+
if isFilespaceExpired(fileURL: fileURL, availableTabs: availableTabs) {
356361
Task {
357362
try await copilotSuggestionService?.notifyCloseTextDocument(fileURL: fileURL)
358363
}
359364
filespaces[fileURL] = nil
360365
}
361366
}
362367
}
368+
369+
func isFilespaceExpired(fileURL: URL, availableTabs: Set<String>) -> Bool {
370+
let filename = fileURL.lastPathComponent
371+
if availableTabs.contains(filename) { return false }
372+
guard let filespace = filespaces[fileURL] else { return true }
373+
return filespace.isExpired
374+
}
363375

364376
func cancelInFlightRealtimeSuggestionRequests() {
365377
for task in realtimeSuggestionRequests {

0 commit comments

Comments
 (0)