mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[lsp] remove the Context generic in handlers
The LSP library was earlier augmented to be generic over the handler context (the one that is supplied to request and notification handlers). However, this makes handlers potentially incompatible with each other and hinders composition. After a review it was decided to remove this generic parameter. GitOrigin-RevId: 8902b147f0027425e47b2188fe4d9781b77bbe07
This commit is contained in:
committed by
intellij-monorepo-bot
parent
98090b220c
commit
c38b281fec
@@ -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<T> {
|
||||
interface LspHandlersBuilder {
|
||||
fun <Request, Response, Error> request(
|
||||
requestType: RequestType<Request, Response, Error>,
|
||||
handler: suspend context(T) CoroutineScope.(Request) -> Response,
|
||||
handler: suspend context(LspHandlerContext) CoroutineScope.(Request) -> Response,
|
||||
)
|
||||
|
||||
fun <Notification> notification(
|
||||
notifcationType: NotificationType<Notification>,
|
||||
handler: suspend context(T) CoroutineScope.(Notification) -> Unit,
|
||||
handler: suspend context(LspHandlerContext) CoroutineScope.(Notification) -> Unit,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -61,31 +61,49 @@ fun LspClient.reportProgressMessage(
|
||||
}
|
||||
|
||||
|
||||
class LspRequestHandler<Params, Result, Error, Context>(
|
||||
class LspRequestHandler<Params, Result, Error>(
|
||||
val requestType: RequestType<Params, Result, Error>,
|
||||
val handler: suspend context(Context) CoroutineScope.(Params) -> Result,
|
||||
val handler: suspend context(LspHandlerContext) CoroutineScope.(Params) -> Result,
|
||||
)
|
||||
|
||||
class LspNotificationHandler<Params, Context>(
|
||||
class LspNotificationHandler<Params>(
|
||||
val notificationType: NotificationType<Params>,
|
||||
val handler: suspend context(Context) CoroutineScope.(Params) -> Unit,
|
||||
val handler: suspend context(LspHandlerContext) CoroutineScope.(Params) -> Unit,
|
||||
)
|
||||
|
||||
fun <T> lspHandlers(builder: LspHandlersBuilder<T>.() -> Unit): LspHandlers {
|
||||
val requests = mutableMapOf<String, LspRequestHandler<*, *, *, T>>()
|
||||
val notifications = mutableMapOf<String, LspNotificationHandler<*, T>>()
|
||||
interface LspHandlersMiddleware {
|
||||
fun <P, R, E> requestHandler(handler: LspRequestHandler<P, R, E>): LspRequestHandler<P, R, E>
|
||||
|
||||
object : LspHandlersBuilder<T> {
|
||||
fun <P> notificationHandler(handler: LspNotificationHandler<P>): LspNotificationHandler<P>
|
||||
|
||||
companion object {
|
||||
val IDENTITY: LspHandlersMiddleware = object : LspHandlersMiddleware {
|
||||
override fun <P, R, E> requestHandler(handler: LspRequestHandler<P, R, E>): LspRequestHandler<P, R, E> {
|
||||
return handler
|
||||
}
|
||||
|
||||
override fun <P> notificationHandler(handler: LspNotificationHandler<P>): LspNotificationHandler<P> {
|
||||
return handler
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun lspHandlers(builder: LspHandlersBuilder.() -> Unit): LspHandlers {
|
||||
val requests = mutableMapOf<String, LspRequestHandler<*, *, *>>()
|
||||
val notifications = mutableMapOf<String, LspNotificationHandler<*>>()
|
||||
|
||||
object : LspHandlersBuilder {
|
||||
override fun <Request, Response, Error> request(
|
||||
requestType: RequestType<Request, Response, Error>,
|
||||
handler: suspend context(T) CoroutineScope.(Request) -> Response,
|
||||
handler: suspend context(LspHandlerContext) CoroutineScope.(Request) -> Response,
|
||||
) {
|
||||
requests[requestType.method] = LspRequestHandler(requestType, handler)
|
||||
}
|
||||
|
||||
override fun <Notification> notification(
|
||||
notifcationType: NotificationType<Notification>,
|
||||
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 <T> lspHandlers(builder: LspHandlersBuilder<T>.() -> 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]
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ suspend fun withBaseProtocolFraming(
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val handler = lspHandlers<LspHandlerContext> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,13 +33,13 @@ private class OutgoingRequest(
|
||||
val requestType: RequestType<*, *, *>,
|
||||
)
|
||||
|
||||
suspend fun <T> withLsp(
|
||||
suspend fun withLsp(
|
||||
incoming: ReceiveChannel<JsonElement>,
|
||||
outgoing: SendChannel<JsonElement>,
|
||||
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<StringOrInt, OutgoingRequest>()
|
||||
@@ -87,7 +87,7 @@ suspend fun <T> withLsp(
|
||||
}
|
||||
}
|
||||
|
||||
val lspHandlerContext = createContext(lspClient)
|
||||
val lspHandlerContext = LspHandlerContext(lspClient)
|
||||
|
||||
launch(createCoroutineContext(lspClient)) {
|
||||
withSupervisor { supervisor ->
|
||||
@@ -102,6 +102,7 @@ suspend fun <T> 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 <T> withLsp(
|
||||
val deserializedParams = request.params?.let { params ->
|
||||
LSP.json.decodeFromJsonElement(handler.requestType.paramsSerializer, params)
|
||||
}
|
||||
val result = (handler as LspRequestHandler<Any?, Any?, Any?, T>).handler(
|
||||
val result = (handler as LspRequestHandler<Any?, Any?, Any?>).handler(
|
||||
lspHandlerContext,
|
||||
this,
|
||||
deserializedParams
|
||||
@@ -241,15 +242,16 @@ suspend fun <T> 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<Any?, T>).handler(lspHandlerContext, this, deserializedParams)
|
||||
(handler as LspNotificationHandler<Any?>).handler(lspHandlerContext, this, deserializedParams)
|
||||
}
|
||||
}
|
||||
}.onFailure { error ->
|
||||
@@ -266,7 +268,7 @@ suspend fun <T> 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<LspHandlerContext> {
|
||||
handlers = lspHandlers {
|
||||
request(HelloRequestType) { str ->
|
||||
"Hello, $str"
|
||||
}
|
||||
@@ -297,23 +300,21 @@ fun main() {
|
||||
}
|
||||
}
|
||||
},
|
||||
createContext = ::LspHandlerContext,
|
||||
) { server ->
|
||||
withLsp(
|
||||
incoming = serverToClient,
|
||||
outgoing = clientToServer,
|
||||
handlers = lspHandlers<LspHandlerContext> {
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user