Skip to content

Commit c31dff2

Browse files
authored
Merge pull request #668 from bwhiteley/textualinterface
SourceKitLSP: generate Swift textual interfaces for module references
2 parents 5b16c3e + d2f7f2f commit c31dff2

File tree

17 files changed

+361
-13
lines changed

17 files changed

+361
-13
lines changed

Sources/LanguageServerProtocol/Messages.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public let builtinRequests: [_RequestType.Type] = [
5656
InlineValueRequest.self,
5757
LinkedEditingRangeRequest.self,
5858
MonikersRequest.self,
59+
OpenInterfaceRequest.self,
5960
PollIndexRequest.self,
6061
PrepareRenameRequest.self,
6162
ReferencesRequest.self,
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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+
/// Request a textual interface of a module to display in the IDE.
14+
/// **(LSP Extension)**
15+
public struct OpenInterfaceRequest: TextDocumentRequest, Hashable {
16+
public static let method: String = "textDocument/openInterface"
17+
public typealias Response = InterfaceDetails?
18+
19+
/// The document whose compiler arguments should be used to generate the interface.
20+
public var textDocument: TextDocumentIdentifier
21+
22+
/// The module to generate an index for.
23+
public var name: String
24+
25+
public init(textDocument: TextDocumentIdentifier, name: String) {
26+
self.textDocument = textDocument
27+
self.name = name
28+
}
29+
}
30+
31+
/// The textual output of a module interface.
32+
public struct InterfaceDetails: ResponseType, Hashable {
33+
34+
public var uri: DocumentURI
35+
36+
public init(uri: DocumentURI) {
37+
self.uri = uri
38+
}
39+
}

Sources/LanguageServerProtocol/Requests/SymbolInfoRequest.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,20 @@ public struct SymbolDetails: ResponseType, Hashable {
7878
/// translation unit.
7979
public var bestLocalDeclaration: Location? = nil
8080

81+
/// The kind of the symbol
82+
public var kind: SymbolKind?
83+
8184
public init(
8285
name: String?,
8386
containerName: String? = nil,
8487
usr: String?,
85-
bestLocalDeclaration: Location? = nil)
88+
bestLocalDeclaration: Location? = nil,
89+
kind: SymbolKind? = nil)
8690
{
8791
self.name = name
8892
self.containerName = containerName
8993
self.usr = usr
9094
self.bestLocalDeclaration = bestLocalDeclaration
95+
self.kind = kind
9196
}
9297
}

Sources/SKSupport/FileSystem.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,8 @@ extension AbsolutePath {
3030
}
3131
}
3232
}
33+
34+
/// The directory to write generated module interfaces
35+
public var defaultDirectoryForGeneratedInterfaces: AbsolutePath {
36+
return AbsolutePath(NSTemporaryDirectory()).appending(component: "GeneratedInterfaces")
37+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
import lib
1+
import /*lib:import*/lib
22

33
Lib() . /*Lib.foo:call*/foo()

Sources/SourceKitD/SourceKitD.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ extension SourceKitD {
112112

113113
return handle
114114
}
115+
116+
public func cancel(_ handle: sourcekitd_request_handle_t) {
117+
api.cancel_request(handle)
118+
}
115119
}
116120

