diff --git a/fleet/lsp.protocol/src/main/com/jetbrains/lsp/implementation/LspHandlers.kt b/fleet/lsp.protocol/src/main/com/jetbrains/lsp/implementation/LspHandlers.kt index 0e2937cdf1a4..ef2fddf200ae 100644 --- a/fleet/lsp.protocol/src/main/com/jetbrains/lsp/implementation/LspHandlers.kt +++ b/fleet/lsp.protocol/src/main/com/jetbrains/lsp/implementation/LspHandlers.kt @@ -12,27 +12,27 @@ import kotlinx.coroutines.CoroutineScope interface LspHandlers { fun requestHandler( requestTypeName: String, - ): LspRequestHandler<*, *, *, *>? + ): LspRequestHandler<*, *, *>? fun notificationHandler( notificationTypeName: String, - ): LspNotificationHandler<*, *>? + ): LspNotificationHandler<*>? object EMPTY : LspHandlers { - override fun requestHandler(requestTypeName: String): LspRequestHandler<*, *, *, *>? = null - override fun notificationHandler(notificationTypeName: String): LspNotificationHandler<*, *>? = null + override fun requestHandler(requestTypeName: String): LspRequestHandler<*, *, *>? = null + override fun notificationHandler(notificationTypeName: String): LspNotificationHandler<*>? = null } } -interface LspHandlersBuilder { +interface LspHandlersBuilder { fun request( requestType: RequestType, - handler: suspend context(T) CoroutineScope.(Request) -> Response, + handler: suspend context(LspHandlerContext) CoroutineScope.(Request) -> Response, ) fun notification( notifcationType: NotificationType, - handler: suspend context(T) CoroutineScope.(Notification) -> Unit, + handler: suspend context(LspHandlerContext) CoroutineScope.(Notification) -> Unit, ) } @@ -61,31 +61,49 @@ fun LspClient.reportProgressMessage( } -class LspRequestHandler( +class LspRequestHandler( val requestType: RequestType, - val handler: suspend context(Context) CoroutineScope.(Params) -> Result, + val handler: suspend context(LspHandlerContext) CoroutineScope.(Params) -> Result, ) -class LspNotificationHandler( +class LspNotificationHandler( val notificationType: NotificationType, - val handler: suspend context(Context) CoroutineScope.(Params) -> Unit, + val handler: suspend context(LspHandlerContext) CoroutineScope.(Params) -> Unit, ) -fun lspHandlers(builder: LspHandlersBuilder.() -> Unit): LspHandlers { - val requests = mutableMapOf>() - val notifications = mutableMapOf>() +interface LspHandlersMiddleware { + fun requestHandler(handler: LspRequestHandler): LspRequestHandler - object : LspHandlersBuilder { + fun

notificationHandler(handler: LspNotificationHandler

): LspNotificationHandler

+ + companion object { + val IDENTITY: LspHandlersMiddleware = object : LspHandlersMiddleware { + override fun requestHandler(handler: LspRequestHandler): LspRequestHandler { + return handler + } + + override fun

notificationHandler(handler: LspNotificationHandler

): LspNotificationHandler

{ + return handler + } + } + } +} + +fun lspHandlers(builder: LspHandlersBuilder.() -> Unit): LspHandlers { + val requests = mutableMapOf>() + val notifications = mutableMapOf>() + + object : LspHandlersBuilder { override fun request( requestType: RequestType, - handler: suspend context(T) CoroutineScope.(Request) -> Response, + handler: suspend context(LspHandlerContext) CoroutineScope.(Request) -> Response, ) { requests[requestType.method] = LspRequestHandler(requestType, handler) } override fun notification( notifcationType: NotificationType, - handler: suspend context(T) CoroutineScope.(Notification) -> Unit, + handler: suspend context(LspHandlerContext) CoroutineScope.(Notification) -> Unit, ) { notifications[notifcationType.method] = LspNotificationHandler(notifcationType, handler) } @@ -95,13 +113,13 @@ fun lspHandlers(builder: LspHandlersBuilder.() -> Unit): LspHandlers { @Suppress("UNCHECKED_CAST") override fun requestHandler( requestType: String, - ): LspRequestHandler<*, *, *, T>? = + ): LspRequestHandler<*, *, *>? = requests[requestType] @Suppress("UNCHECKED_CAST") override fun notificationHandler( notificationType: String, - ): LspNotificationHandler<*, T>? = + ): LspNotificationHandler<*>? = notifications[notificationType] } } \ No newline at end of file diff --git a/fleet/lsp.protocol/src/main/com/jetbrains/lsp/implementation/protocolFraming.kt b/fleet/lsp.protocol/src/main/com/jetbrains/lsp/implementation/protocolFraming.kt index 3f095cb46155..f946f783ff0a 100644 --- a/fleet/lsp.protocol/src/main/com/jetbrains/lsp/implementation/protocolFraming.kt +++ b/fleet/lsp.protocol/src/main/com/jetbrains/lsp/implementation/protocolFraming.kt @@ -126,7 +126,7 @@ suspend fun withBaseProtocolFraming( } fun main() { - val handler = lspHandlers { + val handler = lspHandlers { request(Initialize) { initParams -> InitializeResult( capabilities = ServerCapabilities( @@ -148,7 +148,7 @@ fun main() { runBlocking(Dispatchers.Default) { tcpServer(TcpConnectionConfig.Server("127.0.0.1", 9999, isMultiClient = true)) { connection -> withBaseProtocolFraming(connection, exitSignal = null) { incoming, outgoing -> - withLsp(incoming, outgoing, handler, ::LspHandlerContext) { lsp -> + withLsp(incoming, outgoing, handler) { lsp -> awaitCancellation() } } diff --git a/fleet/lsp.protocol/src/main/com/jetbrains/lsp/implementation/withLsp.kt b/fleet/lsp.protocol/src/main/com/jetbrains/lsp/implementation/withLsp.kt index 04f0906605f8..2ccd68987933 100644 --- a/fleet/lsp.protocol/src/main/com/jetbrains/lsp/implementation/withLsp.kt +++ b/fleet/lsp.protocol/src/main/com/jetbrains/lsp/implementation/withLsp.kt @@ -33,13 +33,13 @@ private class OutgoingRequest( val requestType: RequestType<*, *, *>, ) -suspend fun withLsp( +suspend fun withLsp( incoming: ReceiveChannel, outgoing: SendChannel, handlers: LspHandlers, - createContext: (LspClient) -> T, + middleware: LspHandlersMiddleware = LspHandlersMiddleware.IDENTITY, createCoroutineContext: (LspClient) -> CoroutineContext = { EmptyCoroutineContext }, - body: suspend CoroutineScope.(T) -> Unit, + body: suspend CoroutineScope.(LspClient) -> Unit, ) { coroutineScope { val outgoingRequests = ConcurrentHashMap() @@ -87,7 +87,7 @@ suspend fun withLsp( } } - val lspHandlerContext = createContext(lspClient) + val lspHandlerContext = LspHandlerContext(lspClient) launch(createCoroutineContext(lspClient)) { withSupervisor { supervisor -> @@ -102,6 +102,7 @@ suspend fun withLsp( val request = LSP.json.decodeFromJsonElement(RequestMessage.serializer(), jsonMessage) supervisor.launch(start = CoroutineStart.ATOMIC) { val maybeHandler = handlers.requestHandler(request.method) + ?.let { handler -> middleware.requestHandler(handler)} runCatching { val handler = requireNotNull(maybeHandler) { "no handler for request: ${request.method}" @@ -109,7 +110,7 @@ suspend fun withLsp( val deserializedParams = request.params?.let { params -> LSP.json.decodeFromJsonElement(handler.requestType.paramsSerializer, params) } - val result = (handler as LspRequestHandler).handler( + val result = (handler as LspRequestHandler).handler( lspHandlerContext, this, deserializedParams @@ -241,15 +242,16 @@ suspend fun withLsp( else -> runCatching { - when (val handler = handlers.notificationHandler(notification.method)) { + when (val originalHandler = handlers.notificationHandler(notification.method)) { null -> LOG.debug("no handler for notification: ${notification.method}") else -> { + val handler = middleware.notificationHandler(originalHandler) val deserializedParams = notification.params?.let { params -> LSP.json.decodeFromJsonElement(handler.notificationType.paramsSerializer, params) } - (handler as LspNotificationHandler).handler(lspHandlerContext, this, deserializedParams) + (handler as LspNotificationHandler).handler(lspHandlerContext, this, deserializedParams) } } }.onFailure { error -> @@ -266,7 +268,7 @@ suspend fun withLsp( } } }.use { - body(lspHandlerContext) + body(lspHandlerContext.lspClient) } } } @@ -278,10 +280,11 @@ fun main() { val HelloRequestType = RequestType("hello", String.serializer(), String.serializer(), Unit.serializer()) val HangRequestType = RequestType("hand", Unit.serializer(), Unit.serializer(), Unit.serializer()) val PrintHelloNotification = NotificationType("printHello", String.serializer()) + withLsp( incoming = clientToServer, outgoing = serverToClient, - handlers = lspHandlers { + handlers = lspHandlers { request(HelloRequestType) { str -> "Hello, $str" } @@ -297,23 +300,21 @@ fun main() { } } }, - createContext = ::LspHandlerContext, ) { server -> withLsp( incoming = serverToClient, outgoing = clientToServer, - handlers = lspHandlers { + handlers = lspHandlers { notification(PrintHelloNotification) { str -> println("client: $str") } }, - createContext = ::LspHandlerContext, - ) { context -> - println(context.lspClient.request(HelloRequestType, "World")) - context.lspClient.notify(PrintHelloNotification, "Hello World") - context.lspClient.notify(PrintHelloNotification, "Hello World") + ) { client -> + println(client.request(HelloRequestType, "World")) + client.notify(PrintHelloNotification, "Hello World") + client.notify(PrintHelloNotification, "Hello World") val hangingRequestJob = launch { - context.lspClient.request(HangRequestType, Unit) + client.request(HangRequestType, Unit) } delay(100) hangingRequestJob.cancel()