mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-03-02 18:23:24 +01:00
Generate swiftinterface for symbol lookup When a symbol definition returns it is in a swiftinterface file, create textual version of swiftinterface and return that in response. Extend OpenInterface to also seatch for a symbol Fix warning Syntax changes after review Move module name split into OpenInterfaceRequest Use group names when running open interface request Requested changes from PR rename symbol to symbolUSR Cleanup OpenInterfaceRequest.init Fix tests Added testDefinitionInSystemModuleInterface Use SwiftPMPackage test module Added version of buildAndIndex that includes system symbols Merge buildAndIndexWithSystemSymbols with buildAndIndex Added specific test project for system swiftinterface tests Add multiple tests for various system modules
158 lines
5.9 KiB
Swift
158 lines
5.9 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import Foundation
|
|
import SourceKitD
|
|
import LanguageServerProtocol
|
|
import LSPLogging
|
|
import SKSupport
|
|
|
|
struct InterfaceInfo {
|
|
var contents: String
|
|
}
|
|
|
|
struct FindUSRInfo {
|
|
let position: Position?
|
|
}
|
|
|
|
extension SwiftLanguageServer {
|
|
public func openInterface(_ request: LanguageServerProtocol.Request<LanguageServerProtocol.OpenInterfaceRequest>) {
|
|
let uri = request.params.textDocument.uri
|
|
let moduleName = request.params.moduleName
|
|
let name = request.params.name
|
|
let symbol = request.params.symbolUSR
|
|
self.queue.async {
|
|
let interfaceFilePath = self.generatedInterfacesPath.appendingPathComponent("\(name).swiftinterface")
|
|
let interfaceDocURI = DocumentURI(interfaceFilePath)
|
|
// has interface already been generated
|
|
if let snapshot = self.documentManager.latestSnapshot(interfaceDocURI) {
|
|
self._findUSRAndRespond(request: request, uri: interfaceDocURI, snapshot: snapshot, symbol: symbol)
|
|
} else {
|
|
// generate interface
|
|
self._openInterface(request: request, uri: uri, name: moduleName, interfaceURI: interfaceDocURI) { result in
|
|
switch result {
|
|
case .success(let interfaceInfo):
|
|
do {
|
|
// write to file
|
|
try interfaceInfo.contents.write(to: interfaceFilePath, atomically: true, encoding: String.Encoding.utf8)
|
|
// store snapshot
|
|
let snapshot = try self.documentManager.open(interfaceDocURI, language: .swift, version: 0, text: interfaceInfo.contents)
|
|
self._findUSRAndRespond(request: request, uri: interfaceDocURI, snapshot: snapshot, symbol: symbol)
|
|
} catch {
|
|
request.reply(.failure(ResponseError.unknown(error.localizedDescription)))
|
|
}
|
|
case .failure(let error):
|
|
log("open interface failed: \(error)", level: .warning)
|
|
request.reply(.failure(ResponseError(error)))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Open the Swift interface for a module.
|
|
///
|
|
/// - Parameters:
|
|
/// - request: The OpenInterfaceRequest.
|
|
/// - uri: The document whose compiler arguments should be used to generate the interface.
|
|
/// - name: The name of the module whose interface should be generated.
|
|
/// - interfaceURI: The file where the generated interface should be written.
|
|
/// - completion: Completion block to asynchronously receive the InterfaceInfo, or error.
|
|
private func _openInterface(request: LanguageServerProtocol.Request<LanguageServerProtocol.OpenInterfaceRequest>,
|
|
uri: DocumentURI,
|
|
name: String,
|
|
interfaceURI: DocumentURI,
|
|
completion: @escaping (Swift.Result<InterfaceInfo, SKDError>) -> Void) {
|
|
let keys = self.keys
|
|
let skreq = SKDRequestDictionary(sourcekitd: sourcekitd)
|
|
skreq[keys.request] = requests.editor_open_interface
|
|
skreq[keys.modulename] = name
|
|
if request.params.groupNames.count > 0 {
|
|
skreq[keys.groupname] = request.params.groupNames
|
|
}
|
|
skreq[keys.name] = interfaceURI.pseudoPath
|
|
skreq[keys.synthesizedextensions] = 1
|
|
if let compileCommand = self.commandsByFile[uri] {
|
|
skreq[keys.compilerargs] = compileCommand.compilerArgs
|
|
}
|
|
|
|
let handle = self.sourcekitd.send(skreq, self.queue) { result in
|
|
switch result {
|
|
case .success(let dict):
|
|
return completion(.success(InterfaceInfo(contents: dict[keys.sourcetext] ?? "")))
|
|
case .failure(let error):
|
|
return completion(.failure(error))
|
|
}
|
|
}
|
|
|
|
if let handle = handle {
|
|
request.cancellationToken.addCancellationHandler { [weak self] in
|
|
self?.sourcekitd.cancel(handle)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func _findUSRAndRespond(
|
|
request: LanguageServerProtocol.Request<LanguageServerProtocol.OpenInterfaceRequest>,
|
|
uri: DocumentURI,
|
|
snapshot: DocumentSnapshot,
|
|
symbol: String?
|
|
) {
|
|
self._findUSR(request: request, uri: uri, snapshot: snapshot, symbol: symbol) { result in
|
|
switch result {
|
|
case .success(let info):
|
|
request.reply(.success(InterfaceDetails(uri: uri, position: info.position)))
|
|
case .failure:
|
|
request.reply(.success(InterfaceDetails(uri: uri, position: nil)))
|
|
}
|
|
}
|
|
}
|
|
|
|
private func _findUSR(
|
|
request: LanguageServerProtocol.Request<LanguageServerProtocol.OpenInterfaceRequest>,
|
|
uri: DocumentURI,
|
|
snapshot: DocumentSnapshot,
|
|
symbol: String?,
|
|
completion: @escaping (Swift.Result<FindUSRInfo, SKDError>) -> Void
|
|
) {
|
|
guard let symbol = symbol else {
|
|
return completion(.success(FindUSRInfo(position: nil)))
|
|
}
|
|
let keys = self.keys
|
|
let skreq = SKDRequestDictionary(sourcekitd: sourcekitd)
|
|
|
|
skreq[keys.request] = requests.find_usr
|
|
skreq[keys.sourcefile] = uri.pseudoPath
|
|
skreq[keys.usr] = symbol
|
|
|
|
let handle = self.sourcekitd.send(skreq, self.queue) { result in
|
|
switch result {
|
|
case .success(let dict):
|
|
if let offset: Int = dict[keys.offset],
|
|
let position = snapshot.positionOf(utf8Offset: offset) {
|
|
return completion(.success(FindUSRInfo(position: position)))
|
|
} else {
|
|
return completion(.success(FindUSRInfo(position: nil)))
|
|
}
|
|
case .failure(let error):
|
|
return completion(.failure(error))
|
|
}
|
|
}
|
|
|
|
if let handle = handle {
|
|
request.cancellationToken.addCancellationHandler { [weak self] in
|
|
self?.sourcekitd.cancel(handle)
|
|
}
|
|
}
|
|
}
|
|
}
|