Skip to content

Commit 6479a8f

Browse files
authored
Merge branch 'apple:main' into rename-note-to-notification-wherever-necessary
2 parents e211824 + 7a1324d commit 6479a8f

File tree

56 files changed

+1085
-737
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1085
-737
lines changed

Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ let package = Package(
367367
name: "SourceKitLSPTests",
368368
dependencies: [
369369
"BuildServerProtocol",
370+
"CAtomics",
370371
"LSPLogging",
371372
"LSPTestSupport",
372373
"LanguageServerProtocol",

Sources/SKCore/BuildServerBuildSystem.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ extension BuildServerBuildSystem: BuildSystem {
279279
return [ConfiguredTarget(targetID: "dummy", runDestinationID: "dummy")]
280280
}
281281

282-
public func generateBuildGraph() {}
282+
public func generateBuildGraph(allowFileSystemWrites: Bool) {}
283283

284284
public func topologicalSort(of targets: [ConfiguredTarget]) async -> [ConfiguredTarget]? {
285285
return nil

Sources/SKCore/BuildSystem.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,14 @@ public protocol BuildSystem: AnyObject, Sendable {
135135
/// Return the list of targets and run destinations that the given document can be built for.
136136
func configuredTargets(for document: DocumentURI) async -> [ConfiguredTarget]
137137

138-
/// Re-generate the build graph including all the tasks that are necessary for building the entire build graph, like
139-
/// resolving package versions.
140-
func generateBuildGraph() async throws
138+
/// Re-generate the build graph.
139+
///
140+
/// If `allowFileSystemWrites` is `true`, this should include all the tasks that are necessary for building the entire
141+
/// build graph, like resolving package versions.
142+
///
143+
/// If `allowFileSystemWrites` is `false`, no files must be written to disk. This mode is used to determine whether
144+
/// the build system can handle a source file, and decide whether a workspace should be opened with this build system
145+
func generateBuildGraph(allowFileSystemWrites: Bool) async throws
141146

142147
/// Sort the targets so that low-level targets occur before high-level targets.
143148
///

Sources/SKCore/BuildSystemManager.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,8 @@ extension BuildSystemManager {
219219
return settings
220220
}
221221

222-
public func generateBuildGraph() async throws {
223-
try await self.buildSystem?.generateBuildGraph()
222+
public func generateBuildGraph(allowFileSystemWrites: Bool) async throws {
223+
try await self.buildSystem?.generateBuildGraph(allowFileSystemWrites: allowFileSystemWrites)
224224
}
225225

226226
public func topologicalSort(of targets: [ConfiguredTarget]) async throws -> [ConfiguredTarget]? {

Sources/SKCore/CompilationDatabaseBuildSystem.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ extension CompilationDatabaseBuildSystem: BuildSystem {
132132
throw PrepareNotSupportedError()
133133
}
134134

135-
public func generateBuildGraph() {}
135+
public func generateBuildGraph(allowFileSystemWrites: Bool) {}
136136

137137
public func topologicalSort(of targets: [ConfiguredTarget]) -> [ConfiguredTarget]? {
138138
return nil

Sources/SKCore/TaskScheduler.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,11 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
182182
private let executionStateChangedCallback: (@Sendable (QueuedTask, TaskExecutionState) async -> Void)?
183183

184184
init(
185-
priority: TaskPriority? = nil,
185+
priority: TaskPriority,
186186
description: TaskDescription,
187187
executionStateChangedCallback: (@Sendable (QueuedTask, TaskExecutionState) async -> Void)?
188188
) async {
189-
self._priority = AtomicUInt8(initialValue: priority?.rawValue ?? Task.currentPriority.rawValue)
189+
self._priority = AtomicUInt8(initialValue: priority.rawValue)
190190
self.description = description
191191
self.executionStateChangedCallback = executionStateChangedCallback
192192

@@ -277,6 +277,10 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
277277
"Elevating priority of \(self.description.forLogging) from \(self.priority.rawValue) to \(targetPriority.rawValue)"
278278
)
279279
}
280+
// Awaiting the result task from a higher-priority task will eventually update `priority` through
281+
// `withTaskPriorityChangedHandler` but that might take a while because `withTaskPriorityChangedHandler` polls.
282+
// Since we know that the priority will be elevated, set it now. That way we don't try to elevate it again.
283+
self.priority = targetPriority
280284
Task(priority: targetPriority) {
281285
await self.resultTask.value
282286
}
@@ -352,7 +356,7 @@ public actor TaskScheduler<TaskDescription: TaskDescriptionProtocol> {
352356
)? = nil
353357
) async -> QueuedTask<TaskDescription> {
354358
let queuedTask = await QueuedTask(
355-
priority: priority,
359+
priority: priority ?? Task.currentPriority,
356360
description: taskDescription,
357361
executionStateChangedCallback: executionStateChangedCallback
358362
)

Sources/SKSupport/AsyncUtils.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,28 @@ extension Collection where Element: Sendable {
166166
}
167167
}
168168
}
169+
170+
public struct TimeoutError: Error, CustomStringConvertible {
171+
public var description: String { "Timed out" }
172+
}
173+
174+
/// Executes `body`. If it doesn't finish after `duration`, throws a `TimeoutError`.
175+
public func withTimeout<T: Sendable>(
176+
_ duration: Duration,
177+
_ body: @escaping @Sendable () async throws -> T
178+
) async throws -> T {
179+
try await withThrowingTaskGroup(of: T.self) { taskGroup in
180+
taskGroup.addTask {
181+
try await Task.sleep(for: duration)
182+
throw TimeoutError()
183+
}
184+
taskGroup.addTask {
185+
return try await body()
186+
}
187+
for try await value in taskGroup {
188+
taskGroup.cancelAll()
189+
return value
190+
}
191+
throw CancellationError()
192+
}
193+
}

Sources/SKSwiftPMWorkspace/SwiftPMBuildSystem.swift

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,9 @@ public actor SwiftPMBuildSystem {
178178
forRootPackage: AbsolutePath(packageRoot),
179179
fileSystem: fileSystem
180180
)
181-
if let scratchDirectory = buildSetup.path {
181+
if isForIndexBuild {
182+
location.scratchDirectory = AbsolutePath(packageRoot.appending(component: ".index-build"))
183+
} else if let scratchDirectory = buildSetup.path {
182184
location.scratchDirectory = AbsolutePath(scratchDirectory)
183185
}
184186

@@ -227,7 +229,6 @@ public actor SwiftPMBuildSystem {
227229
}
228230
await delegate.filesDependenciesUpdated(filesWithUpdatedDependencies)
229231
}
230-
try await reloadPackage()
231232
}
232233

233234
/// Creates a build system using the Swift Package Manager, if this workspace is a package.
@@ -261,13 +262,9 @@ public actor SwiftPMBuildSystem {
261262
}
262263

263264
extension SwiftPMBuildSystem {
264-
public func generateBuildGraph() async throws {
265-
try await self.reloadPackage()
266-
}
267-
268265
/// (Re-)load the package settings by parsing the manifest and resolving all the targets and
269266
/// dependencies.
270-
func reloadPackage() async throws {
267+
public func reloadPackage(forceResolvedVersions: Bool) async throws {
271268
await reloadPackageStatusCallback(.start)
272269
defer {
273270
Task {
@@ -277,7 +274,7 @@ extension SwiftPMBuildSystem {
277274

278275
let modulesGraph = try self.workspace.loadPackageGraph(
279276
rootInput: PackageGraphRootInput(packages: [AbsolutePath(projectRoot)]),
280-
forceResolvedVersions: !isForIndexBuild,
277+
forceResolvedVersions: forceResolvedVersions,
281278
observabilityScope: observabilitySystem.topScope
282279
)
283280

@@ -430,6 +427,10 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
430427
return []
431428
}
432429

430+
public func generateBuildGraph(allowFileSystemWrites: Bool) async throws {
431+
try await self.reloadPackage(forceResolvedVersions: !isForIndexBuild || !allowFileSystemWrites)
432+
}
433+
433434
public func topologicalSort(of targets: [ConfiguredTarget]) -> [ConfiguredTarget]? {
434435
return targets.sorted { (lhs: ConfiguredTarget, rhs: ConfiguredTarget) -> Bool in
435436
let lhsIndex = self.targets[lhs]?.index ?? self.targets.count
@@ -590,7 +591,7 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
590591
logger.log("Reloading package because of file change")
591592
await orLog("Reloading package") {
592593
// TODO: It should not be necessary to reload the entire package just to get build settings for one file.
593-
try await self.reloadPackage()
594+
try await self.reloadPackage(forceResolvedVersions: !isForIndexBuild)
594595
}
595596
}
596597

Sources/SKTestSupport/MultiFileTestProject.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public class MultiFileTestProject {
8282
workspaces: (URL) async throws -> [WorkspaceFolder] = { [WorkspaceFolder(uri: DocumentURI($0))] },
8383
capabilities: ClientCapabilities = ClientCapabilities(),
8484
serverOptions: SourceKitLSPServer.Options = .testDefault,
85+
enableBackgroundIndexing: Bool = false,
8586
usePullDiagnostics: Bool = true,
8687
preInitialization: ((TestSourceKitLSPClient) -> Void)? = nil,
8788
cleanUp: (() -> Void)? = nil,
@@ -117,6 +118,7 @@ public class MultiFileTestProject {
117118
serverOptions: serverOptions,
118119
capabilities: capabilities,
119120
usePullDiagnostics: usePullDiagnostics,
121+
enableBackgroundIndexing: enableBackgroundIndexing,
120122
workspaceFolders: workspaces(scratchDirectory),
121123
preInitialization: preInitialization,
122124
cleanUp: { [scratchDirectory] in

Sources/SKTestSupport/SkipUnless.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public actor SkipUnless {
103103
) async throws {
104104
try await shared.skipUnlessSupportedByToolchain(swiftVersion: SwiftVersion(5, 11), file: file, line: line) {
105105
let testClient = try await TestSourceKitLSPClient()
106-
let uri = DocumentURI.for(.swift)
106+
let uri = DocumentURI(for: .swift)
107107
testClient.openDocument("0.bitPattern", uri: uri)
108108
let response = try unwrap(
109109
await testClient.send(DocumentSemanticTokensRequest(textDocument: TextDocumentIdentifier(uri)))
@@ -134,7 +134,7 @@ public actor SkipUnless {
134134
) async throws {
135135
try await shared.skipUnlessSupportedByToolchain(swiftVersion: SwiftVersion(5, 11), file: file, line: line) {
136136
let testClient = try await TestSourceKitLSPClient()
137-
let uri = DocumentURI.for(.swift)
137+
let uri = DocumentURI(for: .swift)
138138
let positions = testClient.openDocument("func 1️⃣test() {}", uri: uri)
139139
do {
140140
_ = try await testClient.send(
@@ -154,7 +154,7 @@ public actor SkipUnless {
154154
) async throws {
155155
try await shared.skipUnlessSupportedByToolchain(swiftVersion: SwiftVersion(5, 11), file: file, line: line) {
156156
let testClient = try await TestSourceKitLSPClient()
157-
let uri = DocumentURI.for(.c)
157+
let uri = DocumentURI(for: .c)
158158
let positions = testClient.openDocument("void 1️⃣test() {}", uri: uri)
159159
do {
160160
_ = try await testClient.send(
@@ -214,7 +214,7 @@ public actor SkipUnless {
214214
return try await shared.skipUnlessSupportedByToolchain(swiftVersion: SwiftVersion(6, 0), file: file, line: line) {
215215
// The XML-based doc comment conversion did not preserve `Precondition`.
216216
let testClient = try await TestSourceKitLSPClient()
217-
let uri = DocumentURI.for(.swift)
217+
let uri = DocumentURI(for: .swift)
218218
let positions = testClient.openDocument(
219219
"""
220220
/// - Precondition: Must have an apple

Sources/SKTestSupport/SwiftPMTestProject.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ public class SwiftPMTestProject: MultiFileTestProject {
4444
allowBuildFailure: Bool = false,
4545
capabilities: ClientCapabilities = ClientCapabilities(),
4646
serverOptions: SourceKitLSPServer.Options = .testDefault,
47+
enableBackgroundIndexing: Bool = false,
48+
usePullDiagnostics: Bool = true,
4749
pollIndex: Bool = true,
4850
preInitialization: ((TestSourceKitLSPClient) -> Void)? = nil,
49-
usePullDiagnostics: Bool = true,
5051
cleanUp: (() -> Void)? = nil,
5152
testName: String = #function
5253
) async throws {
@@ -71,6 +72,7 @@ public class SwiftPMTestProject: MultiFileTestProject {
7172
workspaces: workspaces,
7273
capabilities: capabilities,
7374
serverOptions: serverOptions,
75+
enableBackgroundIndexing: enableBackgroundIndexing,
7476
usePullDiagnostics: usePullDiagnostics,
7577
preInitialization: preInitialization,
7678
cleanUp: cleanUp,

Sources/SKTestSupport/TestSourceKitLSPClient.swift

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ public final class TestSourceKitLSPClient: MessageHandler {
8888
/// `true` by default
8989
/// - initializationOptions: Initialization options to pass to the SourceKit-LSP server.
9090
/// - capabilities: The test client's capabilities.
91-
/// - usePullDiagnostics: Whether to use push diagnostics or use push-based diagnostics
91+
/// - usePullDiagnostics: Whether to use push diagnostics or use push-based diagnostics.
92+
/// - enableBackgroundIndexing: Whether background indexing should be enabled in the project.
9293
/// - workspaceFolders: Workspace folders to open.
9394
/// - preInitialization: A closure that is called after the test client is created but before SourceKit-LSP is
9495
/// initialized. This can be used to eg. register request handlers.
@@ -102,6 +103,7 @@ public final class TestSourceKitLSPClient: MessageHandler {
102103
initializationOptions: LSPAny? = nil,
103104
capabilities: ClientCapabilities = ClientCapabilities(),
104105
usePullDiagnostics: Bool = true,
106+
enableBackgroundIndexing: Bool = false,
105107
workspaceFolders: [WorkspaceFolder]? = nil,
106108
preInitialization: ((TestSourceKitLSPClient) -> Void)? = nil,
107109
cleanUp: @Sendable @escaping () -> Void = {}
@@ -115,6 +117,7 @@ public final class TestSourceKitLSPClient: MessageHandler {
115117
if let moduleCache {
116118
serverOptions.buildSetup.flags.swiftCompilerFlags += ["-module-cache-path", moduleCache.path]
117119
}
120+
serverOptions.indexOptions.enableBackgroundIndexing = enableBackgroundIndexing
118121

119122
var notificationYielder: AsyncStream<any NotificationType>.Continuation!
120123
self.notifications = AsyncStream { continuation in
@@ -155,8 +158,8 @@ public final class TestSourceKitLSPClient: MessageHandler {
155158
XCTAssertEqual(request.registrations.only?.method, DocumentDiagnosticsRequest.method)
156159
return VoidResponse()
157160
}
158-
preInitialization?(self)
159161
}
162+
preInitialization?(self)
160163
if initialize {
161164
_ = try await self.send(
162165
InitializeRequest(
@@ -193,12 +196,22 @@ public final class TestSourceKitLSPClient: MessageHandler {
193196
/// Send the request to `server` and return the request result.
194197
public func send<R: RequestType>(_ request: R) async throws -> R.Response {
195198
return try await withCheckedThrowingContinuation { continuation in
196-
server.handle(request, id: .number(Int(nextRequestID.fetchAndIncrement()))) { result in
199+
self.send(request) { result in
197200
continuation.resume(with: result)
198201
}
199202
}
200203
}
201204

205+
/// Send the request to `server` and return the result via a completion handler.
206+
///
207+
/// This version of the `send` function should only be used if some action needs to be performed after the request is
208+
/// sent but before it returns a result.
209+
public func send<R: RequestType>(_ request: R, completionHandler: @escaping (LSPResult<R.Response>) -> Void) {
210+
server.handle(request, id: .number(Int(nextRequestID.fetchAndIncrement()))) { result in
211+
completionHandler(result)
212+
}
213+
}
214+
202215
/// Send the notification to `server`.
203216
public func send(_ notification: some NotificationType) {
204217
server.handle(notification)

Sources/SKTestSupport/Utils.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,17 @@ extension Language {
3636
}
3737

3838
extension DocumentURI {
39-
/// Create a unique URI for a document of the given language.
40-
public static func `for`(_ language: Language, testName: String = #function) -> DocumentURI {
39+
/// Construct a `DocumentURI` by creating a unique URI for a document of the given language.
40+
public init(for language: Language, testName: String = #function) {
4141
let testBaseName = testName.prefix(while: \.isLetter)
4242

4343
#if os(Windows)
4444
let url = URL(fileURLWithPath: "C:/\(testBaseName)/\(UUID())/test.\(language.fileExtension)")
4545
#else
4646
let url = URL(fileURLWithPath: "/\(testBaseName)/\(UUID())/test.\(language.fileExtension)")
4747
#endif
48-
return DocumentURI(url)
48+
49+
self.init(url)
4950
}
5051
}
5152

Sources/SemanticIndex/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
add_library(SemanticIndex STATIC
33
CheckedIndex.swift
44
CompilerCommandLineOption.swift
5-
IndexStatusManager.swift
65
IndexTaskDescription.swift
76
PreparationTaskDescription.swift
87
SemanticIndexManager.swift
98
TestHooks.swift
109
UpdateIndexStoreTaskDescription.swift
10+
UpToDateTracker.swift
1111
)
1212
set_target_properties(SemanticIndex PROPERTIES
1313
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})

Sources/SemanticIndex/PreparationTaskDescription.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public struct PreparationTaskDescription: IndexTaskDescription {
3535
/// The build system manager that is used to get the toolchain and build settings for the files to index.
3636
private let buildSystemManager: BuildSystemManager
3737

38-
private let preparationUpToDateStatus: IndexUpToDateStatusManager<ConfiguredTarget>
38+
private let preparationUpToDateTracker: UpToDateTracker<ConfiguredTarget>
3939

4040
/// See `SemanticIndexManager.indexProcessDidProduceResult`
4141
private let indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
@@ -59,13 +59,13 @@ public struct PreparationTaskDescription: IndexTaskDescription {
5959
init(
6060
targetsToPrepare: [ConfiguredTarget],
6161
buildSystemManager: BuildSystemManager,
62-
preparationUpToDateStatus: IndexUpToDateStatusManager<ConfiguredTarget>,
62+
preparationUpToDateTracker: UpToDateTracker<ConfiguredTarget>,
6363
indexProcessDidProduceResult: @escaping @Sendable (IndexProcessResult) -> Void,
6464
testHooks: IndexTestHooks
6565
) {
6666
self.targetsToPrepare = targetsToPrepare
6767
self.buildSystemManager = buildSystemManager
68-
self.preparationUpToDateStatus = preparationUpToDateStatus
68+
self.preparationUpToDateTracker = preparationUpToDateTracker
6969
self.indexProcessDidProduceResult = indexProcessDidProduceResult
7070
self.testHooks = testHooks
7171
}
@@ -76,7 +76,7 @@ public struct PreparationTaskDescription: IndexTaskDescription {
7676
// The last 2 digits should be sufficient to differentiate between multiple concurrently running preparation operations
7777
await withLoggingSubsystemAndScope(subsystem: indexLoggingSubsystem, scope: "preparation-\(id % 100)") {
7878
let targetsToPrepare = await targetsToPrepare.asyncFilter {
79-
await !preparationUpToDateStatus.isUpToDate($0)
79+
await !preparationUpToDateTracker.isUpToDate($0)
8080
}.sorted(by: {
8181
($0.targetID, $0.runDestinationID) < ($1.targetID, $1.runDestinationID)
8282
})
@@ -114,7 +114,7 @@ public struct PreparationTaskDescription: IndexTaskDescription {
114114
}
115115
await testHooks.preparationTaskDidFinish?(self)
116116
if !Task.isCancelled {
117-
await preparationUpToDateStatus.markUpToDate(targetsToPrepare, updateOperationStartDate: startDate)
117+
await preparationUpToDateTracker.markUpToDate(targetsToPrepare, updateOperationStartDate: startDate)
118118
}
119119
}
120120
}

0 commit comments

Comments
 (0)