Skip to content

Commit 32aaa89

Browse files
authored
Fix WatchedFiles misreporting target statuses (#52)
* Fix WatchedFiles misreporting target statuses * Lint changes
1 parent 46159ec commit 32aaa89

File tree

6 files changed

+71
-56
lines changed

6 files changed

+71
-56
lines changed

Sources/SourceKitBazelBSP/RequestHandlers/PrepareHandler.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ final class PrepareHandler {
8989
}
9090

9191
extension PrepareHandler: InvalidatedTargetObserver {
92-
func invalidate(targets: Set<AffectedTarget>) throws {
92+
func invalidate(targets: [InvalidatedTarget]) throws {
9393
// Extract just the URIs from the affected targets for the build cache
9494
let targetURIs = Set(targets.map(\.uri))
9595
buildCache.subtract(targetURIs)

Sources/SourceKitBazelBSP/RequestHandlers/SKOptions/SKOptionsHandler.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ final class SKOptionsHandler: InvalidatedTargetObserver {
9292

9393
// MARK: - InvalidatedTargetObserver
9494

95-
func invalidate(targets: Set<AffectedTarget>) throws {
95+
func invalidate(targets: [InvalidatedTarget]) throws {
9696
// Only clear cache if at least one file was created or deleted
9797
if targets.contains(where: { $0.kind == .created || $0.kind == .deleted }) {
9898
extractor.clearCache()

Sources/SourceKitBazelBSP/RequestHandlers/WatchedFiles/InvalidatedTargetObserver.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@
2020
import BuildServerProtocol
2121
import LanguageServerProtocol
2222

23-
/// Represents a target that was affected by a file change
24-
struct AffectedTarget: Hashable {
25-
let uri: URI // Build target URI, not document URI
23+
/// Represents a target that was affected by a file change.
24+
struct InvalidatedTarget: Hashable {
25+
/// The URI of the build target that was invalidated.
26+
let uri: URI
27+
/// The URI of the file that triggered the invalidation.
28+
let fileUri: URI
29+
/// The kind of file change that triggered the invalidation.
2630
let kind: FileChangeType
2731
}
2832

2933
/// Protocol for objects that need to be notified when build targets are invalidated
3034
protocol InvalidatedTargetObserver: AnyObject {
31-
func invalidate(targets: Set<AffectedTarget>) throws
35+
func invalidate(targets: [InvalidatedTarget]) throws
3236
}

Sources/SourceKitBazelBSP/RequestHandlers/WatchedFiles/WatchedFileChangeHandler.swift

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,16 @@ final class WatchedFileChangeHandler {
6767

6868
logger.info("Received \(changes.count) file changes")
6969

70-
// First, calculate deleted targets before we clear them from the targetStore
71-
let deletedTargets = {
70+
let deletedFiles = changes.filter { $0.type == .deleted }
71+
let createdFiles = changes.filter { $0.type == .created }
72+
let changedFiles = changes.filter { $0.type == .changed }
73+
74+
// First, determine which targets had removed files.
75+
let targetsAffectedByDeletions: [InvalidatedTarget] = {
7276
do {
73-
return try changes.filter { $0.type == .deleted }.flatMap { change -> [AffectedTarget] in
77+
return try deletedFiles.flatMap { change in
7478
try targetStore.bspURIs(containingSrc: change.uri).map {
75-
AffectedTarget(uri: $0, kind: change.type)
79+
InvalidatedTarget(uri: $0, fileUri: change.uri, kind: .deleted)
7680
}
7781
}
7882
} catch {
@@ -81,9 +85,10 @@ final class WatchedFileChangeHandler {
8185
}
8286
}()
8387

84-
// If there are any 'created' files, we need to clear the targetStore and fetch targets again
85-
// Otherwise, the targetStore won't know about them
86-
if changes.contains(where: { $0.type == .created }) {
88+
// If there are any 'created' files, we need to clear the targetStore immediately and fetch targets again.
89+
// Otherwise, the targetStore won't know about them.
90+
// FIXME: This is quite expensive, but the easier thing to do. We can try improving this later.
91+
if !createdFiles.isEmpty {
8792
let taskId = TaskId(id: "watchedFiles-\(UUID().uuidString)")
8893
connection?.startWorkTask(id: taskId, title: "Indexing: Re-processing build graph")
8994
targetStore.clearCache()
@@ -96,12 +101,13 @@ final class WatchedFileChangeHandler {
96101
connection?.finishTask(id: taskId, status: .ok)
97102
}
98103

99-
// Now that the targetStore knows about the newly created files, we can calculate the created targets
100-
let createdTargets = {
104+
// Now that the targetStore knows about the newly created files, we can determine which targets
105+
// were affected by those creations.
106+
let targetsAffectedByCreations: [InvalidatedTarget] = {
101107
do {
102-
return try changes.filter { $0.type == .created }.flatMap { change -> [AffectedTarget] in
108+
return try createdFiles.flatMap { change in
103109
try targetStore.bspURIs(containingSrc: change.uri).map {
104-
AffectedTarget(uri: $0, kind: change.type)
110+
InvalidatedTarget(uri: $0, fileUri: change.uri, kind: .created)
105111
}
106112
}
107113
} catch {
@@ -110,12 +116,12 @@ final class WatchedFileChangeHandler {
110116
}
111117
}()
112118

113-
// Finally, calculate the changed targets
114-
let changedTargets = {
119+
// Finally, calculate the targets affected by regular changes.
120+
let targetsAffectedByChanges: [InvalidatedTarget] = {
115121
do {
116-
return try changes.filter { $0.type == .changed }.flatMap { change -> [AffectedTarget] in
122+
return try changedFiles.flatMap { change in
117123
try targetStore.bspURIs(containingSrc: change.uri).map {
118-
AffectedTarget(uri: $0, kind: change.type)
124+
InvalidatedTarget(uri: $0, fileUri: change.uri, kind: .changed)
119125
}
120126
}
121127
} catch {
@@ -124,24 +130,20 @@ final class WatchedFileChangeHandler {
124130
}
125131
}()
126132

127-
let affectedTargets: Set<AffectedTarget> = Set(deletedTargets + createdTargets + changedTargets)
133+
let invalidatedTargets = targetsAffectedByDeletions + targetsAffectedByCreations + targetsAffectedByChanges
128134

129-
// Invalidate our observers about the affected targets
135+
// Notify our observers about the affected targets
130136
for observer in observers {
131-
do {
132-
try observer.invalidate(targets: affectedTargets)
133-
} catch {
134-
logger.error("Error invalidating observer: \(error)")
135-
// Continue with other observers
136-
}
137+
try? observer.invalidate(targets: invalidatedTargets)
137138
}
138139

139140
// Notify SK-LSP about the affected targets
141+
let uniqueInvalidatedTargets = Set(invalidatedTargets.map { $0.uri })
140142
let response = OnBuildTargetDidChangeNotification(
141-
changes: affectedTargets.map { target in
143+
changes: uniqueInvalidatedTargets.map { targetUri in
142144
BuildTargetEvent(
143-
target: BuildTargetIdentifier(uri: target.uri),
144-
kind: target.kind.buildTargetEventKind,
145+
target: BuildTargetIdentifier(uri: targetUri),
146+
kind: .changed, // FIXME: We should eventually detect here also if the target is new/deleted.
145147
dataKind: nil,
146148
data: nil
147149
)
@@ -160,14 +162,3 @@ final class WatchedFileChangeHandler {
160162
return supportedFileExtensions.contains(String(result.reversed()))
161163
}
162164
}
163-
164-
extension FileChangeType {
165-
fileprivate var buildTargetEventKind: BuildTargetEventKind? {
166-
switch self {
167-
case .changed: return .changed
168-
case .created: return .created
169-
case .deleted: return .deleted
170-
default: return nil
171-
}
172-
}
173-
}

Tests/SourceKitBazelBSPTests/Fakes/InvalidatedTargetObserverFake.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ final class InvalidatedTargetObserverFake: InvalidatedTargetObserver {
2727
case intentional
2828
}
2929

30-
var invalidatedTargets: Set<AffectedTarget> = []
30+
var invalidatedTargets: [InvalidatedTarget] = []
3131
var invalidateCalled = false
3232
var shouldThrowOnInvalidate = false
3333

34-
func invalidate(targets: Set<AffectedTarget>) throws {
34+
func invalidate(targets: [InvalidatedTarget]) throws {
3535
invalidateCalled = true
3636
invalidatedTargets = targets
3737
if shouldThrowOnInvalidate {

Tests/SourceKitBazelBSPTests/WatchedFileChangeHandlerTests.swift

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,12 @@ struct WatchedFileChangeHandlerTests {
8080
// Check that the observer was notified
8181
#expect(observer.invalidateCalled)
8282
#expect(observer.invalidatedTargets.count == 2)
83-
#expect(observer.invalidatedTargets.contains(AffectedTarget(uri: targetURI1, kind: .deleted)))
84-
#expect(observer.invalidatedTargets.contains(AffectedTarget(uri: targetURI2, kind: .deleted)))
83+
#expect(
84+
observer.invalidatedTargets.contains(InvalidatedTarget(uri: targetURI1, fileUri: fileURI, kind: .deleted))
85+
)
86+
#expect(
87+
observer.invalidatedTargets.contains(InvalidatedTarget(uri: targetURI2, fileUri: fileURI, kind: .deleted))
88+
)
8589

8690
// Check that the LSP connection received the notification
8791
#expect(connection.sentNotifications.count == 1)
@@ -94,7 +98,7 @@ struct WatchedFileChangeHandlerTests {
9498
#expect(actualTargetURIs == expectedTargetURIs)
9599

96100
for change in changes {
97-
#expect(change.kind == .deleted)
101+
#expect(change.kind == .changed)
98102
}
99103
}
100104
} else {
@@ -129,14 +133,16 @@ struct WatchedFileChangeHandlerTests {
129133
// Check that the observer was notified
130134
#expect(observer.invalidateCalled)
131135
#expect(observer.invalidatedTargets.count == 1)
132-
#expect(observer.invalidatedTargets.contains(AffectedTarget(uri: targetURI, kind: .created)))
136+
#expect(
137+
observer.invalidatedTargets.contains(InvalidatedTarget(uri: targetURI, fileUri: fileURI, kind: .created))
138+
)
133139

134140
// Check that the LSP connection received the notification
135141
#expect(connection.sentNotifications.count == 1)
136142
if let sentNotification = connection.sentNotifications.first as? OnBuildTargetDidChangeNotification {
137143
#expect(sentNotification.changes?.count == 1)
138144
#expect(sentNotification.changes?[0].target.uri == targetURI)
139-
#expect(sentNotification.changes?[0].kind == .created)
145+
#expect(sentNotification.changes?[0].kind == .changed)
140146
} else {
141147
Issue.record("Expected OnBuildTargetDidChangeNotification")
142148
}
@@ -169,7 +175,9 @@ struct WatchedFileChangeHandlerTests {
169175
// Check that the observer was notified
170176
#expect(observer.invalidateCalled)
171177
#expect(observer.invalidatedTargets.count == 1)
172-
#expect(observer.invalidatedTargets.contains(AffectedTarget(uri: targetURI, kind: .changed)))
178+
#expect(
179+
observer.invalidatedTargets.contains(InvalidatedTarget(uri: targetURI, fileUri: fileURI, kind: .changed))
180+
)
173181

174182
// Check that the LSP connection received the notification
175183
#expect(connection.sentNotifications.count == 1)
@@ -217,9 +225,21 @@ struct WatchedFileChangeHandlerTests {
217225
// Check that the observer was notified with all affected targets
218226
#expect(observer.invalidateCalled)
219227
#expect(observer.invalidatedTargets.count == 3)
220-
#expect(observer.invalidatedTargets.contains(AffectedTarget(uri: deletedTargetURI, kind: .deleted)))
221-
#expect(observer.invalidatedTargets.contains(AffectedTarget(uri: createdTargetURI, kind: .created)))
222-
#expect(observer.invalidatedTargets.contains(AffectedTarget(uri: changedTargetURI, kind: .changed)))
228+
#expect(
229+
observer.invalidatedTargets.contains(
230+
InvalidatedTarget(uri: deletedTargetURI, fileUri: deletedFileURI, kind: .deleted)
231+
)
232+
)
233+
#expect(
234+
observer.invalidatedTargets.contains(
235+
InvalidatedTarget(uri: createdTargetURI, fileUri: createdFileURI, kind: .created)
236+
)
237+
)
238+
#expect(
239+
observer.invalidatedTargets.contains(
240+
InvalidatedTarget(uri: changedTargetURI, fileUri: changedFileURI, kind: .changed)
241+
)
242+
)
223243

224244
// Check that the LSP connection received the notification with all changes
225245
#expect(connection.sentNotifications.count == 1)
@@ -228,8 +248,8 @@ struct WatchedFileChangeHandlerTests {
228248

229249
if let notificationChanges = sentNotification.changes {
230250
let changes = notificationChanges.map { (uri: $0.target.uri, kind: $0.kind) }
231-
#expect(changes.contains { $0.uri == deletedTargetURI && $0.kind == .deleted })
232-
#expect(changes.contains { $0.uri == createdTargetURI && $0.kind == .created })
251+
#expect(changes.contains { $0.uri == deletedTargetURI && $0.kind == .changed })
252+
#expect(changes.contains { $0.uri == createdTargetURI && $0.kind == .changed })
233253
#expect(changes.contains { $0.uri == changedTargetURI && $0.kind == .changed })
234254
}
235255
} else {

0 commit comments

Comments
 (0)