mirror of
https://github.com/enso-org/enso.git
synced 2024-11-25 21:25:20 +03:00
Scaffold the Project Manager (#610)
This commit is contained in:
parent
530d54bce0
commit
2863498da3
39
build.sbt
39
build.sbt
@ -347,9 +347,19 @@ lazy val project_manager = (project in file("common/project-manager"))
|
||||
)
|
||||
.settings(
|
||||
libraryDependencies ++= akka,
|
||||
libraryDependencies ++= circe
|
||||
libraryDependencies ++= circe,
|
||||
libraryDependencies ++= Seq(
|
||||
// config
|
||||
"com.typesafe" % "config" % "1.4.0",
|
||||
"com.github.pureconfig" %% "pureconfig" % "0.12.2",
|
||||
// logging
|
||||
"ch.qos.logback" % "logback-classic" % "1.2.3",
|
||||
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2"
|
||||
)
|
||||
)
|
||||
.dependsOn(pkg)
|
||||
.dependsOn(`json-rpc-server`)
|
||||
.dependsOn(`json-rpc-server-test` % Test)
|
||||
|
||||
//////////////////////
|
||||
//// Sub Projects ////
|
||||
@ -453,6 +463,8 @@ lazy val language_server = (project in file("engine/language-server"))
|
||||
)
|
||||
)
|
||||
.dependsOn(polyglot_api)
|
||||
.dependsOn(`json-rpc-server`)
|
||||
.dependsOn(`json-rpc-server-test` % Test)
|
||||
|
||||
lazy val runtime = (project in file("engine/runtime"))
|
||||
.configs(Benchmark)
|
||||
@ -611,3 +623,28 @@ lazy val runner = project
|
||||
.dependsOn(pkg)
|
||||
.dependsOn(language_server)
|
||||
.dependsOn(polyglot_api)
|
||||
|
||||
lazy val `json-rpc-server` = project
|
||||
.in(file("common/json-rpc-server"))
|
||||
.settings(
|
||||
libraryDependencies ++= akka,
|
||||
libraryDependencies ++= circe,
|
||||
libraryDependencies ++= Seq(
|
||||
"io.circe" %% "circe-literal" % circeVersion,
|
||||
akkaTestkit % Test,
|
||||
"org.scalatest" %% "scalatest" % "3.2.0-M2" % Test
|
||||
)
|
||||
)
|
||||
|
||||
lazy val `json-rpc-server-test` = project
|
||||
.in(file("common/json-rpc-server-test"))
|
||||
.settings(
|
||||
libraryDependencies ++= akka,
|
||||
libraryDependencies ++= circe,
|
||||
libraryDependencies ++= Seq(
|
||||
"io.circe" %% "circe-literal" % circeVersion,
|
||||
akkaTestkit,
|
||||
"org.scalatest" %% "scalatest" % "3.2.0-M2"
|
||||
)
|
||||
)
|
||||
.dependsOn(`json-rpc-server`)
|
||||
|
@ -1,35 +1,26 @@
|
||||
package org.enso.languageserver.websocket
|
||||
import java.nio.file.Files
|
||||
import java.util.UUID
|
||||
package org.enso.jsonrpc.test
|
||||
|
||||
import akka.NotUsed
|
||||
import akka.actor.{ActorRef, ActorSystem, PoisonPill, Props}
|
||||
import akka.actor.{ActorRef, ActorSystem, PoisonPill}
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model.ws.{Message, TextMessage, WebSocketRequest}
|
||||
import akka.stream.OverflowStrategy
|
||||
import akka.stream.scaladsl.{Flow, Sink, Source}
|
||||
import akka.testkit.{ImplicitSender, TestKit, TestProbe}
|
||||
import cats.effect.IO
|
||||
import io.circe.Json
|
||||
import io.circe.parser.parse
|
||||
import org.enso.languageserver.capability.CapabilityRouter
|
||||
import org.enso.languageserver.data.{Config, Sha3_224VersionCalculator}
|
||||
import org.enso.languageserver.{
|
||||
LanguageProtocol,
|
||||
LanguageServer,
|
||||
WebSocketServer
|
||||
}
|
||||
import org.enso.languageserver.filemanager.FileSystem
|
||||
import org.enso.languageserver.runtime.RuntimeConnector
|
||||
import org.enso.languageserver.text.BufferRegistry
|
||||
import org.scalatest.{Assertion, BeforeAndAfterAll, BeforeAndAfterEach}
|
||||
import org.enso.jsonrpc.{ClientControllerFactory, JsonRpcServer, Protocol}
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpecLike
|
||||
import org.scalatest.{Assertion, BeforeAndAfterAll, BeforeAndAfterEach}
|
||||
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration._
|
||||
|
||||
abstract class WebSocketServerTest
|
||||
/**
|
||||
* Test kit for testing JSON RPC servers.
|
||||
*/
|
||||
abstract class JsonRpcServerTestKit
|
||||
extends TestKit(ActorSystem("TestSystem"))
|
||||
with ImplicitSender
|
||||
with AnyWordSpecLike
|
||||
@ -44,37 +35,16 @@ abstract class WebSocketServerTest
|
||||
val interface = "127.0.0.1"
|
||||
var address: String = _
|
||||
|
||||
val testContentRoot = Files.createTempDirectory(null)
|
||||
val testContentRootId = UUID.randomUUID()
|
||||
val config = Config(Map(testContentRootId -> testContentRoot.toFile))
|
||||
|
||||
testContentRoot.toFile.deleteOnExit()
|
||||
|
||||
var server: WebSocketServer = _
|
||||
var server: JsonRpcServer = _
|
||||
var binding: Http.ServerBinding = _
|
||||
|
||||
def protocol: Protocol
|
||||
|
||||
def clientControllerFactory: ClientControllerFactory
|
||||
|
||||
override def beforeEach(): Unit = {
|
||||
val languageServer =
|
||||
system.actorOf(
|
||||
Props(new LanguageServer(config, new FileSystem[IO]))
|
||||
)
|
||||
languageServer ! LanguageProtocol.Initialize
|
||||
val bufferRegistry =
|
||||
system.actorOf(
|
||||
BufferRegistry.props(languageServer)(Sha3_224VersionCalculator)
|
||||
)
|
||||
|
||||
lazy val capabilityRouter =
|
||||
system.actorOf(CapabilityRouter.props(bufferRegistry))
|
||||
|
||||
lazy val runtimeConnector = system.actorOf(RuntimeConnector.props)
|
||||
|
||||
server = new WebSocketServer(
|
||||
languageServer,
|
||||
bufferRegistry,
|
||||
capabilityRouter,
|
||||
runtimeConnector
|
||||
)
|
||||
server = new JsonRpcServer(protocol, clientControllerFactory)
|
||||
binding = Await.result(server.bind(interface, port = 0), 3.seconds)
|
||||
address = s"ws://$interface:${binding.localAddress.getPort}"
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package org.enso.jsonrpc
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import akka.actor.ActorRef
|
||||
|
||||
/**
|
||||
* Classes implementing this trait are responsible for creating client
|
||||
* controllers upon a new connection. An client controller handles
|
||||
* communications between a single client and the JSON RPC server.
|
||||
*/
|
||||
trait ClientControllerFactory {
|
||||
|
||||
/**
|
||||
* Creates a client controller actor.
|
||||
*
|
||||
* @param clientId the internal client id.
|
||||
* @return an actor ref to the client controller
|
||||
*/
|
||||
def createClientController(clientId: UUID): ActorRef
|
||||
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
package org.enso.languageserver.jsonrpc
|
||||
package org.enso.jsonrpc
|
||||
|
||||
import io.circe.Decoder.Result
|
||||
import io.circe._
|
||||
|
@ -1,4 +1,4 @@
|
||||
package org.enso.languageserver
|
||||
package org.enso.jsonrpc
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
@ -6,50 +6,27 @@ import akka.NotUsed
|
||||
import akka.actor.{ActorRef, ActorSystem, Props}
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model.ws.{BinaryMessage, Message, TextMessage}
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.server.Directives.{get, handleWebSocketMessages, path}
|
||||
import akka.http.scaladsl.server.Route
|
||||
import akka.stream.scaladsl.{Flow, Sink, Source}
|
||||
import akka.stream.{Materializer, OverflowStrategy}
|
||||
import org.enso.languageserver.jsonrpc.MessageHandler
|
||||
import akka.stream.scaladsl.{Flow, Sink, Source}
|
||||
|
||||
import scala.concurrent.duration.{FiniteDuration, _}
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
object WebSocketServer {
|
||||
|
||||
/**
|
||||
* A configuration object for properties of the WebSocketServer.
|
||||
*
|
||||
* @param outgoingBufferSize the number of messages buffered internally
|
||||
* if the downstream connection is lagging behind.
|
||||
* @param lazyMessageTimeout the timeout for downloading the whole of a lazy
|
||||
* stream message from the user.
|
||||
*/
|
||||
case class Config(outgoingBufferSize: Int, lazyMessageTimeout: FiniteDuration)
|
||||
|
||||
case object Config {
|
||||
|
||||
/**
|
||||
* Creates a default instance of [[Config]].
|
||||
*
|
||||
* @return a default config.
|
||||
*/
|
||||
def default: Config =
|
||||
Config(outgoingBufferSize = 10, lazyMessageTimeout = 10.seconds)
|
||||
}
|
||||
}
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* Exposes a multi-client Lanugage Server instance over WebSocket connections.
|
||||
* @param languageServer an instance of a running and initialized Language
|
||||
* Server.
|
||||
* Exposes a multi-client JSON RPC Server instance over WebSocket connections.
|
||||
*
|
||||
* @param protocol a protocol supported be the server
|
||||
* @param clientControllerFactory a factory used to create a client controller
|
||||
* @param config a server config
|
||||
* @param system an actor system
|
||||
* @param materializer a materializer
|
||||
*/
|
||||
class WebSocketServer(
|
||||
languageServer: ActorRef,
|
||||
bufferRegistry: ActorRef,
|
||||
capabilityRouter: ActorRef,
|
||||
runtimeConnector: ActorRef,
|
||||
config: WebSocketServer.Config = WebSocketServer.Config.default
|
||||
class JsonRpcServer(
|
||||
protocol: Protocol,
|
||||
clientControllerFactory: ClientControllerFactory,
|
||||
config: JsonRpcServer.Config = JsonRpcServer.Config.default
|
||||
)(
|
||||
implicit val system: ActorSystem,
|
||||
implicit val materializer: Materializer
|
||||
@ -57,27 +34,15 @@ class WebSocketServer(
|
||||
|
||||
implicit val ec: ExecutionContext = system.dispatcher
|
||||
|
||||
private val newConnectionPath: String = ""
|
||||
|
||||
private def newUser(): Flow[Message, Message, NotUsed] = {
|
||||
val clientId = UUID.randomUUID()
|
||||
val clientActor =
|
||||
system.actorOf(
|
||||
Props(
|
||||
new ClientController(
|
||||
clientId,
|
||||
languageServer,
|
||||
bufferRegistry,
|
||||
capabilityRouter
|
||||
)
|
||||
)
|
||||
)
|
||||
val clientId = UUID.randomUUID()
|
||||
val clientActor = clientControllerFactory.createClientController(clientId)
|
||||
|
||||
val messageHandler =
|
||||
system.actorOf(
|
||||
Props(new MessageHandler(ClientApi.protocol, clientActor))
|
||||
Props(new MessageHandler(protocol, clientActor))
|
||||
)
|
||||
clientActor ! ClientApi.WebConnect(messageHandler)
|
||||
clientActor ! JsonRpcServer.WebConnect(messageHandler)
|
||||
|
||||
val incomingMessages: Sink[Message, NotUsed] =
|
||||
Flow[Message]
|
||||
@ -117,7 +82,7 @@ class WebSocketServer(
|
||||
Flow.fromSinkAndSource(incomingMessages, outgoingMessages)
|
||||
}
|
||||
|
||||
private val route: Route = path(newConnectionPath) {
|
||||
private val route: Route = path(config.path) {
|
||||
get { handleWebSocketMessages(newUser()) }
|
||||
}
|
||||
|
||||
@ -132,3 +97,35 @@ class WebSocketServer(
|
||||
def bind(interface: String, port: Int): Future[Http.ServerBinding] =
|
||||
Http().bindAndHandle(route, interface, port)
|
||||
}
|
||||
|
||||
object JsonRpcServer {
|
||||
|
||||
/**
|
||||
* A configuration object for properties of the JsonRpcServer.
|
||||
*
|
||||
* @param outgoingBufferSize the number of messages buffered internally
|
||||
* if the downstream connection is lagging behind.
|
||||
* @param lazyMessageTimeout the timeout for downloading the whole of a lazy
|
||||
* stream message from the user.
|
||||
* @param path the http path that the server listen to.
|
||||
*/
|
||||
case class Config(
|
||||
outgoingBufferSize: Int,
|
||||
lazyMessageTimeout: FiniteDuration,
|
||||
path: String = ""
|
||||
)
|
||||
|
||||
case object Config {
|
||||
|
||||
/**
|
||||
* Creates a default instance of [[Config]].
|
||||
*
|
||||
* @return a default config.
|
||||
*/
|
||||
def default: Config =
|
||||
Config(outgoingBufferSize = 10, lazyMessageTimeout = 10.seconds)
|
||||
}
|
||||
|
||||
case class WebConnect(webActor: ActorRef)
|
||||
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package org.enso.languageserver.jsonrpc
|
||||
package org.enso.jsonrpc
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Stash}
|
||||
import io.circe.Json
|
||||
import org.enso.languageserver.jsonrpc.Errors.InvalidParams
|
||||
import org.enso.jsonrpc.Errors.InvalidParams
|
||||
|
||||
/**
|
||||
* An actor responsible for passing parsed massages between the web and
|
@ -1,4 +1,4 @@
|
||||
package org.enso.languageserver.jsonrpc
|
||||
package org.enso.jsonrpc
|
||||
|
||||
import io.circe.{Decoder, Encoder, Json}
|
||||
|
@ -1,31 +1,18 @@
|
||||
package org.enso.languageserver
|
||||
package org.enso.jsonrpc
|
||||
|
||||
import akka.actor.{ActorRef, ActorSystem, Props}
|
||||
import akka.testkit.{ImplicitSender, TestKit, TestProbe}
|
||||
import io.circe.Json
|
||||
import io.circe.literal._
|
||||
import io.circe.parser._
|
||||
import org.enso.languageserver.jsonrpc.MessageHandler.{Connected, WebMessage}
|
||||
import org.enso.languageserver.jsonrpc.{
|
||||
Error,
|
||||
HasParams,
|
||||
HasResult,
|
||||
Id,
|
||||
MessageHandler,
|
||||
Method,
|
||||
Notification,
|
||||
Protocol,
|
||||
Request,
|
||||
ResponseError,
|
||||
ResponseResult,
|
||||
Unused
|
||||
}
|
||||
import org.enso.jsonrpc.MessageHandler.{Connected, WebMessage}
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpecLike
|
||||
import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class MessageHandlerTest
|
||||
class MessageHandlerSpec
|
||||
extends TestKit(ActorSystem("TestSystem"))
|
||||
with ImplicitSender
|
||||
with AnyWordSpecLike
|
@ -16,4 +16,6 @@ project-manager {
|
||||
tutorials {
|
||||
github-organisation = "luna-packages"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
akka.http.server.idle-timeout = infinite
|
@ -1,30 +0,0 @@
|
||||
package org.enso.projectmanager
|
||||
|
||||
import akka.http.scaladsl.model.Uri
|
||||
import akka.http.scaladsl.model.Uri.Path
|
||||
import akka.http.scaladsl.server.{PathMatcher0, PathMatcher1}
|
||||
import akka.http.scaladsl.server.PathMatchers.JavaUUID
|
||||
import org.enso.projectmanager.model.ProjectId
|
||||
|
||||
class RouteHelper {
|
||||
|
||||
val tutorials: String = "tutorials"
|
||||
val projects: String = "projects"
|
||||
val thumb: String = "thumb"
|
||||
|
||||
val tutorialsPath: Path = Path / tutorials
|
||||
val tutorialsPathMatcher: PathMatcher0 = tutorials
|
||||
|
||||
val projectsPath: Path = Path / projects
|
||||
val projectsPathMatcher: PathMatcher0 = projects
|
||||
|
||||
def projectPath(id: ProjectId): Path = projectsPath / id.toString
|
||||
|
||||
val projectPathMatcher: PathMatcher1[ProjectId] =
|
||||
(projectsPathMatcher / JavaUUID).map(ProjectId)
|
||||
|
||||
def thumbPath(id: ProjectId): Path = projectPath(id) / thumb
|
||||
val thumbPathMatcher: PathMatcher1[ProjectId] = projectPathMatcher / thumb
|
||||
|
||||
def uriFor(base: Uri, path: Path): Uri = base.withPath(path)
|
||||
}
|
@ -1,168 +0,0 @@
|
||||
package org.enso.projectmanager
|
||||
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.Scheduler
|
||||
import akka.actor.typed.scaladsl.AskPattern._
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model.{HttpResponse, StatusCodes, Uri}
|
||||
import akka.http.scaladsl.server.{Directives, Route}
|
||||
import akka.util.Timeout
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import org.enso.projectmanager.api.{ProjectFactory, ProjectJsonSupport}
|
||||
import org.enso.projectmanager.model.{Project, ProjectId}
|
||||
import org.enso.projectmanager.services._
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
case class Server(
|
||||
host: String,
|
||||
port: Int,
|
||||
repository: ActorRef[ProjectsCommand],
|
||||
routeHelper: RouteHelper,
|
||||
apiFactory: ProjectFactory
|
||||
)(implicit val system: ActorSystem,
|
||||
implicit val executor: ExecutionContext,
|
||||
implicit val askTimeout: Timeout)
|
||||
extends Directives
|
||||
with ProjectJsonSupport {
|
||||
|
||||
implicit val scheduler: Scheduler = system.scheduler.toTyped
|
||||
|
||||
def projectDoesNotExistResponse(id: ProjectId): HttpResponse =
|
||||
HttpResponse(StatusCodes.NotFound, entity = s"Project $id does not exist")
|
||||
|
||||
def thumbDoesNotExistResponse: HttpResponse =
|
||||
HttpResponse(StatusCodes.NotFound, entity = "Thumbnail does not exist")
|
||||
|
||||
def withSuccess[T](
|
||||
fut: Future[T],
|
||||
errorResponse: HttpResponse = HttpResponse(StatusCodes.InternalServerError)
|
||||
)(successHandler: T => Route
|
||||
): Route = {
|
||||
onComplete(fut) {
|
||||
case Success(r) => successHandler(r)
|
||||
case Failure(_) => complete(errorResponse)
|
||||
}
|
||||
}
|
||||
|
||||
def withProject(id: ProjectId)(route: Project => Route): Route = {
|
||||
val projectFuture =
|
||||
repository
|
||||
.ask(
|
||||
(ref: ActorRef[GetProjectResponse]) => GetProjectById(id, ref)
|
||||
)
|
||||
.map(_.project)
|
||||
withSuccess(projectFuture) {
|
||||
case Some(project) => route(project)
|
||||
case None => complete(projectDoesNotExistResponse(id))
|
||||
}
|
||||
}
|
||||
|
||||
def listProjectsWith(
|
||||
reqBuilder: ActorRef[ListProjectsResponse] => ProjectsCommand
|
||||
)(baseUri: Uri
|
||||
): Route = {
|
||||
val projectsFuture = repository.ask(reqBuilder)
|
||||
withSuccess(projectsFuture) { projectsResponse =>
|
||||
val response = projectsResponse.projects.toSeq.map {
|
||||
case (id, project) => apiFactory.fromModel(id, project, baseUri)
|
||||
}
|
||||
complete(response)
|
||||
}
|
||||
}
|
||||
|
||||
def createProject(baseUri: Uri): Route = {
|
||||
val projectFuture = repository.ask(
|
||||
(ref: ActorRef[CreateTemporaryResponse]) =>
|
||||
CreateTemporary("NewProject", ref)
|
||||
)
|
||||
withSuccess(projectFuture) { response =>
|
||||
complete(apiFactory.fromModel(response.id, response.project, baseUri))
|
||||
}
|
||||
}
|
||||
|
||||
def getThumb(projectId: ProjectId): Route = {
|
||||
withProject(projectId) { project =>
|
||||
if (project.pkg.hasThumb) getFromFile(project.pkg.thumbFile)
|
||||
else complete(thumbDoesNotExistResponse)
|
||||
}
|
||||
}
|
||||
|
||||
val route: Route = ignoreTrailingSlash {
|
||||
path(routeHelper.projectsPathMatcher)(
|
||||
(get & extractUri)(listProjectsWith(ListProjectsRequest)) ~
|
||||
(post & extractUri)(createProject)
|
||||
) ~
|
||||
(get & path(routeHelper.tutorialsPathMatcher) & extractUri)(
|
||||
listProjectsWith(ListTutorialsRequest)
|
||||
) ~
|
||||
(get & path(routeHelper.thumbPathMatcher))(getThumb)
|
||||
}
|
||||
|
||||
def serve: Future[Http.ServerBinding] = {
|
||||
Http().bindAndHandle(route, host, port)
|
||||
}
|
||||
}
|
||||
|
||||
object Server {
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
|
||||
val config = ConfigFactory.load.getConfig("project-manager")
|
||||
val serverConfig = config.getConfig("server")
|
||||
val storageConfig = config.getConfig("storage")
|
||||
|
||||
val host = serverConfig.getString("host")
|
||||
val port = serverConfig.getInt("port")
|
||||
|
||||
val timeout =
|
||||
FiniteDuration(
|
||||
serverConfig.getDuration("timeout").toNanos,
|
||||
TimeUnit.NANOSECONDS
|
||||
)
|
||||
|
||||
implicit val system: ActorSystem = ActorSystem("project-manager")
|
||||
implicit val executor: ExecutionContext = system.dispatcher
|
||||
implicit val askTimeout: Timeout = new Timeout(timeout)
|
||||
|
||||
val localProjectsPath =
|
||||
new File(storageConfig.getString("local-projects-path"))
|
||||
val tmpProjectsPath = new File(
|
||||
storageConfig.getString("temporary-projects-path")
|
||||
)
|
||||
val tutorialsPath =
|
||||
new File(storageConfig.getString("tutorials-path"))
|
||||
val tutorialsCachePath =
|
||||
new File(storageConfig.getString("tutorials-cache-path"))
|
||||
|
||||
val tutorialsDownloader =
|
||||
TutorialsDownloader(
|
||||
tutorialsPath,
|
||||
tutorialsCachePath,
|
||||
config.getString("tutorials.github-organisation")
|
||||
)
|
||||
val storageManager = StorageManager(
|
||||
localProjectsPath,
|
||||
tmpProjectsPath,
|
||||
tutorialsPath
|
||||
)
|
||||
|
||||
val repoActor = system.spawn(
|
||||
ProjectsService.behavior(storageManager, tutorialsDownloader),
|
||||
"projects-repository"
|
||||
)
|
||||
|
||||
val routeHelper = new RouteHelper
|
||||
val apiFactory = ProjectFactory(routeHelper)
|
||||
|
||||
val server = Server(host, port, repoActor, routeHelper, apiFactory)
|
||||
server.serve: Unit
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ package org.enso.projectmanager.api
|
||||
|
||||
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
|
||||
import akka.http.scaladsl.model.Uri
|
||||
import org.enso.projectmanager.{RouteHelper, model}
|
||||
import org.enso.projectmanager.model
|
||||
import org.enso.projectmanager.model.ProjectId
|
||||
import spray.json.DefaultJsonProtocol
|
||||
|
||||
@ -11,24 +11,21 @@ case class Project(
|
||||
name: String,
|
||||
path: String,
|
||||
thumb: Option[String],
|
||||
persisted: Boolean)
|
||||
persisted: Boolean
|
||||
)
|
||||
|
||||
case class ProjectFactory(routeHelper: RouteHelper) {
|
||||
case class ProjectFactory() {
|
||||
|
||||
def fromModel(
|
||||
id: ProjectId,
|
||||
project: model.Project,
|
||||
baseUri: Uri
|
||||
): Project = {
|
||||
val thumbUri =
|
||||
if (project.hasThumb)
|
||||
Some(routeHelper.uriFor(baseUri, routeHelper.thumbPath(id)))
|
||||
else None
|
||||
Project(
|
||||
id.toString,
|
||||
project.pkg.name,
|
||||
project.pkg.root.getAbsolutePath,
|
||||
thumbUri.map(_.toString),
|
||||
None,
|
||||
project.isPersistent
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
package org.enso.projectmanager.main
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.SystemMaterializer
|
||||
import org.enso.jsonrpc.JsonRpcServer
|
||||
import org.enso.projectmanager.main.configuration.ProjectManagerConfig
|
||||
import org.enso.projectmanager.protocol.{
|
||||
JsonRpc,
|
||||
ManagerClientControllerFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* A main module containing all components of the project manager.
|
||||
*
|
||||
* @param config a server config
|
||||
*/
|
||||
class MainModule(config: ProjectManagerConfig) {
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
|
||||
implicit val materializer = SystemMaterializer.get(system)
|
||||
|
||||
lazy val clientControllerFactory = new ManagerClientControllerFactory(system)
|
||||
|
||||
lazy val server = new JsonRpcServer(JsonRpc.protocol, clientControllerFactory)
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package org.enso.projectmanager.main
|
||||
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
import org.enso.projectmanager.main.configuration.ProjectManagerConfig
|
||||
import pureconfig.ConfigSource
|
||||
|
||||
import scala.concurrent.Await
|
||||
import scala.io.StdIn
|
||||
import scala.concurrent.duration._
|
||||
import pureconfig.generic.auto._
|
||||
|
||||
/**
|
||||
* Project manager runner containing the main method.
|
||||
*/
|
||||
object ProjectManager extends App with LazyLogging {
|
||||
|
||||
logger.info("Starting Language Server...")
|
||||
|
||||
val config: ProjectManagerConfig =
|
||||
ConfigSource
|
||||
.resources("application.conf")
|
||||
.withFallback(ConfigSource.systemProperties)
|
||||
.at("project-manager")
|
||||
.loadOrThrow[ProjectManagerConfig]
|
||||
|
||||
val mainModule = new MainModule(config)
|
||||
|
||||
val binding =
|
||||
Await.result(
|
||||
mainModule.server.bind(config.server.host, config.server.port),
|
||||
3.seconds
|
||||
)
|
||||
|
||||
logger.info(
|
||||
s"Started server at ${config.server.host}:${config.server.port}, press enter to kill server"
|
||||
)
|
||||
StdIn.readLine()
|
||||
logger.info("Stopping server...")
|
||||
|
||||
binding.unbind()
|
||||
mainModule.system.terminate()
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.enso.projectmanager.main
|
||||
|
||||
object configuration {
|
||||
|
||||
/**
|
||||
* A configuration object for properties of the Project Manager.
|
||||
*
|
||||
* @param server a JSON RPC server configuration
|
||||
*/
|
||||
case class ProjectManagerConfig(server: ServerConfig)
|
||||
|
||||
/**
|
||||
* A configuration object for properties of the JSON RPC server.
|
||||
*
|
||||
* @param host an address that the server listen on
|
||||
* @param port a port that the server listen on
|
||||
*/
|
||||
case class ServerConfig(host: String, port: Int)
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package org.enso.projectmanager.protocol
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import akka.actor.{Actor, Props}
|
||||
|
||||
/**
|
||||
* An actor handling communications between a single client and the project
|
||||
* manager.
|
||||
*
|
||||
* @param clientId the internal client id.
|
||||
*/
|
||||
class ClientController(clientId: UUID) extends Actor {
|
||||
override def receive: Receive = ???
|
||||
}
|
||||
|
||||
object ClientController {
|
||||
|
||||
/**
|
||||
* Creates a configuration object used to create a [[ClientController]].
|
||||
*
|
||||
* @param clientId the internal client id.
|
||||
* @return a configuration object
|
||||
*/
|
||||
def props(clientId: UUID): Props = Props(new ClientController(clientId))
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package org.enso.projectmanager.protocol
|
||||
|
||||
import io.circe.generic.auto._
|
||||
import org.enso.jsonrpc.Protocol
|
||||
import org.enso.projectmanager.protocol.ProjectManagementApi.ProjectCreate
|
||||
|
||||
object JsonRpc {
|
||||
|
||||
/**
|
||||
* A description of supported JSON RPC messages.
|
||||
*/
|
||||
lazy val protocol: Protocol =
|
||||
Protocol.empty
|
||||
.registerRequest(ProjectCreate)
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package org.enso.projectmanager.protocol
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import akka.actor.{ActorRef, ActorSystem}
|
||||
import org.enso.jsonrpc.ClientControllerFactory
|
||||
|
||||
/**
|
||||
* Project manager client controller factory.
|
||||
*
|
||||
* @param system the actor system
|
||||
*/
|
||||
class ManagerClientControllerFactory(system: ActorSystem)
|
||||
extends ClientControllerFactory {
|
||||
|
||||
/**
|
||||
* Creates a client controller actor.
|
||||
*
|
||||
* @param clientId the internal client id.
|
||||
* @return an actor ref to the client controller
|
||||
*/
|
||||
override def createClientController(clientId: UUID): ActorRef =
|
||||
system.actorOf(ClientController.props(clientId))
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package org.enso.projectmanager.protocol
|
||||
|
||||
import org.enso.jsonrpc.{HasParams, HasResult, Method, Unused}
|
||||
|
||||
/**
|
||||
* The project management JSON RPC API provided by the project manager.
|
||||
* See [[https://github.com/luna/enso/blob/master/doc/design/engine/engine-services.md]]
|
||||
* for message specifications.
|
||||
*/
|
||||
object ProjectManagementApi {
|
||||
|
||||
case object ProjectCreate extends Method("project/create") {
|
||||
|
||||
case class Params(name: String)
|
||||
|
||||
implicit val hasParams = new HasParams[this.type] {
|
||||
type Params = ProjectCreate.Params
|
||||
}
|
||||
implicit val hasResult = new HasResult[this.type] {
|
||||
type Result = Unused.type
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -2,13 +2,11 @@ package org.enso.languageserver
|
||||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.UUID
|
||||
|
||||
import akka.actor.{ActorRef, ActorSystem, Props}
|
||||
import akka.actor.{ActorSystem, Props}
|
||||
import akka.stream.SystemMaterializer
|
||||
import cats.effect.IO
|
||||
import org.enso.languageserver
|
||||
import org.enso.jsonrpc.JsonRpcServer
|
||||
import org.enso.languageserver.capability.CapabilityRouter
|
||||
import org.enso.languageserver.data.{
|
||||
Config,
|
||||
@ -16,9 +14,10 @@ import org.enso.languageserver.data.{
|
||||
Sha3_224VersionCalculator
|
||||
}
|
||||
import org.enso.languageserver.filemanager.{FileSystem, FileSystemApi}
|
||||
import org.enso.languageserver.protocol.{JsonRpc, ServerClientControllerFactory}
|
||||
import org.enso.languageserver.runtime.RuntimeConnector
|
||||
import org.enso.languageserver.text.BufferRegistry
|
||||
import org.enso.polyglot.{RuntimeApi, LanguageInfo, RuntimeServerInfo}
|
||||
import org.enso.polyglot.{LanguageInfo, RuntimeServerInfo}
|
||||
import org.graalvm.polyglot.Context
|
||||
import org.graalvm.polyglot.io.MessageEndpoint
|
||||
|
||||
@ -74,11 +73,13 @@ class MainModule(serverConfig: LanguageServerConfig) {
|
||||
})
|
||||
.build()
|
||||
|
||||
lazy val clientControllerFactory = new ServerClientControllerFactory(
|
||||
languageServer,
|
||||
bufferRegistry,
|
||||
capabilityRouter
|
||||
)
|
||||
|
||||
lazy val server =
|
||||
new WebSocketServer(
|
||||
languageServer,
|
||||
bufferRegistry,
|
||||
capabilityRouter,
|
||||
runtimeConnector
|
||||
)
|
||||
new JsonRpcServer(JsonRpc.protocol, clientControllerFactory)
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.enso.languageserver.capability
|
||||
|
||||
import org.enso.languageserver.data.CapabilityRegistration
|
||||
import org.enso.languageserver.jsonrpc.{HasParams, HasResult, Method, Unused}
|
||||
import org.enso.jsonrpc.{HasParams, HasResult, Method, Unused}
|
||||
|
||||
/**
|
||||
* The capability JSON RPC API provided by the language server.
|
||||
|
@ -1,12 +1,6 @@
|
||||
package org.enso.languageserver.filemanager
|
||||
|
||||
import org.enso.languageserver.jsonrpc.{
|
||||
Error,
|
||||
HasParams,
|
||||
HasResult,
|
||||
Method,
|
||||
Unused
|
||||
}
|
||||
import org.enso.jsonrpc.{Error, HasParams, HasResult, Method, Unused}
|
||||
|
||||
/**
|
||||
* The file manager JSON RPC API provided by the language server.
|
||||
|
@ -9,7 +9,7 @@ import org.enso.languageserver.filemanager.FileManagerApi.{
|
||||
NotDirectoryError,
|
||||
OperationTimeoutError
|
||||
}
|
||||
import org.enso.languageserver.jsonrpc.Error
|
||||
import org.enso.jsonrpc.Error
|
||||
|
||||
object FileSystemFailureMapper {
|
||||
|
||||
|
@ -1,8 +1,12 @@
|
||||
package org.enso.languageserver
|
||||
package org.enso.languageserver.protocol
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import akka.actor.{Actor, ActorLogging, ActorRef, Props, Stash}
|
||||
import akka.pattern.ask
|
||||
import akka.util.Timeout
|
||||
import org.enso.jsonrpc.Errors.ServiceError
|
||||
import org.enso.jsonrpc._
|
||||
import org.enso.languageserver.capability.CapabilityApi.{
|
||||
AcquireCapability,
|
||||
ForceReleaseCapability,
|
||||
@ -21,64 +25,22 @@ import org.enso.languageserver.filemanager.{
|
||||
FileManagerProtocol,
|
||||
FileSystemFailureMapper
|
||||
}
|
||||
import org.enso.languageserver.jsonrpc.Errors.ServiceError
|
||||
import org.enso.languageserver.jsonrpc._
|
||||
import org.enso.languageserver.requesthandler.{
|
||||
AcquireCapabilityHandler,
|
||||
ApplyEditHandler,
|
||||
CloseFileHandler,
|
||||
OpenFileHandler,
|
||||
ReleaseCapabilityHandler,
|
||||
SaveFileHandler
|
||||
}
|
||||
import org.enso.languageserver.text.TextApi.{
|
||||
ApplyEdit,
|
||||
CloseFile,
|
||||
OpenFile,
|
||||
SaveFile,
|
||||
TextDidChange
|
||||
}
|
||||
import org.enso.languageserver.requesthandler._
|
||||
import org.enso.languageserver.text.TextApi._
|
||||
import org.enso.languageserver.text.TextProtocol
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
/**
|
||||
* The JSON RPC API provided by the language server.
|
||||
* See [[https://github.com/luna/enso/blob/master/doc/design/engine/engine-services.md]]
|
||||
* for message specifications.
|
||||
*/
|
||||
object ClientApi {
|
||||
import io.circe.generic.auto._
|
||||
|
||||
val protocol: Protocol = Protocol.empty
|
||||
.registerRequest(AcquireCapability)
|
||||
.registerRequest(ReleaseCapability)
|
||||
.registerRequest(WriteFile)
|
||||
.registerRequest(ReadFile)
|
||||
.registerRequest(CreateFile)
|
||||
.registerRequest(OpenFile)
|
||||
.registerRequest(CloseFile)
|
||||
.registerRequest(SaveFile)
|
||||
.registerRequest(ApplyEdit)
|
||||
.registerRequest(DeleteFile)
|
||||
.registerRequest(CopyFile)
|
||||
.registerRequest(MoveFile)
|
||||
.registerRequest(ExistsFile)
|
||||
.registerRequest(TreeFile)
|
||||
.registerNotification(ForceReleaseCapability)
|
||||
.registerNotification(GrantCapability)
|
||||
.registerNotification(TextDidChange)
|
||||
|
||||
case class WebConnect(webActor: ActorRef)
|
||||
}
|
||||
|
||||
/**
|
||||
* An actor handling communications between a single client and the language
|
||||
* server.
|
||||
*
|
||||
* @param clientId the internal client id.
|
||||
* @param server the language server actor.
|
||||
* @param server the language server actor ref.
|
||||
* @param bufferRegistry a router that dispatches text editing requests
|
||||
* @param capabilityRouter a router that dispatches capability requests
|
||||
* @param requestTimeout a request timeout
|
||||
*/
|
||||
class ClientController(
|
||||
val clientId: Client.Id,
|
||||
@ -114,7 +76,7 @@ class ClientController(
|
||||
log.warning("Received unknown message: {}", message)
|
||||
|
||||
override def receive: Receive = {
|
||||
case ClientApi.WebConnect(webActor) =>
|
||||
case JsonRpcServer.WebConnect(webActor) =>
|
||||
context.system.eventStream
|
||||
.publish(ClientConnected(Client(clientId, self)))
|
||||
unstashAll()
|
||||
@ -343,3 +305,34 @@ class ClientController(
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object ClientController {
|
||||
|
||||
/**
|
||||
* Creates a configuration object used to create a [[ClientController]].
|
||||
*
|
||||
* @param clientId the internal client id.
|
||||
* @param server the language server actor ref.
|
||||
* @param bufferRegistry a router that dispatches text editing requests
|
||||
* @param capabilityRouter a router that dispatches capability requests
|
||||
* @param requestTimeout a request timeout
|
||||
* @return a configuration object
|
||||
*/
|
||||
def props(
|
||||
clientId: UUID,
|
||||
server: ActorRef,
|
||||
bufferRegistry: ActorRef,
|
||||
capabilityRouter: ActorRef,
|
||||
requestTimeout: FiniteDuration = 10.seconds
|
||||
): Props =
|
||||
Props(
|
||||
new ClientController(
|
||||
clientId,
|
||||
server,
|
||||
bufferRegistry,
|
||||
capabilityRouter,
|
||||
requestTimeout
|
||||
)
|
||||
)
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package org.enso.languageserver.protocol
|
||||
|
||||
import io.circe.generic.auto._
|
||||
import org.enso.jsonrpc.Protocol
|
||||
import org.enso.languageserver.capability.CapabilityApi.{
|
||||
AcquireCapability,
|
||||
ForceReleaseCapability,
|
||||
GrantCapability,
|
||||
ReleaseCapability
|
||||
}
|
||||
import org.enso.languageserver.filemanager.FileManagerApi._
|
||||
import org.enso.languageserver.text.TextApi._
|
||||
|
||||
object JsonRpc {
|
||||
|
||||
/**
|
||||
* A description of supported JSON RPC messages.
|
||||
*/
|
||||
val protocol: Protocol = Protocol.empty
|
||||
.registerRequest(AcquireCapability)
|
||||
.registerRequest(ReleaseCapability)
|
||||
.registerRequest(WriteFile)
|
||||
.registerRequest(ReadFile)
|
||||
.registerRequest(CreateFile)
|
||||
.registerRequest(OpenFile)
|
||||
.registerRequest(CloseFile)
|
||||
.registerRequest(SaveFile)
|
||||
.registerRequest(ApplyEdit)
|
||||
.registerRequest(DeleteFile)
|
||||
.registerRequest(CopyFile)
|
||||
.registerRequest(MoveFile)
|
||||
.registerRequest(ExistsFile)
|
||||
.registerRequest(TreeFile)
|
||||
.registerNotification(ForceReleaseCapability)
|
||||
.registerNotification(GrantCapability)
|
||||
.registerNotification(TextDidChange)
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package org.enso.languageserver.protocol
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import akka.actor.{ActorRef, ActorSystem}
|
||||
import org.enso.jsonrpc.ClientControllerFactory
|
||||
|
||||
/**
|
||||
* Language server client controller factory.
|
||||
*
|
||||
* @param server the language server actor ref
|
||||
* @param bufferRegistry the buffer registry actor ref
|
||||
* @param capabilityRouter the capability router actor ref
|
||||
* @param system the actor system
|
||||
*/
|
||||
class ServerClientControllerFactory(
|
||||
server: ActorRef,
|
||||
bufferRegistry: ActorRef,
|
||||
capabilityRouter: ActorRef
|
||||
)(implicit system: ActorSystem)
|
||||
extends ClientControllerFactory {
|
||||
|
||||
/**
|
||||
* Creates a client controller actor.
|
||||
*
|
||||
* @param clientId the internal client id.
|
||||
* @return
|
||||
*/
|
||||
override def createClientController(clientId: UUID): ActorRef =
|
||||
system.actorOf(
|
||||
ClientController.props(clientId, server, bufferRegistry, capabilityRouter)
|
||||
)
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package org.enso.languageserver.requesthandler
|
||||
|
||||
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
|
||||
import org.enso.jsonrpc.Errors.ServiceError
|
||||
import org.enso.jsonrpc._
|
||||
import org.enso.languageserver.capability.CapabilityApi.AcquireCapability
|
||||
import org.enso.languageserver.capability.CapabilityProtocol
|
||||
import org.enso.languageserver.capability.CapabilityProtocol.{
|
||||
@ -8,8 +10,6 @@ import org.enso.languageserver.capability.CapabilityProtocol.{
|
||||
CapabilityAcquisitionBadRequest
|
||||
}
|
||||
import org.enso.languageserver.data.{CapabilityRegistration, Client}
|
||||
import org.enso.languageserver.jsonrpc.Errors.ServiceError
|
||||
import org.enso.languageserver.jsonrpc._
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
|
@ -1,24 +1,12 @@
|
||||
package org.enso.languageserver.requesthandler
|
||||
|
||||
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
|
||||
import org.enso.jsonrpc.Errors.ServiceError
|
||||
import org.enso.jsonrpc._
|
||||
import org.enso.languageserver.data.Client
|
||||
import org.enso.languageserver.jsonrpc.Errors.ServiceError
|
||||
import org.enso.languageserver.jsonrpc._
|
||||
import org.enso.languageserver.text.TextApi.{
|
||||
ApplyEdit,
|
||||
FileNotOpenedError,
|
||||
InvalidVersionError,
|
||||
TextEditValidationError,
|
||||
WriteDeniedError
|
||||
}
|
||||
import org.enso.languageserver.text.TextApi._
|
||||
import org.enso.languageserver.text.TextProtocol
|
||||
import org.enso.languageserver.text.TextProtocol.{
|
||||
ApplyEditSuccess,
|
||||
FileNotOpened,
|
||||
TextEditInvalidVersion,
|
||||
TextEditValidationFailed,
|
||||
WriteDenied
|
||||
}
|
||||
import org.enso.languageserver.text.TextProtocol.{ApplyEdit => _, _}
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
package org.enso.languageserver.requesthandler
|
||||
|
||||
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
|
||||
import org.enso.jsonrpc.Errors.ServiceError
|
||||
import org.enso.jsonrpc._
|
||||
import org.enso.languageserver.data.Client
|
||||
import org.enso.languageserver.jsonrpc.Errors.ServiceError
|
||||
import org.enso.languageserver.jsonrpc._
|
||||
import org.enso.languageserver.text.TextApi.{CloseFile, FileNotOpenedError}
|
||||
import org.enso.languageserver.text.TextProtocol
|
||||
import org.enso.languageserver.text.TextProtocol.{FileClosed, FileNotOpened}
|
||||
|
@ -1,15 +1,10 @@
|
||||
package org.enso.languageserver.requesthandler
|
||||
|
||||
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
|
||||
import org.enso.jsonrpc.Errors.ServiceError
|
||||
import org.enso.jsonrpc.{Id, Request, ResponseError, ResponseResult}
|
||||
import org.enso.languageserver.data.Client
|
||||
import org.enso.languageserver.filemanager.FileSystemFailureMapper
|
||||
import org.enso.languageserver.jsonrpc.Errors.ServiceError
|
||||
import org.enso.languageserver.jsonrpc.{
|
||||
Id,
|
||||
Request,
|
||||
ResponseError,
|
||||
ResponseResult
|
||||
}
|
||||
import org.enso.languageserver.text.TextApi.OpenFile
|
||||
import org.enso.languageserver.text.TextProtocol
|
||||
import org.enso.languageserver.text.TextProtocol.{
|
||||
|
@ -1,6 +1,8 @@
|
||||
package org.enso.languageserver.requesthandler
|
||||
|
||||
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
|
||||
import org.enso.jsonrpc.Errors.ServiceError
|
||||
import org.enso.jsonrpc._
|
||||
import org.enso.languageserver.capability.CapabilityApi.ReleaseCapability
|
||||
import org.enso.languageserver.capability.CapabilityProtocol
|
||||
import org.enso.languageserver.capability.CapabilityProtocol.{
|
||||
@ -8,8 +10,6 @@ import org.enso.languageserver.capability.CapabilityProtocol.{
|
||||
CapabilityReleased
|
||||
}
|
||||
import org.enso.languageserver.data.{CapabilityRegistration, Client}
|
||||
import org.enso.languageserver.jsonrpc.Errors.ServiceError
|
||||
import org.enso.languageserver.jsonrpc._
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
package org.enso.languageserver.requesthandler
|
||||
|
||||
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
|
||||
import org.enso.jsonrpc.Errors.ServiceError
|
||||
import org.enso.jsonrpc._
|
||||
import org.enso.languageserver.data.Client
|
||||
import org.enso.languageserver.filemanager.FileSystemFailureMapper
|
||||
import org.enso.languageserver.jsonrpc.Errors.ServiceError
|
||||
import org.enso.languageserver.jsonrpc._
|
||||
import org.enso.languageserver.text.TextApi.{
|
||||
FileNotOpenedError,
|
||||
InvalidVersionError,
|
||||
@ -12,13 +12,7 @@ import org.enso.languageserver.text.TextApi.{
|
||||
WriteDeniedError
|
||||
}
|
||||
import org.enso.languageserver.text.TextProtocol
|
||||
import org.enso.languageserver.text.TextProtocol.{
|
||||
FileNotOpened,
|
||||
FileSaved,
|
||||
SaveDenied,
|
||||
SaveFailed,
|
||||
SaveFileInvalidVersion
|
||||
}
|
||||
import org.enso.languageserver.text.TextProtocol._
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
|
@ -2,13 +2,7 @@ package org.enso.languageserver.text
|
||||
|
||||
import org.enso.languageserver.data.CapabilityRegistration
|
||||
import org.enso.languageserver.filemanager.Path
|
||||
import org.enso.languageserver.jsonrpc.{
|
||||
Error,
|
||||
HasParams,
|
||||
HasResult,
|
||||
Method,
|
||||
Unused
|
||||
}
|
||||
import org.enso.jsonrpc.{Error, HasParams, HasResult, Method, Unused}
|
||||
import org.enso.languageserver.text.editing.model.FileEdit
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,48 @@
|
||||
package org.enso.languageserver.websocket
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.util.UUID
|
||||
|
||||
import akka.actor.Props
|
||||
import cats.effect.IO
|
||||
import org.enso.jsonrpc.{ClientControllerFactory, Protocol}
|
||||
import org.enso.jsonrpc.test.JsonRpcServerTestKit
|
||||
import org.enso.languageserver.{LanguageProtocol, LanguageServer}
|
||||
import org.enso.languageserver.capability.CapabilityRouter
|
||||
import org.enso.languageserver.data.{Config, Sha3_224VersionCalculator}
|
||||
import org.enso.languageserver.filemanager.FileSystem
|
||||
import org.enso.languageserver.protocol.{JsonRpc, ServerClientControllerFactory}
|
||||
import org.enso.languageserver.text.BufferRegistry
|
||||
|
||||
class BaseServerTest extends JsonRpcServerTestKit {
|
||||
|
||||
val testContentRoot = Files.createTempDirectory(null)
|
||||
val testContentRootId = UUID.randomUUID()
|
||||
val config = Config(Map(testContentRootId -> testContentRoot.toFile))
|
||||
|
||||
testContentRoot.toFile.deleteOnExit()
|
||||
|
||||
override def protocol: Protocol = JsonRpc.protocol
|
||||
|
||||
override def clientControllerFactory: ClientControllerFactory = {
|
||||
val languageServer =
|
||||
system.actorOf(
|
||||
Props(new LanguageServer(config, new FileSystem[IO]))
|
||||
)
|
||||
languageServer ! LanguageProtocol.Initialize
|
||||
val bufferRegistry =
|
||||
system.actorOf(
|
||||
BufferRegistry.props(languageServer)(Sha3_224VersionCalculator)
|
||||
)
|
||||
|
||||
lazy val capabilityRouter =
|
||||
system.actorOf(CapabilityRouter.props(bufferRegistry))
|
||||
|
||||
new ServerClientControllerFactory(
|
||||
languageServer,
|
||||
bufferRegistry,
|
||||
capabilityRouter
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -6,7 +6,7 @@ import java.util.UUID
|
||||
import io.circe.literal._
|
||||
import org.apache.commons.io.FileUtils
|
||||
|
||||
class FileManagerTest extends WebSocketServerTest {
|
||||
class FileManagerTest extends BaseServerTest {
|
||||
"File Server" must {
|
||||
|
||||
"write textual content to a file" in {
|
||||
|
@ -5,7 +5,7 @@ import io.circe.literal._
|
||||
import org.enso.languageserver.event.BufferClosed
|
||||
import org.enso.languageserver.filemanager.Path
|
||||
|
||||
class TextOperationsTest extends WebSocketServerTest {
|
||||
class TextOperationsTest extends BaseServerTest {
|
||||
|
||||
"text/openFile" must {
|
||||
"fail opening a file if it does not exist" in {
|
||||
|
@ -1,21 +1,10 @@
|
||||
package org.enso.runner
|
||||
|
||||
import java.io.File
|
||||
|
||||
import akka.actor.{ActorSystem, Props}
|
||||
import akka.stream.{ActorMaterializer, SystemMaterializer}
|
||||
import cats.effect.IO
|
||||
import org.enso.interpreter.instrument.ReplDebuggerInstrument
|
||||
import org.enso.languageserver.data.Config
|
||||
import org.enso.languageserver.filemanager.FileSystem
|
||||
import org.enso.languageserver.{
|
||||
LanguageProtocol,
|
||||
LanguageServer,
|
||||
LanguageServerConfig,
|
||||
MainModule,
|
||||
WebSocketServer
|
||||
MainModule
|
||||
}
|
||||
import org.enso.polyglot.LanguageInfo
|
||||
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration._
|
||||
@ -47,6 +36,7 @@ object LanguageServerApp {
|
||||
s"Started server at ${config.interface}:${config.port}, press enter to kill server"
|
||||
)
|
||||
StdIn.readLine()
|
||||
binding.terminate(10.seconds)
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user