117121
private func logRequest(_ request: SKDRequestDictionary) {

Sources/SourceKitD/sourcekitd_uids.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public struct sourcekitd_keys {
4545
public let kind: sourcekitd_uid_t
4646
public let length: sourcekitd_uid_t
4747
public let line: sourcekitd_uid_t
48+
public let modulename: sourcekitd_uid_t
4849
public let name: sourcekitd_uid_t
4950
public let namelength: sourcekitd_uid_t
5051
public let nameoffset: sourcekitd_uid_t
@@ -63,6 +64,7 @@ public struct sourcekitd_keys {
6364
public let substructure: sourcekitd_uid_t
6465
public let syntactic_only: sourcekitd_uid_t
6566
public let syntaxmap: sourcekitd_uid_t
67+
public let synthesizedextensions: sourcekitd_uid_t
6668
public let enablesyntaxmap: sourcekitd_uid_t
6769
public let text: sourcekitd_uid_t
6870
public let typename: sourcekitd_uid_t
@@ -118,6 +120,7 @@ public struct sourcekitd_keys {
118120
kind = api.uid_get_from_cstr("key.kind")!
119121
length = api.uid_get_from_cstr("key.length")!
120122
line = api.uid_get_from_cstr("key.line")!
123+
modulename = api.uid_get_from_cstr("key.modulename")!
121124
name = api.uid_get_from_cstr("key.name")!
122125
namelength = api.uid_get_from_cstr("key.namelength")!
123126
nameoffset = api.uid_get_from_cstr("key.nameoffset")!
@@ -137,6 +140,7 @@ public struct sourcekitd_keys {
137140
syntactic_only = api.uid_get_from_cstr("key.syntactic_only")!
138141
syntaxmap = api.uid_get_from_cstr("key.syntaxmap")!
139142
enablesyntaxmap = api.uid_get_from_cstr("key.enablesyntaxmap")!
143+
synthesizedextensions = api.uid_get_from_cstr("key.synthesizedextensions")!
140144
text = api.uid_get_from_cstr("key.text")!
141145
typename = api.uid_get_from_cstr("key.typename")!
142146
usr = api.uid_get_from_cstr("key.usr")!
@@ -163,6 +167,7 @@ public struct sourcekitd_keys {
163167
public struct sourcekitd_requests {
164168
public let crash_exit: sourcekitd_uid_t
165169
public let editor_open: sourcekitd_uid_t
170+
public let editor_open_interface: sourcekitd_uid_t
166171
public let editor_close: sourcekitd_uid_t
167172
public let editor_replacetext: sourcekitd_uid_t
168173
public let codecomplete: sourcekitd_uid_t
@@ -178,6 +183,7 @@ public struct sourcekitd_requests {
178183
public init(api: sourcekitd_functions_t) {
179184
crash_exit = api.uid_get_from_cstr("source.request.crash_exit")!
180185
editor_open = api.uid_get_from_cstr("source.request.editor.open")!
186+
editor_open_interface = api.uid_get_from_cstr("source.request.editor.open.interface")!
181187
editor_close = api.uid_get_from_cstr("source.request.editor.close")!
182188
editor_replacetext = api.uid_get_from_cstr("source.request.editor.replacetext")!
183189
codecomplete = api.uid_get_from_cstr("source.request.codecomplete")!

Sources/SourceKitLSP/Clang/ClangLanguageServer.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,10 @@ extension ClangLanguageServerShim {
522522
}
523523
}
524524

525+
func openInterface(_ request: Request<OpenInterfaceRequest>) {
526+
request.reply(.failure(.unknown("unsupported method")))
527+
}
528+
525529
// MARK: - Other
526530

527531
func executeCommand(_ req: Request<ExecuteCommandRequest>) {

Sources/SourceKitLSP/SourceKitServer+Options.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
import LanguageServerProtocol
1414
import SKCore
15+
import struct TSCBasic.AbsolutePath
16+
import SKSupport
1517

1618
extension SourceKitServer {
1719

@@ -30,17 +32,22 @@ extension SourceKitServer {
3032

3133
/// Options for code-completion.
3234
public var completionOptions: SKCompletionOptions
35+
36+
/// Override the default directory where generated interfaces will be stored
37+
public var generatedInterfacesPath: AbsolutePath
3338

3439
public init(
3540
buildSetup: BuildSetup = .default,
3641
clangdOptions: [String] = [],
3742
indexOptions: IndexOptions = .init(),
38-
completionOptions: SKCompletionOptions = .init())
43+
completionOptions: SKCompletionOptions = .init(),
44+
generatedInterfacesPath: AbsolutePath = defaultDirectoryForGeneratedInterfaces)
3945
{
4046
self.buildSetup = buildSetup
4147
self.clangdOptions = clangdOptions
4248
self.indexOptions = indexOptions
4349
self.completionOptions = completionOptions
50+
self.generatedInterfacesPath = generatedInterfacesPath
4451
}
4552
}
4653
}

Sources/SourceKitLSP/SourceKitServer.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ public final class SourceKitServer: LanguageServer {
184184
registerToolchainTextDocumentRequest(SourceKitServer.completion,
185185
CompletionList(isIncomplete: false, items: []))
186186
registerToolchainTextDocumentRequest(SourceKitServer.hover, nil)
187+
registerToolchainTextDocumentRequest(SourceKitServer.openInterface, nil)
187188
registerToolchainTextDocumentRequest(SourceKitServer.declaration, .locations([]))
188189
registerToolchainTextDocumentRequest(SourceKitServer.definition, .locations([]))
189190
registerToolchainTextDocumentRequest(SourceKitServer.references, [])
@@ -985,6 +986,14 @@ extension SourceKitServer {
985986
) {
986987
languageService.hover(req)
987988
}
989+
990+
func openInterface(
991+
_ req: Request<OpenInterfaceRequest>,
992+
workspace: Workspace,
993+
languageService: ToolchainLanguageServer
994+
) {
995+
languageService.openInterface(req)
996+
}
988997

989998
/// Find all symbols in the workspace that include a string in their name.
990999
/// - returns: An array of SymbolOccurrences that match the string.
@@ -1272,6 +1281,26 @@ extension SourceKitServer {
12721281
let symbolInfo = SymbolInfoRequest(textDocument: req.params.textDocument, position: req.params.position)
12731282
let index = self.workspaceForDocument(uri: req.params.textDocument.uri)?.index
12741283
let callback = callbackOnQueue(self.queue) { (result: LSPResult<SymbolInfoRequest.Response>) in
1284+
1285+
// If this symbol is a module then generate a textual interface
1286+
if case .success(let symbols) = result, let symbol = symbols.first, symbol.kind == .module, let name = symbol.name {
1287+
let openInterface = OpenInterfaceRequest(textDocument: req.params.textDocument, name: name)
1288+
let request = Request(openInterface, id: req.id, clientID: ObjectIdentifier(self),
1289+
cancellation: req.cancellationToken, reply: { (result: Result<OpenInterfaceRequest.Response, ResponseError>) in
1290+
switch result {
1291+
case .success(let interfaceDetails?):
1292+
let loc = Location(uri: interfaceDetails.uri, range: Range(Position(line: 0, utf16index: 0)))
1293+
req.reply(.locations([loc]))
1294+
case .success(nil):
1295+
req.reply(.failure(.unknown("Could not generate Swift Interface for \(name)")))
1296+
case .failure(let error):
1297+
req.reply(.failure(error))
1298+
}
1299+
})
1300+
languageService.openInterface(request)
1301+
return
1302+
}
1303+
12751304
let extractedResult = self.extractIndexedOccurrences(result: result, index: index, useLocalFallback: true) { (usr, index) in
12761305
log("performing indexed jump-to-def with usr \(usr)")
12771306
var occurs = index.occurrences(ofUSR: usr, roles: [.definition])

0 commit comments

Comments
 (0)