@@ -139,6 +139,8 @@ public actor SwiftLanguageServer: ToolchainLanguageServer {
139
139
140
140
private var stateChangeHandlers : [ ( _ oldState: LanguageServerState , _ newState: LanguageServerState ) -> Void ] = [ ]
141
141
142
+ private let diagnosticReportManager : DiagnosticReportManager
143
+
142
144
/// Creates a language server for the given client using the sourcekitd dylib specified in `toolchain`.
143
145
/// `reopenDocuments` is a closure that will be called if sourcekitd crashes and the `SwiftLanguageServer` asks its parent server to reopen all of its documents.
144
146
/// Returns `nil` if `sourcektid` couldn't be found.
@@ -157,6 +159,7 @@ public actor SwiftLanguageServer: ToolchainLanguageServer {
157
159
self . state = . connected
158
160
self . generatedInterfacesPath = options. generatedInterfacesPath. asURL
159
161
try FileManager . default. createDirectory ( at: generatedInterfacesPath, withIntermediateDirectories: true )
162
+ self . diagnosticReportManager = DiagnosticReportManager ( )
160
163
}
161
164
162
165
/// - Important: For testing only
@@ -196,7 +199,8 @@ public actor SwiftLanguageServer: ToolchainLanguageServer {
196
199
197
200
extension SwiftLanguageServer {
198
201
199
- public func initialize( _ initialize: InitializeRequest ) throws -> InitializeResult {
202
+ public func initialize( _ initialize: InitializeRequest ) async throws -> InitializeResult {
203
+ await diagnosticReportManager. setDelegate ( self )
200
204
sourcekitd. addNotificationHandler ( self )
201
205
202
206
return InitializeResult (
@@ -396,8 +400,11 @@ extension SwiftLanguageServer {
396
400
return
397
401
}
398
402
do {
399
- let diagnosticReport = try await self . fullDocumentDiagnosticReport (
400
- DocumentDiagnosticsRequest ( textDocument: TextDocumentIdentifier ( document) )
403
+ let snapshot = try await documentManager. latestSnapshot ( document)
404
+ let buildSettings = await self . buildSettings ( for: document)
405
+ let diagnosticReport = try await self . diagnosticReportManager. diagnosticReport (
406
+ for: snapshot,
407
+ buildSettings: buildSettings
401
408
)
402
409
403
410
await sourceKitServer. sendNotificationToClient (
@@ -967,10 +974,12 @@ extension SwiftLanguageServer {
967
974
}
968
975
969
976
func retrieveQuickFixCodeActions( _ params: CodeActionRequest ) async throws -> [ CodeAction ] {
970
- let diagnosticReport = try await self . fullDocumentDiagnosticReport (
971
- DocumentDiagnosticsRequest (
972
- textDocument: params. textDocument
973
- )
977
+ let snapshot = try documentManager. latestSnapshot ( params. textDocument. uri)
978
+ let buildSettings = await self . buildSettings ( for: params. textDocument. uri)
979
+ let diagnosticReport = try await self . diagnosticReportManager. diagnosticReport (
980
+ for: snapshot,
981
+ buildSettings: buildSettings,
982
+ useCache: true
974
983
)
975
984
976
985
let codeActions = diagnosticReport. items. flatMap { ( diag) -> [ CodeAction ] in
@@ -1061,55 +1070,13 @@ extension SwiftLanguageServer {
1061
1070
}
1062
1071
1063
1072
public func documentDiagnostic( _ req: DocumentDiagnosticsRequest ) async throws -> DocumentDiagnosticReport {
1064
- return try await . full( fullDocumentDiagnosticReport ( req) )
1065
- }
1066
-
1067
- private func fullDocumentDiagnosticReport(
1068
- _ req: DocumentDiagnosticsRequest
1069
- ) async throws -> RelatedFullDocumentDiagnosticReport {
1070
1073
let snapshot = try documentManager. latestSnapshot ( req. textDocument. uri)
1071
- guard let buildSettings = await self . buildSettings ( for: req. textDocument. uri) , !buildSettings. isFallback else {
1072
- logger. log (
1073
- " Producing syntactic diagnostics from the built-in swift-syntax because we have fallback arguments "
1074
- )
1075
- // If we don't have build settings or we only have fallback build settings,
1076
- // sourcekitd won't be able to give us accurate semantic diagnostics.
1077
- // Fall back to providing syntactic diagnostics from the built-in
1078
- // swift-syntax. That's the best we can do for now.
1079
- return try await syntacticDiagnosticFromBuiltInSwiftSyntax ( for: snapshot)
1080
- }
1081
-
1082
- try Task . checkCancellation ( )
1083
-
1084
- let keys = self . keys
1085
-
1086
- let skreq = SKDRequestDictionary ( sourcekitd: self . sourcekitd)
1087
- skreq [ keys. request] = requests. diagnostics
1088
- skreq [ keys. sourcefile] = snapshot. uri. pseudoPath
1089
-
1090
- // FIXME: SourceKit should probably cache this for us.
1091
- skreq [ keys. compilerargs] = buildSettings. compilerArgs
1092
-
1093
- let dict = try await self . sourcekitd. send ( skreq, fileContents: snapshot. text)
1094
-
1095
- try Task . checkCancellation ( )
1096
- guard ( try ? documentManager. latestSnapshot ( req. textDocument. uri) . id) == snapshot. id else {
1097
- // Check that the document wasn't modified while we were getting diagnostics. This could happen because we are
1098
- // calling `fullDocumentDiagnosticReport` from `publishDiagnosticsIfNeeded` outside of `messageHandlingQueue`
1099
- // and thus a concurrent edit is possible while we are waiting for the sourcekitd request to return a result.
1100
- throw ResponseError . unknown ( " Document was modified while loading document " )
1101
- }
1102
-
1103
- let supportsCodeDescription = capabilityRegistry. clientHasDiagnosticsCodeDescriptionSupport
1104
- var diagnostics : [ Diagnostic ] = [ ]
1105
- dict [ keys. diagnostics] ? . forEach { _, diag in
1106
- if let diag = Diagnostic ( diag, in: snapshot, useEducationalNoteAsCode: supportsCodeDescription) {
1107
- diagnostics. append ( diag)
1108
- }
1109
- return true
1110
- }
1111
-
1112
- return RelatedFullDocumentDiagnosticReport ( items: diagnostics)
1074
+ let buildSettings = await self . buildSettings ( for: req. textDocument. uri)
1075
+ let diagnosticReport = try await self . diagnosticReportManager. diagnosticReport (
1076
+ for: snapshot,
1077
+ buildSettings: buildSettings
1078
+ )
1079
+ return . full( diagnosticReport)
1113
1080
}
1114
1081
1115
1082
public func executeCommand( _ req: ExecuteCommandRequest ) async throws -> LSPAny ? {
@@ -1331,3 +1298,51 @@ extension sourcekitd_uid_t {
1331
1298
}
1332
1299
}
1333
1300
}
1301
+
1302
+ extension SwiftLanguageServer : DiagnosticReportManagerDelegate {
1303
+ func reportWithSnapshotReceived( _ snapshot: DocumentSnapshot , compilerArgs: [ String ] ) async throws
1304
+ -> LanguageServerProtocol . RelatedFullDocumentDiagnosticReport
1305
+ {
1306
+ try Task . checkCancellation ( )
1307
+
1308
+ let keys = self . keys
1309
+
1310
+ let skreq = SKDRequestDictionary ( sourcekitd: self . sourcekitd)
1311
+ skreq [ keys. request] = requests. diagnostics
1312
+ skreq [ keys. sourcefile] = snapshot. uri. pseudoPath
1313
+
1314
+ // FIXME: SourceKit should probably cache this for us.
1315
+ skreq [ keys. compilerargs] = compilerArgs
1316
+
1317
+ let dict = try await self . sourcekitd. send ( skreq, fileContents: snapshot. text)
1318
+
1319
+ try Task . checkCancellation ( )
1320
+ guard ( try ? documentManager. latestSnapshot ( snapshot. uri) . id) == snapshot. id else {
1321
+ // Check that the document wasn't modified while we were getting diagnostics. This could happen because we are
1322
+ // calling `fullDocumentDiagnosticReport` from `publishDiagnosticsIfNeeded` outside of `messageHandlingQueue`
1323
+ // and thus a concurrent edit is possible while we are waiting for the sourcekitd request to return a result.
1324
+ throw ResponseError . unknown ( " Document was modified while loading document " )
1325
+ }
1326
+
1327
+ let supportsCodeDescription = capabilityRegistry. clientHasDiagnosticsCodeDescriptionSupport
1328
+ var diagnostics : [ Diagnostic ] = [ ]
1329
+ dict [ keys. diagnostics] ? . forEach { _, diag in
1330
+ if let diag = Diagnostic ( diag, in: snapshot, useEducationalNoteAsCode: supportsCodeDescription) {
1331
+ diagnostics. append ( diag)
1332
+ }
1333
+ return true
1334
+ }
1335
+
1336
+ return RelatedFullDocumentDiagnosticReport ( items: diagnostics)
1337
+ }
1338
+
1339
+ func fallbackReportWithSnapshotReceived( _ snapshot: DocumentSnapshot ) async throws
1340
+ -> LanguageServerProtocol . RelatedFullDocumentDiagnosticReport
1341
+ {
1342
+ // If we don't have build settings or we only have fallback build settings,
1343
+ // sourcekitd won't be able to give us accurate semantic diagnostics.
1344
+ // Fall back to providing syntactic diagnostics from the built-in
1345
+ // swift-syntax. That's the best we can do for now.
1346
+ return try await syntacticDiagnosticFromBuiltInSwiftSyntax ( for: snapshot)
1347
+ }
1348
+ }
0 commit comments