Skip to content

Add a request to check if SourceKit-LSP is currently performing any indexing #2016

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 28, 2025
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
21 changes: 21 additions & 0 deletions Contributor Documentation/LSP Extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,27 @@ export interface DocumentTestsParams {
}
```

## `sourceKit/_isIndexing`

Request from the client to the server querying whether SourceKit-LSP is currently performing an background indexing tasks, including target preparation.

> [!IMPORTANT]
> This request is experimental and may be modified or removed in future versions of SourceKit-LSP without notice. Do not rely on it.

- params: `IsIndexingParams`
- result: `IsIndexingResult`

```ts
export interface IsIndexingParams {}

export interface IsIndexingResult {
/**
* Whether SourceKit-LSP is currently performing an indexing task.
*/
indexing: boolean;
}
```

## `window/didChangeActiveDocument`

New notification from the client to the server, telling SourceKit-LSP which document is the currently active primary document.
Expand Down
2 changes: 1 addition & 1 deletion Documentation/Configuration File.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ The structure of the file is currently not guaranteed to be stable. Options may
- `noLazy`: Prepare a target without generating object files but do not do lazy type checking and function body skipping. This uses SwiftPM's `--experimental-prepare-for-indexing-no-lazy` flag.
- `enabled`: Prepare a target without generating object files.
- `cancelTextDocumentRequestsOnEditAndClose: boolean`: Whether sending a `textDocument/didChange` or `textDocument/didClose` notification for a document should cancel all pending requests for that document.
- `experimentalFeatures: ("on-type-formatting"|"set-options-request"|"sourcekit-options-request")[]`: Experimental features that are enabled.
- `experimentalFeatures: ("on-type-formatting"|"set-options-request"|"sourcekit-options-request"|"is-indexing-request")[]`: Experimental features that are enabled.
- `swiftPublishDiagnosticsDebounceDuration: number`: The time that `SwiftLanguageService` should wait after an edit before starting to compute diagnostics and sending a `PublishDiagnosticsNotification`.
- `workDoneProgressDebounceDuration: number`: When a task is started that should be displayed to the client as a work done progress, how many milliseconds to wait before actually starting the work done progress. This prevents flickering of the work done progress in the client for short-lived index tasks which end within this duration.
- `sourcekitdRequestTimeout: number`: The maximum duration that a sourcekitd request should be allowed to execute before being declared as timed out. In general, editors should cancel requests that they are no longer interested in, but in case editors don't cancel requests, this ensures that a long-running non-cancelled request is not blocking sourcekitd and thus most semantic functionality. In particular, VS Code does not cancel the semantic tokens request, which can cause a long-running AST build that blocks sourcekitd.
1 change: 1 addition & 0 deletions Sources/LanguageServerProtocol/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ add_library(LanguageServerProtocol STATIC
Requests/InlayHintResolveRequest.swift
Requests/InlineValueRefreshRequest.swift
Requests/InlineValueRequest.swift
Requests/IsIndexingRequest.swift
Requests/LinkedEditingRangeRequest.swift
Requests/MonikersRequest.swift
Requests/PeekDocumentsRequest.swift
Expand Down
1 change: 1 addition & 0 deletions Sources/LanguageServerProtocol/Messages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public let builtinRequests: [_RequestType.Type] = [
InlayHintResolveRequest.self,
InlineValueRefreshRequest.self,
InlineValueRequest.self,
IsIndexingRequest.self,
LinkedEditingRangeRequest.self,
MonikersRequest.self,
PeekDocumentsRequest.self,
Expand Down
27 changes: 27 additions & 0 deletions Sources/LanguageServerProtocol/Requests/IsIndexingRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

public struct IsIndexingRequest: RequestType {
public static let method: String = "sourceKit/_isIndexing"
public typealias Response = IsIndexingResponse

public init() {}
}

public struct IsIndexingResponse: ResponseType {
/// Whether SourceKit-LSP is currently performing an indexing task.
public var indexing: Bool

public init(indexing: Bool) {
self.indexing = indexing
}
}
3 changes: 3 additions & 0 deletions Sources/SKOptions/ExperimentalFeatures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@ public enum ExperimentalFeature: String, Codable, Sendable, CaseIterable {

/// Enable the `workspace/_sourceKitOptions` request.
case sourceKitOptionsRequest = "sourcekit-options-request"

/// Enable the `sourceKit/_isIndexing` request.
case isIndexingRequest = "is-indexing-request"
}
2 changes: 1 addition & 1 deletion Sources/SemanticIndex/SemanticIndexManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ package enum IndexTaskStatus: Comparable {
/// In reality, these status are not exclusive. Eg. the index might be preparing one target for editor functionality,
/// re-generating the build graph and indexing files at the same time. To avoid showing too many concurrent status
/// messages to the user, we only show the highest priority task.
package enum IndexProgressStatus: Sendable {
package enum IndexProgressStatus: Sendable, Equatable {
case preparingFileForEditorFunctionality
case schedulingIndexing
case indexing(preparationTasks: [BuildTargetIdentifier: IndexTaskStatus], indexTasks: [DocumentURI: IndexTaskStatus])
Expand Down
2 changes: 2 additions & 0 deletions Sources/SourceKitLSP/MessageHandlingDependencyTracker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ package enum MessageHandlingDependencyTracker: QueueBasedMessageHandlerDependenc
self = .freestanding
case is InlineValueRefreshRequest:
self = .freestanding
case is IsIndexingRequest:
self = .freestanding
case is PollIndexRequest:
self = .globalConfigurationChange
case is RenameRequest:
Expand Down
13 changes: 13 additions & 0 deletions Sources/SourceKitLSP/SourceKitLSPServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,8 @@ extension SourceKitLSPServer: QueueBasedMessageHandler {
initialized = true
case let request as RequestAndReply<InlayHintRequest>:
await self.handleRequest(for: request, requestHandler: self.inlayHint)
case let request as RequestAndReply<IsIndexingRequest>:
await request.reply { try await self.isIndexing(request.params) }
case let request as RequestAndReply<PollIndexRequest>:
await request.reply { try await pollIndex(request.params) }
case let request as RequestAndReply<PrepareRenameRequest>:
Expand Down Expand Up @@ -1270,6 +1272,17 @@ extension SourceKitLSPServer {
)
}

func isIndexing(_ request: IsIndexingRequest) async throws -> IsIndexingResponse {
guard self.options.hasExperimentalFeature(.isIndexingRequest) else {
throw ResponseError.unknown("\(IsIndexingRequest.method) indexing is an experimental request")
}
let isIndexing =
await workspaces
.compactMap(\.semanticIndexManager)
.asyncContains { await $0.progressStatus != .upToDate }
return IsIndexingResponse(indexing: isIndexing)
}

// MARK: - Text synchronization

func openDocument(_ notification: DidOpenTextDocumentNotification) async {
Expand Down
27 changes: 27 additions & 0 deletions Tests/SourceKitLSPTests/BackgroundIndexingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1883,6 +1883,33 @@ final class BackgroundIndexingTests: XCTestCase {
) != nil
}
}

func testIsIndexingRequest() async throws {
let checkedIsIndexStatus = AtomicBool(initialValue: false)
let hooks = Hooks(
indexHooks: IndexHooks(updateIndexStoreTaskDidStart: { task in
while !checkedIsIndexStatus.value {
try? await Task.sleep(for: .seconds(0.1))
}
})
)
let project = try await SwiftPMTestProject(
files: [
"Test.swift": ""
],
options: .testDefault(experimentalFeatures: [.isIndexingRequest]),
hooks: hooks,
enableBackgroundIndexing: true,
pollIndex: false
)
let isIndexingResponseWhileIndexing = try await project.testClient.send(IsIndexingRequest())
XCTAssert(isIndexingResponseWhileIndexing.indexing)
checkedIsIndexStatus.value = true

try await repeatUntilExpectedResult {
try await project.testClient.send(IsIndexingRequest()).indexing == false
}
}
}

extension HoverResponseContents {
Expand Down
6 changes: 4 additions & 2 deletions config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@
"enum" : [
"on-type-formatting",
"set-options-request",
"sourcekit-options-request"
"sourcekit-options-request",
"is-indexing-request"
],
"markdownEnumDescriptions" : [
"Enable support for the `textDocument\/onTypeFormatting` request.",
"Enable support for the `workspace\/_setOptions` request.",
"Enable the `workspace\/_sourceKitOptions` request."
"Enable the `workspace\/_sourceKitOptions` request.",
"Enable the `sourceKit\/_isIndexing` request."
],
"type" : "string"
},
Expand Down