Skip to content

Commit cbc0648

Browse files
committed
Add ReportTask and refine the code
1 parent 9904dcb commit cbc0648

File tree

1 file changed

+55
-54
lines changed

1 file changed

+55
-54
lines changed

Sources/SourceKitLSP/Swift/DiagnosticReportManager.swift

Lines changed: 55 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ import SourceKitD
1616
import SwiftParserDiagnostics
1717

1818
actor DiagnosticReportManager {
19+
/// A task that gets report from sourcekitd
20+
private typealias ReportTask = Task<RelatedFullDocumentDiagnosticReport, Error>
21+
1922
private let sourcekitd: SourceKitD
2023
private let syntaxTreeManager: SyntaxTreeManager
2124
private let documentManager: DocumentManager
@@ -24,19 +27,19 @@ actor DiagnosticReportManager {
2427
private nonisolated var keys: sourcekitd_keys { return sourcekitd.keys }
2528
private nonisolated var requests: sourcekitd_requests { return sourcekitd.requests }
2629

27-
/// The cache that stores reports for snapshot id and buildSettings
30+
/// The cache that stores reportTasks for snapshot id and buildSettings
2831
///
2932
/// Conceptually, this is a dictionary. To prevent excessive memory usage we
3033
/// only keep `cacheSize` entries within the array. Older entries are at the
3134
/// end of the list, newer entries at the front.
32-
private var reportCache:
35+
private var reportTaskCache:
3336
[(
3437
snapshotID: DocumentSnapshot.ID,
3538
buildSettings: SwiftCompileCommand?,
36-
report: RelatedFullDocumentDiagnosticReport
39+
reportTask: ReportTask
3740
)] = []
3841

39-
/// The number of reports to keep
42+
/// The number of reportTasks to keep
4043
///
4144
/// - Note: This has been chosen without scientific measurements.
4245
private let cacheSize = 5
@@ -53,46 +56,39 @@ actor DiagnosticReportManager {
5356
self.clientHasDiagnosticsCodeDescriptionSupport = clientHasDiagnosticsCodeDescriptionSupport
5457
}
5558

56-
func diagnosticReport(for snapshot: DocumentSnapshot, buildSettings: SwiftCompileCommand?, useCache: Bool = false)
57-
async throws -> RelatedFullDocumentDiagnosticReport
58-
{
59-
if useCache, let report = report(for: snapshot.id, buildSettings: buildSettings) {
60-
return report
61-
}
62-
guard let buildSettings = buildSettings else {
63-
logger.log(
64-
"Producing syntactic diagnostics from the built-in swift-syntax because we don't have buildSettings"
65-
)
66-
// If we don't have build settings,
67-
// sourcekitd won't be able to give us accurate semantic diagnostics.
68-
// Fall back to providing syntactic diagnostics from the built-in
69-
// swift-syntax. That's the best we can do for now.
70-
let report = try await requestFallbackReport(with: snapshot)
71-
setReport(for: snapshot.id, buildSettings: nil, report: report)
59+
func diagnosticReport(
60+
for snapshot: DocumentSnapshot,
61+
buildSettings: SwiftCompileCommand?,
62+
useCache: Bool = false
63+
) async throws -> RelatedFullDocumentDiagnosticReport {
64+
if useCache, let report = try await reportTask(for: snapshot.id, buildSettings: buildSettings)?.value {
7265
return report
7366
}
74-
guard !buildSettings.isFallback else {
67+
let reportTask: Task<RelatedFullDocumentDiagnosticReport, Error>
68+
if let buildSettings, !buildSettings.isFallback {
69+
reportTask = Task {
70+
return try await requestReport(with: snapshot, compilerArgs: buildSettings.compilerArgs)
71+
}
72+
} else {
7573
logger.log(
76-
"Producing syntactic diagnostics from the built-in swift-syntax because we have fallback arguments"
74+
"Producing syntactic diagnostics from the built-in swift-syntax because we \(buildSettings != nil ? "have fallback build settings" : "don't have build settings")"
7775
)
78-
// If we only have fallback build settings,
76+
// If we don't have build settings or we only have fallback build settings,
7977
// sourcekitd won't be able to give us accurate semantic diagnostics.
8078
// Fall back to providing syntactic diagnostics from the built-in
8179
// swift-syntax. That's the best we can do for now.
82-
let report = try await requestFallbackReport(with: snapshot)
83-
setReport(for: snapshot.id, buildSettings: buildSettings, report: report)
84-
return report
80+
reportTask = Task {
81+
return try await requestFallbackReport(with: snapshot)
82+
}
8583
}
86-
let report = try await requestReport(with: snapshot, compilerArgs: buildSettings.compilerArgs)
87-
setReport(for: snapshot.id, buildSettings: buildSettings, report: report)
88-
return report
84+
setReportTask(for: snapshot.id, buildSettings: buildSettings, reportTask: reportTask)
85+
return try await reportTask.value
8986
}
90-
}
9187

92-
private extension DiagnosticReportManager {
93-
func requestReport(with snapshot: DocumentSnapshot, compilerArgs: [String]) async throws
94-
-> LanguageServerProtocol.RelatedFullDocumentDiagnosticReport
95-
{
88+
private func requestReport(
89+
with snapshot: DocumentSnapshot,
90+
compilerArgs: [String]
91+
) async throws -> LanguageServerProtocol.RelatedFullDocumentDiagnosticReport {
9692
try Task.checkCancellation()
9793

9894
let keys = self.keys
@@ -111,13 +107,16 @@ private extension DiagnosticReportManager {
111107
// Check that the document wasn't modified while we were getting diagnostics. This could happen because we are
112108
// calling `fullDocumentDiagnosticReport` from `publishDiagnosticsIfNeeded` outside of `messageHandlingQueue`
113109
// and thus a concurrent edit is possible while we are waiting for the sourcekitd request to return a result.
114-
throw ResponseError.unknown("Document was modified while loading document")
110+
throw ResponseError.unknown("Document was modified while loading diagnostics")
115111
}
116112

117-
let supportsCodeDescription = self.clientHasDiagnosticsCodeDescriptionSupport
118113
var diagnostics: [Diagnostic] = []
119114
dict[keys.diagnostics]?.forEach { _, diag in
120-
if let diag = Diagnostic(diag, in: snapshot, useEducationalNoteAsCode: supportsCodeDescription) {
115+
if let diag = Diagnostic(
116+
diag,
117+
in: snapshot,
118+
useEducationalNoteAsCode: self.clientHasDiagnosticsCodeDescriptionSupport
119+
) {
121120
diagnostics.append(diag)
122121
}
123122
return true
@@ -126,9 +125,9 @@ private extension DiagnosticReportManager {
126125
return RelatedFullDocumentDiagnosticReport(items: diagnostics)
127126
}
128127

129-
func requestFallbackReport(with snapshot: DocumentSnapshot) async throws
130-
-> LanguageServerProtocol.RelatedFullDocumentDiagnosticReport
131-
{
128+
private func requestFallbackReport(
129+
with snapshot: DocumentSnapshot
130+
) async throws -> LanguageServerProtocol.RelatedFullDocumentDiagnosticReport {
132131
// If we don't have build settings or we only have fallback build settings,
133132
// sourcekitd won't be able to give us accurate semantic diagnostics.
134133
// Fall back to providing syntactic diagnostics from the built-in
@@ -145,31 +144,33 @@ private extension DiagnosticReportManager {
145144
return RelatedFullDocumentDiagnosticReport(items: diagnostics)
146145
}
147146

148-
/// The report for the given document snapshot and buildSettings.
149-
func report(for snapshotID: DocumentSnapshot.ID, buildSettings: SwiftCompileCommand?)
150-
-> RelatedFullDocumentDiagnosticReport?
151-
{
152-
return reportCache.first(where: { $0.snapshotID == snapshotID && $0.buildSettings == buildSettings })?.report
147+
/// The reportTask for the given document snapshot and buildSettings.
148+
private func reportTask(
149+
for snapshotID: DocumentSnapshot.ID,
150+
buildSettings: SwiftCompileCommand?
151+
) -> ReportTask? {
152+
return reportTaskCache.first(where: { $0.snapshotID == snapshotID && $0.buildSettings == buildSettings })?
153+
.reportTask
153154
}
154155

155-
/// Set the report for the given document snapshot and buildSettings.
156+
/// Set the reportTask for the given document snapshot and buildSettings.
156157
///
157158
/// If we are already storing `cacheSize` many reports, the oldest one
158159
/// will get discarded.
159-
func setReport(
160+
private func setReportTask(
160161
for snapshotID: DocumentSnapshot.ID,
161162
buildSettings: SwiftCompileCommand?,
162-
report: RelatedFullDocumentDiagnosticReport
163+
reportTask: ReportTask
163164
) {
164-
reportCache.insert((snapshotID, buildSettings, report), at: 0)
165+
reportTaskCache.insert((snapshotID, buildSettings, reportTask), at: 0)
165166

166-
// Remove any reports for old versions of this document.
167-
reportCache.removeAll(where: { $0.snapshotID < snapshotID })
167+
// Remove any reportTasks for old versions of this document.
168+
reportTaskCache.removeAll(where: { $0.snapshotID < snapshotID })
168169

169-
// If we still have more than `cacheSize` reports, delete the ones that
170+
// If we still have more than `cacheSize` reportTasks, delete the ones that
170171
// were produced last. We can always re-request them on-demand.
171-
while reportCache.count > cacheSize {
172-
reportCache.removeLast()
172+
while reportTaskCache.count > cacheSize {
173+
reportTaskCache.removeLast()
173174
}
174175
}
175176
}

0 commit comments

Comments
 (0)