Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ var targets: [Target] = [
dependencies: [
"LanguageServerProtocolTransport",
"SKLogging",
"ToolsProtocolsSwiftExtensions",
"ToolsProtocolsTestSupport",
],
swiftSettings: globalSwiftSettings
Expand Down
17 changes: 16 additions & 1 deletion Sources/BuildServerProtocol/Messages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,22 @@ private let notificationTypes: [NotificationType.Type] = [

extension MessageRegistry {
public static let bspProtocol: MessageRegistry =
MessageRegistry(requests: requestTypes, notifications: notificationTypes)
MessageRegistry(requests: requestTypes, notifications: notificationTypes, legacyNames: bspLegacyNames)

/// Maps current `sourcekit/`-prefixed BSP method names to the legacy names used before the
/// prefix migration. Consumed by `MessageRegistry` (incoming routing) and
/// `LegacyNameFallbackConnection` (outgoing retries).
///
/// This table is frozen. Do not add new entries for newly introduced methods.
public static let bspLegacyNames: [String: String] = [
BuildTargetPrepareRequest.method: "buildTarget/prepare",
FileOptionsChangedNotification.method: "build/sourceKitOptionsChanged",
TextDocumentSourceKitOptionsRequest.method: "textDocument/sourceKitOptions",
WorkspaceWaitForBuildSystemUpdatesRequest.method: "workspace/waitForBuildSystemUpdates",
]
#if compiler(>=6.6)
#warning("Remove the legacy method names")
#endif
}

@available(*, deprecated, message: "use MessageRegistry.bspProtocol instead")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public typealias OriginId = String
/// The server communicates during the initialize handshake whether this method is supported or not by setting
/// `prepareProvider: true` in `SourceKitInitializeBuildResponseData`.
public struct BuildTargetPrepareRequest: BSPRequest, Hashable {
public static let method: String = "buildTarget/prepare"
public static let method: String = "sourcekit/buildTarget/prepare"
public typealias Response = BuildTargetPrepareResponse

/// A list of build targets to prepare.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,13 @@ public struct BuildClientCapabilities: Codable, Hashable, Sendable {
/// it's safe to return classpath in ScalacOptionsItem empty. */
public var jvmCompileClasspathReceiver: Bool?

public init(languageIds: [Language], jvmCompileClasspathReceiver: Bool? = nil) {
/// Experimental client capabilities.
public var experimental: LSPAny?

public init(languageIds: [Language], jvmCompileClasspathReceiver: Bool? = nil, experimental: LSPAny? = nil) {
self.languageIds = languageIds
self.jvmCompileClasspathReceiver = jvmCompileClasspathReceiver
self.experimental = experimental
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public struct FileOptionsChangedNotification: BSPNotification {
public var workingDirectory: String?
}

public static let method: String = "build/sourceKitOptionsChanged"
public static let method: String = "sourcekit/build/sourceKitOptionsChanged"

/// The URI of the document that has changed settings.
public var uri: URI
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public import LanguageServerProtocol
///
/// The request may return `nil` if it doesn't have any build settings for this file in the given target.
public struct TextDocumentSourceKitOptionsRequest: BSPRequest, Hashable {
public static let method: String = "textDocument/sourceKitOptions"
public static let method: String = "sourcekit/textDocument/sourceKitOptions"
public typealias Response = TextDocumentSourceKitOptionsResponse?

/// The URI of the document to get options for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public import LanguageServerProtocol
public struct WorkspaceWaitForBuildSystemUpdatesRequest: BSPRequest, Hashable {
public typealias Response = VoidResponse

public static let method: String = "workspace/waitForBuildSystemUpdates"
public static let method: String = "sourcekit/workspace/waitForBuildSystemUpdates"

public init() {}
}
5 changes: 4 additions & 1 deletion Sources/LanguageServerProtocol/Connection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ public protocol Connection: Sendable {
/// Send a request with a pre-defined request ID and (asynchronously) receive a reply.
///
/// The request ID must not conflict with any request ID generated by `nextRequestID()`.
/// `method` is the JSON-RPC method string; pass `Request.method` for the normal case or a
/// legacy method name when talking to an old peer.
func send<Request: RequestType>(
_ request: Request,
method: String,
id: RequestID,
reply: @escaping @Sendable (LSPResult<Request.Response>) -> Void
)
Expand All @@ -42,7 +45,7 @@ extension Connection {
reply: @escaping @Sendable (LSPResult<Request.Response>) -> Void
) -> RequestID {
let id = nextRequestID()
self.send(request, id: id, reply: reply)
self.send(request, method: Request.method, id: id, reply: reply)
return id
}
}
Expand Down
23 changes: 20 additions & 3 deletions Sources/LanguageServerProtocol/MessageRegistry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,26 @@ public final class MessageRegistry: Sendable {
private let methodToRequest: [String: _RequestType.Type]
private let methodToNotification: [String: NotificationType.Type]

public init(requests: [_RequestType.Type], notifications: [NotificationType.Type]) {
self.methodToRequest = Dictionary(uniqueKeysWithValues: requests.map { ($0.method, $0) })
self.methodToNotification = Dictionary(uniqueKeysWithValues: notifications.map { ($0.method, $0) })
public init(
requests: [_RequestType.Type],
notifications: [NotificationType.Type],
legacyNames: [String: String] = [:]
) {
var methodToRequest: [String: _RequestType.Type] = Dictionary(
uniqueKeysWithValues: requests.map { ($0.method, $0) }
)
for request in requests {
if let legacy = legacyNames[request.method] { methodToRequest[legacy] = request }
}
self.methodToRequest = methodToRequest

var methodToNotification: [String: NotificationType.Type] = Dictionary(
uniqueKeysWithValues: notifications.map { ($0.method, $0) }
)
for notification in notifications {
if let legacy = legacyNames[notification.method] { methodToNotification[legacy] = notification }
}
self.methodToNotification = methodToNotification
}

/// Returns the type of the message named `method`, or nil if it is unknown.
Expand Down
28 changes: 27 additions & 1 deletion Sources/LanguageServerProtocol/Messages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,33 @@ public let builtinNotifications: [NotificationType.Type] = [

extension MessageRegistry {
public static let lspProtocol: MessageRegistry =
MessageRegistry(requests: builtinRequests, notifications: builtinNotifications)
MessageRegistry(requests: builtinRequests, notifications: builtinNotifications, legacyNames: lspLegacyNames)

/// Maps current `sourcekit/`-prefixed method names to the legacy names used before the prefix
/// migration. Consumed by `MessageRegistry` (incoming routing) and `LegacyNameFallbackConnection`
/// (outgoing retries).
///
/// This table is frozen. Do not add new entries for newly introduced methods.
public static let lspLegacyNames: [String: String] = [
DidChangeActiveDocumentNotification.method: "window/didChangeActiveDocument",
DoccDocumentationRequest.method: "textDocument/doccDocumentation",
DocumentTestsRequest.method: "textDocument/tests",
GetReferenceDocumentRequest.method: "workspace/getReferenceDocument",
IsIndexingRequest.method: "sourceKit/_isIndexing",
OutputPathsRequest.method: "workspace/_outputPaths",
PeekDocumentsRequest.method: "workspace/peekDocuments",
SetOptionsRequest.method: "workspace/_setOptions",
SourceKitOptionsRequest.method: "workspace/_sourceKitOptions",
SynchronizeRequest.method: "workspace/synchronize",
TriggerReindexRequest.method: "workspace/triggerReindex",
WorkspacePlaygroundsRefreshRequest.method: "workspace/playgrounds/refresh",
WorkspacePlaygroundsRequest.method: "workspace/playgrounds",
WorkspaceTestsRefreshRequest.method: "workspace/tests/refresh",
WorkspaceTestsRequest.method: "workspace/tests",
]
#if compiler(>=6.6)
#warning("Remove the legacy method names")
#endif
}

// MARK: Miscellaneous Message Types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//===----------------------------------------------------------------------===//

public struct DidChangeActiveDocumentNotification: LSPNotification {
public static let method: String = "window/didChangeActiveDocument"
public static let method: String = "sourcekit/window/didChangeActiveDocument"

/// The document that is being displayed in the active editor or `null` to indicate that either no document is active
/// or that the currently open document is not handled by SourceKit-LSP.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
/// This request is an extension to LSP supported by SourceKit-LSP.
/// The client is expected to display the documentation in an editor using swift-docc-render.
public struct DoccDocumentationRequest: TextDocumentRequest, Hashable {
public static let method: String = "textDocument/doccDocumentation"
public static let method: String = "sourcekit/textDocument/doccDocumentation"
public typealias Response = DoccDocumentationResponse

/// The document in which to lookup the symbol location.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
///
/// **(LSP Extension)**
public struct DocumentTestsRequest: TextDocumentRequest, Hashable {
public static let method: String = "textDocument/tests"
public static let method: String = "sourcekit/textDocument/tests"
public typealias Response = [TestItem]

public var textDocument: TextDocumentIdentifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
/// Enable the experimental client capability `"workspace/getReferenceDocument"` so that the server responds with
/// reference document URLs for certain requests or commands whenever possible.
public struct GetReferenceDocumentRequest: LSPRequest {
public static let method: String = "workspace/getReferenceDocument"
public static let method: String = "sourcekit/workspace/getReferenceDocument"
public typealias Response = GetReferenceDocumentResponse

public var uri: DocumentURI
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//===----------------------------------------------------------------------===//

public struct IsIndexingRequest: LSPRequest {
public static let method: String = "sourceKit/_isIndexing"
public static let method: String = "sourcekit/isIndexing"
public typealias Response = IsIndexingResponse

public init() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
///
/// **(LSP Extension)**.
public struct OutputPathsRequest: LSPRequest, Hashable {
public static let method: String = "workspace/_outputPaths"
public static let method: String = "sourcekit/workspace/outputPaths"
public typealias Response = OutputPathsResponse

/// The target whose output file paths to get.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
/// It requires the experimental client capability `"workspace/peekDocuments"` to use.
/// It also needs the client to handle the request and present the "peeked" editor.
public struct PeekDocumentsRequest: LSPRequest {
public static let method: String = "workspace/peekDocuments"
public static let method: String = "sourcekit/workspace/peekDocuments"
public typealias Response = PeekDocumentsResponse

public var uri: DocumentURI
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
//
/// Any options not specified in this request will be left as-is.
public struct SetOptionsRequest: LSPRequest {
public static let method: String = "workspace/_setOptions"
public static let method: String = "sourcekit/workspace/setOptions"
public typealias Response = VoidResponse

/// `true` to pause background indexing or `false` to resume background indexing.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
///
/// **(LSP Extension)**.
public struct SourceKitOptionsRequest: LSPRequest, Hashable {
public static let method: String = "workspace/_sourceKitOptions"
public static let method: String = "sourcekit/workspace/sourceKitOptions"
public typealias Response = SourceKitOptionsResponse

/// The document to get options for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
/// user a result based on the data that is available and (let the user) re-perform the action if the underlying index
/// data has changed.
public struct SynchronizeRequest: LSPRequest {
public static let method: String = "workspace/synchronize"
public static let method: String = "sourcekit/workspace/synchronize"
public typealias Response = VoidResponse

/// Wait for the build server to have an up-to-date build graph by sending a `workspace/waitForBuildSystemUpdates` to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
///
/// **LSP Extension**
public struct TriggerReindexRequest: LSPRequest {
public static let method: String = "workspace/triggerReindex"
public static let method: String = "sourcekit/workspace/triggerReindex"
public typealias Response = VoidResponse

public init() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
///
/// **(LSP Extension)**
public struct WorkspacePlaygroundsRefreshRequest: LSPRequest {
public static let method: String = "workspace/playgrounds/refresh"
public static let method: String = "sourcekit/workspace/playgrounds/refresh"
public typealias Response = VoidResponse

public init() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
///
/// **(LSP Extension)**
public struct WorkspacePlaygroundsRequest: LSPRequest, Hashable {
public static let method: String = "workspace/playgrounds"
public static let method: String = "sourcekit/workspace/playgrounds"
public typealias Response = [Playground]

public init() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
///
/// **(LSP Extension)**
public struct WorkspaceTestsRefreshRequest: LSPRequest {
public static let method: String = "workspace/tests/refresh"
public static let method: String = "sourcekit/workspace/tests/refresh"
public typealias Response = VoidResponse

public init() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
///
/// **(LSP Extension)**
public struct WorkspaceTestsRequest: LSPRequest, Hashable {
public static let method: String = "workspace/tests"
public static let method: String = "sourcekit/workspace/tests"
public typealias Response = [TestItem]

public init() {}
Expand Down
1 change: 1 addition & 0 deletions Sources/LanguageServerProtocolTransport/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ add_library(LanguageServerProtocolTransport
DisableSigpipe.swift
DocumentURI+CustomLogStringConvertible.swift
JSONRPCConnection.swift
LegacyNameFallbackConnection.swift
LocalConnection.swift
LoggableMessageTypes.swift
MessageCoding.swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ public import LanguageServerProtocol
@_spi(SourceKitLSP) private import ToolsProtocolsSwiftExtensions

extension Connection {
public func send<R: RequestType>(_ request: R) async throws -> R.Response {
public func send<R: RequestType>(_ request: R, method: String = R.method) async throws -> R.Response {
return try await withCancellableCheckedThrowingContinuation { continuation in
return self.send(request) { result in
let id = self.nextRequestID()
self.send(request, method: method, id: id) { result in
continuation.resume(with: result)
}
return id
} cancel: { requestID in
self.send(CancelRequestNotification(id: requestID))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ public final class JSONRPCConnection: Connection {
switch message {
case .notification(let notification):
notification._handle(receiveHandler)
case .request(let request, let id):
case .request(let request, _, let id):
request._handle(receiveHandler, id: id) { (response, id) in
self.sendReply(response, id: id)
}
Expand Down Expand Up @@ -514,7 +514,7 @@ public final class JSONRPCConnection: Connection {
message: "sourcekit-lsp failed to encode a notification to the editor"
)
return
case .request(_, _):
case .request(_, _, _):
// We want to send a notification to the editor but failed to encode it. We don't know the `reply` handle for
// the request at this point so we can't synthesize an errorResponse for the request. This means that the
// request will never receive a reply. Inform the user about it.
Expand Down Expand Up @@ -620,6 +620,7 @@ public final class JSONRPCConnection: Connection {
/// When the receiving end replies to the request, execute `reply` with the response.
public func send<Request: RequestType>(
_ request: Request,
method: String,
id: RequestID,
reply: @escaping @Sendable (LSPResult<Request.Response>) -> Void
) {
Expand All @@ -638,15 +639,15 @@ public final class JSONRPCConnection: Connection {
logger.info(
"""
Received reply for request \(id, privacy: .public) from \(self.name, privacy: .public)
\(Request.method, privacy: .public)
\(method, privacy: .public)
\(response.forLogging)
"""
)
case .failure(let error):
logger.error(
"""
Received error for request \(id, privacy: .public) from \(self.name, privacy: .public)
\(Request.method, privacy: .public)
\(method, privacy: .public)
\(error.forLogging)
"""
)
Expand All @@ -660,8 +661,7 @@ public final class JSONRPCConnection: Connection {
\(request.forLogging)
"""
)

self.sendAssumingOnQueue(.request(request, id: id))
self.sendAssumingOnQueue(.request(request, method: method, id: id))
}
}

Expand Down
Loading
Loading