Skip to content
Draft
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
2 changes: 1 addition & 1 deletion Sources/BuildServerIntegration/BuildServerManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ package actor BuildServerManager: QueueBasedMessageHandler {
package func handle<Request: RequestType>(
request: Request,
id: RequestID,
reply: @Sendable @escaping (LSPResult<Request.Response>) -> Void
reply: @Sendable @escaping (Result<Request.Response, any Error>) -> Void
) async {
let request = RequestAndReply(request, reply: reply)
switch request {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ actor BuiltInBuildServerAdapter: QueueBasedMessageHandler {
func handle<Request: RequestType>(
request: Request,
id: RequestID,
reply: @Sendable @escaping (LSPResult<Request.Response>) -> Void
reply: @Sendable @escaping (Result<Request.Response, any Error>) -> Void
) async {
let request = RequestAndReply(request, reply: reply)
await buildServerHooks.preHandleRequest?(request.params)
Expand Down
2 changes: 1 addition & 1 deletion Sources/SKOptions/SourceKitLSPOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ public struct SourceKitLSPOptions: Sendable, Codable, Equatable {
public var cancelTextDocumentRequestsOnEditAndClose: Bool? = nil

public var cancelTextDocumentRequestsOnEditAndCloseOrDefault: Bool {
return cancelTextDocumentRequestsOnEditAndClose ?? true
return cancelTextDocumentRequestsOnEditAndClose ?? false
}

/// Experimental features that are enabled.
Expand Down
25 changes: 20 additions & 5 deletions Sources/SourceKitLSP/SourceKitLSPServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -656,10 +656,25 @@ extension SourceKitLSPServer: QueueBasedMessageHandler {
/// - Important: Should be invoked on `textDocumentTrackingQueue` to ensure that new text document requests are
/// registered before a notification that triggers cancellation might come in.
private func cancelTextDocumentRequests(for uri: DocumentURI, reason: ImplicitTextDocumentRequestCancellationReason) {
guard self.options.cancelTextDocumentRequestsOnEditAndCloseOrDefault else {
return
}
let staleRequestSupport = self.capabilityRegistry?.clientCapabilities.general?.staleRequestSupport
for (requestID, requestMethod) in self.inProgressTextDocumentRequests[uri, default: []] {
// Implicitly cancel text document requests if:
// - We have enabled implicit text document request cancellation in the SourceKit options
// - The client has indicated that it does not cancel stale requests. Defaults to `true` because this is an
// option introduced in LSP 3.17 and most clients cancel requests diligently without setting
// `staleRequestSupport.cancel = true`
// - `staleRequestSupport.retryOnContentModified` contains this request method. Documentation for this behavior
// is very limited but it appears that if a request is included in that array, the client (VS Code in
// particular) expects to receive a `ContentModified` response when the LSP server detects an edit, in which
// case it will re-run the request with the new file contents.
guard
self.options.cancelTextDocumentRequestsOnEditAndCloseOrDefault
|| !(staleRequestSupport?.cancel ?? true)
|| staleRequestSupport?.retryOnContentModified.contains(requestMethod) ?? false
else {
continue
}

if reason == .documentChanged && requestMethod == CompletionRequest.method {
// As the user types, we filter the code completion results. Cancelling the completion request on every
// keystroke means that we will never build the initial list of completion results for this code
Expand All @@ -668,7 +683,7 @@ extension SourceKitLSPServer: QueueBasedMessageHandler {
continue
}
logger.info("Implicitly cancelling request \(requestID)")
self.messageHandlingHelper.cancelRequest(id: requestID)
self.messageHandlingHelper.cancelRequest(id: requestID, error: .contentModified)
}
}

Expand Down Expand Up @@ -722,7 +737,7 @@ extension SourceKitLSPServer: QueueBasedMessageHandler {
package func handle<Request: RequestType>(
request params: Request,
id: RequestID,
reply: @Sendable @escaping (LSPResult<Request.Response>) -> Void
reply: @Sendable @escaping (Result<Request.Response, any Error>) -> Void
) async {
defer {
if let request = params as? any TextDocumentRequest {
Expand Down
12 changes: 10 additions & 2 deletions Tests/SourceKitLSPTests/SemanticTokensTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -986,14 +986,22 @@ final class SemanticTokensTests: SourceKitLSPTestCase {
try? await Task.sleep(for: .seconds(1))
}
}
})
}),
capabilities: ClientCapabilities(
general: .init(
staleRequestSupport: .init(
cancel: true,
retryOnContentModified: [DocumentSemanticTokensRequest.method]
)
)
)
)
let uri = DocumentURI(for: .swift)
let positions = testClient.openDocument("1️⃣", uri: uri)

let receivedSemanticTokensResponse = self.expectation(description: "Received semantic tokens response")
testClient.send(DocumentSemanticTokensRequest(textDocument: TextDocumentIdentifier(uri))) { result in
XCTAssertEqual(result, .failure(ResponseError.cancelled))
XCTAssertEqual(result, .failure(ResponseError(code: .contentModified, message: "content modified")))
receivedSemanticTokensResponse.fulfill()
}
testClient.send(
Expand Down