Skip to content

Commit 2a0f8c7

Browse files
committed
A couple of improvements for generated interfaces
- Rename methods to highlight that we’re talking about generated interfaces here, not `.swiftinterface` files - Don’t open the generated interface in `documentManager`. Opening documents in `documentManager` should only be done by the `textDocument/didOpen` notification from the LSP client. Otherwise we might indefinitely keep the document in the document manager - After getting the generated interface from sourcekitd, close the document in sourcekitd again. We don’t provide semantic functionality in the generated interface yet, so we can’t interact with the generated interface path. Before, we left it open in sourcekitd indefinitely. - A couple of code simplifications. Fixes swiftlang#878 rdar://116705653
1 parent e1f80e4 commit 2a0f8c7

File tree

8 files changed

+59
-58
lines changed

8 files changed

+59
-58
lines changed

Sources/LanguageServerProtocol/Messages.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public let builtinRequests: [_RequestType.Type] = [
5858
InlineValueRequest.self,
5959
LinkedEditingRangeRequest.self,
6060
MonikersRequest.self,
61-
OpenInterfaceRequest.self,
61+
OpenGeneratedInterfaceRequest.self,
6262
PollIndexRequest.self,
6363
PrepareRenameRequest.self,
6464
ReferencesRequest.self,

Sources/LanguageServerProtocol/Requests/OpenInterfaceRequest.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
/// Request a textual interface of a module to display in the IDE.
13+
/// Request a generated interface of a module to display in the IDE.
1414
/// **(LSP Extension)**
15-
public struct OpenInterfaceRequest: TextDocumentRequest, Hashable {
15+
public struct OpenGeneratedInterfaceRequest: TextDocumentRequest, Hashable {
1616
public static let method: String = "textDocument/openInterface"
17-
public typealias Response = InterfaceDetails?
17+
public typealias Response = GeneratedInterfaceDetails?
1818

1919
/// The document whose compiler arguments should be used to generate the interface.
2020
public var textDocument: TextDocumentIdentifier
@@ -45,7 +45,7 @@ public struct OpenInterfaceRequest: TextDocumentRequest, Hashable {
4545
}
4646

4747
/// The textual output of a module interface.
48-
public struct InterfaceDetails: ResponseType, Hashable {
48+
public struct GeneratedInterfaceDetails: ResponseType, Hashable {
4949

5050
public var uri: DocumentURI
5151
public var position: Position?

Sources/SourceKitLSP/Clang/ClangLanguageService.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ extension ClangLanguageService {
578578
return try await forwardRequestToClangd(req)
579579
}
580580

581-
func openInterface(_ request: OpenInterfaceRequest) async throws -> InterfaceDetails? {
581+
func openGeneratedInterface(_ request: OpenGeneratedInterfaceRequest) async throws -> GeneratedInterfaceDetails? {
582582
throw ResponseError.unknown("unsupported method")
583583
}
584584

Sources/SourceKitLSP/LanguageService.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ public protocol LanguageService: AnyObject, Sendable {
146146
func completion(_ req: CompletionRequest) async throws -> CompletionList
147147
func hover(_ req: HoverRequest) async throws -> HoverResponse?
148148
func symbolInfo(_ request: SymbolInfoRequest) async throws -> [SymbolDetails]
149-
func openInterface(_ request: OpenInterfaceRequest) async throws -> InterfaceDetails?
149+
func openGeneratedInterface(_ request: OpenGeneratedInterfaceRequest) async throws -> GeneratedInterfaceDetails?
150150

151151
/// - Note: Only called as a fallback if the definition could not be found in the index.
152152
func definition(_ request: DefinitionRequest) async throws -> LocationsOrLocationLinksResponse?

Sources/SourceKitLSP/SourceKitLSPServer.swift

+8-8
Original file line numberDiff line numberDiff line change
@@ -711,8 +711,8 @@ extension SourceKitLSPServer: MessageHandler {
711711
await self.handleRequest(for: request, requestHandler: self.completion)
712712
case let request as RequestAndReply<HoverRequest>:
713713
await self.handleRequest(for: request, requestHandler: self.hover)
714-
case let request as RequestAndReply<OpenInterfaceRequest>:
715-
await self.handleRequest(for: request, requestHandler: self.openInterface)
714+
case let request as RequestAndReply<OpenGeneratedInterfaceRequest>:
715+
await self.handleRequest(for: request, requestHandler: self.openGeneratedInterface)
716716
case let request as RequestAndReply<DeclarationRequest>:
717717
await self.handleRequest(for: request, requestHandler: self.declaration)
718718
case let request as RequestAndReply<DefinitionRequest>:
@@ -1466,12 +1466,12 @@ extension SourceKitLSPServer {
14661466
return try await languageService.hover(req)
14671467
}
14681468

1469-
func openInterface(
1470-
_ req: OpenInterfaceRequest,
1469+
func openGeneratedInterface(
1470+
_ req: OpenGeneratedInterfaceRequest,
14711471
workspace: Workspace,
14721472
languageService: LanguageService
1473-
) async throws -> InterfaceDetails? {
1474-
return try await languageService.openInterface(req)
1473+
) async throws -> GeneratedInterfaceDetails? {
1474+
return try await languageService.openGeneratedInterface(req)
14751475
}
14761476

14771477
/// Find all symbols in the workspace that include a string in their name.
@@ -1808,13 +1808,13 @@ extension SourceKitLSPServer {
18081808
originatorUri: DocumentURI,
18091809
languageService: LanguageService
18101810
) async throws -> Location {
1811-
let openInterface = OpenInterfaceRequest(
1811+
let openInterface = OpenGeneratedInterfaceRequest(
18121812
textDocument: TextDocumentIdentifier(originatorUri),
18131813
name: moduleName,
18141814
groupName: groupName,
18151815
symbolUSR: symbolUSR
18161816
)
1817-
guard let interfaceDetails = try await languageService.openInterface(openInterface) else {
1817+
guard let interfaceDetails = try await languageService.openGeneratedInterface(openInterface) else {
18181818
throw ResponseError.unknown("Could not generate Swift Interface for \(moduleName)")
18191819
}
18201820
let position = interfaceDetails.position ?? Position(line: 0, utf16index: 0)

Sources/SourceKitLSP/Swift/OpenInterface.swift

+42-41
Original file line numberDiff line numberDiff line change
@@ -16,82 +16,83 @@ import LanguageServerProtocol
1616
import SKSupport
1717
import SourceKitD
1818

19-
struct InterfaceInfo {
19+
struct GeneratedInterfaceInfo {
2020
var contents: String
2121
}
2222

2323
extension SwiftLanguageService {
24-
public func openInterface(_ request: OpenInterfaceRequest) async throws -> InterfaceDetails? {
25-
let uri = request.textDocument.uri
26-
let moduleName = request.moduleName
24+
public func openGeneratedInterface(
25+
_ request: OpenGeneratedInterfaceRequest
26+
) async throws -> GeneratedInterfaceDetails? {
2727
let name = request.name
2828
let symbol = request.symbolUSR
2929
let interfaceFilePath = self.generatedInterfacesPath.appendingPathComponent("\(name).swiftinterface")
3030
let interfaceDocURI = DocumentURI(interfaceFilePath)
3131
// has interface already been generated
3232
if let snapshot = try? self.documentManager.latestSnapshot(interfaceDocURI) {
33-
return await self.interfaceDetails(request: request, uri: interfaceDocURI, snapshot: snapshot, symbol: symbol)
33+
return await self.generatedInterfaceDetails(
34+
request: request,
35+
uri: interfaceDocURI,
36+
snapshot: snapshot,
37+
symbol: symbol
38+
)
3439
} else {
35-
// generate interface
36-
let interfaceInfo = try await self.openInterface(
40+
let interfaceInfo = try await self.generatedInterfaceInfo(request: request, interfaceURI: interfaceDocURI)
41+
try interfaceInfo.contents.write(to: interfaceFilePath, atomically: true, encoding: String.Encoding.utf8)
42+
let snapshot = DocumentSnapshot(
43+
uri: interfaceDocURI,
44+
language: .swift,
45+
version: 0,
46+
lineTable: LineTable(interfaceInfo.contents)
47+
)
48+
let result = await self.generatedInterfaceDetails(
3749
request: request,
38-
uri: uri,
39-
name: moduleName,
40-
interfaceURI: interfaceDocURI
50+
uri: interfaceDocURI,
51+
snapshot: snapshot,
52+
symbol: symbol
4153
)
42-
do {
43-
// write to file
44-
try interfaceInfo.contents.write(to: interfaceFilePath, atomically: true, encoding: String.Encoding.utf8)
45-
// store snapshot
46-
let snapshot = try self.documentManager.open(
47-
interfaceDocURI,
48-
language: .swift,
49-
version: 0,
50-
text: interfaceInfo.contents
51-
)
52-
return await self.interfaceDetails(request: request, uri: interfaceDocURI, snapshot: snapshot, symbol: symbol)
53-
} catch {
54-
throw ResponseError.unknown(error.localizedDescription)
54+
_ = await orLog("Closing generated interface") {
55+
try await self.sourcekitd.send(closeDocumentSourcekitdRequest(uri: interfaceDocURI), fileContents: nil)
5556
}
57+
return result
5658
}
5759
}
5860

5961
/// Open the Swift interface for a module.
6062
///
6163
/// - Parameters:
62-
/// - request: The OpenInterfaceRequest.
63-
/// - uri: The document whose compiler arguments should be used to generate the interface.
64-
/// - name: The name of the module whose interface should be generated.
64+
/// - request: The OpenGeneratedInterfaceRequest.
6565
/// - interfaceURI: The file where the generated interface should be written.
66-
private func openInterface(
67-
request: OpenInterfaceRequest,
68-
uri: DocumentURI,
69-
name: String,
66+
///
67+
/// - Important: This opens a document with name `interfaceURI.pseudoPath` in sourcekitd. The caller is responsible
68+
/// for ensuring that the document will eventually get closed in sourcekitd again.
69+
private func generatedInterfaceInfo(
70+
request: OpenGeneratedInterfaceRequest,
7071
interfaceURI: DocumentURI
71-
) async throws -> InterfaceInfo {
72+
) async throws -> GeneratedInterfaceInfo {
7273
let keys = self.keys
7374
let skreq = sourcekitd.dictionary([
7475
keys.request: requests.editorOpenInterface,
75-
keys.moduleName: name,
76+
keys.moduleName: request.moduleName,
7677
keys.groupName: request.groupName,
7778
keys.name: interfaceURI.pseudoPath,
7879
keys.synthesizedExtension: 1,
79-
keys.compilerArgs: await self.buildSettings(for: uri)?.compilerArgs as [SKDRequestValue]?,
80+
keys.compilerArgs: await self.buildSettings(for: request.textDocument.uri)?.compilerArgs as [SKDRequestValue]?,
8081
])
8182

8283
let dict = try await self.sourcekitd.send(skreq, fileContents: nil)
83-
return InterfaceInfo(contents: dict[keys.sourceText] ?? "")
84+
return GeneratedInterfaceInfo(contents: dict[keys.sourceText] ?? "")
8485
}
8586

86-
private func interfaceDetails(
87-
request: OpenInterfaceRequest,
87+
private func generatedInterfaceDetails(
88+
request: OpenGeneratedInterfaceRequest,
8889
uri: DocumentURI,
8990
snapshot: DocumentSnapshot,
9091
symbol: String?
91-
) async -> InterfaceDetails {
92+
) async -> GeneratedInterfaceDetails {
9293
do {
9394
guard let symbol = symbol else {
94-
return InterfaceDetails(uri: uri, position: nil)
95+
return GeneratedInterfaceDetails(uri: uri, position: nil)
9596
}
9697
let keys = self.keys
9798
let skreq = sourcekitd.dictionary([
@@ -102,12 +103,12 @@ extension SwiftLanguageService {
102103

103104
let dict = try await self.sourcekitd.send(skreq, fileContents: snapshot.text)
104105
if let offset: Int = dict[keys.offset] {
105-
return InterfaceDetails(uri: uri, position: snapshot.positionOf(utf8Offset: offset))
106+
return GeneratedInterfaceDetails(uri: uri, position: snapshot.positionOf(utf8Offset: offset))
106107
} else {
107-
return InterfaceDetails(uri: uri, position: nil)
108+
return GeneratedInterfaceDetails(uri: uri, position: nil)
108109
}
109110
} catch {
110-
return InterfaceDetails(uri: uri, position: nil)
111+
return GeneratedInterfaceDetails(uri: uri, position: nil)
111112
}
112113
}
113114
}

Sources/SourceKitLSP/Swift/SwiftLanguageService.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ extension SwiftLanguageService {
391391
])
392392
}
393393

394-
private func closeDocumentSourcekitdRequest(uri: DocumentURI) -> SKDRequestDictionary {
394+
func closeDocumentSourcekitdRequest(uri: DocumentURI) -> SKDRequestDictionary {
395395
return sourcekitd.dictionary([
396396
keys.request: requests.editorClose,
397397
keys.name: uri.pseudoPath,

Tests/SourceKitLSPTests/SwiftInterfaceTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ final class SwiftInterfaceTests: XCTestCase {
7373
)
7474

7575
let (mainUri, _) = try project.openDocument("main.swift")
76-
let openInterface = OpenInterfaceRequest(
76+
let openInterface = OpenGeneratedInterfaceRequest(
7777
textDocument: TextDocumentIdentifier(mainUri),
7878
name: "MyLibrary",
7979
groupName: nil,

0 commit comments

Comments
 (0)