Subset of LSP - Part 1 (#453)

* Rename language-server -> runner, add language-server, gateway

* Add entry point --lsp

* Add Gateway, LanguageServer

* Add stab for Initialize

* Add stubs for Initialize, Initialized

* Add GatewayTest

* fix PR

* increase timeout

* merge with master

* fix compilation after merge

* reformat with scalafmt

* Add TODOs

* Add doc for gateway

* Update CONTRIBUTING.md

* Refactor code for PR

* Add Request#response(..)

* Make Initialize, Initialized extractor objects

* Refactor for -> map

* Fix docs

* Remove DerivationConfig

* Make Request, Notification polymorphic

* Add Param.Array, Params.Array

* Replace Decoder#apply -> tryDecode

* Refactor code

* Add docs

* Refactor code

* Refactor code

* Refactor code

* Make gateway a pure actor

* Add client capabilities

* Add server capabilities

* Add docs for capabilities

* Add docs

* Add docs

* Fix Server.Config

* Update doc for Server

* Add requests, notifications and params

* Improve PR

* Rename Protocol -> JsonRpcController

* Add docs

* Add requests and notifications

* Fix Result

* Add requests and notifications

* Add WillSaveTextDocumentWaitUntil request params and result

* Add params

* Add tests for requests

* Add textDocumentSync.willSaveWaitUntil server capability

* Handle text id

* Fix Edit workspace client capability

* Clean up

* Add initialize, initialized, shutdown, exit

* Add docs

* Fix identation

* Fix identation

* Refactor code

* Add docs

* Improve PR

* Add tests

* Fix docs for RequestExtractor

* Fix docs
This commit is contained in:
Dmytro Mitin 2020-01-27 13:23:32 +02:00 committed by GitHub
parent 175212bf4c
commit 3412606e2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
111 changed files with 1941 additions and 376 deletions

View File

@ -485,9 +485,9 @@ lazy val gateway = (project in file("engine/gateway"))
.dependsOn(language_server) .dependsOn(language_server)
.settings( .settings(
libraryDependencies ++= akka ++ circe ++ Seq( libraryDependencies ++= akka ++ circe ++ Seq(
akkaTestkit % Test,
"io.circe" %% "circe-generic-extras" % "0.12.2", "io.circe" %% "circe-generic-extras" % "0.12.2",
"io.circe" %% "circe-literal" % circeVersion, "io.circe" %% "circe-literal" % circeVersion,
akkaTestkit % Test,
"org.scalatest" %% "scalatest" % "3.2.0-SNAP10" % Test, "org.scalatest" %% "scalatest" % "3.2.0-SNAP10" % Test,
"org.scalacheck" %% "scalacheck" % "1.14.0" % Test "org.scalacheck" %% "scalacheck" % "1.14.0" % Test
) )
@ -496,7 +496,10 @@ lazy val gateway = (project in file("engine/gateway"))
lazy val language_server = (project in file("engine/language-server")) lazy val language_server = (project in file("engine/language-server"))
.settings( .settings(
libraryDependencies ++= akka ++ Seq( libraryDependencies ++= akka ++ Seq(
"org.graalvm.sdk" % "polyglot-tck" % graalVersion % Provided "org.graalvm.sdk" % "polyglot-tck" % graalVersion % Provided,
akkaTestkit % Test,
"org.scalatest" %% "scalatest" % "3.2.0-SNAP10" % Test,
"org.scalacheck" %% "scalacheck" % "1.14.0" % Test
) )
) )
.dependsOn(polyglot_api) .dependsOn(polyglot_api)

View File

@ -3,7 +3,9 @@ gateway {
host = "localhost" host = "localhost"
port = 30000 port = 30000
route = "" route = ""
timeout = 5 timeoutSecs = 5
bindingTimeoutSecs = 5
hardDeadlineSecs = 5
} }
languageServer { languageServer {

View File

@ -2,46 +2,82 @@ package org.enso
import akka.actor.{Actor, ActorLogging, ActorRef, Props} import akka.actor.{Actor, ActorLogging, ActorRef, Props}
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import org.enso.gateway.protocol.response.Result.InitializeResult import org.enso.gateway.protocol.response.Result.{InitializeResult, NullResult}
import org.enso.gateway.protocol.{Id, Notifications, Requests, Response} import org.enso.gateway.protocol.{Id, Notifications, Requests, Response}
import org.enso.gateway.protocol.response.result.{ import org.enso.gateway.protocol.response.result.{
ServerCapabilities, ServerCapabilities,
ServerInfo ServerInfo
} }
import org.enso.languageserver.{
NotificationReceived,
RequestReceived,
Notifications => LsNotifications,
Requests => LsRequests
}
/** The gateway component talks directly to clients using protocol messages, /** The Gateway component of Enso Engine.
* and then handles these messages by talking to the language server. *
* Talks directly to clients using protocol messages, and then handles these
* messages by talking to the language server.
* *
* @param languageServer [[ActorRef]] of [[LanguageServer]] actor. * @param languageServer [[ActorRef]] of [[LanguageServer]] actor.
*/ */
class Gateway(languageServer: ActorRef) extends Actor with ActorLogging { class Gateway(languageServer: ActorRef) extends Actor with ActorLogging {
override def receive: Receive = { override def receive: Receive = {
case Requests.Initialize(Id.Number(id), _) => case Requests.Initialize(id, _) =>
val msg = "Gateway: Initialize received" val msg = "Gateway: Initialize received"
log.info(msg) log.info(msg)
languageServer ! LanguageServer.Initialize(id, sender()) languageServer ! LsRequests.Initialize(
id.toLsModel,
sender()
)
case LanguageServer.InitializeReceived(id, replyTo) => case RequestReceived.Initialize(id, replyTo) =>
val msg = "Gateway: InitializeReceived received" val msg = "Gateway: RequestReceived.Initialize received"
log.info(msg) log.info(msg)
replyTo ! Response.result( replyTo ! Response.result(
id = Some(Id.Number(id)), id = Some(Id.fromLsModel(id)),
result = InitializeResult(ServerCapabilities(), Some(serverInfo)) result = InitializeResult(
capabilities = ServerCapabilities(),
serverInfo = Some(serverInfo)
)
)
case Requests.Shutdown(id, _) =>
val msg = "Gateway: Shutdown received"
log.info(msg)
languageServer ! LsRequests.Shutdown(id.toLsModel, sender())
case RequestReceived.Shutdown(id, replyTo) =>
val msg = "Gateway: RequestReceived.Shutdown received"
log.info(msg)
replyTo ! Response.result(
id = Some(Id.fromLsModel(id)),
result = NullResult
) )
case Notifications.Initialized(_) => case Notifications.Initialized(_) =>
val msg = "Gateway: Initialized received" val msg = "Gateway: Initialized received"
log.info(msg) log.info(msg)
languageServer ! LanguageServer.Initialized languageServer ! LsNotifications.Initialized
case LanguageServer.InitializedReceived => case NotificationReceived.Initialized =>
val msg = "Gateway: InitializedReceived received" val msg = "Gateway: NotificationReceived.Initialized received"
log.info(msg)
case Notifications.Exit(_) =>
val msg = "Gateway: Exit received"
log.info(msg)
languageServer ! LsNotifications.Exit
case NotificationReceived.Exit =>
val msg = "Gateway: NotificationReceived.Exit received"
log.info(msg) log.info(msg)
case requestOrNotification => case requestOrNotification =>
val err = val err = "Gateway: unimplemented request or notification: " +
s"unimplemented request or notification: $requestOrNotification" requestOrNotification
throw new Exception(err) log.error(err)
} }
private val serverInfo: ServerInfo = { private val serverInfo: ServerInfo = {
@ -51,8 +87,10 @@ class Gateway(languageServer: ActorRef) extends Actor with ActorLogging {
val languageServerVersionPath = "version" val languageServerVersionPath = "version"
val gatewayConfig = ConfigFactory.load.getConfig(gatewayPath) val gatewayConfig = ConfigFactory.load.getConfig(gatewayPath)
val languageServerConfig = gatewayConfig.getConfig(languageServerPath) val languageServerConfig = gatewayConfig.getConfig(languageServerPath)
val name = languageServerConfig.getString(languageServerNamePath) val name =
val version = languageServerConfig.getString(languageServerVersionPath) languageServerConfig.getString(languageServerNamePath)
val version =
languageServerConfig.getString(languageServerVersionPath)
ServerInfo(name, Some(version)) ServerInfo(name, Some(version))
} }
} }

View File

@ -23,6 +23,7 @@ import scala.concurrent.Future
object JsonRpcController { object JsonRpcController {
/** A string specifying the version of the JSON-RPC protocol. /** A string specifying the version of the JSON-RPC protocol.
*
* Must be exactly "2.0". * Must be exactly "2.0".
* *
* @see LSP Spec: * @see LSP Spec:
@ -33,6 +34,7 @@ object JsonRpcController {
} }
/** Helper for implementing protocol over text-based transport. /** Helper for implementing protocol over text-based transport.
*
* Requests and responses are marshaled as text using JSON-RPC. * Requests and responses are marshaled as text using JSON-RPC.
* It handles and decodes all JSON-RPC messages and dispatch them to the * It handles and decodes all JSON-RPC messages and dispatch them to the
* Gateway. * Gateway.
@ -40,10 +42,9 @@ object JsonRpcController {
* @param gateway [[ActorRef]] of Gateway actor. * @param gateway [[ActorRef]] of Gateway actor.
*/ */
class JsonRpcController(gateway: ActorRef)(implicit system: ActorSystem) { class JsonRpcController(gateway: ActorRef)(implicit system: ActorSystem) {
import system.dispatcher import system.dispatcher
/** Generate text reply for given request text message, no reply for /** Generates text reply for given request text message, no reply for
* notification. * notification.
*/ */
def getTextOutput( def getTextOutput(

View File

@ -4,9 +4,7 @@ import akka.NotUsed
import akka.actor.ActorSystem import akka.actor.ActorSystem
import akka.event.{Logging, LoggingAdapter} import akka.event.{Logging, LoggingAdapter}
import akka.http.scaladsl.Http import akka.http.scaladsl.Http
import akka.http.scaladsl.model.ws.BinaryMessage import akka.http.scaladsl.model.ws.{BinaryMessage, Message, TextMessage}
import akka.http.scaladsl.model.ws.Message
import akka.http.scaladsl.model.ws.TextMessage
import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route import akka.http.scaladsl.server.Route
import akka.stream.ActorMaterializer import akka.stream.ActorMaterializer
@ -14,54 +12,16 @@ import akka.stream.scaladsl.Flow
import akka.stream.scaladsl.Sink import akka.stream.scaladsl.Sink
import akka.stream.scaladsl.Source import akka.stream.scaladsl.Source
import akka.util.Timeout import akka.util.Timeout
import com.typesafe.config.{Config, ConfigFactory} import org.enso.gateway.server.Config
import scala.concurrent.duration._ import scala.concurrent.{Await, Future}
import scala.util.Failure import scala.util.Failure
import scala.util.Success import scala.util.Success
object Server {
/** Describes endpoint to which [[Server]] can bind (host, port, route) and
* timeout for waiting response.
*
* Gets parameters from typesafe config.
*/
object Config {
private val gatewayPath = "gateway"
private val serverPath = "server"
private val hostPath = "host"
private val portPath = "port"
private val routePath = "route"
private val timeoutPath = "timeout"
private val gatewayConfig: Config =
ConfigFactory.load.getConfig(gatewayPath)
private val serverConfig: Config = gatewayConfig.getConfig(serverPath)
/** Host of endpoint. */
val host: String = serverConfig.getString(hostPath)
/** Port of endpoint. */
val port: Int = serverConfig.getInt(portPath)
/** Route of endpoint. */
val route: String = serverConfig.getString(routePath)
/** Timeout for waiting response after request. */
implicit val timeout: Timeout = Timeout(
serverConfig.getLong(timeoutPath).seconds
)
/** Creates address string. */
val addressString: String = s"ws://$host:$port"
}
}
/** WebSocket server supporting synchronous request-response protocol. /** WebSocket server supporting synchronous request-response protocol.
* *
* Server when run binds to endpoint and accepts establishing web socket * Server when run binds to endpoint and accepts establishing web socket
* connection for any number of peers. * connection for any number of peers.
*
* Server replies to each incoming text request with a single text response, * Server replies to each incoming text request with a single text response,
* no response for notifications. * no response for notifications.
* Server accepts a single Text Message from a peer and responds with another * Server accepts a single Text Message from a peer and responds with another
@ -69,16 +29,16 @@ object Server {
* *
* @param jsonRpcController Encapsulates encoding JSONs and talking to * @param jsonRpcController Encapsulates encoding JSONs and talking to
* [[org.enso.Gateway]]. * [[org.enso.Gateway]].
* @param config Server config.
*/ */
class Server(jsonRpcController: JsonRpcController)( class Server(jsonRpcController: JsonRpcController, config: Config)(
implicit implicit
system: ActorSystem, system: ActorSystem,
materializer: ActorMaterializer materializer: ActorMaterializer
) { ) {
import system.dispatcher import system.dispatcher
import Server.Config.timeout
implicit private val timeout: Timeout = Timeout(config.timeout)
private val log: LoggingAdapter = Logging.getLogger(system, this) private val log: LoggingAdapter = Logging.getLogger(system, this)
/** Akka stream defining server behavior. /** Akka stream defining server behavior.
@ -88,7 +48,7 @@ class Server(jsonRpcController: JsonRpcController)(
* @see [[JsonRpcController.getTextOutput]]. * @see [[JsonRpcController.getTextOutput]].
* Incoming binary messages are ignored. * Incoming binary messages are ignored.
*/ */
val handlerFlow: Flow[Message, TextMessage.Strict, NotUsed] = private val handlerFlow: Flow[Message, TextMessage.Strict, NotUsed] =
Flow[Message] Flow[Message]
.flatMapConcat { .flatMapConcat {
case tm: TextMessage => case tm: TextMessage =>
@ -117,31 +77,31 @@ class Server(jsonRpcController: JsonRpcController)(
* *
* The request's URI is not checked. * The request's URI is not checked.
*/ */
val route: Route = private val route: Route =
path(Server.Config.route) { path(config.route) {
get { get {
handleWebSocketMessages(handlerFlow) handleWebSocketMessages(handlerFlow)
} }
} }
private val bindingFuture: Future[Http.ServerBinding] =
Http().bindAndHandle(
handler = route,
interface = config.host,
port = config.port
)
/** Starts a HTTP server listening at the given endpoint. /** Starts a HTTP server listening at the given endpoint.
* *
* Function is asynchronous, will return immediately. If the server fails to * Function is asynchronous, will return immediately. If the server fails to
* start, function will exit the process with a non-zero code. * start, function will exit the process with a non-zero code.
*/ */
def run(): Unit = { def run(): Unit = {
val bindingFuture =
Http().bindAndHandle(
handler = route,
interface = Server.Config.host,
port = Server.Config.port
)
bindingFuture bindingFuture
.onComplete { .onComplete {
case Success(_) => case Success(_) =>
val serverOnlineMessage = val serverOnlineMessage =
s"Server online at ${Server.Config.addressString}" s"Server online at ${config.addressString}"
val shutDownMessage = "Press ENTER to shut down" val shutDownMessage = "Press ENTER to shut down"
Seq( Seq(
serverOnlineMessage, serverOnlineMessage,
@ -154,4 +114,11 @@ class Server(jsonRpcController: JsonRpcController)(
System.exit(1) System.exit(1)
} }
} }
/** Stops the HTTP server gracefully. */
def shutdown(): Future[Http.HttpTerminated] = {
Await
.result(bindingFuture, config.bindingTimeout)
.terminate(hardDeadline = config.hardDeadline)
}
} }

View File

@ -0,0 +1,65 @@
package org.enso.gateway.protocol
import io.circe.{Decoder, Encoder}
/** Kind of
* [[org.enso.gateway.protocol.request.clientcapabilities.textdocument.CodeAction]].
*
* Used also in
* [[org.enso.gateway.protocol.response.result.servercapabilities.CodeActionProvider]].
*/
sealed abstract class CodeActionKind(val value: String)
object CodeActionKind {
private val empty = ""
private val quickfix = "quickfix"
private val refactor = "refactor"
private val refactorExtract = "refactor.extract"
private val refactorInline = "refactor.inline"
private val refactorRewrite = "refactor.rewrite"
private val source = "source"
private val sourceOrganizeImports = "source.organizeImports"
private val invalidCodeActionKind = "Invalid CodeActionKind"
/** Empty kind. */
case object Empty extends CodeActionKind(empty)
/** Base kind for quickfix actions. */
case object QuickFix extends CodeActionKind(quickfix)
/** Base kind for refactoring actions. */
case object Refactor extends CodeActionKind(refactor)
/** Base kind for refactoring extraction actions. */
case object RefactorExtract extends CodeActionKind(refactorExtract)
/** Base kind for refactoring inline actions. */
case object RefactorInline extends CodeActionKind(refactorInline)
/** Base kind for refactoring rewrite actions. */
case object RefactorRewrite extends CodeActionKind(refactorRewrite)
/** Base kind for source actions. Source code actions apply to the entire
* file.
*/
case object Source extends CodeActionKind(source)
/** Base kind for an organize imports source action. */
case object SourceOrganizeImports
extends CodeActionKind(sourceOrganizeImports)
implicit val codeActionKindDecoder: Decoder[CodeActionKind] =
Decoder.decodeString.emap {
case `empty` => Right(Empty)
case `quickfix` => Right(QuickFix)
case `refactor` => Right(Refactor)
case `refactorExtract` => Right(RefactorExtract)
case `refactorInline` => Right(RefactorInline)
case `refactorRewrite` => Right(RefactorRewrite)
case `source` => Right(Source)
case `sourceOrganizeImports` => Right(SourceOrganizeImports)
case _ => Left(invalidCodeActionKind)
}
implicit val codeActionKindEncoder: Encoder[CodeActionKind] =
Encoder.encodeString.contramap(_.value)
}

View File

@ -7,10 +7,21 @@ import io.circe.generic.extras.semiauto.{
} }
import cats.syntax.functor._ import cats.syntax.functor._
import io.circe.syntax._ import io.circe.syntax._
import org.enso.languageserver
/** Id of [[RequestOrNotification]] or [[Response]]. */ /** Id of [[RequestOrNotification]] or [[Response]]. */
sealed trait Id sealed trait Id {
def toLsModel: languageserver.Id = this match {
case Id.Number(value) => languageserver.Id.Number(value)
case Id.Text(value) => languageserver.Id.Text(value)
}
}
object Id { object Id {
def fromLsModel(id: languageserver.Id): Id = id match {
case languageserver.Id.Number(value) => Id.Number(value)
case languageserver.Id.Text(value) => Id.Text(value)
}
implicit val idEncoder: Encoder[Id] = Encoder.instance { implicit val idEncoder: Encoder[Id] = Encoder.instance {
case number: Number => number.asJson case number: Number => number.asJson
case text: Text => text.asJson case text: Text => text.asJson

View File

@ -3,15 +3,12 @@ package org.enso.gateway.protocol
import io.circe.{ACursor, Decoder, DecodingFailure} import io.circe.{ACursor, Decoder, DecodingFailure}
import org.enso.gateway.JsonRpcController.jsonRpcVersion import org.enso.gateway.JsonRpcController.jsonRpcVersion
import org.enso.gateway.protocol.request.Params import org.enso.gateway.protocol.request.Params
import org.enso.gateway.protocol.request.Params.{ import org.enso.gateway.protocol.request.Params.{InitializeParams, VoidParams}
InitializeParams,
InitializedParams
}
/** Helper object for decoding [[Notification]]. */ /** Helper object for decoding [[Notification]]. */
object NotificationDecoder { object NotificationDecoder {
/** Make Circe decoder for notifications and notification fields of requests. /** Makes Circe decoder for notifications and notification fields of requests.
* *
* @tparam P Subtype of [[Params]] for a notification with specific method. * @tparam P Subtype of [[Params]] for a notification with specific method.
* @return the Circe decoder. * @return the Circe decoder.
@ -38,9 +35,11 @@ object NotificationDecoder {
(method match { (method match {
case Requests.Initialize.method => case Requests.Initialize.method =>
Decoder[Option[InitializeParams]] Decoder[Option[InitializeParams]]
case Requests.Shutdown.method =>
Decoder[Option[VoidParams]]
case Notifications.Initialized.method => case Notifications.Initialized.method | Notifications.Exit.method =>
Decoder[Option[InitializedParams]] Decoder[Option[VoidParams]]
case m => case m =>
Decoder.failed( Decoder.failed(

View File

@ -1,12 +1,13 @@
package org.enso.gateway.protocol package org.enso.gateway.protocol
import org.enso.gateway.protocol.request.Params import org.enso.gateway.protocol.request.Params
import org.enso.gateway.protocol.request.Params.VoidParams
/** Parent trait for notifications extractor objects. */ /** Parent class for notification extractor objects. */
sealed trait Notifications { sealed abstract class NotificationExtractor[T <: Params](
val method: String val method: String
) {
def unapply[T <: Params]( def unapply(
request: Notification[T] request: Notification[T]
): Option[Option[T]] = ): Option[Option[T]] =
request.method match { request.method match {
@ -19,10 +20,20 @@ sealed trait Notifications {
/** All notifications. */ /** All notifications. */
object Notifications { object Notifications {
/** LSP Spec: /** Sent from the client to the server after the client received the result of
* the initialize request but before the client is sending any other request
* or notification to the server.
*
* LSP Spec:
* https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#initialized * https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#initialized
*/ */
object Initialized extends Notifications { object Initialized extends NotificationExtractor[VoidParams]("initialized")
override val method = "initialized"
} /** Asks the server to exit its process.
*
* LSP Spec:
* https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#exit
*/
object Exit extends NotificationExtractor[VoidParams]("exit")
} }

View File

@ -3,12 +3,10 @@ package org.enso.gateway.protocol
import io.circe.Decoder import io.circe.Decoder
import org.enso.gateway.protocol.request.Params import org.enso.gateway.protocol.request.Params
/** /** Helper object for decoding [[Request]]. */
* Helper object for decoding [[Request]].
*/
object RequestDecoder { object RequestDecoder {
/** Make Circe decoder for requests. /** Makes Circe decoder for requests.
* *
* @tparam P Subtype of [[Params]] for a request with specific method. * @tparam P Subtype of [[Params]] for a request with specific method.
* @return The decoder. * @return The decoder.

View File

@ -3,9 +3,7 @@ package org.enso.gateway.protocol
import io.circe.Decoder import io.circe.Decoder
import org.enso.gateway.protocol.request.Params import org.enso.gateway.protocol.request.Params
/** /** Parent trait for [[Request]] and [[Notification]]. */
* Parent trait for [[Request]] and [[Notification]]
*/
sealed trait RequestOrNotification { sealed trait RequestOrNotification {
/** JSON-RPC Version. /** JSON-RPC Version.
@ -22,7 +20,8 @@ object RequestOrNotification {
RequestOrNotificationDecoder.instance RequestOrNotificationDecoder.instance
} }
/** `RequestMessage` in LSP Spec: /** `RequestMessage` in LSP Spec.
*
* https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#requestMessage * https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#requestMessage
* *
* @param jsonrpc JSON-RPC Version * @param jsonrpc JSON-RPC Version
@ -38,20 +37,18 @@ case class Request[P <: Params](
method: String, method: String,
params: Option[P] params: Option[P]
) extends RequestOrNotification ) extends RequestOrNotification
object Request { object Request {
/** Field `id`. */
val idField = "id" val idField = "id"
implicit def requestDecoder[T <: Params]: Decoder[Request[T]] = implicit def requestDecoder[T <: Params]: Decoder[Request[T]] =
RequestDecoder.instance RequestDecoder.instance
} }
/** `NotificationMessage` in LSP Spec: /** `NotificationMessage` in LSP Spec.
* https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#notificationMessage *
* A processed notification message must not send a response back (they work * A processed notification message must not send a response back (they work
* like events). Therefore no `id`. * like events). Therefore no `id`.
* https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#notificationMessage
* *
* @param jsonrpc JSON-RPC Version. * @param jsonrpc JSON-RPC Version.
* @param method The JSON-RPC method to be invoked. * @param method The JSON-RPC method to be invoked.
@ -66,14 +63,8 @@ case class Notification[P <: Params](
params: Option[P] params: Option[P]
) extends RequestOrNotification ) extends RequestOrNotification
object Notification { object Notification {
/** Field `jsonrpc` to be validated. */
val jsonrpcField = "jsonrpc" val jsonrpcField = "jsonrpc"
/** Field `method`, which is discriminator. */
val methodField = "method" val methodField = "method"
/** Field `params`. */
val paramsField = "params" val paramsField = "params"
implicit def notificationDecoder[P <: Params]: Decoder[Notification[P]] = implicit def notificationDecoder[P <: Params]: Decoder[Notification[P]] =

View File

@ -2,10 +2,7 @@ package org.enso.gateway.protocol
import io.circe.CursorOp.DownField import io.circe.CursorOp.DownField
import io.circe.{Decoder, DecodingFailure} import io.circe.{Decoder, DecodingFailure}
import org.enso.gateway.protocol.request.Params.{ import org.enso.gateway.protocol.request.Params.{InitializeParams, VoidParams}
InitializeParams,
InitializedParams
}
/** Helper object for decoding [[RequestOrNotification]]. */ /** Helper object for decoding [[RequestOrNotification]]. */
object RequestOrNotificationDecoder { object RequestOrNotificationDecoder {
@ -19,7 +16,7 @@ object RequestOrNotificationDecoder {
.flatMap(selectRequestOrNotificationDecoder(_).apply(cursor)) .flatMap(selectRequestOrNotificationDecoder(_).apply(cursor))
} }
/** Make Circe failure if method is unknown. /** Makes Circe failure if method is unknown.
* *
* @param method Name of method. * @param method Name of method.
* @return The failure. * @return The failure.
@ -36,9 +33,11 @@ object RequestOrNotificationDecoder {
method match { method match {
case Requests.Initialize.method => case Requests.Initialize.method =>
Decoder[Request[InitializeParams]] Decoder[Request[InitializeParams]]
case Requests.Shutdown.method =>
Decoder[Request[VoidParams]]
case Notifications.Initialized.method => case Notifications.Initialized.method | Notifications.Exit.method =>
Decoder[Notification[InitializedParams]] Decoder[Notification[VoidParams]]
case m => case m =>
Decoder.failed( Decoder.failed(

View File

@ -1,14 +1,16 @@
package org.enso.gateway.protocol package org.enso.gateway.protocol
import org.enso.gateway.protocol.request.Params import org.enso.gateway.protocol.request.Params
import org.enso.gateway.protocol.request.Params.{InitializeParams, VoidParams}
/** Parent trait for requests extractor objects. */ /** Parent trait for request (Scala) extractor objects.
sealed trait Requests { *
* Simplifies matching in [[org.enso.Gateway.receive()]].
*/
sealed abstract class RequestExtractor[T <: Params](
val method: String val method: String
) {
def unapply[T <: Params]( def unapply(request: Request[T]): Option[(Id, Option[T])] =
request: Request[T]
): Option[(Id, Option[T])] =
request.method match { request.method match {
case `method` => case `method` =>
Some((request.id, request.params)) Some((request.id, request.params))
@ -19,11 +21,19 @@ sealed trait Requests {
/** All requests. */ /** All requests. */
object Requests { object Requests {
/** LSP Spec: /** The request sent as the first request from the client to the server.
*
* LSP Spec:
* https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#initialize * https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#initialize
*/ */
object Initialize extends Requests { object Initialize extends RequestExtractor[InitializeParams]("initialize")
override val method = "initialize"
} /** Asks the server to shut down, but to not exit (otherwise the response
* might not be delivered correctly to the client).
*
* LSP Spec:
* https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#shutdown
*/
object Shutdown extends RequestExtractor[VoidParams]("shutdown")
} }

View File

@ -5,7 +5,8 @@ import io.circe.generic.semiauto.deriveEncoder
import org.enso.gateway.JsonRpcController.jsonRpcVersion import org.enso.gateway.JsonRpcController.jsonRpcVersion
import org.enso.gateway.protocol.response.{ResponseError, Result} import org.enso.gateway.protocol.response.{ResponseError, Result}
/** `ResponseMessage` in LSP Spec: /** `ResponseMessage` in LSP Spec.
*
* https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#responseMessage * https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#responseMessage
* *
* @param jsonrpc JSON-RPC Version. * @param jsonrpc JSON-RPC Version.
@ -20,10 +21,9 @@ case class Response private (
result: Option[Result], result: Option[Result],
error: Option[ResponseError] error: Option[ResponseError]
) )
object Response { object Response {
/** Create response with a result. /** Creates response with a result.
* *
* @param id Id of request. * @param id Id of request.
* @param result [[Result]] of response. * @param result [[Result]] of response.
@ -35,7 +35,7 @@ object Response {
): Response = ): Response =
Response(jsonRpcVersion, id, Some(result), None) Response(jsonRpcVersion, id, Some(result), None)
/** Create response with an error. /** Creates response with an error.
* *
* @param id Id of request. * @param id Id of request.
* @param error [[ResponseError]] of response. * @param error [[ResponseError]] of response.

View File

@ -7,6 +7,7 @@ import io.circe.generic.extras.semiauto.{
} }
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
import cats.syntax.functor._ import cats.syntax.functor._
import org.enso.gateway.protocol.request.Params.DocumentUri
/** An element of [[Params.Array]]. */ /** An element of [[Params.Array]]. */
sealed trait Param sealed trait Param
@ -53,7 +54,7 @@ object Param {
* *
* @see [[org.enso.gateway.protocol.request.Params.InitializeParams]]. * @see [[org.enso.gateway.protocol.request.Params.InitializeParams]].
*/ */
case class InitializationOptions(value: Text) extends Param case class InitializationOptions(value: String) extends Param
object InitializationOptions { object InitializationOptions {
implicit val initializationOptionsDecoder: Decoder[InitializationOptions] = implicit val initializationOptionsDecoder: Decoder[InitializationOptions] =
deriveUnwrappedDecoder deriveUnwrappedDecoder
@ -64,8 +65,8 @@ object Param {
* @see [[org.enso.gateway.protocol.request.Params.InitializeParams]]. * @see [[org.enso.gateway.protocol.request.Params.InitializeParams]].
*/ */
case class ClientInfo( case class ClientInfo(
name: Text, name: String,
version: Option[Text] version: Option[String]
) extends Param ) extends Param
object ClientInfo { object ClientInfo {
implicit val clientInfoDecoder: Decoder[ClientInfo] = deriveDecoder implicit val clientInfoDecoder: Decoder[ClientInfo] = deriveDecoder
@ -73,8 +74,9 @@ object Param {
/** A param of the request [[org.enso.gateway.protocol.Requests.Initialize]]. /** A param of the request [[org.enso.gateway.protocol.Requests.Initialize]].
* *
* @see [[org.enso.gateway.protocol.request.Params.InitializeParams]].
* The initial trace setting. * The initial trace setting.
*
* @see [[org.enso.gateway.protocol.request.Params.InitializeParams]].
*/ */
sealed trait Trace extends Param sealed trait Trace extends Param
object Trace { object Trace {
@ -91,26 +93,22 @@ object Param {
* *
* @see [[org.enso.gateway.protocol.request.Params.InitializeParams]]. * @see [[org.enso.gateway.protocol.request.Params.InitializeParams]].
*/ */
sealed trait WorkspaceFolder extends Param case class WorkspaceFolder(
uri: DocumentUri,
name: String
) extends Param
object WorkspaceFolder { object WorkspaceFolder {
implicit val workspaceFolderDecoder: Decoder[WorkspaceFolder] = implicit val workspaceFolderDecoder: Decoder[WorkspaceFolder] =
List[Decoder[WorkspaceFolder]](
Decoder[WorkspaceFolderImpl].widen
).reduceLeft(_ or _)
case class WorkspaceFolderImpl() extends WorkspaceFolder
object WorkspaceFolderImpl {
implicit val workspaceFolderImplDecoder: Decoder[WorkspaceFolderImpl] =
deriveDecoder deriveDecoder
} }
}
/** A param of the request [[org.enso.gateway.protocol.Requests.Initialize]]. /** A param of the request [[org.enso.gateway.protocol.Requests.Initialize]].
* *
* @see [[org.enso.gateway.protocol.request.Params.InitializeParams]].
* The capabilities provided by the client (editor or tool). * The capabilities provided by the client (editor or tool).
* Define capabilities for dynamic registration, workspace and text document * Define capabilities for dynamic registration, workspace and text document
* features the client supports. * features the client supports.
*
* @see [[org.enso.gateway.protocol.request.Params.InitializeParams]].
*/ */
case class ClientCapabilities( case class ClientCapabilities(
workspace: Option[clientcapabilities.Workspace] = None, workspace: Option[clientcapabilities.Workspace] = None,

View File

@ -18,15 +18,33 @@ import org.enso.gateway.protocol.request.Param.{
sealed trait Params sealed trait Params
object Params { object Params {
implicit val paramsDecoder: Decoder[Params] = List[Decoder[Params]]( implicit val paramsDecoder: Decoder[Params] = List[Decoder[Params]](
Decoder[InitializeParams].widen, Decoder[Array].widen,
Decoder[InitializedParams].widen, Decoder[VoidParams].widen,
Decoder[Array].widen Decoder[InitializeParams].widen
).reduceLeft(_ or _) ).reduceLeft(_ or _)
type DocumentUri = String type DocumentUri = String
/** Params of the request /** Array params. */
* [[org.enso.gateway.protocol.Requests.Initialize]]. case class Array(value: Seq[Option[Param]]) extends Params
object Array {
implicit val paramsArrayDecoder: Decoder[Array] =
deriveUnwrappedDecoder
}
/** Void params.
*
* Params of [[org.enso.gateway.protocol.Requests.Shutdown]],
* [[org.enso.gateway.protocol.Notifications.Initialized]],
* [[org.enso.gateway.protocol.Notifications.Exit]].
*/
case class VoidParams() extends Params
object VoidParams {
implicit val voidParamsDecoder: Decoder[VoidParams] =
deriveDecoder
}
/** Params of the request [[org.enso.gateway.protocol.Requests.Initialize]].
*/ */
case class InitializeParams( case class InitializeParams(
processId: Option[Int] = None, processId: Option[Int] = None,
@ -39,7 +57,6 @@ object Params {
trace: Option[Trace] = None, trace: Option[Trace] = None,
workspaceFolders: Option[Seq[WorkspaceFolder]] = None workspaceFolders: Option[Seq[WorkspaceFolder]] = None
) extends Params ) extends Params
object InitializeParams { object InitializeParams {
implicit val initializeParamsDecoder: Decoder[InitializeParams] = implicit val initializeParamsDecoder: Decoder[InitializeParams] =
deriveDecoder deriveDecoder
@ -50,21 +67,4 @@ object Params {
* `rootPath` is deprecated: use `rootUri`, LSP Spec. * `rootPath` is deprecated: use `rootUri`, LSP Spec.
*/ */
/** Params of the notification
* [[org.enso.gateway.protocol.Notifications.Initialized]].
*/
case class InitializedParams() extends Params
object InitializedParams {
implicit val initializedParamsDecoder: Decoder[InitializedParams] =
deriveDecoder
}
/** Array params. */
case class Array(value: Seq[Option[Param]]) extends Params
object Array {
implicit val paramsArrayDecoder: Decoder[Array] =
deriveUnwrappedDecoder
}
} }

View File

@ -3,7 +3,7 @@ package org.enso.gateway.protocol.request.clientcapabilities
import io.circe.Decoder import io.circe.Decoder
import io.circe.generic.extras.semiauto.deriveUnwrappedDecoder import io.circe.generic.extras.semiauto.deriveUnwrappedDecoder
/** Define capabilities for experimental features the client supports. */ /** Defines capabilities for experimental features the client supports. */
case class Experimental(value: String) extends AnyVal case class Experimental(value: String) extends AnyVal
object Experimental { object Experimental {
implicit val clientCapabilitiesExperimentalDecoder: Decoder[Experimental] = implicit val clientCapabilitiesExperimentalDecoder: Decoder[Experimental] =

View File

@ -26,7 +26,7 @@ import org.enso.gateway.protocol.request.clientcapabilities.textdocument.{
TypeDefinition TypeDefinition
} }
/** Define capabilities for text document features the client supports. */ /** Defines capabilities for text document features the client supports. */
case class TextDocument( case class TextDocument(
synchronization: Option[Sync] = None, synchronization: Option[Sync] = None,
completion: Option[Completion] = None, completion: Option[Completion] = None,

View File

@ -10,7 +10,7 @@ import org.enso.gateway.protocol.request.clientcapabilities.workspace.{
WorkspaceSymbol WorkspaceSymbol
} }
/** Define capabilities for workspace features the client supports. /** Defines capabilities for workspace features the client supports.
* *
* @param applyEdit The client supports applying batch edits to * @param applyEdit The client supports applying batch edits to
* the workspace by supporting the request * the workspace by supporting the request

View File

@ -0,0 +1,118 @@
package org.enso.gateway.protocol.request.clientcapabilities.common
import io.circe.Decoder
/** A symbol kind. */
sealed abstract class SymbolKind(value: Int)
object SymbolKind {
private val file = 1
private val module = 2
private val namespace = 3
private val packageKind = 4
private val classKind = 5
private val method = 6
private val property = 7
private val field = 8
private val constructor = 9
private val enum = 10
private val interface = 11
private val function = 12
private val variable = 13
private val constant = 14
private val string = 15
private val number = 16
private val boolean = 17
private val array = 18
private val objectKind = 19
private val key = 20
private val nullKind = 21
private val enumMember = 22
private val struct = 23
private val event = 24
private val operator = 25
private val typeParameter = 26
private val invalidSymbolKind = "Invalid SymbolKind"
case object File extends SymbolKind(file)
case object Module extends SymbolKind(module)
case object Namespace extends SymbolKind(namespace)
case object Package extends SymbolKind(packageKind)
case object Class extends SymbolKind(classKind)
case object Method extends SymbolKind(method)
case object Property extends SymbolKind(property)
case object Field extends SymbolKind(field)
case object Constructor extends SymbolKind(constructor)
case object Enum extends SymbolKind(enum)
case object Interface extends SymbolKind(interface)
case object Function extends SymbolKind(function)
case object Variable extends SymbolKind(variable)
case object Constant extends SymbolKind(constant)
case object StringKind extends SymbolKind(string)
case object Number extends SymbolKind(number)
case object BooleanKind extends SymbolKind(boolean)
case object ArrayKind extends SymbolKind(array)
case object ObjectKind extends SymbolKind(objectKind)
case object Key extends SymbolKind(key)
case object NullKind extends SymbolKind(nullKind)
case object EnumMember extends SymbolKind(enumMember)
case object Struct extends SymbolKind(struct)
case object Event extends SymbolKind(event)
case object Operator extends SymbolKind(operator)
case object TypeParameter extends SymbolKind(typeParameter)
implicit val SymbolKindDecoder: Decoder[SymbolKind] =
Decoder.decodeInt.emap {
case `file` => Right(File)
case `module` => Right(Module)
case `namespace` => Right(Namespace)
case `packageKind` => Right(Package)
case `classKind` => Right(Class)
case `method` => Right(Method)
case `property` => Right(Property)
case `field` => Right(Field)
case `constructor` => Right(Constructor)
case `enum` => Right(Enum)
case `interface` => Right(Interface)
case `function` => Right(Function)
case `variable` => Right(Variable)
case `constant` => Right(Constant)
case `string` => Right(StringKind)
case `number` => Right(Number)
case `boolean` => Right(BooleanKind)
case `array` => Right(ArrayKind)
case `objectKind` => Right(ObjectKind)
case `key` => Right(Key)
case `nullKind` => Right(NullKind)
case `enumMember` => Right(EnumMember)
case `struct` => Right(Struct)
case `event` => Right(Event)
case `operator` => Right(Operator)
case `typeParameter` => Right(TypeParameter)
case _ => Left(invalidSymbolKind)
}
}

View File

@ -0,0 +1,13 @@
package org.enso.gateway.protocol.request.clientcapabilities.common
import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder
/** Part of
* [[org.enso.gateway.protocol.request.clientcapabilities.workspace.WorkspaceSymbol]].
*/
case class SymbolKinds(valueSet: Option[Seq[SymbolKind]]) extends AnyVal
object SymbolKinds {
implicit val symbolKindsDecoder: Decoder[SymbolKinds] =
deriveDecoder
}

View File

@ -2,9 +2,14 @@ package org.enso.gateway.protocol.request.clientcapabilities.textdocument
import io.circe.Decoder import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
import org.enso.gateway.protocol.request.clientcapabilities.textdocument.codeaction.CodeActionLiteralSupport
/** Capabilities specific to the `textDocument/codeAction` request. */ /** Capabilities specific to the `textDocument/codeAction` request. */
case class CodeAction() case class CodeAction(
dynamicRegistration: Option[Boolean] = None,
codeActionLiteralSupport: Option[CodeActionLiteralSupport] = None,
isPreferredSupport: Option[Boolean] = None
)
object CodeAction { object CodeAction {
implicit val clientCapabilitiesTextDocumentCodeActionDecoder implicit val clientCapabilitiesTextDocumentCodeActionDecoder
: Decoder[CodeAction] = : Decoder[CodeAction] =

View File

@ -4,7 +4,9 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/codeLens` request. */ /** Capabilities specific to the `textDocument/codeLens` request. */
case class CodeLens() case class CodeLens(
dynamicRegistration: Option[Boolean] = None
) extends AnyVal
object CodeLens { object CodeLens {
implicit val clientCapabilitiesTextDocumentCodeLensDecoder implicit val clientCapabilitiesTextDocumentCodeLensDecoder
: Decoder[CodeLens] = : Decoder[CodeLens] =

View File

@ -6,7 +6,9 @@ import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/documentColor` and the /** Capabilities specific to the `textDocument/documentColor` and the
* `textDocument/colorPresentation` request. * `textDocument/colorPresentation` request.
*/ */
case class Color() case class Color(
dynamicRegistration: Option[Boolean] = None
)
object Color { object Color {
implicit val clientCapabilitiesTextDocumentColorDecoder: Decoder[Color] = implicit val clientCapabilitiesTextDocumentColorDecoder: Decoder[Color] =
deriveDecoder deriveDecoder

View File

@ -2,9 +2,18 @@ package org.enso.gateway.protocol.request.clientcapabilities.textdocument
import io.circe.Decoder import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
import org.enso.gateway.protocol.request.clientcapabilities.textdocument.completion.{
CompletionItem,
CompletionItemKinds
}
/** Capabilities specific to the `textDocument/completion` request. */ /** Capabilities specific to the `textDocument/completion` request. */
case class Completion() case class Completion(
dynamicRegistration: Option[Boolean] = None,
completionItem: Option[CompletionItem] = None,
completionItemKind: Option[CompletionItemKinds] = None,
contextSupport: Option[Boolean] = None
)
object Completion { object Completion {
implicit val clientCapabilitiesTextDocumentCompletionDecoder implicit val clientCapabilitiesTextDocumentCompletionDecoder
: Decoder[Completion] = : Decoder[Completion] =

View File

@ -4,7 +4,10 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/declaration` request. */ /** Capabilities specific to the `textDocument/declaration` request. */
case class Declaration() case class Declaration(
dynamicRegistration: Option[Boolean] = None,
linkSupport: Option[Boolean] = None
)
object Declaration { object Declaration {
implicit val clientCapabilitiesTextDocumentDeclarationDecoder implicit val clientCapabilitiesTextDocumentDeclarationDecoder
: Decoder[Declaration] = : Decoder[Declaration] =

View File

@ -4,7 +4,10 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/definition` request. */ /** Capabilities specific to the `textDocument/definition` request. */
case class Definition() case class Definition(
dynamicRegistration: Option[Boolean] = None,
linkSupport: Option[Boolean] = None
)
object Definition { object Definition {
implicit val clientCapabilitiesTextDocumentDefinitionDecoder implicit val clientCapabilitiesTextDocumentDefinitionDecoder
: Decoder[Definition] = : Decoder[Definition] =

View File

@ -2,9 +2,14 @@ package org.enso.gateway.protocol.request.clientcapabilities.textdocument
import io.circe.Decoder import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
import org.enso.gateway.protocol.request.clientcapabilities.common.SymbolKinds
/** Capabilities specific to the `textDocument/documentSymbol` request. */ /** Capabilities specific to the `textDocument/documentSymbol` request. */
case class DocumentSymbol() case class DocumentSymbol(
dynamicRegistration: Option[Boolean] = None,
symbolKind: Option[SymbolKinds] = None,
hierarchicalDocumentSymbolSupport: Option[Boolean] = None
)
object DocumentSymbol { object DocumentSymbol {
implicit val clientCapabilitiesTextDocumentSymbolDecoder implicit val clientCapabilitiesTextDocumentSymbolDecoder
: Decoder[DocumentSymbol] = : Decoder[DocumentSymbol] =

View File

@ -4,7 +4,11 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/foldingRange` request. */ /** Capabilities specific to the `textDocument/foldingRange` request. */
case class FoldingRange() case class FoldingRange(
dynamicRegistration: Option[Boolean] = None,
rangeLimit: Option[Int] = None,
lineFoldingOnly: Option[Boolean] = None
)
object FoldingRange { object FoldingRange {
implicit val clientCapabilitiesTextDocumentFoldingRangeDecoder implicit val clientCapabilitiesTextDocumentFoldingRangeDecoder
: Decoder[FoldingRange] = : Decoder[FoldingRange] =

View File

@ -4,7 +4,9 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/formatting` request. */ /** Capabilities specific to the `textDocument/formatting` request. */
case class Formatting() case class Formatting(
dynamicRegistration: Option[Boolean] = None
)
object Formatting { object Formatting {
implicit val clientCapabilitiesTextDocumentFormattingDecoder implicit val clientCapabilitiesTextDocumentFormattingDecoder
: Decoder[Formatting] = : Decoder[Formatting] =

View File

@ -4,7 +4,9 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/documentHighlight` request. */ /** Capabilities specific to the `textDocument/documentHighlight` request. */
case class Highlight() case class Highlight(
dynamicRegistration: Option[Boolean] = None
)
object Highlight { object Highlight {
implicit val clientCapabilitiesTextDocumentHighlightDecoder implicit val clientCapabilitiesTextDocumentHighlightDecoder
: Decoder[Highlight] = : Decoder[Highlight] =

View File

@ -2,9 +2,13 @@ package org.enso.gateway.protocol.request.clientcapabilities.textdocument
import io.circe.Decoder import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
import org.enso.gateway.protocol.request.clientcapabilities.textdocument.common.MarkupKind
/** Capabilities specific to the `textDocument/hover` request. */ /** Capabilities specific to the `textDocument/hover` request. */
case class Hover() case class Hover(
dynamicRegistration: Option[Boolean] = None,
contentFormat: Option[MarkupKind] = None
)
object Hover { object Hover {
implicit val clientCapabilitiesTextDocumentHoverDecoder: Decoder[Hover] = implicit val clientCapabilitiesTextDocumentHoverDecoder: Decoder[Hover] =
deriveDecoder deriveDecoder

View File

@ -4,7 +4,10 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/implementation` request. */ /** Capabilities specific to the `textDocument/implementation` request. */
case class Implementation() case class Implementation(
dynamicRegistration: Option[Boolean] = None,
linkSupport: Option[Boolean] = None
)
object Implementation { object Implementation {
implicit val clientCapabilitiesTextDocumentImplementationDecoder implicit val clientCapabilitiesTextDocumentImplementationDecoder
: Decoder[Implementation] = : Decoder[Implementation] =

View File

@ -4,7 +4,10 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/documentLink` request. */ /** Capabilities specific to the `textDocument/documentLink` request. */
case class Link() case class Link(
dynamicRegistration: Option[Boolean] = None,
tooltipSupport: Option[Boolean] = None
)
object Link { object Link {
implicit val clientCapabilitiesTextDocumentLinkDecoder: Decoder[Link] = implicit val clientCapabilitiesTextDocumentLinkDecoder: Decoder[Link] =
deriveDecoder deriveDecoder

View File

@ -4,7 +4,9 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/onTypeFormatting` request. */ /** Capabilities specific to the `textDocument/onTypeFormatting` request. */
case class OnTypeFormatting() case class OnTypeFormatting(
dynamicRegistration: Option[Boolean] = None
)
object OnTypeFormatting { object OnTypeFormatting {
implicit val clientCapabilitiesTextDocumentOnTypeFormattingDecoder implicit val clientCapabilitiesTextDocumentOnTypeFormattingDecoder
: Decoder[OnTypeFormatting] = : Decoder[OnTypeFormatting] =

View File

@ -2,10 +2,15 @@ package org.enso.gateway.protocol.request.clientcapabilities.textdocument
import io.circe.Decoder import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
import org.enso.gateway.protocol.request.clientcapabilities.textdocument.publishdiagnostics.TagSupport
/** Capabilities specific to the `textDocument/publishDiagnostics` notification. /** Capabilities specific to the `textDocument/publishDiagnostics` notification.
*/ */
case class PublishDiagnostics() case class PublishDiagnostics(
relatedInformation: Option[Boolean] = None,
tagSupport: Option[TagSupport] = None,
versionSupport: Option[Boolean] = None
)
object PublishDiagnostics { object PublishDiagnostics {
implicit val clientCapabilitiesTextDocumentPublishDiagnosticsDecoder implicit val clientCapabilitiesTextDocumentPublishDiagnosticsDecoder
: Decoder[PublishDiagnostics] = : Decoder[PublishDiagnostics] =

View File

@ -4,7 +4,9 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/rangeFormatting` request. */ /** Capabilities specific to the `textDocument/rangeFormatting` request. */
case class RangeFormatting() case class RangeFormatting(
dynamicRegistration: Option[Boolean] = None
)
object RangeFormatting { object RangeFormatting {
implicit val clientCapabilitiesTextDocumentRangeFormattingDecoder implicit val clientCapabilitiesTextDocumentRangeFormattingDecoder
: Decoder[RangeFormatting] = : Decoder[RangeFormatting] =

View File

@ -4,7 +4,9 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/references` request. */ /** Capabilities specific to the `textDocument/references` request. */
case class Reference() case class Reference(
dynamicRegistration: Option[Boolean] = None
)
object Reference { object Reference {
implicit val clientCapabilitiesTextDocumentReferenceDecoder implicit val clientCapabilitiesTextDocumentReferenceDecoder
: Decoder[Reference] = : Decoder[Reference] =

View File

@ -4,7 +4,10 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/rename` request. */ /** Capabilities specific to the `textDocument/rename` request. */
case class Rename() case class Rename(
dynamicRegistration: Option[Boolean] = None,
prepareSupport: Option[Boolean] = None
)
object Rename { object Rename {
implicit val clientCapabilitiesTextDocumentRenameDecoder: Decoder[Rename] = implicit val clientCapabilitiesTextDocumentRenameDecoder: Decoder[Rename] =
deriveDecoder deriveDecoder

View File

@ -2,9 +2,14 @@ package org.enso.gateway.protocol.request.clientcapabilities.textdocument
import io.circe.Decoder import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
import org.enso.gateway.protocol.request.clientcapabilities.textdocument.signaturehelp.SignatureInformation
/** Capabilities specific to the `textDocument/signatureHelp` request. */ /** Capabilities specific to the `textDocument/signatureHelp` request. */
case class SignatureHelp() case class SignatureHelp(
dynamicRegistration: Option[Boolean] = None,
signatureInformation: Option[SignatureInformation] = None,
contextSupport: Option[Boolean] = None
)
object SignatureHelp { object SignatureHelp {
implicit val clientCapabilitiesTextDocumentSignatureHelpDecoder implicit val clientCapabilitiesTextDocumentSignatureHelpDecoder
: Decoder[SignatureHelp] = : Decoder[SignatureHelp] =

View File

@ -4,7 +4,12 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Synchronization capabilities. */ /** Synchronization capabilities. */
case class Sync() case class Sync(
dynamicRegistration: Option[Boolean] = None,
willSave: Option[Boolean] = None,
willSaveWaitUntil: Option[Boolean] = None,
didSave: Option[Boolean] = None
)
object Sync { object Sync {
implicit val clientCapabilitiesTextDocumentSyncDecoder: Decoder[Sync] = implicit val clientCapabilitiesTextDocumentSyncDecoder: Decoder[Sync] =
deriveDecoder deriveDecoder

View File

@ -4,7 +4,10 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `textDocument/typeDefinition` request. */ /** Capabilities specific to the `textDocument/typeDefinition` request. */
case class TypeDefinition() case class TypeDefinition(
dynamicRegistration: Option[Boolean] = None,
linkSupport: Option[Boolean] = None
)
object TypeDefinition { object TypeDefinition {
implicit val clientCapabilitiesTextDocumentTypeDefinitionDecoder implicit val clientCapabilitiesTextDocumentTypeDefinitionDecoder
: Decoder[TypeDefinition] = : Decoder[TypeDefinition] =

View File

@ -0,0 +1,12 @@
package org.enso.gateway.protocol.request.clientcapabilities.textdocument.codeaction
import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder
import org.enso.gateway.protocol.CodeActionKind
/** Array of [[CodeActionKind]]. */
case class CodeActionKinds(valueSet: Seq[CodeActionKind]) extends AnyVal
object CodeActionKinds {
implicit val codeActionKindsDecoder: Decoder[CodeActionKinds] =
deriveDecoder
}

View File

@ -0,0 +1,14 @@
package org.enso.gateway.protocol.request.clientcapabilities.textdocument.codeaction
import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder
/** Part of
* [[org.enso.gateway.protocol.request.clientcapabilities.textdocument.CodeAction]].
*/
case class CodeActionLiteralSupport(codeActionKind: CodeActionKinds)
object CodeActionLiteralSupport {
implicit val codeActionLiteralSupportDecoder
: Decoder[CodeActionLiteralSupport] =
deriveDecoder
}

View File

@ -0,0 +1,22 @@
package org.enso.gateway.protocol.request.clientcapabilities.textdocument.common
import io.circe.Decoder
import io.circe.generic.extras.semiauto.deriveEnumerationDecoder
/** Part of
* [[org.enso.gateway.protocol.request.clientcapabilities.textdocument.completion.CompletionItem]]
* and
* [[org.enso.gateway.protocol.request.clientcapabilities.textdocument.signaturehelp.SignatureInformation]].
*/
sealed trait MarkupKind
object MarkupKind {
/** Plain text is supported as a content format. */
case object plaintext extends MarkupKind
/** Markdown is supported as a content format. */
case object markdown extends MarkupKind
implicit val clientCapabilitiesTextDocumentCompletionMarkupKindDecoder
: Decoder[MarkupKind] = deriveEnumerationDecoder
}

View File

@ -0,0 +1,22 @@
package org.enso.gateway.protocol.request.clientcapabilities.textdocument.completion
import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder
import org.enso.gateway.protocol.request.clientcapabilities.textdocument.common.MarkupKind
/** Part of
* [[org.enso.gateway.protocol.request.clientcapabilities.textdocument.Completion]].
*/
case class CompletionItem(
snippetSupport: Option[Boolean] = None,
commitCharactersSupport: Option[Boolean] = None,
documentationFormat: Option[Seq[MarkupKind]] = None,
deprecatedSupport: Option[Boolean] = None,
preselectSupport: Option[Boolean] = None,
tagSupport: Option[TagSupport] = None
)
object CompletionItem {
implicit val clientCapabilitiesTextDocumentCompletionItemDecoder
: Decoder[CompletionItem] =
deriveDecoder
}

View File

@ -0,0 +1,114 @@
package org.enso.gateway.protocol.request.clientcapabilities.textdocument.completion
import io.circe.Decoder
/** Kind of [[CompletionItem]]. */
sealed abstract class CompletionItemKind(value: Int)
object CompletionItemKind {
private val text = 1
private val method = 2
private val function = 3
private val constructor = 4
private val field = 5
private val variable = 6
private val classKind = 7
private val interface = 8
private val module = 9
private val property = 10
private val unit = 11
private val value = 12
private val enum = 13
private val keyword = 14
private val snippet = 15
private val color = 16
private val file = 17
private val reference = 18
private val folder = 19
private val enumMember = 20
private val constant = 21
private val struct = 22
private val event = 23
private val operator = 24
private val typeParameter = 25
private val invalidCompletionItemKind = "Invalid CompletionItemKind"
case object Text extends CompletionItemKind(text)
case object Method extends CompletionItemKind(method)
case object Function extends CompletionItemKind(function)
case object Constructor extends CompletionItemKind(constructor)
case object Field extends CompletionItemKind(field)
case object Variable extends CompletionItemKind(variable)
case object Class extends CompletionItemKind(classKind)
case object Interface extends CompletionItemKind(interface)
case object Module extends CompletionItemKind(module)
case object Property extends CompletionItemKind(property)
case object Unit extends CompletionItemKind(unit)
case object Value extends CompletionItemKind(value)
case object Enum extends CompletionItemKind(enum)
case object Keyword extends CompletionItemKind(keyword)
case object Snippet extends CompletionItemKind(snippet)
case object Color extends CompletionItemKind(color)
case object File extends CompletionItemKind(file)
case object Reference extends CompletionItemKind(reference)
case object Folder extends CompletionItemKind(folder)
case object EnumMember extends CompletionItemKind(enumMember)
case object Constant extends CompletionItemKind(constant)
case object Struct extends CompletionItemKind(struct)
case object Event extends CompletionItemKind(event)
case object Operator extends CompletionItemKind(operator)
case object TypeParameter extends CompletionItemKind(typeParameter)
implicit val textDocumentSyncKindDecoder: Decoder[CompletionItemKind] =
Decoder.decodeInt.emap {
case `text` => Right(Text)
case `method` => Right(Method)
case `function` => Right(Function)
case `constructor` => Right(Constructor)
case `field` => Right(Field)
case `variable` => Right(Variable)
case `classKind` => Right(Class)
case `interface` => Right(Interface)
case `module` => Right(Module)
case `property` => Right(Property)
case `unit` => Right(Unit)
case `value` => Right(Value)
case `enum` => Right(Enum)
case `keyword` => Right(Keyword)
case `snippet` => Right(Snippet)
case `color` => Right(Color)
case `file` => Right(File)
case `reference` => Right(Reference)
case `folder` => Right(Folder)
case `enumMember` => Right(EnumMember)
case `constant` => Right(Constant)
case `struct` => Right(Struct)
case `event` => Right(Event)
case `operator` => Right(Operator)
case `typeParameter` => Right(TypeParameter)
case _ => Left(invalidCompletionItemKind)
}
}

View File

@ -0,0 +1,14 @@
package org.enso.gateway.protocol.request.clientcapabilities.textdocument.completion
import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder
/** Array of [[CompletionItemKind]]. */
case class CompletionItemKinds(
valueSet: Option[Seq[CompletionItemKind]] = None
) extends AnyVal
object CompletionItemKinds {
implicit val clientCapabilitiesTextDocumentCompletionItemKindHolderDecoder
: Decoder[CompletionItemKinds] =
deriveDecoder
}

View File

@ -0,0 +1,19 @@
package org.enso.gateway.protocol.request.clientcapabilities.textdocument.completion
import io.circe.Decoder
/** Tag of [[CompletionItem]]. */
sealed abstract class CompletionItemTag(value: Int)
object CompletionItemTag {
private val deprecated = 1
private val invalidCompletionItemTag = "Invalid CompletionItemTag"
object Deprecated extends CompletionItemTag(deprecated)
implicit val textDocumentCompletionItemTagDecoder
: Decoder[CompletionItemTag] =
Decoder.decodeInt.emap {
case `deprecated` => Right(Deprecated)
case _ => Left(invalidCompletionItemTag)
}
}

View File

@ -0,0 +1,16 @@
package org.enso.gateway.protocol.request.clientcapabilities.textdocument.completion
import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder
/** Array of [[CompletionItemTag]].
*
* Part of [[CompletionItem]].
*/
case class TagSupport(
valueSet: Seq[CompletionItemTag]
) extends AnyVal
object TagSupport {
implicit val clientCapabilitiesTextDocumentCompletionTagSupportDecoder
: Decoder[TagSupport] = deriveDecoder
}

View File

@ -0,0 +1,31 @@
package org.enso.gateway.protocol.request.clientcapabilities.textdocument.publishdiagnostics
import io.circe.Decoder
/** Element of [[TagSupport]]. */
sealed abstract class DiagnosticTag(value: Int)
object DiagnosticTag {
private val unnecessary = 1
private val deprecated = 2
private val invalidDiagnosticTag = "Invalid DiagnosticTag"
/** Unused or unnecessary code.
*
* Clients are allowed to render diagnostics with this tag faded out instead
* of having an error squiggle.
*/
case object Unnecessary extends DiagnosticTag(unnecessary)
/** Deprecated or obsolete code.
*
* Clients are allowed to rendered diagnostics with this tag strike through.
*/
case object Deprecated extends DiagnosticTag(deprecated)
implicit val diagnosticTagDecoder: Decoder[DiagnosticTag] =
Decoder.decodeInt.emap {
case `unnecessary` => Right(Unnecessary)
case `deprecated` => Right(Deprecated)
case _ => Left(invalidDiagnosticTag)
}
}

View File

@ -0,0 +1,13 @@
package org.enso.gateway.protocol.request.clientcapabilities.textdocument.publishdiagnostics
import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder
/** Part of
* [[org.enso.gateway.protocol.request.clientcapabilities.textdocument.PublishDiagnostics]].
*/
case class TagSupport(valueSet: Seq[DiagnosticTag]) extends AnyVal
object TagSupport {
implicit val tagSupportDecoder: Decoder[TagSupport] =
deriveDecoder
}

View File

@ -0,0 +1,12 @@
package org.enso.gateway.protocol.request.clientcapabilities.textdocument.signaturehelp
import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder
/** Part of [[SignatureInformation]]. */
case class ParameterInformation(labelOffsetSupport: Option[Boolean] = None)
extends AnyVal
object ParameterInformation {
implicit val parameterInformationDecoder: Decoder[ParameterInformation] =
deriveDecoder
}

View File

@ -0,0 +1,17 @@
package org.enso.gateway.protocol.request.clientcapabilities.textdocument.signaturehelp
import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder
import org.enso.gateway.protocol.request.clientcapabilities.textdocument.common.MarkupKind
/** Part of
* [[org.enso.gateway.protocol.request.clientcapabilities.textdocument.SignatureHelp]].
*/
case class SignatureInformation(
documentationFormat: Option[Seq[MarkupKind]] = None,
parameterInformation: Option[ParameterInformation] = None
)
object SignatureInformation {
implicit val signatureInformationDecoder: Decoder[SignatureInformation] =
deriveDecoder
}

View File

@ -6,7 +6,7 @@ import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `workspace/didChangeConfiguration` /** Capabilities specific to the `workspace/didChangeConfiguration`
* notification. * notification.
*/ */
case class DidChangeConfiguration() case class DidChangeConfiguration(dynamicRegistration: Option[Boolean] = None)
object DidChangeConfiguration { object DidChangeConfiguration {
implicit val clientCapabilitiesWorkspaceDidChangeConfigurationDecoder implicit val clientCapabilitiesWorkspaceDidChangeConfigurationDecoder
: Decoder[DidChangeConfiguration] = : Decoder[DidChangeConfiguration] =

View File

@ -5,7 +5,7 @@ import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `workspace/didChangeWatchedFiles` notification. /** Capabilities specific to the `workspace/didChangeWatchedFiles` notification.
*/ */
case class DidChangeWatchedFiles() case class DidChangeWatchedFiles(dynamicRegistration: Option[Boolean] = None)
object DidChangeWatchedFiles { object DidChangeWatchedFiles {
implicit val clientCapabilitiesWorkspaceDidChangeWatchedFilesDecoder implicit val clientCapabilitiesWorkspaceDidChangeWatchedFilesDecoder
: Decoder[DidChangeWatchedFiles] = : Decoder[DidChangeWatchedFiles] =

View File

@ -2,9 +2,17 @@ package org.enso.gateway.protocol.request.clientcapabilities.workspace
import io.circe.Decoder import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
import org.enso.gateway.protocol.request.clientcapabilities.workspace.edit.{
FailureHandlingKind,
ResourceOperationKind
}
/** Capabilities specific to `WorkspaceEdit`s. */ /** Capabilities specific to `WorkspaceEdit`s. */
case class Edit() case class Edit(
documentChanges: Option[Boolean],
resourceOperations: Option[Seq[ResourceOperationKind]],
failureHandling: Option[FailureHandlingKind]
)
object Edit { object Edit {
implicit val clientCapabilitiesWorkspaceEditDecoder: Decoder[Edit] = implicit val clientCapabilitiesWorkspaceEditDecoder: Decoder[Edit] =
deriveDecoder deriveDecoder

View File

@ -4,7 +4,7 @@ import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
/** Capabilities specific to the `workspace/executeCommand` request. */ /** Capabilities specific to the `workspace/executeCommand` request. */
case class ExecuteCommand() case class ExecuteCommand(dynamicRegistration: Option[Boolean] = None)
object ExecuteCommand { object ExecuteCommand {
implicit val clientCapabilitiesWorkspaceExecuteCommandDecoder implicit val clientCapabilitiesWorkspaceExecuteCommandDecoder
: Decoder[ExecuteCommand] = : Decoder[ExecuteCommand] =

View File

@ -2,9 +2,13 @@ package org.enso.gateway.protocol.request.clientcapabilities.workspace
import io.circe.Decoder import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder import io.circe.generic.semiauto.deriveDecoder
import org.enso.gateway.protocol.request.clientcapabilities.common.SymbolKinds
/** Capabilities specific to the `workspace/symbol` request. */ /** Capabilities specific to the `workspace/symbol` request. */
case class WorkspaceSymbol() case class WorkspaceSymbol(
dynamicRegistration: Option[Boolean] = None,
symbolKind: Option[SymbolKinds] = None
)
object WorkspaceSymbol { object WorkspaceSymbol {
implicit val clientCapabilitiesWorkspaceWorkspaceSymbolDecoder implicit val clientCapabilitiesWorkspaceWorkspaceSymbolDecoder
: Decoder[WorkspaceSymbol] = : Decoder[WorkspaceSymbol] =

View File

@ -0,0 +1,39 @@
package org.enso.gateway.protocol.request.clientcapabilities.workspace.edit
import io.circe.Decoder
import io.circe.generic.extras.semiauto.deriveEnumerationDecoder
/** Part of
* [[org.enso.gateway.protocol.request.clientcapabilities.workspace.Edit]].
*/
sealed trait FailureHandlingKind
object FailureHandlingKind {
/** Applying the workspace change is simply aborted if one of the changes
* provided fails. All operations executed before the failing operation stay
* executed.
*/
case object abort extends FailureHandlingKind
/**
* All operations are executed transactional. That means they either all
* succeed or no changes at all are applied to the workspace.
*/
case object transactional extends FailureHandlingKind
/**
* The client tries to undo the operations already executed. But there is no
* guarantee that this is succeeding.
*/
case object undo extends FailureHandlingKind
/**
* If the workspace edit contains only textual file changes they are executed
* transactional. If resource changes (create, rename or delete file) are
* part of the change the failure handling strategy is abort.
*/
case object textOnlyTransactional extends FailureHandlingKind
implicit val failureHandlingKindDecoder: Decoder[FailureHandlingKind] =
deriveEnumerationDecoder
}

View File

@ -0,0 +1,23 @@
package org.enso.gateway.protocol.request.clientcapabilities.workspace.edit
import io.circe.Decoder
import io.circe.generic.extras.semiauto.deriveEnumerationDecoder
/** Part of
* [[org.enso.gateway.protocol.request.clientcapabilities.workspace.Edit]].
*/
sealed trait ResourceOperationKind
object ResourceOperationKind {
/** Supports creating new files and folders. */
case object create extends ResourceOperationKind
/** Supports renaming existing files and folders. */
case object rename extends ResourceOperationKind
/** Supports deleting existing files and folders. */
case object delete extends ResourceOperationKind
implicit val resourceOperationKindDecoder: Decoder[ResourceOperationKind] =
deriveEnumerationDecoder
}

View File

@ -4,7 +4,7 @@ import io.circe.Encoder
import org.enso.gateway.protocol.response.error.{Data, ErrorCode, ErrorMessage} import org.enso.gateway.protocol.response.error.{Data, ErrorCode, ErrorMessage}
import org.enso.gateway.protocol.response.error.Data.{InitializeData, ParseData} import org.enso.gateway.protocol.response.error.Data.{InitializeData, ParseData}
/** [[org.enso.gateway.protocol.Response]] error. /** Error of [[org.enso.gateway.protocol.Response]].
* *
* `ResponseError` in LSP Spec: * `ResponseError` in LSP Spec:
* https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#responseMessage * https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#responseMessage
@ -42,7 +42,8 @@ object ResponseError {
data data
) )
/** [[org.enso.gateway.protocol.Requests.Initialize]] error. /** Error of [[org.enso.gateway.protocol.Requests.Initialize]].
*
* Wrong JSON-RPC version. * Wrong JSON-RPC version.
*/ */
case class InitializeError( case class InitializeError(

View File

@ -1,6 +1,6 @@
package org.enso.gateway.protocol.response package org.enso.gateway.protocol.response
import io.circe.Encoder import io.circe.{Encoder, Json}
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
import org.enso.gateway.protocol.response.result.{ import org.enso.gateway.protocol.response.result.{
@ -9,7 +9,7 @@ import org.enso.gateway.protocol.response.result.{
} }
import io.circe.syntax._ import io.circe.syntax._
/** [[org.enso.gateway.protocol.Response]] result. /** Result of [[org.enso.gateway.protocol.Response]].
* *
* LSP Spec: * LSP Spec:
* https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#responseMessage * https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#responseMessage
@ -20,7 +20,8 @@ object Result {
case text: Text => text.asJson case text: Text => text.asJson
case number: Number => number.asJson case number: Number => number.asJson
case boolean: Bool => boolean.asJson case boolean: Bool => boolean.asJson
case initializeResult: InitializeResult => initializeResult.asJson case result: InitializeResult => result.asJson
case result: NullResult.type => result.asJson
} }
/** A string result. */ /** A string result. */
@ -36,13 +37,13 @@ object Result {
} }
/** A boolean result. */ /** A boolean result. */
case class Bool(value: scala.Boolean) extends Result case class Bool(value: Boolean) extends Result
object Bool { object Bool {
implicit val resultBooleanEncoder: Encoder[Bool] = implicit val resultBooleanEncoder: Encoder[Bool] =
deriveUnwrappedEncoder deriveUnwrappedEncoder
} }
/** [[org.enso.gateway.protocol.Requests.Initialize]] result. */ /** Result of [[org.enso.gateway.protocol.Requests.Initialize]]. */
case class InitializeResult( case class InitializeResult(
capabilities: ServerCapabilities, capabilities: ServerCapabilities,
serverInfo: Option[ServerInfo] = None serverInfo: Option[ServerInfo] = None
@ -51,4 +52,9 @@ object Result {
implicit val initializeResultEncoder: Encoder[InitializeResult] = implicit val initializeResultEncoder: Encoder[InitializeResult] =
deriveEncoder deriveEncoder
} }
/** Result of [[org.enso.gateway.protocol.Requests.Shutdown]]. */
case object NullResult extends Result {
implicit val nullResultEncoder: Encoder[NullResult.type] = _ => Json.Null
}
} }

View File

@ -6,22 +6,35 @@ import io.circe.Encoder
sealed abstract class ErrorCode(val code: Int) sealed abstract class ErrorCode(val code: Int)
object ErrorCode { object ErrorCode {
/** Invalid JSON was received by the server. An error occurred on the server /** Signals that invalid JSON was received by the server.
* while parsing the JSON text. Defined by JSON-RPC Spec. *
* An error occurred on the server while parsing the JSON text.
* Defined by JSON-RPC Spec.
*/ */
case object ParseError extends ErrorCode(-32700) case object ParseError extends ErrorCode(-32700)
/** The JSON sent is not a valid Request object. Defined by JSON-RPC Spec. */ /** Signals that the JSON sent is not a valid Request object.
*
* Defined by JSON-RPC Spec.
*/
case object InvalidRequest extends ErrorCode(-32600) case object InvalidRequest extends ErrorCode(-32600)
/** The method does not exist or is not available. Defined by JSON-RPC Spec. /** Signals that the method does not exist or is not available.
*
* Defined by JSON-RPC Spec.
*/ */
case object MethodNotFound extends ErrorCode(-32601) case object MethodNotFound extends ErrorCode(-32601)
/** Invalid method parameters. Defined by JSON-RPC Spec. */ /** Signals that method parameters are invalid.
*
* Defined by JSON-RPC Spec.
*/
case object InvalidParams extends ErrorCode(-32602) case object InvalidParams extends ErrorCode(-32602)
/** Internal JSON-RPC error. Defined by JSON-RPC Spec. */ /** Internal JSON-RPC error.
*
* Defined by JSON-RPC Spec.
*/
case object InternalError extends ErrorCode(-32603) case object InternalError extends ErrorCode(-32603)
/** Codes from -32000 to -32099 reserved for implementation-defined /** Codes from -32000 to -32099 reserved for implementation-defined

View File

@ -2,17 +2,18 @@ package org.enso.gateway.protocol.response.result
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
import io.circe.Encoder import io.circe.Encoder
import org.enso.gateway.protocol.response.result.servercapabilities.declarationprovider.DocumentFilter
import org.enso.gateway.protocol.response.result.servercapabilities.{ import org.enso.gateway.protocol.response.result.servercapabilities.{
CodeActionProvider, CodeActionProvider,
CodeLensProvider, CodeLensOptions,
ColorProvider, ColorProvider,
CompletionOptions, CompletionOptions,
DeclarationProvider, DeclarationProvider,
DefinitionProvider, DefinitionProvider,
DocumentFormattingProvider, DocumentFormattingProvider,
DocumentHighlightProvider, DocumentHighlightProvider,
DocumentLinkProvider, DocumentLinkOptions,
DocumentOnTypeFormattingProvider, DocumentOnTypeFormattingOptions,
DocumentRangeFormattingProvider, DocumentRangeFormattingProvider,
DocumentSymbolProvider, DocumentSymbolProvider,
ExecuteCommandOptions, ExecuteCommandOptions,
@ -28,8 +29,8 @@ import org.enso.gateway.protocol.response.result.servercapabilities.{
Workspace Workspace
} }
/** [[org.enso.gateway.protocol.response.Result.InitializeResult]] server /** Server capabilities in
* capabilities. * [[org.enso.gateway.protocol.response.Result.InitializeResult]].
* *
* @param textDocumentSync @see [[TextDocumentSync]] * @param textDocumentSync @see [[TextDocumentSync]]
* @param completionProvider @see [[CompletionOptions]] * @param completionProvider @see [[CompletionOptions]]
@ -43,14 +44,14 @@ import org.enso.gateway.protocol.response.result.servercapabilities.{
* @param documentHighlightProvider @see [[DocumentHighlightProvider]] * @param documentHighlightProvider @see [[DocumentHighlightProvider]]
* @param documentSymbolProvider @see [[DocumentSymbolProvider]] * @param documentSymbolProvider @see [[DocumentSymbolProvider]]
* @param codeActionProvider @see [[CodeActionProvider]] * @param codeActionProvider @see [[CodeActionProvider]]
* @param codeLensProvider @see [[CodeLensProvider]] * @param codeLensProvider @see [[CodeLensOptions]]
* @param documentLinkProvider @see [[DocumentLinkProvider]] * @param documentLinkProvider @see [[DocumentLinkOptions]]
* @param colorProvider @see [[ColorProvider]] * @param colorProvider @see [[ColorProvider]]
* @param documentFormattingProvider @see [[DocumentFormattingProvider]] * @param documentFormattingProvider @see [[DocumentFormattingProvider]]
* @param documentRangeFormattingProvider @see * @param documentRangeFormattingProvider @see
* [[DocumentRangeFormattingProvider]] * [[DocumentRangeFormattingProvider]]
* @param documentOnTypeFormattingProvider @see * @param documentOnTypeFormattingProvider @see
* [[DocumentOnTypeFormattingProvider]] * [[DocumentOnTypeFormattingOptions]]
* @param renameProvider @see [[RenameProvider]] * @param renameProvider @see [[RenameProvider]]
* @param foldingRangeProvider @see [[FoldingRangeProvider]] * @param foldingRangeProvider @see [[FoldingRangeProvider]]
* @param executeCommandProvider @see [[ExecuteCommandOptions]] * @param executeCommandProvider @see [[ExecuteCommandOptions]]
@ -72,13 +73,13 @@ case class ServerCapabilities(
documentHighlightProvider: Option[DocumentHighlightProvider] = None, documentHighlightProvider: Option[DocumentHighlightProvider] = None,
documentSymbolProvider: Option[DocumentSymbolProvider] = None, documentSymbolProvider: Option[DocumentSymbolProvider] = None,
codeActionProvider: Option[CodeActionProvider] = None, codeActionProvider: Option[CodeActionProvider] = None,
codeLensProvider: Option[CodeLensProvider] = None, codeLensProvider: Option[CodeLensOptions] = None,
documentLinkProvider: Option[DocumentLinkProvider] = None, documentLinkProvider: Option[DocumentLinkOptions] = None,
colorProvider: Option[ColorProvider] = None, colorProvider: Option[ColorProvider] = None,
documentFormattingProvider: Option[DocumentFormattingProvider] = None, documentFormattingProvider: Option[DocumentFormattingProvider] = None,
documentRangeFormattingProvider: Option[DocumentRangeFormattingProvider] = documentRangeFormattingProvider: Option[DocumentRangeFormattingProvider] =
None, None,
documentOnTypeFormattingProvider: Option[DocumentOnTypeFormattingProvider] = documentOnTypeFormattingProvider: Option[DocumentOnTypeFormattingOptions] =
None, None,
renameProvider: Option[RenameProvider] = None, renameProvider: Option[RenameProvider] = None,
foldingRangeProvider: Option[FoldingRangeProvider] = None, foldingRangeProvider: Option[FoldingRangeProvider] = None,
@ -88,6 +89,7 @@ case class ServerCapabilities(
experimental: Option[Experimental] = None experimental: Option[Experimental] = None
) )
object ServerCapabilities { object ServerCapabilities {
type DocumentSelector = Seq[DocumentFilter]
implicit val serverCapabilitiesEncoder: Encoder[ServerCapabilities] = implicit val serverCapabilitiesEncoder: Encoder[ServerCapabilities] =
deriveEncoder deriveEncoder
} }

View File

@ -3,7 +3,8 @@ package org.enso.gateway.protocol.response.result
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
import io.circe.Encoder import io.circe.Encoder
/** [[org.enso.gateway.protocol.response.Result.InitializeResult]] server info. /** Server info in
* [[org.enso.gateway.protocol.response.Result.InitializeResult]].
* *
* @param name Name of Language Server * @param name Name of Language Server
* @param version Version of Language Server * @param version Version of Language Server

View File

@ -1,15 +1,39 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
import org.enso.gateway.protocol.CodeActionKind
/** The server provides code actions. The `CodeActionOptions` return type is /** Server capability to provide code actions.
* only valid if the client signals code action literal support via the *
* property `textDocument.codeAction.codeActionLiteralSupport`. * The [[CodeActionProvider.CodeActionOptions]] return type is only valid if
* the client signals code action literal support via the property
* `textDocument.codeAction.codeActionLiteralSupport`.
*/ */
case class CodeActionProvider() sealed trait CodeActionProvider
object CodeActionProvider { object CodeActionProvider {
case class Bool(value: Boolean) extends CodeActionProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] =
deriveUnwrappedEncoder
}
case class CodeActionOptions(
workDoneProgress: Option[Boolean] = None,
codeActionKinds: Option[Seq[CodeActionKind]] = None
) extends CodeActionProvider
object CodeActionOptions {
implicit val codeActionOptionsEncoder: Encoder[CodeActionOptions] =
deriveEncoder
}
implicit val serverCapabilitiesCodeActionProviderEncoder implicit val serverCapabilitiesCodeActionProviderEncoder
: Encoder[CodeActionProvider] = : Encoder[CodeActionProvider] =
deriveEncoder Encoder.instance {
case boolean: Bool => boolean.asJson
case options: CodeActionOptions => options.asJson
}
} }

View File

@ -0,0 +1,15 @@
package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder
/** Server capability to provide code lens. */
case class CodeLensOptions(
workDoneProgress: Option[Boolean] = None,
resolveProvider: Option[Boolean] = None
)
object CodeLensOptions {
implicit val serverCapabilitiesCodeLensOptionsEncoder
: Encoder[CodeLensOptions] =
deriveEncoder
}

View File

@ -1,12 +0,0 @@
package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder
/** The server provides code lens. */
case class CodeLensProvider()
object CodeLensProvider {
implicit val serverCapabilitiesCodeLensProviderEncoder
: Encoder[CodeLensProvider] =
deriveEncoder
}

View File

@ -1,11 +1,43 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
import org.enso.gateway.protocol.response.result.ServerCapabilities.DocumentSelector
/** The server provides color provider support. */ /** Server capability to provide color provider support. */
case class ColorProvider() sealed trait ColorProvider
object ColorProvider { object ColorProvider {
implicit val serverCapabilitiesColorProviderEncoder: Encoder[ColorProvider] =
case class Bool(value: Boolean) extends ColorProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] =
deriveUnwrappedEncoder
}
case class DocumentColorOptions(workDoneProgress: Option[Boolean] = None)
extends ColorProvider
object DocumentColorOptions {
implicit val documentColorOptionsEncoder: Encoder[DocumentColorOptions] =
deriveEncoder deriveEncoder
}
case class DocumentColorRegistrationOptions(
workDoneProgress: Option[Boolean] = None,
documentSelector: Option[DocumentSelector] = None,
id: Option[String] = None
) extends ColorProvider
object DocumentColorRegistrationOptions {
implicit val documentColorRegistrationOptionsEncoder
: Encoder[DocumentColorRegistrationOptions] =
deriveEncoder
}
implicit val serverCapabilitiesColorProviderEncoder: Encoder[ColorProvider] =
Encoder.instance {
case boolean: Bool => boolean.asJson
case options: DocumentColorOptions => options.asJson
case options: DocumentColorRegistrationOptions => options.asJson
}
} }

View File

@ -3,8 +3,13 @@ package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
/** The server provides completion support. */ /** Server capability to provide completion support. */
case class CompletionOptions() case class CompletionOptions(
triggerCharacters: Option[Seq[String]] = None,
allCommitCharacters: Option[Seq[String]] = None,
resolveProvider: Option[Boolean] = None,
workDoneProgress: Option[Boolean] = None
)
object CompletionOptions { object CompletionOptions {
implicit val serverCapabilitiesCompletionOptionsEncoder implicit val serverCapabilitiesCompletionOptionsEncoder
: Encoder[CompletionOptions] = : Encoder[CompletionOptions] =

View File

@ -1,12 +1,44 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import org.enso.gateway.protocol.response.result.ServerCapabilities.DocumentSelector
/** The server provides go to declaration support. */ /** Server capability to provide "go to declaration" support. */
case class DeclarationProvider() sealed trait DeclarationProvider
object DeclarationProvider { object DeclarationProvider {
case class Bool(value: Boolean) extends DeclarationProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] =
deriveUnwrappedEncoder
}
case class DeclarationOptions(workDoneProgress: Option[Boolean] = None)
extends DeclarationProvider
object DeclarationOptions {
implicit val declarationOptionsEncoder: Encoder[DeclarationOptions] =
deriveEncoder
}
case class DeclarationRegistrationOptions(
workDoneProgress: Option[Boolean] = None,
documentSelector: Option[DocumentSelector] = None,
id: Option[String] = None
) extends DeclarationProvider
object DeclarationRegistrationOptions {
implicit val declarationRegistrationOptionsEncoder
: Encoder[DeclarationRegistrationOptions] =
deriveEncoder
}
implicit val serverCapabilitiesDeclarationProviderEncoder implicit val serverCapabilitiesDeclarationProviderEncoder
: Encoder[DeclarationProvider] = : Encoder[DeclarationProvider] =
deriveEncoder Encoder.instance {
case boolean: Bool => boolean.asJson
case options: DeclarationOptions => options.asJson
case options: DeclarationRegistrationOptions => options.asJson
}
} }

View File

@ -1,12 +1,31 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
/** The server provides goto definition support. */ /** Server capability to provide "go to definition" support. */
case class DefinitionProvider() sealed trait DefinitionProvider
object DefinitionProvider { object DefinitionProvider {
case class Bool(value: Boolean) extends DefinitionProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] =
deriveUnwrappedEncoder
}
case class DefinitionOptions(workDoneProgress: Option[Boolean] = None)
extends DefinitionProvider
object DefinitionOptions {
implicit val definitionOptionsEncoder: Encoder[DefinitionOptions] =
deriveEncoder
}
implicit val serverCapabilitiesDefinitionProviderEncoder implicit val serverCapabilitiesDefinitionProviderEncoder
: Encoder[DefinitionProvider] = : Encoder[DefinitionProvider] =
deriveEncoder Encoder.instance {
case boolean: Bool => boolean.asJson
case options: DefinitionOptions => options.asJson
}
} }

View File

@ -1,12 +1,32 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
/** The server provides document formatting. */ /** Server capability to provide document formatting. */
case class DocumentFormattingProvider() sealed trait DocumentFormattingProvider
object DocumentFormattingProvider { object DocumentFormattingProvider {
case class Bool(value: Boolean) extends DocumentFormattingProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] =
deriveUnwrappedEncoder
}
case class DocumentFormattingOptions(workDoneProgress: Option[Boolean] = None)
extends DocumentFormattingProvider
object DocumentFormattingOptions {
implicit val documentFormattingOptionsEncoder
: Encoder[DocumentFormattingOptions] =
deriveEncoder
}
implicit val serverCapabilitiesDocumentFormattingProviderEncoder implicit val serverCapabilitiesDocumentFormattingProviderEncoder
: Encoder[DocumentFormattingProvider] = : Encoder[DocumentFormattingProvider] =
deriveEncoder Encoder.instance {
case boolean: Bool => boolean.asJson
case options: DocumentFormattingOptions => options.asJson
}
} }

View File

@ -1,12 +1,32 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
/** The server provides document highlight support. */ /** Server capability to provide document highlight support. */
case class DocumentHighlightProvider() sealed trait DocumentHighlightProvider
object DocumentHighlightProvider { object DocumentHighlightProvider {
case class Bool(value: Boolean) extends DocumentHighlightProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] =
deriveUnwrappedEncoder
}
case class DocumentHighlightOptions(workDoneProgress: Option[Boolean] = None)
extends DocumentHighlightProvider
object DocumentHighlightOptions {
implicit val documentHighlightOptionsEncoder
: Encoder[DocumentHighlightOptions] =
deriveEncoder
}
implicit val serverCapabilitiesDocumentHighlightProviderEncoder implicit val serverCapabilitiesDocumentHighlightProviderEncoder
: Encoder[DocumentHighlightProvider] = : Encoder[DocumentHighlightProvider] =
deriveEncoder Encoder.instance {
case boolean: Bool => boolean.asJson
case options: DocumentHighlightOptions => options.asJson
}
} }

View File

@ -0,0 +1,15 @@
package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder
/** Server capability to provide document link support. */
case class DocumentLinkOptions(
workDoneProgress: Option[Boolean] = None,
resolveProvider: Option[Boolean] = None
)
object DocumentLinkOptions {
implicit val serverCapabilitiesDocumentLinkOptionsEncoder
: Encoder[DocumentLinkOptions] =
deriveEncoder
}

View File

@ -1,12 +0,0 @@
package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder
/** The server provides document link support. */
case class DocumentLinkProvider()
object DocumentLinkProvider {
implicit val serverCapabilitiesDocumentLinkProviderEncoder
: Encoder[DocumentLinkProvider] =
deriveEncoder
}

View File

@ -0,0 +1,15 @@
package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder
/** Server capability to provide document formatting on typing. */
case class DocumentOnTypeFormattingOptions(
firstTriggerCharacter: String,
moreTriggerCharacter: Option[Seq[String]]
)
object DocumentOnTypeFormattingOptions {
implicit val serverCapabilitiesDocumentOnTypeFormattingOptionsEncoder
: Encoder[DocumentOnTypeFormattingOptions] =
deriveEncoder
}

View File

@ -1,12 +0,0 @@
package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder
/** The server provides document formatting on typing. */
case class DocumentOnTypeFormattingProvider()
object DocumentOnTypeFormattingProvider {
implicit val serverCapabilitiesDocumentOnTypeFormattingProviderEncoder
: Encoder[DocumentOnTypeFormattingProvider] =
deriveEncoder
}

View File

@ -1,12 +1,33 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
/** The server provides document range formatting. */ /** Server capability to provide document range formatting. */
case class DocumentRangeFormattingProvider() sealed trait DocumentRangeFormattingProvider
object DocumentRangeFormattingProvider { object DocumentRangeFormattingProvider {
case class Bool(value: Boolean) extends DocumentRangeFormattingProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] =
deriveUnwrappedEncoder
}
case class DocumentRangeFormattingOptions(
workDoneProgress: Option[Boolean] = None
) extends DocumentRangeFormattingProvider
object DocumentRangeFormattingOptions {
implicit val DocumentRangeFormattingOptiondEncoder
: Encoder[DocumentRangeFormattingOptions] =
deriveEncoder
}
implicit val serverCapabilitiesDocumentRangeFormattingProviderEncoder implicit val serverCapabilitiesDocumentRangeFormattingProviderEncoder
: Encoder[DocumentRangeFormattingProvider] = : Encoder[DocumentRangeFormattingProvider] =
deriveEncoder Encoder.instance {
case boolean: Bool => boolean.asJson
case options: DocumentRangeFormattingOptions => options.asJson
}
} }

View File

@ -1,12 +1,31 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
/** The server provides document symbol support. */ /** Server capability to provide document symbol support. */
case class DocumentSymbolProvider() sealed trait DocumentSymbolProvider
object DocumentSymbolProvider { object DocumentSymbolProvider {
case class Bool(value: Boolean) extends DocumentSymbolProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] =
deriveUnwrappedEncoder
}
case class DocumentSymbolOptions(workDoneProgress: Option[Boolean] = None)
extends DocumentSymbolProvider
object DocumentSymbolOptions {
implicit val documentSymbolOptionsEncoder: Encoder[DocumentSymbolOptions] =
deriveEncoder
}
implicit val serverCapabilitiesDocumentSymbolProviderEncoder implicit val serverCapabilitiesDocumentSymbolProviderEncoder
: Encoder[DocumentSymbolProvider] = : Encoder[DocumentSymbolProvider] =
deriveEncoder Encoder.instance {
case boolean: Bool => boolean.asJson
case options: DocumentSymbolOptions => options.asJson
}
} }

View File

@ -3,8 +3,11 @@ package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
/** The server provides execute command support. */ /** Server capability to provide execute command support. */
case class ExecuteCommandOptions() case class ExecuteCommandOptions(
workDoneProgress: Option[Boolean] = None,
commands: Seq[String]
)
object ExecuteCommandOptions { object ExecuteCommandOptions {
implicit val serverCapabilitiesExecuteCommandOptionsEncoder implicit val serverCapabilitiesExecuteCommandOptionsEncoder
: Encoder[ExecuteCommandOptions] = : Encoder[ExecuteCommandOptions] =

View File

@ -4,7 +4,7 @@ import io.circe.Encoder
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
/** Experimental /** Experimental
* [[org.enso.gateway.protocol.response.result.ServerCapabilities]] * [[org.enso.gateway.protocol.response.result.ServerCapabilities]].
*/ */
case class Experimental(value: String) extends AnyVal case class Experimental(value: String) extends AnyVal
object Experimental { object Experimental {

View File

@ -1,12 +1,43 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
import org.enso.gateway.protocol.response.result.ServerCapabilities.DocumentSelector
/** The server provides folding provider support. */ /** Server capability to provide folding provider support. */
case class FoldingRangeProvider() sealed trait FoldingRangeProvider
object FoldingRangeProvider { object FoldingRangeProvider {
case class Bool(value: Boolean) extends FoldingRangeProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] = deriveUnwrappedEncoder
}
case class FoldingRangeOptions(workDoneProgress: Option[Boolean] = None)
extends FoldingRangeProvider
object FoldingRangeOptions {
implicit val foldingRangeOptionsEncoder: Encoder[FoldingRangeOptions] =
deriveEncoder
}
case class FoldingRangeRegistrationOptions(
documentSelector: Option[DocumentSelector] = None,
workDoneProgress: Option[Boolean] = None,
id: Option[String] = None
) extends FoldingRangeProvider
object FoldingRangeRegistrationOptions {
implicit val foldingRangeRegistrationOptionsEncoder
: Encoder[FoldingRangeRegistrationOptions] =
deriveEncoder
}
implicit val serverCapabilitiesFoldingRangeProviderEncoder implicit val serverCapabilitiesFoldingRangeProviderEncoder
: Encoder[FoldingRangeProvider] = : Encoder[FoldingRangeProvider] =
deriveEncoder Encoder.instance {
case boolean: Bool => boolean.asJson
case options: FoldingRangeOptions => options.asJson
case options: FoldingRangeRegistrationOptions => options.asJson
}
} }

View File

@ -1,11 +1,28 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
import io.circe.syntax._
/** The server provides hover support. */ /** Server capability to provide hover support. */
case class HoverProvider() sealed trait HoverProvider
object HoverProvider { object HoverProvider {
case class Bool(value: Boolean) extends HoverProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] = deriveUnwrappedEncoder
}
case class HoverOptions(workDoneProgress: Option[Boolean] = None)
extends HoverProvider
object HoverOptions {
implicit val hoverOptionsEncoder: Encoder[HoverOptions] = deriveEncoder
}
implicit val serverCapabilitiesHoverProviderEncoder: Encoder[HoverProvider] = implicit val serverCapabilitiesHoverProviderEncoder: Encoder[HoverProvider] =
deriveEncoder Encoder.instance {
case boolean: Bool => boolean.asJson
case options: HoverOptions => options.asJson
}
} }

View File

@ -1,12 +1,43 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
import org.enso.gateway.protocol.response.result.ServerCapabilities.DocumentSelector
/** The server provides goto implementation support. */ /** Server capability to provide "go to implementation" support. */
case class ImplementationProvider() sealed trait ImplementationProvider
object ImplementationProvider { object ImplementationProvider {
case class Bool(value: Boolean) extends ImplementationProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] = deriveUnwrappedEncoder
}
case class ImplementationOptions(workDoneProgress: Option[Boolean] = None)
extends ImplementationProvider
object ImplementationOptions {
implicit val implementationOptionsEncoder: Encoder[ImplementationOptions] =
deriveEncoder
}
case class ImplementationRegistrationOptions(
documentSelector: Option[DocumentSelector] = None,
workDoneProgress: Option[Boolean] = None,
id: Option[String] = None
) extends ImplementationProvider
object ImplementationRegistrationOptions {
implicit val implementationRegistrationOptionsEncoder
: Encoder[ImplementationRegistrationOptions] =
deriveEncoder
}
implicit val serverCapabilitiesImplementationProviderEncoder implicit val serverCapabilitiesImplementationProviderEncoder
: Encoder[ImplementationProvider] = : Encoder[ImplementationProvider] =
deriveEncoder Encoder.instance {
case boolean: Bool => boolean.asJson
case options: ImplementationOptions => options.asJson
case options: ImplementationRegistrationOptions => options.asJson
}
} }

View File

@ -1,12 +1,30 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
/** The server provides find references support. */ /** Server capability to provide find references support. */
case class ReferencesProvider() sealed trait ReferencesProvider
object ReferencesProvider { object ReferencesProvider {
case class Bool(value: Boolean) extends ReferencesProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] = deriveUnwrappedEncoder
}
case class ReferenceOptions(workDoneProgress: Option[Boolean] = None)
extends ReferencesProvider
object ReferenceOptions {
implicit val referenceOptionsEncoder: Encoder[ReferenceOptions] =
deriveEncoder
}
implicit val serverCapabilitiesReferencesProviderEncoder implicit val serverCapabilitiesReferencesProviderEncoder
: Encoder[ReferencesProvider] = : Encoder[ReferencesProvider] =
deriveEncoder Encoder.instance {
case boolean: Bool => boolean.asJson
case options: ReferenceOptions => options.asJson
}
} }

View File

@ -1,15 +1,37 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
/** The server provides rename support. RenameOptions may only be specified if /** Server capability to provide rename support.
*
* [[RenameProvider.RenameOptions]] may only be specified if
* the client states that it supports `prepareSupport` in its initial * the client states that it supports `prepareSupport` in its initial
* `initialize` request. * `initialize` request.
*/ */
case class RenameProvider() sealed trait RenameProvider
object RenameProvider { object RenameProvider {
case class Bool(value: Boolean) extends RenameProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] = deriveUnwrappedEncoder
}
case class RenameOptions(
workDoneProgress: Option[Boolean] = None,
prepareProvider: Option[Boolean] = None
) extends RenameProvider
object RenameOptions {
implicit val renameOptionsEncoder: Encoder[RenameOptions] =
deriveEncoder
}
implicit val serverCapabilitiesRenameProviderEncoder implicit val serverCapabilitiesRenameProviderEncoder
: Encoder[RenameProvider] = : Encoder[RenameProvider] =
deriveEncoder Encoder.instance {
case boolean: Bool => boolean.asJson
case options: RenameOptions => options.asJson
}
} }

View File

@ -3,8 +3,12 @@ package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
/** The server provides signature help support. */ /** Server capability to provide signature help support. */
case class SignatureHelpOptions() case class SignatureHelpOptions(
triggerCharacters: Option[Seq[String]] = None,
retriggerCharacters: Option[Seq[String]] = None,
workDoneProgress: Option[Boolean] = None
)
object SignatureHelpOptions { object SignatureHelpOptions {
implicit val serverCapabilitiesSignatureHelpOptionsEncoder implicit val serverCapabilitiesSignatureHelpOptionsEncoder
: Encoder[SignatureHelpOptions] = : Encoder[SignatureHelpOptions] =

View File

@ -1,16 +1,46 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
import org.enso.gateway.protocol.response.result.servercapabilities.textdocumentsync.TextDocumentSyncKind
/** Defines how text documents are synced. Is either a detailed structure /** Defines how text documents are synced.
* defining each notification or for backwards compatibility the *
* `TextDocumentSyncKind` number. If omitted it defaults to * Is either a detailed structure defining each notification or for backwards
* `TextDocumentSyncKind.None`. * compatibility the [[TextDocumentSyncKind]] number. If omitted it defaults to
* [[TextDocumentSyncKind.NoneKind]].
*/ */
case class TextDocumentSync() sealed trait TextDocumentSync
object TextDocumentSync { object TextDocumentSync {
case class Number(value: Int) extends TextDocumentSync
object Number {
implicit val textDocumentSyncNumberEncoder: Encoder[Number] =
deriveUnwrappedEncoder
}
case class TextDocumentSyncOptions(
openClose: Option[Boolean] = None,
change: Option[TextDocumentSyncKind] = None
) extends TextDocumentSync
object TextDocumentSyncOptions {
implicit val textDocumentSyncTextDocumentSyncOptionsEncoder
: Encoder[TextDocumentSyncOptions] = deriveEncoder
}
case class WillSaveWaitUntil(willSaveWaitUntil: Boolean)
extends TextDocumentSync
object WillSaveWaitUntil {
implicit val textDocumentSyncWillSaveWaitUntilEncoder
: Encoder[WillSaveWaitUntil] = deriveEncoder
}
implicit val serverCapabilitiesTextDocumentSyncEncoder implicit val serverCapabilitiesTextDocumentSyncEncoder
: Encoder[TextDocumentSync] = : Encoder[TextDocumentSync] = Encoder.instance {
deriveEncoder case number: Number => number.asJson
case capability: TextDocumentSyncOptions => capability.asJson
case capability: WillSaveWaitUntil => capability.asJson
}
} }

View File

@ -1,12 +1,44 @@
package org.enso.gateway.protocol.response.result.servercapabilities package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
import org.enso.gateway.protocol.response.result.ServerCapabilities.DocumentSelector
/** The server provides goto type definition support. */ /** Server capability to provide "go to type definition" support. */
case class TypeDefinitionProvider() sealed trait TypeDefinitionProvider
object TypeDefinitionProvider { object TypeDefinitionProvider {
case class Bool(value: Boolean) extends TypeDefinitionProvider
object Bool {
implicit val boolEncoder: Encoder[Bool] = deriveUnwrappedEncoder
}
case class TypeDefinitionOptions(workDoneProgress: Option[Boolean] = None)
extends TypeDefinitionProvider
object TypeDefinitionOptions {
implicit val typeDefinitionOptionsEncoder: Encoder[TypeDefinitionOptions] =
deriveEncoder
}
case class TypeDefinitionRegistrationOptions(
documentSelector: Option[DocumentSelector] = None,
workDoneProgress: Option[Boolean] = None,
id: Option[String] = None
) extends TypeDefinitionProvider
object TypeDefinitionRegistrationOptions {
implicit val typeDefinitionRegistrationOptionsEncoder
: Encoder[TypeDefinitionRegistrationOptions] =
deriveEncoder
}
implicit val serverCapabilitiesTypeDefinitionProviderEncoder implicit val serverCapabilitiesTypeDefinitionProviderEncoder
: Encoder[TypeDefinitionProvider] = : Encoder[TypeDefinitionProvider] =
deriveEncoder Encoder.instance {
case boolean: Bool => boolean.asJson
case options: TypeDefinitionOptions => options.asJson
case options: TypeDefinitionRegistrationOptions => options.asJson
}
} }

View File

@ -2,11 +2,14 @@ package org.enso.gateway.protocol.response.result.servercapabilities
import io.circe.Encoder import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder import io.circe.generic.semiauto.deriveEncoder
import org.enso.gateway.protocol.response.result.servercapabilities.workspace.WorkspaceFoldersServerCapabilities
/** Workspace specific /** Workspace-specific
* [[org.enso.gateway.protocol.response.result.ServerCapabilities]]. * [[org.enso.gateway.protocol.response.result.ServerCapabilities]].
*/ */
case class Workspace() case class Workspace(
workspaceFolders: Option[WorkspaceFoldersServerCapabilities] = None
)
object Workspace { object Workspace {
implicit val serverCapabilitiesWorkspaceEncoder: Encoder[Workspace] = implicit val serverCapabilitiesWorkspaceEncoder: Encoder[Workspace] =
deriveEncoder deriveEncoder

View File

@ -0,0 +1,15 @@
package org.enso.gateway.protocol.response.result.servercapabilities.declarationprovider
import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder
/** A document filter. */
case class DocumentFilter(
language: Option[String] = None,
scheme: Option[String] = None,
pattern: Option[String] = None
)
object DocumentFilter {
implicit val documentFilterEncoder: Encoder[DocumentFilter] =
deriveEncoder
}

View File

@ -0,0 +1,28 @@
package org.enso.gateway.protocol.response.result.servercapabilities.textdocumentsync
import io.circe.Encoder
/** Kind of document sync. */
sealed abstract class TextDocumentSyncKind(val value: Int)
object TextDocumentSyncKind {
private val none = 0
private val full = 1
private val incremental = 2
/** Signals that documents should not be synced at all. */
object NoneKind extends TextDocumentSyncKind(none)
/** Signals that documents are synced by always sending the full content of
* the document.
*/
object Full extends TextDocumentSyncKind(full)
/** Signals that documents are synced by sending the full content on open.
*
* After that only incremental updates to the document are sent.
*/
object Incremental extends TextDocumentSyncKind(incremental)
implicit val textDocumentSyncKindEncoder: Encoder[TextDocumentSyncKind] =
Encoder.encodeInt.contramap(_.value)
}

View File

@ -0,0 +1,30 @@
package org.enso.gateway.protocol.response.result.servercapabilities.workspace
import io.circe.Encoder
import io.circe.syntax._
import io.circe.generic.extras.semiauto.deriveUnwrappedEncoder
/** Part of [[WorkspaceFoldersServerCapabilities]]. */
sealed trait ChangeNotifications
object ChangeNotifications {
/** String [[ChangeNotifications]]. */
case class Text(value: String) extends ChangeNotifications
object Text {
implicit val textEncoder: Encoder[Text] =
deriveUnwrappedEncoder
}
/** Boolean [[ChangeNotifications]]. */
case class Bool(value: Boolean) extends ChangeNotifications
object Bool {
implicit val boolEncoder: Encoder[Bool] =
deriveUnwrappedEncoder
}
implicit val textEncoder: Encoder[ChangeNotifications] =
Encoder.instance {
case text: Text => text.asJson
case boolean: Bool => boolean.asJson
}
}

View File

@ -0,0 +1,17 @@
package org.enso.gateway.protocol.response.result.servercapabilities.workspace
import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder
/** @see
* [[org.enso.gateway.protocol.response.result.servercapabilities.Workspace]].
*/
case class WorkspaceFoldersServerCapabilities(
supported: Option[Boolean] = None,
changeNotifications: Option[ChangeNotifications] = None
)
object WorkspaceFoldersServerCapabilities {
implicit val workspaceFoldersServerCapabilitiesEncoder
: Encoder[WorkspaceFoldersServerCapabilities] =
deriveEncoder
}

View File

@ -0,0 +1,55 @@
package org.enso.gateway.server
import com.typesafe.config.{ConfigFactory, Config => TypesafeConfig}
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.duration._
/** Describes endpoint to which [[org.enso.gateway.Server]] can bind and
* timeouts.
*
* Gets default values of parameters from typesafe config.
*
* @param port Port of endpoint.
* @param host Host of endpoint.
* @param route Route of endpoint.
* @param timeout Timeout for waiting response after request.
* @param bindingTimeout Timeout for waiting binding result.
* @param hardDeadline Timeout for waiting result of binding termination.
*/
class Config(
val port: Int = Config.port,
val host: String = Config.host,
val route: String = Config.route,
val timeout: FiniteDuration = Config.timeout,
val bindingTimeout: FiniteDuration = Config.bindingTimeout,
val hardDeadline: FiniteDuration = Config.hardDeadline
) {
val addressString: String = s"ws://$host:$port"
}
object Config {
private val gatewayPath = "gateway"
private val serverPath = "server"
private val hostPath = "host"
private val portPath = "port"
private val routePath = "route"
private val timeoutPath = "timeoutSecs"
private val bindingTimeoutPath = "bindingTimeoutSecs"
private val hardDeadlinePath = "hardDeadlineSecs"
private val gatewayConfig: TypesafeConfig =
ConfigFactory.load.getConfig(gatewayPath)
private val serverConfig: TypesafeConfig =
gatewayConfig.getConfig(serverPath)
private val host: String = serverConfig.getString(hostPath)
private val port: Int = serverConfig.getInt(portPath)
private val route: String = serverConfig.getString(routePath)
private val timeout: FiniteDuration =
serverConfig.getLong(timeoutPath).seconds
private val bindingTimeout: FiniteDuration =
serverConfig.getLong(bindingTimeoutPath).seconds
private val hardDeadline: FiniteDuration =
serverConfig.getLong(hardDeadlinePath).seconds
}

View File

@ -7,8 +7,12 @@ import akka.http.scaladsl.model.ws.{Message, TextMessage, WebSocketRequest}
import akka.stream.ActorMaterializer import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Flow, Keep, Sink, Source} import akka.stream.scaladsl.{Flow, Keep, Sink, Source}
import io.circe.Json import io.circe.Json
import org.enso.gateway.Server.Config import org.enso.gateway.TestJson.{
import org.enso.gateway.TestJson.{Initialize, WrongJsonrpc, WrongMethod} Initialize,
Shutdown,
WrongJsonrpc,
WrongMethod
}
import org.enso.{Gateway, LanguageServer} import org.enso.{Gateway, LanguageServer}
import org.scalatest.{ import org.scalatest.{
Assertion, Assertion,
@ -18,10 +22,12 @@ import org.scalatest.{
Matchers Matchers
} }
import io.circe.parser.parse import io.circe.parser.parse
import org.enso.gateway.server.Config
import scala.concurrent.Future import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
class GatewaySpec class GatewayJsonSpec
extends AsyncFlatSpec extends AsyncFlatSpec
with Matchers with Matchers
with BeforeAndAfterAll with BeforeAndAfterAll
@ -32,22 +38,34 @@ class GatewaySpec
import system.dispatcher import system.dispatcher
override def beforeAll: Unit = { private val languageServerActorName = "testingLanguageServer"
val languageServerActorName = "languageServer" private val gatewayActorName = "testingGateway"
val gatewayActorName = "gateway" private val languageServer: ActorRef =
val languageServer: ActorRef =
system.actorOf(LanguageServer.props(null), languageServerActorName) system.actorOf(LanguageServer.props(null), languageServerActorName)
val gateway: ActorRef = private val gateway: ActorRef =
system.actorOf(Gateway.props(languageServer), gatewayActorName) system.actorOf(Gateway.props(languageServer), gatewayActorName)
val jsonRpcController = new JsonRpcController(gateway) private val jsonRpcController = new JsonRpcController(gateway)
val server = new Server(jsonRpcController)
private val config = {
val port = 30001
val host = "localhost"
new Config(port, host)
}
private val server = new Server(jsonRpcController, config)
override def beforeAll: Unit = {
server.run() server.run()
} }
override def afterAll: Unit = { override def afterAll: Unit = {
system.terminate() val terminationFuture = for {
() _ <- server.shutdown()
_ <- system.terminate()
} yield ()
val timeout = 5.seconds
Await.result(terminationFuture, timeout)
} }
"Gateway" should "reply with a proper response to request with initialize method" in { "Gateway" should "reply with a proper response to request with initialize method" in {
@ -62,19 +80,23 @@ class GatewaySpec
checkRequestResponse(WrongMethod) checkRequestResponse(WrongMethod)
} }
"Gateway" should "reply with a proper response to request with shutdown method" in {
checkRequestResponse(Shutdown)
}
private def checkRequestResponse( private def checkRequestResponse(
testJsons: TestJson testJson: TestJson
): Future[Assertion] = { ): Future[Assertion] = {
Given("server replies with responses to requests") Given("server replies with responses to requests")
val messageToMessageFlow: Flow[Message, Message, Future[Message]] = val messageToMessageFlow: Flow[Message, Message, Future[Message]] =
createFlow( createFlow(
TextMessage(testJsons.request.toString) TextMessage(testJson.request.toString)
) )
When("server receives request") When("server receives request")
val (_, messageFuture) = Http() val (_, messageFuture) = Http()
.singleWebSocketRequest( .singleWebSocketRequest(
WebSocketRequest(Config.addressString), WebSocketRequest(config.addressString),
messageToMessageFlow messageToMessageFlow
) )
@ -82,7 +104,7 @@ class GatewaySpec
messageFuture.map { messageFuture.map {
case message: TextMessage.Strict => case message: TextMessage.Strict =>
val actualResponse = parse(message.text).getOrElse(Json.Null) val actualResponse = parse(message.text).getOrElse(Json.Null)
assert(actualResponse === testJsons.expectedResponse) assert(actualResponse === testJson.expectedResponse)
case _ => assert(false, "binary or streamed text message") case _ => assert(false, "binary or streamed text message")
} }
} }

View File

@ -0,0 +1,43 @@
package org.enso.gateway
import akka.actor.{ActorRef, ActorSystem}
import akka.testkit.{ImplicitSender, TestKit}
import org.enso.{Gateway, LanguageServer}
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import org.enso.gateway.TestMessage.{Initialize, Shutdown}
import org.enso.gateway.TestNotification.{Exit, Initialized}
class GatewayMessageSpec()
extends TestKit(ActorSystem("GatewayMessageSpec"))
with ImplicitSender
with WordSpecLike
with Matchers
with BeforeAndAfterAll {
private val languageServerActorName = "testingLanguageServer"
private val gatewayActorName = "testingGateway"
private val languageServer: ActorRef =
system.actorOf(LanguageServer.props(null), languageServerActorName)
protected val gateway: ActorRef =
system.actorOf(Gateway.props(languageServer), gatewayActorName)
override def afterAll: Unit = {
TestKit.shutdownActorSystem(system)
}
"Gateway" must {
"properly handle init/shutdown workflow" in {
gateway ! Initialize.request
expectMsg(Initialize.response)
gateway ! Initialized.notification
expectNoMessage()
gateway ! Shutdown.request
expectMsg(Shutdown.response)
gateway ! Exit.notification
expectNoMessage()
}
}
}

View File

@ -81,4 +81,21 @@ object TestJson {
} }
}""" }"""
} }
object Shutdown extends TestJson {
val request =
json"""
{
"jsonrpc": "2.0",
"id": 10,
"method": "shutdown"
}"""
val expectedResponse =
json"""
{
"jsonrpc" : "2.0",
"id" : 10
}"""
}
} }

Some files were not shown because too many files have changed in this diff Show More