Skip to content

[sourcekitd] Add support for having multiple notification handlers with correct sharing #282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 3, 2020
33 changes: 26 additions & 7 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ let package = Package(
.target(
name: "SourceKit",
dependencies: [
"Csourcekitd",
"BuildServerProtocol",
"IndexStoreDB",
"LanguageServerProtocol",
"LanguageServerProtocolJSONRPC",
"SKCore",
"SourceKitD",
"SKSwiftPMWorkspace",
"SwiftToolsSupport-auto",
]
Expand Down Expand Up @@ -85,17 +85,12 @@ let package = Package(
]
),

// Csourcekitd: C modules wrapper for sourcekitd.
.target(
name: "Csourcekitd",
dependencies: []
),

// SKCore: Data structures and algorithms useful across the project, but not necessarily
// suitable for use in other packages.
.target(
name: "SKCore",
dependencies: [
"SourceKitD",
"BuildServerProtocol",
"LanguageServerProtocol",
"LanguageServerProtocolJSONRPC",
Expand All @@ -111,6 +106,30 @@ let package = Package(
]
),

// SourceKitD: Swift bindings for sourcekitd.
.target(
name: "SourceKitD",
dependencies: [
"Csourcekitd",
"LSPLogging",
"SKSupport",
"SwiftToolsSupport-auto",
]
),
.testTarget(
name: "SourceKitDTests",
dependencies: [
"SourceKitD",
"SKCore",
]
),

// Csourcekitd: C modules wrapper for sourcekitd.
.target(
name: "Csourcekitd",
dependencies: []
),

// Logging support used in LSP modules.
.target(
name: "LSPLogging",
Expand Down
2 changes: 1 addition & 1 deletion Sources/Csourcekitd/include/module.modulemap
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module sourcekitd {
module Csourcekitd {
// header "sourcekitd.h"
header "sourcekitd_functions.h"
export *
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import LanguageServerProtocol
import TSCBasic
import sourcekitd
import SourceKitD

/// Detailed information about a symbol under the cursor.
///
Expand Down Expand Up @@ -76,7 +76,7 @@ extension SwiftLanguageServer {
func _cursorInfo(
_ uri: DocumentURI,
_ range: Range<Position>,
additionalParameters appendAdditionalParameters: ((SKRequestDictionary) -> Void)? = nil,
additionalParameters appendAdditionalParameters: ((SKDRequestDictionary) -> Void)? = nil,
_ completion: @escaping (Swift.Result<CursorInfo?, CursorInfoError>) -> Void)
{
guard let snapshot = documentManager.latestSnapshot(uri) else {
Expand All @@ -89,7 +89,7 @@ extension SwiftLanguageServer {

let keys = self.keys

let skreq = SKRequestDictionary(sourcekitd: sourcekitd)
let skreq = SKDRequestDictionary(sourcekitd: sourcekitd)
skreq[keys.request] = requests.cursorinfo
skreq[keys.offset] = offsetRange.lowerBound
if offsetRange.upperBound != offsetRange.lowerBound {
Expand All @@ -107,7 +107,7 @@ extension SwiftLanguageServer {
let handle = self.sourcekitd.send(skreq, self.queue) { [weak self] result in
guard let self = self else { return }
guard let dict = result.success else {
return completion(.failure(.responseError(result.failure!)))
return completion(.failure(.responseError(ResponseError(result.failure!))))
}

guard let _: sourcekitd_uid_t = dict[keys.kind] else {
Expand All @@ -123,7 +123,7 @@ extension SwiftLanguageServer {
location = Location(uri: DocumentURI(URL(fileURLWithPath: filepath)), range: Range(pos))
}

let refactorActionsArray: SKResponseArray? = dict[keys.refactor_actions]
let refactorActionsArray: SKDResponseArray? = dict[keys.refactor_actions]

completion(.success(
CursorInfo(
Expand All @@ -140,7 +140,7 @@ extension SwiftLanguageServer {
range: range,
textDocument: TextDocumentIdentifier(uri),
keys,
self.api)
self.sourcekitd.api)
)))
}

Expand All @@ -160,7 +160,7 @@ extension SwiftLanguageServer {
func cursorInfo(
_ uri: DocumentURI,
_ range: Range<Position>,
additionalParameters appendAdditionalParameters: ((SKRequestDictionary) -> Void)? = nil,
additionalParameters appendAdditionalParameters: ((SKDRequestDictionary) -> Void)? = nil,
_ completion: @escaping (Swift.Result<CursorInfo?, CursorInfoError>) -> Void)
{
self.queue.async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
import LanguageServerProtocol
import LSPLogging
import SKSupport
import sourcekitd
import SourceKitD

extension CodeAction {

/// Creates a CodeAction from a list for sourcekit fixits.
///
/// If this is from a note, the note's description should be passed as `fromNote`.
init?(fixits: SKResponseArray, in snapshot: DocumentSnapshot, fromNote: String?) {
init?(fixits: SKDResponseArray, in snapshot: DocumentSnapshot, fromNote: String?) {
var edits: [TextEdit] = []
let editsMapped = fixits.forEach { (_, skfixit) -> Bool in
if let edit = TextEdit(fixit: skfixit, in: snapshot) {
Expand Down Expand Up @@ -88,7 +88,7 @@ extension CodeAction {
extension TextEdit {

/// Creates a TextEdit from a sourcekitd fixit response dictionary.
init?(fixit: SKResponseDictionary, in snapshot: DocumentSnapshot) {
init?(fixit: SKDResponseDictionary, in snapshot: DocumentSnapshot) {
let keys = fixit.sourcekitd.keys
if let utf8Offset: Int = fixit[keys.offset],
let length: Int = fixit[keys.length],
Expand All @@ -107,7 +107,7 @@ extension TextEdit {
extension Diagnostic {

/// Creates a diagnostic from a sourcekitd response dictionary.
init?(_ diag: SKResponseDictionary, in snapshot: DocumentSnapshot) {
init?(_ diag: SKDResponseDictionary, in snapshot: DocumentSnapshot) {
// FIXME: this assumes that the diagnostics are all in the same file.

let keys = diag.sourcekitd.keys
Expand Down Expand Up @@ -142,13 +142,13 @@ extension Diagnostic {
}

var actions: [CodeAction]? = nil
if let skfixits: SKResponseArray = diag[keys.fixits],
if let skfixits: SKDResponseArray = diag[keys.fixits],
let action = CodeAction(fixits: skfixits, in: snapshot, fromNote: nil) {
actions = [action]
}

var notes: [DiagnosticRelatedInformation]? = nil
if let sknotes: SKResponseArray = diag[keys.diagnostics] {
if let sknotes: SKDResponseArray = diag[keys.diagnostics] {
notes = []
sknotes.forEach { (_, sknote) -> Bool in
guard let note = DiagnosticRelatedInformation(sknote, in: snapshot) else { return true }
Expand All @@ -171,7 +171,7 @@ extension Diagnostic {
extension DiagnosticRelatedInformation {

/// Creates related information from a sourcekitd note response dictionary.
init?(_ diag: SKResponseDictionary, in snapshot: DocumentSnapshot) {
init?(_ diag: SKDResponseDictionary, in snapshot: DocumentSnapshot) {
let keys = diag.sourcekitd.keys

var position: Position? = nil
Expand All @@ -191,7 +191,7 @@ extension DiagnosticRelatedInformation {
guard let message: String = diag[keys.description] else { return nil }

var actions: [CodeAction]? = nil
if let skfixits: SKResponseArray = diag[keys.fixits],
if let skfixits: SKDResponseArray = diag[keys.fixits],
let action = CodeAction(fixits: skfixits, in: snapshot, fromNote: message) {
actions = [action]
}
Expand All @@ -209,7 +209,7 @@ struct CachedDiagnostic {
}

extension CachedDiagnostic {
init?(_ diag: SKResponseDictionary, in snapshot: DocumentSnapshot) {
init?(_ diag: SKDResponseDictionary, in snapshot: DocumentSnapshot) {
let sk = diag.sourcekitd
guard let diagnostic = Diagnostic(diag, in: snapshot) else { return nil }
self.diagnostic = diagnostic
Expand Down Expand Up @@ -243,7 +243,7 @@ enum DiagnosticStage: Hashable {
}

extension DiagnosticStage {
init?(_ uid: sourcekitd_uid_t, sourcekitd: SwiftSourceKitFramework) {
init?(_ uid: sourcekitd_uid_t, sourcekitd: SourceKitD) {
switch uid {
case sourcekitd.values.diag_stage_parse:
self = .parse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//
//===----------------------------------------------------------------------===//
import LanguageServerProtocol
import sourcekitd
import SourceKitD

public struct SemanticRefactorCommand: SwiftCommand {

Expand Down Expand Up @@ -64,7 +64,7 @@ public struct SemanticRefactorCommand: SwiftCommand {
}

extension Array where Element == SemanticRefactorCommand {
init?(array: SKResponseArray?, range: Range<Position>, textDocument: TextDocumentIdentifier, _ keys: sourcekitd_keys, _ api: sourcekitd_functions_t) {
init?(array: SKDResponseArray?, range: Range<Position>, textDocument: TextDocumentIdentifier, _ keys: sourcekitd_keys, _ api: sourcekitd_functions_t) {
guard let results = array else {
return nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//===----------------------------------------------------------------------===//
import LanguageServerProtocol
import TSCBasic
import sourcekitd
import SourceKitD

/// Detailed information about the result of a specific refactoring operation.
///
Expand All @@ -36,15 +36,15 @@ struct SemanticRefactoring {
/// - dict: Response dictionary to extract information from.
/// - url: The client URL that triggered the `semantic_refactoring` request.
/// - keys: The sourcekitd key set to use for looking up into `dict`.
init?(_ title: String, _ dict: SKResponseDictionary, _ snapshot: DocumentSnapshot, _ keys: sourcekitd_keys) {
guard let categorizedEdits: SKResponseArray = dict[keys.categorizededits] else {
init?(_ title: String, _ dict: SKDResponseDictionary, _ snapshot: DocumentSnapshot, _ keys: sourcekitd_keys) {
guard let categorizedEdits: SKDResponseArray = dict[keys.categorizededits] else {
return nil
}

var textEdits = [TextEdit]()

categorizedEdits.forEach { _, value in
guard let edits: SKResponseArray = value[keys.edits] else {
guard let edits: SKDResponseArray = value[keys.edits] else {
return false
}
edits.forEach { _, value in
Expand Down Expand Up @@ -142,7 +142,7 @@ extension SwiftLanguageServer {
return completion(.failure(.invalidRange(refactorCommand.positionRange)))
}

let skreq = SKRequestDictionary(sourcekitd: self.sourcekitd)
let skreq = SKDRequestDictionary(sourcekitd: self.sourcekitd)
skreq[keys.request] = self.requests.semantic_refactoring
// Preferred name for e.g. an extracted variable.
// Empty string means sourcekitd chooses a name automatically.
Expand All @@ -162,7 +162,7 @@ extension SwiftLanguageServer {
let handle = self.sourcekitd.send(skreq, self.queue) { [weak self] result in
guard let self = self else { return }
guard let dict = result.success else {
return completion(.failure(.responseError(result.failure!)))
return completion(.failure(.responseError(ResponseError(result.failure!))))
}
guard let refactor = SemanticRefactoring(refactorCommand.title, dict, snapshot, self.keys) else {
return completion(.failure(.noEditsNeeded(uri)))
Expand Down
31 changes: 31 additions & 0 deletions Sources/SourceKit/Swift/SourceKitD+ResponseError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 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 SourceKitD
import LanguageServerProtocol

extension ResponseError {
public init(_ value: SKDError) {
switch value {
case .requestCancelled:
self = .cancelled
case .requestFailed(let desc):
self = .unknown("sourcekitd request failed: \(desc)")
case .requestInvalid(let desc):
self = .unknown("sourcekitd invalid request \(desc)")
case .missingRequiredSymbol(let desc):
self = .unknown("sourcekitd missing required symbol '\(desc)'")
case .connectionInterrupted:
self = .unknown("sourcekitd connection interrupted")
}
}
}
Loading