Skip to content

Commit 560d3e9

Browse files
authored
Merge pull request #1414 from ahoppen/no-cancel-if-removing-diag-from-cache
Don’t cancel in-progress diagnostic generation when calling `DiagnosticReportManager.removeItemsFromCache`
2 parents e4d8331 + d04bf84 commit 560d3e9

File tree

8 files changed

+73
-16
lines changed

8 files changed

+73
-16
lines changed

Sources/LanguageServerProtocol/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_library(LanguageServerProtocol STATIC
1919
Notifications/LogMessageNotification.swift
2020
Notifications/LogTraceNotification.swift
2121
Notifications/PublishDiagnosticsNotification.swift
22+
Notifications/ReopenTextDocumentNotifications.swift
2223
Notifications/SetTraceNotification.swift
2324
Notifications/ShowMessageNotification.swift
2425
Notifications/TextSynchronizationNotifications.swift
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
/// Re-open the given document, discarding any in-memory state.
14+
///
15+
/// This notification is designed to be used internally in SourceKit-LSP: When build setting have changed, we re-open
16+
/// the document in sourcekitd to re-build its AST. This needs to be handled via a notification to ensure that no other
17+
/// request for this document is executing at the same time.
18+
///
19+
/// **(LSP Extension)**
20+
public struct ReopenTextDocumentNotification: NotificationType, Hashable {
21+
public static let method: String = "textDocument/reopen"
22+
23+
/// The document identifier and initial contents.
24+
public var textDocument: TextDocumentIdentifier
25+
26+
public init(textDocument: TextDocumentIdentifier) {
27+
self.textDocument = textDocument
28+
}
29+
}

Sources/SourceKitLSP/Clang/ClangLanguageService.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,8 @@ extension ClangLanguageService {
451451
clangd.send(notification)
452452
}
453453

454+
func reopenDocument(_ notification: ReopenTextDocumentNotification) {}
455+
454456
public func changeDocument(_ notification: DidChangeTextDocumentNotification) {
455457
clangd.send(notification)
456458
}

Sources/SourceKitLSP/LanguageService.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@ public protocol LanguageService: AnyObject, Sendable {
118118

119119
/// Sent to close a document on the Language Server.
120120
func closeDocument(_ notification: DidCloseTextDocumentNotification) async
121+
122+
/// Re-open the given document, discarding any in-memory state and forcing an AST to be re-built after build settings
123+
/// have been changed. This needs to be handled via a notification to ensure that no other request for this document
124+
/// is executing at the same time.
125+
///
126+
/// Only intended for `SwiftLanguageService`.
127+
func reopenDocument(_ notification: ReopenTextDocumentNotification) async
128+
121129
func changeDocument(_ notification: DidChangeTextDocumentNotification) async
122130
func willSaveDocument(_ notification: WillSaveTextDocumentNotification) async
123131
func didSaveDocument(_ notification: DidSaveTextDocumentNotification) async

Sources/SourceKitLSP/MessageHandlingDependencyTracker.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ enum MessageHandlingDependencyTracker: DependencyTracker {
126126
self = .freestanding
127127
case is PublishDiagnosticsNotification:
128128
self = .freestanding
129+
case let notification as ReopenTextDocumentNotification:
130+
self = .documentUpdate(notification.textDocument.uri)
129131
case is SetTraceNotification:
130132
self = .globalConfigurationChange
131133
case is ShowMessageNotification:

Sources/SourceKitLSP/SourceKitLSPServer.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,8 @@ extension SourceKitLSPServer: MessageHandler {
569569
await self.openDocument(notification)
570570
case let notification as DidCloseTextDocumentNotification:
571571
await self.closeDocument(notification)
572+
case let notification as ReopenTextDocumentNotification:
573+
await self.reopenDocument(notification)
572574
case let notification as DidChangeTextDocumentNotification:
573575
await self.changeDocument(notification)
574576
case let notification as DidChangeWorkspaceFoldersNotification:
@@ -1303,6 +1305,17 @@ extension SourceKitLSPServer {
13031305
await self.closeDocument(notification, workspace: workspace)
13041306
}
13051307

1308+
func reopenDocument(_ notification: ReopenTextDocumentNotification) async {
1309+
let uri = notification.textDocument.uri
1310+
guard let workspace = await workspaceForDocument(uri: uri) else {
1311+
logger.error(
1312+
"received reopen notification for file '\(uri.forLogging)' without a corresponding workspace, ignoring..."
1313+
)
1314+
return
1315+
}
1316+
await workspace.documentService.value[uri]?.reopenDocument(notification)
1317+
}
1318+
13061319
func closeDocument(_ notification: DidCloseTextDocumentNotification, workspace: Workspace) async {
13071320
// Immediately close the document. We need to be sure to clear our pending work queue in case
13081321
// the build system still isn't ready.

Sources/SourceKitLSP/Swift/DiagnosticReportManager.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,7 @@ actor DiagnosticReportManager {
8686
}
8787

8888
func removeItemsFromCache(with uri: DocumentURI) async {
89-
let tasksToCancel = reportTaskCache.filter { $0.snapshotID.uri == uri }
9089
reportTaskCache.removeAll(where: { $0.snapshotID.uri == uri })
91-
for task in tasksToCancel {
92-
await task.reportTask.cancel()
93-
}
9490
}
9591

9692
private func requestReport(

Sources/SourceKitLSP/Swift/SwiftLanguageService.swift

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -327,15 +327,24 @@ extension SwiftLanguageService {
327327

328328
// MARK: - Build System Integration
329329

330-
private func reopenDocument(_ snapshot: DocumentSnapshot, _ compileCmd: SwiftCompileCommand?) async {
330+
public func reopenDocument(_ notification: ReopenTextDocumentNotification) async {
331+
let snapshot = orLog("Getting snapshot to re-open document") {
332+
try documentManager.latestSnapshot(notification.textDocument.uri)
333+
}
334+
guard let snapshot else {
335+
return
336+
}
331337
cancelInFlightPublishDiagnosticsTask(for: snapshot.uri)
332338
await diagnosticReportManager.removeItemsFromCache(with: snapshot.uri)
333339

334340
let closeReq = closeDocumentSourcekitdRequest(uri: snapshot.uri)
335-
_ = try? await self.sourcekitd.send(closeReq, fileContents: nil)
341+
_ = await orLog("Closing document to re-open it") { try await self.sourcekitd.send(closeReq, fileContents: nil) }
336342

337-
let openReq = openDocumentSourcekitdRequest(snapshot: snapshot, compileCommand: compileCmd)
338-
_ = try? await self.sourcekitd.send(openReq, fileContents: snapshot.text)
343+
let openReq = openDocumentSourcekitdRequest(
344+
snapshot: snapshot,
345+
compileCommand: await buildSettings(for: snapshot.uri)
346+
)
347+
_ = await orLog("Re-opening document") { try await self.sourcekitd.send(openReq, fileContents: snapshot.text) }
339348

340349
if await capabilityRegistry.clientSupportsPullDiagnostics(for: .swift) {
341350
await self.refreshDiagnosticsDebouncer.scheduleCall()
@@ -345,14 +354,11 @@ extension SwiftLanguageService {
345354
}
346355

347356
public func documentUpdatedBuildSettings(_ uri: DocumentURI) async {
348-
// We may not have a snapshot if this is called just before `openDocument`.
349-
guard let snapshot = try? self.documentManager.latestSnapshot(uri) else {
350-
return
351-
}
352-
353-
// Close and re-open the document internally to inform sourcekitd to update the compile
354-
// command. At the moment there's no better way to do this.
355-
await self.reopenDocument(snapshot, await self.buildSettings(for: uri))
357+
// Close and re-open the document internally to inform sourcekitd to update the compile command. At the moment
358+
// there's no better way to do this.
359+
// Schedule the document re-open in the SourceKit-LSP server. This ensures that the re-open happens exclusively with
360+
// no other request running at the same time.
361+
sourceKitLSPServer?.handle(ReopenTextDocumentNotification(textDocument: TextDocumentIdentifier(uri)))
356362
}
357363

358364
public func documentDependenciesUpdated(_ uri: DocumentURI) async {

0 commit comments

Comments
 (0)