mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 22:10:15 +03:00
New Language Server API Implementations / Mocks (#1875)
This commit is contained in:
parent
4235d345aa
commit
86fcd86055
@ -1,5 +1,12 @@
|
|||||||
# Enso Next
|
# Enso Next
|
||||||
|
|
||||||
|
## Tooling
|
||||||
|
|
||||||
|
- Implement parts of the new Language Server API related to library support
|
||||||
|
([#1875](https://github.com/enso-org/enso/pull/1875)). Parts of the API are
|
||||||
|
still mocked internally, but they are supported externally for testing
|
||||||
|
purposes.
|
||||||
|
|
||||||
# Enso 0.2.14 (2021-07-15)
|
# Enso 0.2.14 (2021-07-15)
|
||||||
|
|
||||||
## Interpreter/Runtime
|
## Interpreter/Runtime
|
||||||
|
20
build.sbt
20
build.sbt
@ -233,6 +233,7 @@ lazy val enso = (project in file("."))
|
|||||||
logger.jvm,
|
logger.jvm,
|
||||||
pkg,
|
pkg,
|
||||||
cli,
|
cli,
|
||||||
|
`task-progress-notifications`,
|
||||||
`logging-utils`,
|
`logging-utils`,
|
||||||
`logging-service`,
|
`logging-service`,
|
||||||
`akka-native`,
|
`akka-native`,
|
||||||
@ -716,6 +717,20 @@ lazy val cli = project
|
|||||||
Test / parallelExecution := false
|
Test / parallelExecution := false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
lazy val `task-progress-notifications` = project
|
||||||
|
.in(file("lib/scala/task-progress-notifications"))
|
||||||
|
.configs(Test)
|
||||||
|
.settings(
|
||||||
|
version := "0.1",
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
"com.beachape" %% "enumeratum-circe" % enumeratumCirceVersion,
|
||||||
|
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
||||||
|
),
|
||||||
|
Test / parallelExecution := false
|
||||||
|
)
|
||||||
|
.dependsOn(cli)
|
||||||
|
.dependsOn(`json-rpc-server`)
|
||||||
|
|
||||||
lazy val `version-output` = (project in file("lib/scala/version-output"))
|
lazy val `version-output` = (project in file("lib/scala/version-output"))
|
||||||
.settings(
|
.settings(
|
||||||
version := "0.1"
|
version := "0.1"
|
||||||
@ -798,6 +813,7 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager"))
|
|||||||
.dependsOn(`version-output`)
|
.dependsOn(`version-output`)
|
||||||
.dependsOn(editions)
|
.dependsOn(editions)
|
||||||
.dependsOn(cli)
|
.dependsOn(cli)
|
||||||
|
.dependsOn(`task-progress-notifications`)
|
||||||
.dependsOn(`polyglot-api`)
|
.dependsOn(`polyglot-api`)
|
||||||
.dependsOn(`runtime-version-manager`)
|
.dependsOn(`runtime-version-manager`)
|
||||||
.dependsOn(`library-manager`)
|
.dependsOn(`library-manager`)
|
||||||
@ -977,6 +993,7 @@ lazy val `language-server` = (project in file("engine/language-server"))
|
|||||||
),
|
),
|
||||||
Test / testOptions += Tests
|
Test / testOptions += Tests
|
||||||
.Argument(TestFrameworks.ScalaCheck, "-minSuccessfulTests", "1000"),
|
.Argument(TestFrameworks.ScalaCheck, "-minSuccessfulTests", "1000"),
|
||||||
|
Test / envVars ++= distributionEnvironmentOverrides,
|
||||||
GenerateFlatbuffers.flatcVersion := flatbuffersVersion,
|
GenerateFlatbuffers.flatcVersion := flatbuffersVersion,
|
||||||
Compile / sourceGenerators += GenerateFlatbuffers.task
|
Compile / sourceGenerators += GenerateFlatbuffers.task
|
||||||
)
|
)
|
||||||
@ -991,6 +1008,8 @@ lazy val `language-server` = (project in file("engine/language-server"))
|
|||||||
)
|
)
|
||||||
.dependsOn(`json-rpc-server-test` % Test)
|
.dependsOn(`json-rpc-server-test` % Test)
|
||||||
.dependsOn(`json-rpc-server`)
|
.dependsOn(`json-rpc-server`)
|
||||||
|
.dependsOn(`task-progress-notifications`)
|
||||||
|
.dependsOn(`library-manager`)
|
||||||
.dependsOn(`logging-service`)
|
.dependsOn(`logging-service`)
|
||||||
.dependsOn(`polyglot-api`)
|
.dependsOn(`polyglot-api`)
|
||||||
.dependsOn(`searcher`)
|
.dependsOn(`searcher`)
|
||||||
@ -998,6 +1017,7 @@ lazy val `language-server` = (project in file("engine/language-server"))
|
|||||||
.dependsOn(`version-output`)
|
.dependsOn(`version-output`)
|
||||||
.dependsOn(pkg)
|
.dependsOn(pkg)
|
||||||
.dependsOn(testkit % Test)
|
.dependsOn(testkit % Test)
|
||||||
|
.dependsOn(`runtime-version-manager-test` % Test)
|
||||||
|
|
||||||
lazy val ast = (project in file("lib/scala/ast"))
|
lazy val ast = (project in file("lib/scala/ast"))
|
||||||
.settings(
|
.settings(
|
||||||
|
@ -4014,13 +4014,11 @@ the repositories and include them in the result as well.
|
|||||||
> available. In the future it should emit warnings using proper notification
|
> available. In the future it should emit warnings using proper notification
|
||||||
> channels.
|
> channels.
|
||||||
|
|
||||||
The `update` field is optional and if it is not provided, it defaults to false.
|
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
{
|
{
|
||||||
update?: Boolean;
|
update: Boolean;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,27 +1,26 @@
|
|||||||
package org.enso.languageserver.boot
|
package org.enso.languageserver.boot
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
import java.net.URI
|
|
||||||
import java.time.Clock
|
|
||||||
|
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
|
import org.enso.distribution.{
|
||||||
|
DistributionManager,
|
||||||
|
EditionManager,
|
||||||
|
Environment,
|
||||||
|
LanguageHome
|
||||||
|
}
|
||||||
|
import org.enso.editions.EditionResolver
|
||||||
import org.enso.jsonrpc.JsonRpcServer
|
import org.enso.jsonrpc.JsonRpcServer
|
||||||
import org.enso.languageserver.boot.DeploymentType.{Azure, Desktop}
|
import org.enso.languageserver.boot.DeploymentType.{Azure, Desktop}
|
||||||
import org.enso.languageserver.capability.CapabilityRouter
|
import org.enso.languageserver.capability.CapabilityRouter
|
||||||
import org.enso.languageserver.data._
|
import org.enso.languageserver.data._
|
||||||
import org.enso.languageserver.effect.ZioExec
|
import org.enso.languageserver.effect.ZioExec
|
||||||
import org.enso.languageserver.filemanager.{
|
import org.enso.languageserver.filemanager._
|
||||||
ContentRoot,
|
|
||||||
ContentRootManager,
|
|
||||||
ContentRootManagerActor,
|
|
||||||
ContentRootManagerWrapper,
|
|
||||||
ContentRootWithFile,
|
|
||||||
FileManager,
|
|
||||||
FileSystem,
|
|
||||||
ReceivesTreeUpdatesHandler
|
|
||||||
}
|
|
||||||
import org.enso.languageserver.http.server.BinaryWebSocketServer
|
import org.enso.languageserver.http.server.BinaryWebSocketServer
|
||||||
import org.enso.languageserver.io._
|
import org.enso.languageserver.io._
|
||||||
|
import org.enso.languageserver.libraries.{
|
||||||
|
EditionReferenceResolver,
|
||||||
|
LocalLibraryManager,
|
||||||
|
ProjectSettingsManager
|
||||||
|
}
|
||||||
import org.enso.languageserver.monitoring.{
|
import org.enso.languageserver.monitoring.{
|
||||||
HealthCheckEndpoint,
|
HealthCheckEndpoint,
|
||||||
IdlenessEndpoint,
|
IdlenessEndpoint,
|
||||||
@ -49,6 +48,9 @@ import org.graalvm.polyglot.Context
|
|||||||
import org.graalvm.polyglot.io.MessageEndpoint
|
import org.graalvm.polyglot.io.MessageEndpoint
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URI
|
||||||
|
import java.time.Clock
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
/** A main module containing all components of the server.
|
/** A main module containing all components of the server.
|
||||||
@ -270,20 +272,48 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
|
|||||||
context
|
context
|
||||||
)(system.dispatcher)
|
)(system.dispatcher)
|
||||||
|
|
||||||
|
val environment = new Environment {}
|
||||||
|
val languageHome = LanguageHome.detectFromExecutableLocation(environment)
|
||||||
|
val distributionManager = new DistributionManager(environment)
|
||||||
|
|
||||||
|
val editionProvider =
|
||||||
|
EditionManager.makeEditionProvider(distributionManager, Some(languageHome))
|
||||||
|
val editionResolver = EditionResolver(editionProvider)
|
||||||
|
val editionReferenceResolver = new EditionReferenceResolver(
|
||||||
|
contentRoot.file,
|
||||||
|
editionProvider,
|
||||||
|
editionResolver
|
||||||
|
)
|
||||||
|
val editionManager = EditionManager(distributionManager, Some(languageHome))
|
||||||
|
|
||||||
|
val projectSettingsManager = system.actorOf(
|
||||||
|
ProjectSettingsManager.props(contentRoot.file, editionResolver),
|
||||||
|
"project-settings-manager"
|
||||||
|
)
|
||||||
|
|
||||||
|
val localLibraryManager = system.actorOf(
|
||||||
|
LocalLibraryManager.props(contentRoot.file, distributionManager),
|
||||||
|
"local-library-manager"
|
||||||
|
)
|
||||||
|
|
||||||
val jsonRpcControllerFactory = new JsonConnectionControllerFactory(
|
val jsonRpcControllerFactory = new JsonConnectionControllerFactory(
|
||||||
initializationComponent,
|
mainComponent = initializationComponent,
|
||||||
bufferRegistry,
|
bufferRegistry = bufferRegistry,
|
||||||
capabilityRouter,
|
capabilityRouter = capabilityRouter,
|
||||||
fileManager,
|
fileManager = fileManager,
|
||||||
contentRootManagerActor,
|
contentRootManager = contentRootManagerActor,
|
||||||
contextRegistry,
|
contextRegistry = contextRegistry,
|
||||||
suggestionsHandler,
|
suggestionsHandler = suggestionsHandler,
|
||||||
stdOutController,
|
stdOutController = stdOutController,
|
||||||
stdErrController,
|
stdErrController = stdErrController,
|
||||||
stdInController,
|
stdInController = stdInController,
|
||||||
runtimeConnector,
|
runtimeConnector = runtimeConnector,
|
||||||
idlenessMonitor,
|
idlenessMonitor = idlenessMonitor,
|
||||||
languageServerConfig
|
projectSettingsManager = projectSettingsManager,
|
||||||
|
localLibraryManager = localLibraryManager,
|
||||||
|
editionReferenceResolver = editionReferenceResolver,
|
||||||
|
editionManager = editionManager,
|
||||||
|
config = languageServerConfig
|
||||||
)
|
)
|
||||||
log.trace(
|
log.trace(
|
||||||
"Created JSON connection controller factory [{}].",
|
"Created JSON connection controller factory [{}].",
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package org.enso.languageserver.libraries
|
||||||
|
|
||||||
|
import io.circe.{Decoder, DecodingFailure, Encoder, Json}
|
||||||
|
import io.circe.syntax._
|
||||||
|
import io.circe.generic.auto._
|
||||||
|
|
||||||
|
/** A reference to an edition - either a named edition or an unnamed one
|
||||||
|
* associated with the current project.
|
||||||
|
*/
|
||||||
|
sealed trait EditionReference
|
||||||
|
object EditionReference {
|
||||||
|
|
||||||
|
/** An edition identified by its name. */
|
||||||
|
case class NamedEdition(editionName: String) extends EditionReference
|
||||||
|
|
||||||
|
/** The edition associated with the current project. */
|
||||||
|
case object CurrentProjectEdition extends EditionReference
|
||||||
|
|
||||||
|
object CodecField {
|
||||||
|
val Type = "type"
|
||||||
|
val EditionName = "editionName"
|
||||||
|
}
|
||||||
|
|
||||||
|
object CodecType {
|
||||||
|
val NamedEdition = "NamedEdition"
|
||||||
|
val CurrentProjectEdition = "CurrentProjectEdition"
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val encoder: Encoder[EditionReference] = {
|
||||||
|
case NamedEdition(editionName) =>
|
||||||
|
Json.obj(
|
||||||
|
CodecField.Type -> CodecType.NamedEdition.asJson,
|
||||||
|
CodecField.EditionName -> editionName.asJson
|
||||||
|
)
|
||||||
|
case CurrentProjectEdition =>
|
||||||
|
Json.obj(CodecField.Type -> CodecType.CurrentProjectEdition.asJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val decoder: Decoder[EditionReference] = { json =>
|
||||||
|
val typeCursor = json.downField(CodecField.Type)
|
||||||
|
typeCursor.as[String].flatMap {
|
||||||
|
case CodecType.NamedEdition =>
|
||||||
|
Decoder[NamedEdition].tryDecode(json)
|
||||||
|
case CodecType.CurrentProjectEdition => Right(CurrentProjectEdition)
|
||||||
|
case unknownType =>
|
||||||
|
Left(
|
||||||
|
DecodingFailure(
|
||||||
|
s"Unknown EditionReference type [$unknownType].",
|
||||||
|
typeCursor.history
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package org.enso.languageserver.libraries
|
||||||
|
|
||||||
|
import org.enso.editions.provider.EditionProvider
|
||||||
|
import org.enso.editions.{DefaultEdition, EditionResolver, Editions}
|
||||||
|
import org.enso.languageserver.libraries.EditionReference.NamedEdition
|
||||||
|
import org.enso.pkg.PackageManager
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
|
/** Resolves [[EditionReference]] to a raw or resolved edition. */
|
||||||
|
class EditionReferenceResolver(
|
||||||
|
projectRoot: File,
|
||||||
|
editionProvider: EditionProvider,
|
||||||
|
editionResolver: EditionResolver
|
||||||
|
) {
|
||||||
|
private lazy val projectPackage =
|
||||||
|
PackageManager.Default.loadPackage(projectRoot).get
|
||||||
|
|
||||||
|
/** Loads the raw edition corresponding to the given [[EditionReference]]. */
|
||||||
|
def resolveReference(
|
||||||
|
editionReference: EditionReference
|
||||||
|
): Try[Editions.RawEdition] = editionReference match {
|
||||||
|
case EditionReference.NamedEdition(editionName) =>
|
||||||
|
editionProvider.findEditionForName(editionName)
|
||||||
|
case EditionReference.CurrentProjectEdition =>
|
||||||
|
Try {
|
||||||
|
projectPackage.config.edition.getOrElse {
|
||||||
|
// TODO [RW] default edition from config (#1864)
|
||||||
|
DefaultEdition.getDefaultEdition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Resolves all edition dependencies of an edition identified by
|
||||||
|
* [[EditionReference]].
|
||||||
|
*/
|
||||||
|
def resolveEdition(
|
||||||
|
editionReference: EditionReference
|
||||||
|
): Try[Editions.ResolvedEdition] = for {
|
||||||
|
raw <- resolveReference(editionReference)
|
||||||
|
resolved <- editionResolver.resolve(raw).toTry
|
||||||
|
} yield resolved
|
||||||
|
|
||||||
|
/** Resolves all edition dependencies of an edition identified by its name. */
|
||||||
|
def resolveEdition(name: String): Try[Editions.ResolvedEdition] =
|
||||||
|
resolveEdition(NamedEdition(name))
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package org.enso.languageserver.libraries
|
||||||
|
|
||||||
|
import org.enso.cli.task.{
|
||||||
|
ProgressReporter,
|
||||||
|
ProgressUnit,
|
||||||
|
TaskProgress,
|
||||||
|
TaskProgressImplementation
|
||||||
|
}
|
||||||
|
|
||||||
|
import scala.util.Success
|
||||||
|
|
||||||
|
/** A temporary helper for mocked parts of the API.
|
||||||
|
*
|
||||||
|
* It should be removed soon, when the missing parts are implemented.
|
||||||
|
*/
|
||||||
|
object FakeDownload {
|
||||||
|
|
||||||
|
/** Creates a [[TaskProgress]] which reports progress updates for a few seconds.
|
||||||
|
*
|
||||||
|
* Intended for mocking download-like endpoints.
|
||||||
|
*/
|
||||||
|
def make(seconds: Int = 10): TaskProgress[Unit] = {
|
||||||
|
val tracker = new TaskProgressImplementation[Unit](ProgressUnit.Bytes)
|
||||||
|
val thread = new Thread(() => {
|
||||||
|
val n = (seconds * 10).toLong
|
||||||
|
for (i <- 0L to n) {
|
||||||
|
tracker.reportProgress(i, Some(n))
|
||||||
|
Thread.sleep(100)
|
||||||
|
}
|
||||||
|
tracker.setComplete(Success(()))
|
||||||
|
})
|
||||||
|
thread.start()
|
||||||
|
tracker
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Simulates a download operation reporting progress updates to the
|
||||||
|
* [[ProgressReporter]].
|
||||||
|
*/
|
||||||
|
def simulateDownload(
|
||||||
|
message: String,
|
||||||
|
progressReporter: ProgressReporter,
|
||||||
|
seconds: Int = 10
|
||||||
|
): Unit = {
|
||||||
|
val download = make(seconds = seconds)
|
||||||
|
progressReporter.trackProgress(message, download)
|
||||||
|
download.force()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,235 @@
|
|||||||
|
package org.enso.languageserver.libraries
|
||||||
|
|
||||||
|
import io.circe.Json
|
||||||
|
import io.circe.literal.JsonStringContext
|
||||||
|
import org.enso.editions.{LibraryName, LibraryVersion}
|
||||||
|
import org.enso.jsonrpc.{Error, HasParams, HasResult, Method, Unused}
|
||||||
|
|
||||||
|
object LibraryApi {
|
||||||
|
case object EditionsListAvailable extends Method("editions/listAvailable") {
|
||||||
|
self =>
|
||||||
|
|
||||||
|
case class Params(update: Boolean)
|
||||||
|
|
||||||
|
case class Result(editionNames: Seq[String])
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = self.Params
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = self.Result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object EditionsResolve extends Method("editions/resolve") {
|
||||||
|
self =>
|
||||||
|
|
||||||
|
case class Params(edition: EditionReference)
|
||||||
|
|
||||||
|
case class Result(engineVersion: String)
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = self.Params
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = self.Result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object EditionsGetProjectSettings
|
||||||
|
extends Method("editions/getProjectSettings") { self =>
|
||||||
|
|
||||||
|
case class Result(
|
||||||
|
parentEdition: Option[String],
|
||||||
|
preferLocalLibraries: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = Unused.type
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = self.Result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object EditionsSetParentEdition
|
||||||
|
extends Method("editions/setParentEdition") { self =>
|
||||||
|
|
||||||
|
case class Params(newEditionName: String)
|
||||||
|
|
||||||
|
case class Result(needsRestart: Option[Boolean])
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = self.Params
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = self.Result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object EditionsSetLocalLibrariesPreference
|
||||||
|
extends Method("editions/setProjectLocalLibrariesPreference") { self =>
|
||||||
|
|
||||||
|
case class Params(preferLocalLibraries: Boolean)
|
||||||
|
|
||||||
|
case class Result(needsRestart: Option[Boolean])
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = self.Params
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = self.Result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object EditionsListDefinedLibraries
|
||||||
|
extends Method("editions/listDefinedLibraries") { self =>
|
||||||
|
|
||||||
|
case class Params(edition: EditionReference)
|
||||||
|
|
||||||
|
case class Result(availableLibraries: Seq[LibraryEntry])
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = self.Params
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = self.Result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object LibraryListLocal extends Method("library/listLocal") { self =>
|
||||||
|
|
||||||
|
case class Result(localLibraries: Seq[LibraryEntry])
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = Unused.type
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = self.Result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object LibraryCreate extends Method("library/create") { self =>
|
||||||
|
|
||||||
|
case class Params(
|
||||||
|
namespace: String,
|
||||||
|
name: String,
|
||||||
|
authors: Seq[String],
|
||||||
|
maintainers: Seq[String],
|
||||||
|
license: String
|
||||||
|
)
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = self.Params
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = Unused.type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object LibraryGetMetadata extends Method("library/getMetadata") { self =>
|
||||||
|
|
||||||
|
case class Params(namespace: String, name: String)
|
||||||
|
|
||||||
|
case class Result(description: Option[String], tagLine: Option[String])
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = self.Params
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = self.Result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object LibrarySetMetadata extends Method("library/setMetadata") { self =>
|
||||||
|
|
||||||
|
case class Params(
|
||||||
|
namespace: String,
|
||||||
|
name: String,
|
||||||
|
description: Option[String],
|
||||||
|
tagLine: Option[String]
|
||||||
|
)
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = self.Params
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = Unused.type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object LibraryPublish extends Method("library/publish") { self =>
|
||||||
|
|
||||||
|
case class Params(
|
||||||
|
namespace: String,
|
||||||
|
name: String,
|
||||||
|
authToken: String,
|
||||||
|
bumpVersionAfterPublish: Option[Boolean]
|
||||||
|
)
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = self.Params
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = Unused.type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object LibraryPreinstall extends Method("library/preinstall") { self =>
|
||||||
|
|
||||||
|
case class Params(namespace: String, name: String)
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = self.Params
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = Unused.type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class EditionNotFoundError(editionName: String)
|
||||||
|
extends Error(8001, s"Edition [$editionName] could not be found.") {
|
||||||
|
override def payload: Option[Json] = Some(
|
||||||
|
json""" { "editionName" : $editionName } """
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class LibraryAlreadyExists(libraryName: LibraryName)
|
||||||
|
extends Error(8002, s"Library [$libraryName] already exists.")
|
||||||
|
|
||||||
|
case class LibraryRepositoryAuthenticationError(reason: String)
|
||||||
|
extends Error(8003, s"Authentication failed: $reason.")
|
||||||
|
|
||||||
|
case class LibraryPublishError(reason: String)
|
||||||
|
extends Error(8004, s"Could not publish the library: $reason.")
|
||||||
|
|
||||||
|
case class LibraryUploadError(reason: String)
|
||||||
|
extends Error(8005, s"Could not upload the library: $reason.")
|
||||||
|
|
||||||
|
case class LibraryDownloadError(
|
||||||
|
name: LibraryName,
|
||||||
|
version: LibraryVersion,
|
||||||
|
reason: String
|
||||||
|
) extends Error(8006, s"Could not download the library: $reason.") {
|
||||||
|
override def payload: Option[Json] = Some(
|
||||||
|
json""" {
|
||||||
|
"namespace" : ${name.namespace},
|
||||||
|
"name" : ${name.name},
|
||||||
|
"version" : ${version.toString}
|
||||||
|
} """
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class LocalLibraryNotFound(libraryName: LibraryName)
|
||||||
|
extends Error(8007, s"Local library [$libraryName] has not been found.")
|
||||||
|
|
||||||
|
case class LibraryNotResolved(name: LibraryName)
|
||||||
|
extends Error(8008, s"Could not resolve [$name].") {
|
||||||
|
override def payload: Option[Json] = Some(
|
||||||
|
json""" {
|
||||||
|
"namespace" : ${name.namespace},
|
||||||
|
"name" : ${name.name}
|
||||||
|
} """
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
package org.enso.languageserver.libraries
|
||||||
|
|
||||||
|
import io.circe.generic.semiauto._
|
||||||
|
import io.circe.syntax._
|
||||||
|
import io.circe.{Decoder, DecodingFailure, Encoder, Json}
|
||||||
|
import org.enso.editions
|
||||||
|
|
||||||
|
/** An entry in library lists sent to the client.
|
||||||
|
*
|
||||||
|
* @param namespace namespace of the library
|
||||||
|
* @param name name of the library
|
||||||
|
* @param version version of the library
|
||||||
|
*/
|
||||||
|
case class LibraryEntry(
|
||||||
|
namespace: String,
|
||||||
|
name: String,
|
||||||
|
version: LibraryEntry.LibraryVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
object LibraryEntry {
|
||||||
|
|
||||||
|
/** Version of a library. */
|
||||||
|
sealed trait LibraryVersion
|
||||||
|
|
||||||
|
/** A library version that references a locally editable version of the
|
||||||
|
* library.
|
||||||
|
*/
|
||||||
|
case object LocalLibraryVersion extends LibraryVersion
|
||||||
|
|
||||||
|
/** A library version that references a version of the library published in
|
||||||
|
* some repository.
|
||||||
|
*/
|
||||||
|
case class PublishedLibraryVersion(version: String, repositoryUrl: String)
|
||||||
|
extends LibraryVersion
|
||||||
|
|
||||||
|
/** Converts an instance of [[editions.LibraryVersion]] into one that is used
|
||||||
|
* in the Language Server protocol.
|
||||||
|
*/
|
||||||
|
implicit def convertLibraryVersion(
|
||||||
|
libraryVersion: editions.LibraryVersion
|
||||||
|
): LibraryVersion = libraryVersion match {
|
||||||
|
case editions.LibraryVersion.Local => LocalLibraryVersion
|
||||||
|
case editions.LibraryVersion.Published(version, repository) =>
|
||||||
|
PublishedLibraryVersion(version.toString, repository.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val encoder: Encoder[LibraryEntry] = deriveEncoder[LibraryEntry]
|
||||||
|
implicit val decoder: Decoder[LibraryEntry] = deriveDecoder[LibraryEntry]
|
||||||
|
|
||||||
|
object CodecField {
|
||||||
|
val Type = "type"
|
||||||
|
val Version = "version"
|
||||||
|
val RepositoryUrl = "repositoryUrl"
|
||||||
|
}
|
||||||
|
|
||||||
|
object CodecType {
|
||||||
|
val LocalLibraryVersion = "LocalLibraryVersion"
|
||||||
|
val PublishedLibraryVersion = "PublishedLibraryVersion"
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val versionEncoder: Encoder[LibraryVersion] = {
|
||||||
|
case LocalLibraryVersion =>
|
||||||
|
Json.obj(CodecField.Type -> CodecType.LocalLibraryVersion.asJson)
|
||||||
|
case PublishedLibraryVersion(version, repositoryUrl) =>
|
||||||
|
Json.obj(
|
||||||
|
CodecField.Type -> CodecType.PublishedLibraryVersion.asJson,
|
||||||
|
CodecField.Version -> version.asJson,
|
||||||
|
CodecField.RepositoryUrl -> repositoryUrl.asJson
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val versionDecoder: Decoder[LibraryVersion] = { json =>
|
||||||
|
val typeCursor = json.downField(CodecField.Type)
|
||||||
|
typeCursor.as[String].flatMap {
|
||||||
|
case CodecType.LocalLibraryVersion =>
|
||||||
|
Right(LocalLibraryVersion)
|
||||||
|
case CodecType.PublishedLibraryVersion =>
|
||||||
|
for {
|
||||||
|
version <- json.get[String](CodecField.Version)
|
||||||
|
repositoryUrl <- json.get[String](CodecField.RepositoryUrl)
|
||||||
|
} yield PublishedLibraryVersion(version, repositoryUrl)
|
||||||
|
case unknownType =>
|
||||||
|
Left(
|
||||||
|
DecodingFailure(
|
||||||
|
s"Unknown LibraryVersion type [$unknownType].",
|
||||||
|
typeCursor.history
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
package org.enso.languageserver.libraries
|
||||||
|
|
||||||
|
import akka.actor.{Actor, Props}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.distribution.{DistributionManager, FileSystem}
|
||||||
|
import org.enso.editions.{Editions, LibraryName}
|
||||||
|
import org.enso.languageserver.libraries.LocalLibraryManagerProtocol._
|
||||||
|
import org.enso.librarymanager.local.LocalLibraryProvider
|
||||||
|
import org.enso.pkg.PackageManager
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Files
|
||||||
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
/** An Actor that manages local libraries. */
|
||||||
|
class LocalLibraryManager(
|
||||||
|
currentProjectRoot: File,
|
||||||
|
distributionManager: DistributionManager
|
||||||
|
) extends Actor
|
||||||
|
with LazyLogging {
|
||||||
|
override def receive: Receive = { case request: Request =>
|
||||||
|
request match {
|
||||||
|
case GetMetadata(_) =>
|
||||||
|
logger.warn(
|
||||||
|
"Getting local library metadata is currently not implemented."
|
||||||
|
)
|
||||||
|
sender() ! Success(GetMetadataResponse(None, None))
|
||||||
|
case SetMetadata(_, _, _) =>
|
||||||
|
logger.error(
|
||||||
|
"Setting local library metadata is currently not implemented."
|
||||||
|
)
|
||||||
|
sender() ! Failure(new NotImplementedError())
|
||||||
|
case ListLocalLibraries =>
|
||||||
|
sender() ! listLocalLibraries()
|
||||||
|
case Create(libraryName, authors, maintainers, license) =>
|
||||||
|
sender() ! createLibrary(libraryName, authors, maintainers, license)
|
||||||
|
case Publish(_, _, _) =>
|
||||||
|
logger.error("Publishing libraries is currently not implemented.")
|
||||||
|
sender() ! Failure(new NotImplementedError())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a new local library project.
|
||||||
|
*
|
||||||
|
* The project is created in the first directory of the local library search
|
||||||
|
* path that is writable.
|
||||||
|
*/
|
||||||
|
private def createLibrary(
|
||||||
|
libraryName: LibraryName,
|
||||||
|
authors: Seq[String],
|
||||||
|
maintainers: Seq[String],
|
||||||
|
license: String
|
||||||
|
): Try[Unit] = Try {
|
||||||
|
// TODO [RW] modify protocol to be able to create Contact instances
|
||||||
|
val _ = (authors, maintainers)
|
||||||
|
|
||||||
|
// TODO [RW] make the exceptions more relevant
|
||||||
|
val possibleRoots = LazyList
|
||||||
|
.from(distributionManager.paths.localLibrariesSearchPaths)
|
||||||
|
.filter { path =>
|
||||||
|
Try { if (Files.notExists(path)) Files.createDirectories(path) }
|
||||||
|
Files.isWritable(path)
|
||||||
|
}
|
||||||
|
val librariesRoot = possibleRoots.headOption.getOrElse {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Cannot find a writable directory on local library path."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val libraryPath =
|
||||||
|
LocalLibraryProvider.resolveLibraryPath(librariesRoot, libraryName)
|
||||||
|
if (Files.exists(libraryPath)) {
|
||||||
|
throw new RuntimeException("Local library already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageManager.Default.create(
|
||||||
|
libraryPath.toFile,
|
||||||
|
name = libraryName.name,
|
||||||
|
namespace = libraryName.namespace,
|
||||||
|
edition = findCurrentProjectEdition(),
|
||||||
|
license = license
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Lists all local libraries. */
|
||||||
|
private def listLocalLibraries(): Try[ListLocalLibrariesResponse] = for {
|
||||||
|
libraryNames <- findLocalLibraries()
|
||||||
|
libraryEntries = libraryNames.distinct.map { name =>
|
||||||
|
LibraryEntry(name.namespace, name.name, LibraryEntry.LocalLibraryVersion)
|
||||||
|
}
|
||||||
|
} yield ListLocalLibrariesResponse(libraryEntries)
|
||||||
|
|
||||||
|
private def findLocalLibraries(): Try[Seq[LibraryName]] = Try {
|
||||||
|
for {
|
||||||
|
searchPathRoot <- distributionManager.paths.localLibrariesSearchPaths
|
||||||
|
namespaceDir <- FileSystem
|
||||||
|
.listDirectory(searchPathRoot)
|
||||||
|
.filter(Files.isDirectory(_))
|
||||||
|
nameDir <- FileSystem
|
||||||
|
.listDirectory(namespaceDir)
|
||||||
|
.filter(Files.isDirectory(_))
|
||||||
|
namespace = namespaceDir.getFileName.toString
|
||||||
|
name = nameDir.getFileName.toString
|
||||||
|
} yield LibraryName(namespace, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Finds the edition associated with the current project, if specified in its
|
||||||
|
* config.
|
||||||
|
*/
|
||||||
|
private def findCurrentProjectEdition(): Option[Editions.RawEdition] = {
|
||||||
|
val pkg = PackageManager.Default.loadPackage(currentProjectRoot).get
|
||||||
|
pkg.config.edition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object LocalLibraryManager {
|
||||||
|
def props(
|
||||||
|
currentProjectRoot: File,
|
||||||
|
distributionManager: DistributionManager
|
||||||
|
): Props = Props(
|
||||||
|
new LocalLibraryManager(currentProjectRoot, distributionManager)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package org.enso.languageserver.libraries
|
||||||
|
|
||||||
|
import org.enso.editions.LibraryName
|
||||||
|
|
||||||
|
object LocalLibraryManagerProtocol {
|
||||||
|
|
||||||
|
/** A top class representing any request to the [[LocalLibraryManager]]. */
|
||||||
|
sealed trait Request
|
||||||
|
|
||||||
|
/** A request to get metadata of a library. */
|
||||||
|
case class GetMetadata(libraryName: LibraryName) extends Request
|
||||||
|
|
||||||
|
/** Response to [[GetMetadata]]. */
|
||||||
|
case class GetMetadataResponse(
|
||||||
|
description: Option[String],
|
||||||
|
tagLine: Option[String]
|
||||||
|
)
|
||||||
|
|
||||||
|
/** A request to update metadata of a library. */
|
||||||
|
case class SetMetadata(
|
||||||
|
libraryName: LibraryName,
|
||||||
|
description: Option[String],
|
||||||
|
tagLine: Option[String]
|
||||||
|
) extends Request
|
||||||
|
|
||||||
|
/** A request to list local libraries. */
|
||||||
|
case object ListLocalLibraries extends Request
|
||||||
|
|
||||||
|
/** A response to [[ListLocalLibraries]]. */
|
||||||
|
case class ListLocalLibrariesResponse(libraries: Seq[LibraryEntry])
|
||||||
|
|
||||||
|
/** A request to create a new library project. */
|
||||||
|
case class Create(
|
||||||
|
libraryName: LibraryName,
|
||||||
|
authors: Seq[String],
|
||||||
|
maintainers: Seq[String],
|
||||||
|
license: String
|
||||||
|
) extends Request
|
||||||
|
|
||||||
|
/** A request to publish a library. */
|
||||||
|
case class Publish(
|
||||||
|
libraryName: LibraryName,
|
||||||
|
authToken: String,
|
||||||
|
bumpVersionAfterPublish: Boolean
|
||||||
|
) extends Request
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package org.enso.languageserver.libraries
|
||||||
|
|
||||||
|
import akka.actor.{Actor, Props}
|
||||||
|
import org.enso.editions.{DefaultEdition, EditionResolver, Editions}
|
||||||
|
import org.enso.pkg.PackageManager
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
|
/** An Actor that manages edition-related settings of the current project. */
|
||||||
|
class ProjectSettingsManager(
|
||||||
|
projectRoot: File,
|
||||||
|
editionResolver: EditionResolver
|
||||||
|
) extends Actor {
|
||||||
|
import ProjectSettingsManager._
|
||||||
|
|
||||||
|
override def receive: Receive = { case request: Request =>
|
||||||
|
request match {
|
||||||
|
case GetSettings =>
|
||||||
|
sender() ! loadSettings()
|
||||||
|
case SetParentEdition(editionName) =>
|
||||||
|
sender() ! setParentEdition(editionName)
|
||||||
|
case SetPreferLocalLibraries(preferLocalLibraries) =>
|
||||||
|
sender() ! setPreferLocalLibraries(preferLocalLibraries)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def loadSettings(): Try[SettingsResponse] = for {
|
||||||
|
pkg <- PackageManager.Default.loadPackage(projectRoot)
|
||||||
|
edition = pkg.config.edition.getOrElse(DefaultEdition.getDefaultEdition)
|
||||||
|
} yield SettingsResponse(edition.parent, pkg.config.preferLocalLibraries)
|
||||||
|
|
||||||
|
private def setParentEdition(editionName: String): Try[Unit] = for {
|
||||||
|
pkg <- PackageManager.Default.loadPackage(projectRoot)
|
||||||
|
newEdition = pkg.config.edition match {
|
||||||
|
case Some(edition) => edition.copy(parent = Some(editionName))
|
||||||
|
case None => Editions.Raw.Edition(parent = Some(editionName))
|
||||||
|
}
|
||||||
|
_ <- editionResolver.resolve(newEdition).toTry
|
||||||
|
updated = pkg.updateConfig { config =>
|
||||||
|
config.copy(edition = Some(newEdition))
|
||||||
|
}
|
||||||
|
_ <- updated.save()
|
||||||
|
} yield ()
|
||||||
|
|
||||||
|
private def setPreferLocalLibraries(
|
||||||
|
preferLocalLibraries: Boolean
|
||||||
|
): Try[Unit] = for {
|
||||||
|
pkg <- PackageManager.Default.loadPackage(projectRoot)
|
||||||
|
updated = pkg.updateConfig { config =>
|
||||||
|
config.copy(preferLocalLibraries = preferLocalLibraries)
|
||||||
|
}
|
||||||
|
_ <- updated.save()
|
||||||
|
} yield ()
|
||||||
|
}
|
||||||
|
|
||||||
|
object ProjectSettingsManager {
|
||||||
|
def props(projectRoot: File, editionResolver: EditionResolver): Props = Props(
|
||||||
|
new ProjectSettingsManager(projectRoot, editionResolver)
|
||||||
|
)
|
||||||
|
|
||||||
|
/** A request to the [[ProjectSettingsManager]]. */
|
||||||
|
sealed trait Request
|
||||||
|
|
||||||
|
/** A request to get the current project settings. */
|
||||||
|
case object GetSettings extends Request
|
||||||
|
|
||||||
|
/** Response to [[GetSettings]]. */
|
||||||
|
case class SettingsResponse(
|
||||||
|
parentEdition: Option[String],
|
||||||
|
preferLocalLibraries: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
/** A request to set the parent edition for the project. */
|
||||||
|
case class SetParentEdition(editionName: String) extends Request
|
||||||
|
|
||||||
|
/** A request to set the local libraries preference. */
|
||||||
|
case class SetPreferLocalLibraries(preferLocalLibraries: Boolean)
|
||||||
|
extends Request
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package org.enso.languageserver.libraries.handler
|
||||||
|
|
||||||
|
import akka.actor.{Actor, ActorRef, Cancellable, Props}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.jsonrpc.{Id, Request, ResponseError, ResponseResult}
|
||||||
|
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
|
import org.enso.languageserver.libraries.ProjectSettingsManager
|
||||||
|
import org.enso.languageserver.requesthandler.RequestTimeout
|
||||||
|
import org.enso.languageserver.util.UnhandledLogging
|
||||||
|
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
|
/** A request handler for the `editions/getProjectSettings` endpoint.
|
||||||
|
*
|
||||||
|
* @param timeout request timeout
|
||||||
|
* @param projectSettingsManager a reference to the [[ProjectSettingsManager]]
|
||||||
|
*/
|
||||||
|
class EditionsGetProjectSettingsHandler(
|
||||||
|
timeout: FiniteDuration,
|
||||||
|
projectSettingsManager: ActorRef
|
||||||
|
) extends Actor
|
||||||
|
with LazyLogging
|
||||||
|
with UnhandledLogging {
|
||||||
|
|
||||||
|
import context.dispatcher
|
||||||
|
|
||||||
|
override def receive: Receive = requestStage
|
||||||
|
|
||||||
|
private def requestStage: Receive = {
|
||||||
|
case Request(EditionsGetProjectSettings, id, _) =>
|
||||||
|
projectSettingsManager ! ProjectSettingsManager.GetSettings
|
||||||
|
val cancellable =
|
||||||
|
context.system.scheduler.scheduleOnce(timeout, self, RequestTimeout)
|
||||||
|
context.become(responseStage(id, sender(), cancellable))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def responseStage(
|
||||||
|
id: Id,
|
||||||
|
replyTo: ActorRef,
|
||||||
|
cancellable: Cancellable
|
||||||
|
): Receive = {
|
||||||
|
case RequestTimeout =>
|
||||||
|
replyTo ! RequestTimeout
|
||||||
|
context.stop(self)
|
||||||
|
|
||||||
|
case Success(settings: ProjectSettingsManager.SettingsResponse) =>
|
||||||
|
replyTo ! ResponseResult(
|
||||||
|
EditionsGetProjectSettings,
|
||||||
|
id,
|
||||||
|
EditionsGetProjectSettings.Result(
|
||||||
|
parentEdition = settings.parentEdition,
|
||||||
|
preferLocalLibraries = settings.preferLocalLibraries
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cancellable.cancel()
|
||||||
|
context.stop(self)
|
||||||
|
|
||||||
|
case Failure(exception) =>
|
||||||
|
replyTo ! ResponseError(Some(id), FileSystemError(exception.getMessage))
|
||||||
|
cancellable.cancel()
|
||||||
|
context.stop(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object EditionsGetProjectSettingsHandler {
|
||||||
|
|
||||||
|
/** Creates a configuration object to create
|
||||||
|
* [[EditionsGetProjectSettingsHandler]].
|
||||||
|
*
|
||||||
|
* @param timeout request timeout
|
||||||
|
* @param projectSettingsManager a reference to the
|
||||||
|
* [[ProjectSettingsManager]]
|
||||||
|
*/
|
||||||
|
def props(timeout: FiniteDuration, projectSettingsManager: ActorRef): Props =
|
||||||
|
Props(
|
||||||
|
new EditionsGetProjectSettingsHandler(timeout, projectSettingsManager)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package org.enso.languageserver.libraries.handler
|
||||||
|
|
||||||
|
import akka.actor.{Actor, Props}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.distribution.EditionManager
|
||||||
|
import org.enso.jsonrpc.{Request, ResponseError, ResponseResult}
|
||||||
|
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
|
import org.enso.languageserver.util.UnhandledLogging
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
/** A request handler for the `editions/listAvailable` endpoint.
|
||||||
|
*
|
||||||
|
* It is a partial implementation - it already allows to list existing
|
||||||
|
* editions, but updating is not yet implemented.
|
||||||
|
*
|
||||||
|
* @param editionManager an edition manager instance
|
||||||
|
*/
|
||||||
|
class EditionsListAvailableHandler(editionManager: EditionManager)
|
||||||
|
extends Actor
|
||||||
|
with LazyLogging
|
||||||
|
with UnhandledLogging {
|
||||||
|
override def receive: Receive = {
|
||||||
|
case Request(EditionsListAvailable, id, _: EditionsListAvailable.Params) =>
|
||||||
|
// TODO [RW] once updating editions is implemented this should be made asynchronous
|
||||||
|
Try(editionManager.findAllAvailableEditions()) match {
|
||||||
|
case Success(editions) =>
|
||||||
|
sender() ! ResponseResult(
|
||||||
|
EditionsListAvailable,
|
||||||
|
id,
|
||||||
|
EditionsListAvailable.Result(editions)
|
||||||
|
)
|
||||||
|
case Failure(exception) =>
|
||||||
|
sender() ! ResponseError(
|
||||||
|
Some(id),
|
||||||
|
FileSystemError(exception.toString)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object EditionsListAvailableHandler {
|
||||||
|
|
||||||
|
/** Creates a configuration object to create [[EditionsListAvailableHandler]].
|
||||||
|
*
|
||||||
|
* @param editionManager an edition manager instance
|
||||||
|
*/
|
||||||
|
def props(editionManager: EditionManager): Props = Props(
|
||||||
|
new EditionsListAvailableHandler(editionManager)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
package org.enso.languageserver.libraries.handler
|
||||||
|
|
||||||
|
import akka.actor.{Actor, Props}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.jsonrpc.{Request, ResponseError, ResponseResult}
|
||||||
|
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||||
|
import org.enso.languageserver.libraries.{
|
||||||
|
EditionReferenceResolver,
|
||||||
|
LibraryEntry
|
||||||
|
}
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
|
import org.enso.languageserver.util.UnhandledLogging
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
|
/** A request handler for the `editions/listDefinedLibraries` endpoint.
|
||||||
|
*
|
||||||
|
* @param editionReferenceResolver an [[EditionReferenceResolver]] instance
|
||||||
|
*/
|
||||||
|
class EditionsListDefinedLibrariesHandler(
|
||||||
|
editionReferenceResolver: EditionReferenceResolver
|
||||||
|
) extends Actor
|
||||||
|
with LazyLogging
|
||||||
|
with UnhandledLogging {
|
||||||
|
override def receive: Receive = {
|
||||||
|
case Request(
|
||||||
|
EditionsListDefinedLibraries,
|
||||||
|
id,
|
||||||
|
EditionsListDefinedLibraries.Params(reference)
|
||||||
|
) =>
|
||||||
|
val result = for {
|
||||||
|
edition <- editionReferenceResolver.resolveEdition(reference)
|
||||||
|
} yield edition.getAllDefinedLibraries.toSeq.map { case (name, version) =>
|
||||||
|
LibraryEntry(
|
||||||
|
namespace = name.namespace,
|
||||||
|
name = name.name,
|
||||||
|
version = version
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
result match {
|
||||||
|
case Success(libraries) =>
|
||||||
|
sender() ! ResponseResult(
|
||||||
|
EditionsListDefinedLibraries,
|
||||||
|
id,
|
||||||
|
EditionsListDefinedLibraries.Result(libraries)
|
||||||
|
)
|
||||||
|
|
||||||
|
case Failure(exception) =>
|
||||||
|
// TODO [RW] more detailed errors
|
||||||
|
sender() ! ResponseError(
|
||||||
|
Some(id),
|
||||||
|
FileSystemError(exception.getMessage)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object EditionsListDefinedLibrariesHandler {
|
||||||
|
|
||||||
|
/** Creates a configuration object to create
|
||||||
|
* [[EditionsListDefinedLibrariesHandler]].
|
||||||
|
*
|
||||||
|
* @param editionReferenceResolver an [[EditionReferenceResolver]] instance
|
||||||
|
*/
|
||||||
|
def props(editionReferenceResolver: EditionReferenceResolver): Props = Props(
|
||||||
|
new EditionsListDefinedLibrariesHandler(editionReferenceResolver)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package org.enso.languageserver.libraries.handler
|
||||||
|
|
||||||
|
import akka.actor.{Actor, Props}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.jsonrpc.{Request, ResponseError, ResponseResult}
|
||||||
|
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||||
|
import org.enso.languageserver.libraries.EditionReferenceResolver
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
|
import org.enso.languageserver.util.UnhandledLogging
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
|
/** A request handler for the `editions/resolve` endpoint.
|
||||||
|
*
|
||||||
|
* @param editionReferenceResolver an [[EditionReferenceResolver]] instance
|
||||||
|
*/
|
||||||
|
class EditionsResolveHandler(editionReferenceResolver: EditionReferenceResolver)
|
||||||
|
extends Actor
|
||||||
|
with LazyLogging
|
||||||
|
with UnhandledLogging {
|
||||||
|
override def receive: Receive = {
|
||||||
|
case Request(EditionsResolve, id, EditionsResolve.Params(reference)) =>
|
||||||
|
val result = for {
|
||||||
|
edition <- editionReferenceResolver.resolveEdition(reference)
|
||||||
|
} yield edition.getEngineVersion
|
||||||
|
|
||||||
|
result match {
|
||||||
|
case Success(engineVersion) =>
|
||||||
|
sender() ! ResponseResult(
|
||||||
|
EditionsResolve,
|
||||||
|
id,
|
||||||
|
EditionsResolve.Result(engineVersion.toString)
|
||||||
|
)
|
||||||
|
|
||||||
|
case Failure(exception) =>
|
||||||
|
// TODO [RW] more detailed errors
|
||||||
|
sender() ! ResponseError(
|
||||||
|
Some(id),
|
||||||
|
FileSystemError(exception.getMessage)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object EditionsResolveHandler {
|
||||||
|
|
||||||
|
/** Creates a configuration object to create [[EditionsResolveHandler]].
|
||||||
|
*
|
||||||
|
* @param editionReferenceResolver an [[EditionReferenceResolver]] instance
|
||||||
|
*/
|
||||||
|
def props(editionReferenceResolver: EditionReferenceResolver): Props = Props(
|
||||||
|
new EditionsResolveHandler(editionReferenceResolver)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
package org.enso.languageserver.libraries.handler
|
||||||
|
|
||||||
|
import akka.actor.{Actor, ActorRef, Cancellable, Props}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.jsonrpc.{Id, Request, ResponseError, ResponseResult}
|
||||||
|
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
|
import org.enso.languageserver.libraries.ProjectSettingsManager
|
||||||
|
import org.enso.languageserver.requesthandler.RequestTimeout
|
||||||
|
import org.enso.languageserver.util.UnhandledLogging
|
||||||
|
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
|
/** A request handler for the `editions/setParentEdition` endpoint.
|
||||||
|
*
|
||||||
|
* @param timeout request timeout
|
||||||
|
* @param projectSettingsManager a reference to the [[ProjectSettingsManager]]
|
||||||
|
*/
|
||||||
|
class EditionsSetParentEditionHandler(
|
||||||
|
timeout: FiniteDuration,
|
||||||
|
projectSettingsManager: ActorRef
|
||||||
|
) extends Actor
|
||||||
|
with LazyLogging
|
||||||
|
with UnhandledLogging {
|
||||||
|
|
||||||
|
import context.dispatcher
|
||||||
|
|
||||||
|
override def receive: Receive = requestStage
|
||||||
|
|
||||||
|
private def requestStage: Receive = {
|
||||||
|
case Request(
|
||||||
|
EditionsSetParentEdition,
|
||||||
|
id,
|
||||||
|
EditionsSetParentEdition.Params(newEditionName)
|
||||||
|
) =>
|
||||||
|
projectSettingsManager ! ProjectSettingsManager.SetParentEdition(
|
||||||
|
newEditionName
|
||||||
|
)
|
||||||
|
val cancellable =
|
||||||
|
context.system.scheduler.scheduleOnce(timeout, self, RequestTimeout)
|
||||||
|
context.become(responseStage(id, sender(), cancellable))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def responseStage(
|
||||||
|
id: Id,
|
||||||
|
replyTo: ActorRef,
|
||||||
|
cancellable: Cancellable
|
||||||
|
): Receive = {
|
||||||
|
case RequestTimeout =>
|
||||||
|
replyTo ! RequestTimeout
|
||||||
|
context.stop(self)
|
||||||
|
|
||||||
|
case Success(_) =>
|
||||||
|
replyTo ! ResponseResult(
|
||||||
|
EditionsSetParentEdition,
|
||||||
|
id,
|
||||||
|
EditionsSetParentEdition.Result(needsRestart = Some(true))
|
||||||
|
)
|
||||||
|
cancellable.cancel()
|
||||||
|
context.stop(self)
|
||||||
|
|
||||||
|
case Failure(exception) =>
|
||||||
|
replyTo ! ResponseError(
|
||||||
|
Some(id),
|
||||||
|
FileSystemError(
|
||||||
|
s"Failed to update the settings: ${exception.getMessage}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cancellable.cancel()
|
||||||
|
context.stop(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object EditionsSetParentEditionHandler {
|
||||||
|
|
||||||
|
/** Creates a configuration object to create
|
||||||
|
* [[EditionsSetParentEditionHandler]].
|
||||||
|
*
|
||||||
|
* @param timeout request timeout
|
||||||
|
* @param projectSettingsManager a reference to the
|
||||||
|
* [[ProjectSettingsManager]]
|
||||||
|
*/
|
||||||
|
def props(timeout: FiniteDuration, projectSettingsManager: ActorRef): Props =
|
||||||
|
Props(
|
||||||
|
new EditionsSetParentEditionHandler(timeout, projectSettingsManager)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
package org.enso.languageserver.libraries.handler
|
||||||
|
|
||||||
|
import akka.actor.{Actor, ActorRef, Cancellable, Props}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.jsonrpc.{Id, Request, ResponseError, ResponseResult}
|
||||||
|
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
|
import org.enso.languageserver.libraries.ProjectSettingsManager
|
||||||
|
import org.enso.languageserver.requesthandler.RequestTimeout
|
||||||
|
import org.enso.languageserver.util.UnhandledLogging
|
||||||
|
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
|
/** A request handler for the `editions/setProjectLocalLibrariesPreference`
|
||||||
|
* endpoint.
|
||||||
|
*
|
||||||
|
* @param timeout request timeout
|
||||||
|
* @param projectSettingsManager a reference to the [[ProjectSettingsManager]]
|
||||||
|
*/
|
||||||
|
class EditionsSetProjectLocalLibrariesPreferenceHandler(
|
||||||
|
timeout: FiniteDuration,
|
||||||
|
projectSettingsManager: ActorRef
|
||||||
|
) extends Actor
|
||||||
|
with LazyLogging
|
||||||
|
with UnhandledLogging {
|
||||||
|
|
||||||
|
import context.dispatcher
|
||||||
|
|
||||||
|
override def receive: Receive = requestStage
|
||||||
|
|
||||||
|
private def requestStage: Receive = {
|
||||||
|
case Request(
|
||||||
|
EditionsSetLocalLibrariesPreference,
|
||||||
|
id,
|
||||||
|
EditionsSetLocalLibrariesPreference.Params(preferLocalLibraries)
|
||||||
|
) =>
|
||||||
|
projectSettingsManager ! ProjectSettingsManager.SetPreferLocalLibraries(
|
||||||
|
preferLocalLibraries
|
||||||
|
)
|
||||||
|
val cancellable =
|
||||||
|
context.system.scheduler.scheduleOnce(timeout, self, RequestTimeout)
|
||||||
|
context.become(responseStage(id, sender(), cancellable))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def responseStage(
|
||||||
|
id: Id,
|
||||||
|
replyTo: ActorRef,
|
||||||
|
cancellable: Cancellable
|
||||||
|
): Receive = {
|
||||||
|
case RequestTimeout =>
|
||||||
|
replyTo ! RequestTimeout
|
||||||
|
context.stop(self)
|
||||||
|
|
||||||
|
case Success(_) =>
|
||||||
|
replyTo ! ResponseResult(
|
||||||
|
EditionsSetLocalLibrariesPreference,
|
||||||
|
id,
|
||||||
|
EditionsSetLocalLibrariesPreference.Result(needsRestart = Some(true))
|
||||||
|
)
|
||||||
|
cancellable.cancel()
|
||||||
|
context.stop(self)
|
||||||
|
|
||||||
|
case Failure(exception) =>
|
||||||
|
replyTo ! ResponseError(
|
||||||
|
Some(id),
|
||||||
|
FileSystemError(
|
||||||
|
s"Failed to update the settings: ${exception.getMessage}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cancellable.cancel()
|
||||||
|
context.stop(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object EditionsSetProjectLocalLibrariesPreferenceHandler {
|
||||||
|
|
||||||
|
/** Creates a configuration object to create
|
||||||
|
* [[EditionsSetProjectLocalLibrariesPreferenceHandler]].
|
||||||
|
*
|
||||||
|
* @param timeout request timeout
|
||||||
|
* @param projectSettingsManager a reference to the
|
||||||
|
* [[ProjectSettingsManager]]
|
||||||
|
*/
|
||||||
|
def props(
|
||||||
|
timeout: FiniteDuration,
|
||||||
|
projectSettingsManager: ActorRef
|
||||||
|
): Props = Props(
|
||||||
|
new EditionsSetProjectLocalLibrariesPreferenceHandler(
|
||||||
|
timeout,
|
||||||
|
projectSettingsManager
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package org.enso.languageserver.libraries.handler
|
||||||
|
|
||||||
|
import akka.actor.{Actor, ActorRef, Cancellable, Props}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.editions.LibraryName
|
||||||
|
import org.enso.jsonrpc._
|
||||||
|
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
|
import org.enso.languageserver.libraries.LocalLibraryManagerProtocol
|
||||||
|
import org.enso.languageserver.requesthandler.RequestTimeout
|
||||||
|
import org.enso.languageserver.util.UnhandledLogging
|
||||||
|
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
|
/** A request handler for the `library/create` endpoint.
|
||||||
|
*
|
||||||
|
* @param timeout request timeout
|
||||||
|
* @param localLibraryManager a reference to the LocalLibraryManager
|
||||||
|
*/
|
||||||
|
class LibraryCreateHandler(
|
||||||
|
timeout: FiniteDuration,
|
||||||
|
localLibraryManager: ActorRef
|
||||||
|
) extends Actor
|
||||||
|
with LazyLogging
|
||||||
|
with UnhandledLogging {
|
||||||
|
import context.dispatcher
|
||||||
|
|
||||||
|
override def receive: Receive = requestStage
|
||||||
|
|
||||||
|
private def requestStage: Receive = {
|
||||||
|
case Request(
|
||||||
|
LibraryCreate,
|
||||||
|
id,
|
||||||
|
LibraryCreate.Params(namespace, name, authors, maintainers, license)
|
||||||
|
) =>
|
||||||
|
localLibraryManager ! LocalLibraryManagerProtocol.Create(
|
||||||
|
LibraryName(namespace, name),
|
||||||
|
authors = authors,
|
||||||
|
maintainers = maintainers,
|
||||||
|
license = license
|
||||||
|
)
|
||||||
|
val cancellable =
|
||||||
|
context.system.scheduler.scheduleOnce(timeout, self, RequestTimeout)
|
||||||
|
context.become(responseStage(id, sender(), cancellable))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def responseStage(
|
||||||
|
id: Id,
|
||||||
|
replyTo: ActorRef,
|
||||||
|
cancellable: Cancellable
|
||||||
|
): Receive = {
|
||||||
|
case RequestTimeout =>
|
||||||
|
replyTo ! RequestTimeout
|
||||||
|
context.stop(self)
|
||||||
|
|
||||||
|
case Success(_) =>
|
||||||
|
replyTo ! ResponseResult(LibraryCreate, id, Unused)
|
||||||
|
cancellable.cancel()
|
||||||
|
context.stop(self)
|
||||||
|
|
||||||
|
case Failure(exception) =>
|
||||||
|
// TODO [RW] handle LibraryAlreadyExists error
|
||||||
|
replyTo ! ResponseError(Some(id), FileSystemError(exception.getMessage))
|
||||||
|
cancellable.cancel()
|
||||||
|
context.stop(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object LibraryCreateHandler {
|
||||||
|
|
||||||
|
/** Creates a configuration object to create [[LibraryCreateHandler]].
|
||||||
|
*
|
||||||
|
* @param timeout request timeout
|
||||||
|
* @param localLibraryManager a reference to the LocalLibraryManager
|
||||||
|
*/
|
||||||
|
def props(timeout: FiniteDuration, localLibraryManager: ActorRef): Props =
|
||||||
|
Props(
|
||||||
|
new LibraryCreateHandler(timeout, localLibraryManager)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package org.enso.languageserver.libraries.handler
|
||||||
|
|
||||||
|
import akka.actor.{Actor, Props}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.jsonrpc.{Request, ResponseResult}
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
|
import org.enso.languageserver.util.UnhandledLogging
|
||||||
|
|
||||||
|
/** A request handler for the `library/create` endpoint.
|
||||||
|
*
|
||||||
|
* It is currently a stub implementation which will be refined later on.
|
||||||
|
*/
|
||||||
|
class LibraryGetMetadataHandler
|
||||||
|
extends Actor
|
||||||
|
with LazyLogging
|
||||||
|
with UnhandledLogging {
|
||||||
|
override def receive: Receive = {
|
||||||
|
case Request(LibraryGetMetadata, id, _: LibraryGetMetadata.Params) =>
|
||||||
|
// TODO [RW] actual implementation
|
||||||
|
sender() ! ResponseResult(
|
||||||
|
LibraryGetMetadata,
|
||||||
|
id,
|
||||||
|
LibraryGetMetadata.Result(None, None)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object LibraryGetMetadataHandler {
|
||||||
|
|
||||||
|
/** Creates a configuration object to create [[LibraryGetMetadataHandler]]. */
|
||||||
|
def props(): Props = Props(new LibraryGetMetadataHandler)
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
package org.enso.languageserver.libraries.handler
|
||||||
|
|
||||||
|
import akka.actor.{Actor, ActorRef, Cancellable, Props}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.jsonrpc._
|
||||||
|
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
|
import org.enso.languageserver.libraries.LocalLibraryManagerProtocol
|
||||||
|
import org.enso.languageserver.requesthandler.RequestTimeout
|
||||||
|
import org.enso.languageserver.util.UnhandledLogging
|
||||||
|
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
|
/** A request handler for the `library/listLocal` endpoint.
|
||||||
|
*
|
||||||
|
* @param timeout request timeout
|
||||||
|
* @param localLibraryManager a reference to the LocalLibraryManager
|
||||||
|
*/
|
||||||
|
class LibraryListLocalHandler(
|
||||||
|
timeout: FiniteDuration,
|
||||||
|
localLibraryManager: ActorRef
|
||||||
|
) extends Actor
|
||||||
|
with LazyLogging
|
||||||
|
with UnhandledLogging {
|
||||||
|
import context.dispatcher
|
||||||
|
|
||||||
|
override def receive: Receive = requestStage
|
||||||
|
|
||||||
|
private def requestStage: Receive = { case Request(LibraryListLocal, id, _) =>
|
||||||
|
localLibraryManager ! LocalLibraryManagerProtocol.ListLocalLibraries
|
||||||
|
val cancellable =
|
||||||
|
context.system.scheduler.scheduleOnce(timeout, self, RequestTimeout)
|
||||||
|
context.become(responseStage(id, sender(), cancellable))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def responseStage(
|
||||||
|
id: Id,
|
||||||
|
replyTo: ActorRef,
|
||||||
|
cancellable: Cancellable
|
||||||
|
): Receive = {
|
||||||
|
case RequestTimeout =>
|
||||||
|
replyTo ! RequestTimeout
|
||||||
|
context.stop(self)
|
||||||
|
|
||||||
|
case Success(
|
||||||
|
LocalLibraryManagerProtocol.ListLocalLibrariesResponse(libraries)
|
||||||
|
) =>
|
||||||
|
replyTo ! ResponseResult(
|
||||||
|
LibraryListLocal,
|
||||||
|
id,
|
||||||
|
LibraryListLocal.Result(libraries)
|
||||||
|
)
|
||||||
|
cancellable.cancel()
|
||||||
|
context.stop(self)
|
||||||
|
|
||||||
|
case Failure(exception) =>
|
||||||
|
replyTo ! ResponseError(Some(id), FileSystemError(exception.getMessage))
|
||||||
|
cancellable.cancel()
|
||||||
|
context.stop(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object LibraryListLocalHandler {
|
||||||
|
|
||||||
|
/** Creates a configuration object to create [[LibraryListLocalHandler]].
|
||||||
|
*
|
||||||
|
* @param timeout request timeout
|
||||||
|
* @param localLibraryManager a reference to the LocalLibraryManager
|
||||||
|
*/
|
||||||
|
def props(timeout: FiniteDuration, localLibraryManager: ActorRef): Props =
|
||||||
|
Props(
|
||||||
|
new LibraryListLocalHandler(timeout, localLibraryManager)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package org.enso.languageserver.libraries.handler
|
||||||
|
|
||||||
|
import akka.actor.{Actor, Props}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.cli.task.notifications.ActorProgressNotificationForwarder
|
||||||
|
import org.enso.jsonrpc.{Request, ResponseError}
|
||||||
|
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||||
|
import org.enso.languageserver.libraries.FakeDownload
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
|
import org.enso.languageserver.util.UnhandledLogging
|
||||||
|
|
||||||
|
/** A request handler for the `library/preinstall` endpoint.
|
||||||
|
*
|
||||||
|
* It is currently a stub implementation which will be refined later on.
|
||||||
|
*/
|
||||||
|
class LibraryPreinstallHandler
|
||||||
|
extends Actor
|
||||||
|
with LazyLogging
|
||||||
|
with UnhandledLogging {
|
||||||
|
override def receive: Receive = {
|
||||||
|
case Request(LibraryPreinstall, id, LibraryPreinstall.Params(_, name)) =>
|
||||||
|
// TODO [RW] actual implementation
|
||||||
|
val progressReporter =
|
||||||
|
ActorProgressNotificationForwarder.translateAndForward(
|
||||||
|
LibraryPreinstall.name,
|
||||||
|
sender()
|
||||||
|
)
|
||||||
|
|
||||||
|
if (name == "Test") {
|
||||||
|
FakeDownload.simulateDownload(
|
||||||
|
"Download Test",
|
||||||
|
progressReporter,
|
||||||
|
seconds = 1
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
FakeDownload.simulateDownload(
|
||||||
|
"Downloading something...",
|
||||||
|
progressReporter
|
||||||
|
)
|
||||||
|
FakeDownload.simulateDownload(
|
||||||
|
"Downloading something else...",
|
||||||
|
progressReporter
|
||||||
|
)
|
||||||
|
}
|
||||||
|
sender() ! ResponseError(
|
||||||
|
Some(id),
|
||||||
|
FileSystemError("Feature not implemented")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object LibraryPreinstallHandler {
|
||||||
|
|
||||||
|
/** Creates a configuration object to create [[LibraryPreinstallHandler]]. */
|
||||||
|
def props(): Props = Props(new LibraryPreinstallHandler)
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package org.enso.languageserver.libraries.handler
|
||||||
|
|
||||||
|
import akka.actor.{Actor, Props}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.jsonrpc.{Request, ResponseError}
|
||||||
|
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
|
import org.enso.languageserver.util.UnhandledLogging
|
||||||
|
|
||||||
|
/** A request handler for the `library/publish` endpoint.
|
||||||
|
*
|
||||||
|
* It is currently a stub implementation which will be refined later on.
|
||||||
|
*/
|
||||||
|
class LibraryPublishHandler
|
||||||
|
extends Actor
|
||||||
|
with LazyLogging
|
||||||
|
with UnhandledLogging {
|
||||||
|
override def receive: Receive = {
|
||||||
|
case Request(LibraryPublish, id, _: LibraryPublish.Params) =>
|
||||||
|
// TODO [RW] actual implementation
|
||||||
|
sender() ! ResponseError(
|
||||||
|
Some(id),
|
||||||
|
FileSystemError("Feature not implemented")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object LibraryPublishHandler {
|
||||||
|
|
||||||
|
/** Creates a configuration object to create [[LibraryPublishHandler]]. */
|
||||||
|
def props(): Props = Props(new LibraryPublishHandler)
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package org.enso.languageserver.libraries.handler
|
||||||
|
|
||||||
|
import akka.actor.{Actor, Props}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.jsonrpc.{Request, ResponseError}
|
||||||
|
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
|
import org.enso.languageserver.util.UnhandledLogging
|
||||||
|
|
||||||
|
/** A request handler for the `library/setMetadata` endpoint.
|
||||||
|
*
|
||||||
|
* It is currently a stub implementation which will be refined later on.
|
||||||
|
*/
|
||||||
|
class LibrarySetMetadataHandler
|
||||||
|
extends Actor
|
||||||
|
with LazyLogging
|
||||||
|
with UnhandledLogging {
|
||||||
|
override def receive: Receive = {
|
||||||
|
case Request(LibrarySetMetadata, id, _: LibrarySetMetadata.Params) =>
|
||||||
|
// TODO [RW] actual implementation
|
||||||
|
sender() ! ResponseError(
|
||||||
|
Some(id),
|
||||||
|
FileSystemError("Feature not implemented")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object LibrarySetMetadataHandler {
|
||||||
|
|
||||||
|
/** Creates a configuration object to create [[LibrarySetMetadataHandler]]. */
|
||||||
|
def props(): Props = Props(new LibrarySetMetadataHandler)
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
package org.enso.languageserver.protocol.json
|
package org.enso.languageserver.protocol.json
|
||||||
|
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef, Cancellable, Props, Stash, Status}
|
import akka.actor.{Actor, ActorRef, Cancellable, Props, Stash, Status}
|
||||||
import akka.pattern.pipe
|
import akka.pattern.pipe
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import com.typesafe.scalalogging.LazyLogging
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import org.enso.cli.task.ProgressUnit
|
||||||
|
import org.enso.cli.task.notifications.TaskNotificationApi
|
||||||
|
import org.enso.distribution.EditionManager
|
||||||
import org.enso.jsonrpc._
|
import org.enso.jsonrpc._
|
||||||
import org.enso.languageserver.boot.resource.InitializationComponent
|
import org.enso.languageserver.boot.resource.InitializationComponent
|
||||||
import org.enso.languageserver.capability.CapabilityApi.{
|
import org.enso.languageserver.capability.CapabilityApi.{
|
||||||
@ -26,6 +27,9 @@ import org.enso.languageserver.filemanager._
|
|||||||
import org.enso.languageserver.io.InputOutputApi._
|
import org.enso.languageserver.io.InputOutputApi._
|
||||||
import org.enso.languageserver.io.OutputKind.{StandardError, StandardOutput}
|
import org.enso.languageserver.io.OutputKind.{StandardError, StandardOutput}
|
||||||
import org.enso.languageserver.io.{InputOutputApi, InputOutputProtocol}
|
import org.enso.languageserver.io.{InputOutputApi, InputOutputProtocol}
|
||||||
|
import org.enso.languageserver.libraries.EditionReferenceResolver
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
|
import org.enso.languageserver.libraries.handler._
|
||||||
import org.enso.languageserver.monitoring.MonitoringApi.{InitialPing, Ping}
|
import org.enso.languageserver.monitoring.MonitoringApi.{InitialPing, Ping}
|
||||||
import org.enso.languageserver.monitoring.MonitoringProtocol
|
import org.enso.languageserver.monitoring.MonitoringProtocol
|
||||||
import org.enso.languageserver.refactoring.RefactoringApi.RenameProject
|
import org.enso.languageserver.refactoring.RefactoringApi.RenameProject
|
||||||
@ -66,7 +70,10 @@ import org.enso.languageserver.text.TextApi._
|
|||||||
import org.enso.languageserver.text.TextProtocol
|
import org.enso.languageserver.text.TextProtocol
|
||||||
import org.enso.languageserver.util.UnhandledLogging
|
import org.enso.languageserver.util.UnhandledLogging
|
||||||
import org.enso.languageserver.workspace.WorkspaceApi.ProjectInfo
|
import org.enso.languageserver.workspace.WorkspaceApi.ProjectInfo
|
||||||
|
import org.enso.polyglot.runtime.Runtime.Api
|
||||||
|
import org.enso.polyglot.runtime.Runtime.Api.ProgressNotification
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
/** An actor handling communications between a single client and the language
|
/** An actor handling communications between a single client and the language
|
||||||
@ -81,6 +88,7 @@ import scala.concurrent.duration._
|
|||||||
* @param contextRegistry a router that dispatches execution context requests
|
* @param contextRegistry a router that dispatches execution context requests
|
||||||
* @param suggestionsHandler a reference to the suggestions requests handler
|
* @param suggestionsHandler a reference to the suggestions requests handler
|
||||||
* @param idlenessMonitor a reference to the idleness monitor actor
|
* @param idlenessMonitor a reference to the idleness monitor actor
|
||||||
|
* @param projectSettingsManager a reference to the project settings manager
|
||||||
* @param requestTimeout a request timeout
|
* @param requestTimeout a request timeout
|
||||||
*/
|
*/
|
||||||
class JsonConnectionController(
|
class JsonConnectionController(
|
||||||
@ -97,6 +105,10 @@ class JsonConnectionController(
|
|||||||
val stdInController: ActorRef,
|
val stdInController: ActorRef,
|
||||||
val runtimeConnector: ActorRef,
|
val runtimeConnector: ActorRef,
|
||||||
val idlenessMonitor: ActorRef,
|
val idlenessMonitor: ActorRef,
|
||||||
|
val projectSettingsManager: ActorRef,
|
||||||
|
val localLibraryManager: ActorRef,
|
||||||
|
val editionReferenceResolver: EditionReferenceResolver,
|
||||||
|
val editionManager: EditionManager,
|
||||||
val languageServerConfig: Config,
|
val languageServerConfig: Config,
|
||||||
requestTimeout: FiniteDuration = 10.seconds
|
requestTimeout: FiniteDuration = 10.seconds
|
||||||
) extends Actor
|
) extends Actor
|
||||||
@ -230,8 +242,7 @@ class JsonConnectionController(
|
|||||||
InitProtocolConnection.Result(allRoots.map(_.toContentRoot).toSet)
|
InitProtocolConnection.Result(allRoots.map(_.toContentRoot).toSet)
|
||||||
)
|
)
|
||||||
|
|
||||||
val requestHandlers = createRequestHandlers(rpcSession)
|
initialize(webActor, rpcSession)
|
||||||
context.become(initialised(webActor, rpcSession, requestHandlers))
|
|
||||||
} else {
|
} else {
|
||||||
context.become(
|
context.become(
|
||||||
waitingForContentRoots(
|
waitingForContentRoots(
|
||||||
@ -254,6 +265,17 @@ class JsonConnectionController(
|
|||||||
stash()
|
stash()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def initialize(
|
||||||
|
webActor: ActorRef,
|
||||||
|
rpcSession: JsonSession
|
||||||
|
): Unit = {
|
||||||
|
val requestHandlers = createRequestHandlers(rpcSession)
|
||||||
|
context.become(initialised(webActor, rpcSession, requestHandlers))
|
||||||
|
|
||||||
|
context.system.eventStream
|
||||||
|
.subscribe(self, classOf[Api.ProgressNotification])
|
||||||
|
}
|
||||||
|
|
||||||
private def initialised(
|
private def initialised(
|
||||||
webActor: ActorRef,
|
webActor: ActorRef,
|
||||||
rpcSession: JsonSession,
|
rpcSession: JsonSession,
|
||||||
@ -364,6 +386,11 @@ class JsonConnectionController(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Api.ProgressNotification(payload) =>
|
||||||
|
val translated: Notification[_, _] =
|
||||||
|
translateProgressNotification(payload)
|
||||||
|
webActor ! translated
|
||||||
|
|
||||||
case req @ Request(method, _, _) if requestHandlers.contains(method) =>
|
case req @ Request(method, _, _) if requestHandlers.contains(method) =>
|
||||||
refreshIdleTime(method)
|
refreshIdleTime(method)
|
||||||
val handler = context.actorOf(
|
val handler = context.actorOf(
|
||||||
@ -458,10 +485,57 @@ class JsonConnectionController(
|
|||||||
RedirectStandardError -> RedirectStdErrHandler
|
RedirectStandardError -> RedirectStdErrHandler
|
||||||
.props(stdErrController, rpcSession.clientId),
|
.props(stdErrController, rpcSession.clientId),
|
||||||
FeedStandardInput -> FeedStandardInputHandler.props(stdInController),
|
FeedStandardInput -> FeedStandardInputHandler.props(stdInController),
|
||||||
ProjectInfo -> ProjectInfoHandler.props(languageServerConfig)
|
ProjectInfo -> ProjectInfoHandler.props(languageServerConfig),
|
||||||
|
EditionsGetProjectSettings -> EditionsGetProjectSettingsHandler
|
||||||
|
.props(requestTimeout, projectSettingsManager),
|
||||||
|
EditionsListAvailable -> EditionsListAvailableHandler.props(
|
||||||
|
editionManager
|
||||||
|
),
|
||||||
|
EditionsListDefinedLibraries -> EditionsListDefinedLibrariesHandler
|
||||||
|
.props(editionReferenceResolver),
|
||||||
|
EditionsResolve -> EditionsResolveHandler
|
||||||
|
.props(editionReferenceResolver),
|
||||||
|
EditionsSetParentEdition -> EditionsSetParentEditionHandler
|
||||||
|
.props(requestTimeout, projectSettingsManager),
|
||||||
|
EditionsSetLocalLibrariesPreference -> EditionsSetProjectLocalLibrariesPreferenceHandler
|
||||||
|
.props(requestTimeout, projectSettingsManager),
|
||||||
|
LibraryCreate -> LibraryCreateHandler
|
||||||
|
.props(requestTimeout, localLibraryManager),
|
||||||
|
LibraryListLocal -> LibraryListLocalHandler
|
||||||
|
.props(requestTimeout, localLibraryManager),
|
||||||
|
LibraryGetMetadata -> LibraryGetMetadataHandler.props(),
|
||||||
|
LibraryPreinstall -> LibraryPreinstallHandler.props(),
|
||||||
|
LibraryPublish -> LibraryPublishHandler.props(),
|
||||||
|
LibrarySetMetadata -> LibrarySetMetadataHandler.props()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def translateProgressNotification(
|
||||||
|
progressNotification: ProgressNotification.NotificationType
|
||||||
|
): Notification[_, _] = progressNotification match {
|
||||||
|
case ProgressNotification.TaskStarted(
|
||||||
|
taskId,
|
||||||
|
relatedOperation,
|
||||||
|
unitStr,
|
||||||
|
total
|
||||||
|
) =>
|
||||||
|
val unit = ProgressUnit.fromString(unitStr)
|
||||||
|
Notification(
|
||||||
|
TaskNotificationApi.TaskStarted,
|
||||||
|
TaskNotificationApi.TaskStarted
|
||||||
|
.Params(taskId, relatedOperation, unit, total)
|
||||||
|
)
|
||||||
|
case ProgressNotification.TaskProgressUpdate(taskId, message, done) =>
|
||||||
|
Notification(
|
||||||
|
TaskNotificationApi.TaskProgressUpdate,
|
||||||
|
TaskNotificationApi.TaskProgressUpdate.Params(taskId, message, done)
|
||||||
|
)
|
||||||
|
case ProgressNotification.TaskFinished(taskId, message, success) =>
|
||||||
|
Notification(
|
||||||
|
TaskNotificationApi.TaskFinished,
|
||||||
|
TaskNotificationApi.TaskFinished.Params(taskId, message, success)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object JsonConnectionController {
|
object JsonConnectionController {
|
||||||
@ -493,26 +567,34 @@ object JsonConnectionController {
|
|||||||
stdInController: ActorRef,
|
stdInController: ActorRef,
|
||||||
runtimeConnector: ActorRef,
|
runtimeConnector: ActorRef,
|
||||||
idlenessMonitor: ActorRef,
|
idlenessMonitor: ActorRef,
|
||||||
|
projectSettingsManager: ActorRef,
|
||||||
|
localLibraryManager: ActorRef,
|
||||||
|
editionReferenceResolver: EditionReferenceResolver,
|
||||||
|
editionManager: EditionManager,
|
||||||
languageServerConfig: Config,
|
languageServerConfig: Config,
|
||||||
requestTimeout: FiniteDuration = 10.seconds
|
requestTimeout: FiniteDuration = 10.seconds
|
||||||
): Props =
|
): Props =
|
||||||
Props(
|
Props(
|
||||||
new JsonConnectionController(
|
new JsonConnectionController(
|
||||||
connectionId,
|
connectionId = connectionId,
|
||||||
mainComponent,
|
mainComponent = mainComponent,
|
||||||
bufferRegistry,
|
bufferRegistry = bufferRegistry,
|
||||||
capabilityRouter,
|
capabilityRouter = capabilityRouter,
|
||||||
fileManager,
|
fileManager = fileManager,
|
||||||
contentRootManager,
|
contentRootManager = contentRootManager,
|
||||||
contextRegistry,
|
contextRegistry = contextRegistry,
|
||||||
suggestionsHandler,
|
suggestionsHandler = suggestionsHandler,
|
||||||
stdOutController,
|
stdOutController = stdOutController,
|
||||||
stdErrController,
|
stdErrController = stdErrController,
|
||||||
stdInController,
|
stdInController = stdInController,
|
||||||
runtimeConnector,
|
runtimeConnector = runtimeConnector,
|
||||||
idlenessMonitor,
|
idlenessMonitor = idlenessMonitor,
|
||||||
languageServerConfig,
|
projectSettingsManager = projectSettingsManager,
|
||||||
requestTimeout
|
localLibraryManager = localLibraryManager,
|
||||||
|
editionReferenceResolver = editionReferenceResolver,
|
||||||
|
editionManager = editionManager,
|
||||||
|
languageServerConfig = languageServerConfig,
|
||||||
|
requestTimeout = requestTimeout
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package org.enso.languageserver.protocol.json
|
package org.enso.languageserver.protocol.json
|
||||||
|
|
||||||
import akka.actor.{ActorRef, ActorSystem}
|
import akka.actor.{ActorRef, ActorSystem}
|
||||||
|
import org.enso.distribution.EditionManager
|
||||||
import org.enso.jsonrpc.ClientControllerFactory
|
import org.enso.jsonrpc.ClientControllerFactory
|
||||||
import org.enso.languageserver.boot.resource.InitializationComponent
|
import org.enso.languageserver.boot.resource.InitializationComponent
|
||||||
import org.enso.languageserver.data.Config
|
import org.enso.languageserver.data.Config
|
||||||
|
import org.enso.languageserver.libraries.EditionReferenceResolver
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@ -27,6 +29,10 @@ class JsonConnectionControllerFactory(
|
|||||||
stdInController: ActorRef,
|
stdInController: ActorRef,
|
||||||
runtimeConnector: ActorRef,
|
runtimeConnector: ActorRef,
|
||||||
idlenessMonitor: ActorRef,
|
idlenessMonitor: ActorRef,
|
||||||
|
projectSettingsManager: ActorRef,
|
||||||
|
localLibraryManager: ActorRef,
|
||||||
|
editionReferenceResolver: EditionReferenceResolver,
|
||||||
|
editionManager: EditionManager,
|
||||||
config: Config
|
config: Config
|
||||||
)(implicit system: ActorSystem)
|
)(implicit system: ActorSystem)
|
||||||
extends ClientControllerFactory {
|
extends ClientControllerFactory {
|
||||||
@ -39,20 +45,24 @@ class JsonConnectionControllerFactory(
|
|||||||
override def createClientController(clientId: UUID): ActorRef =
|
override def createClientController(clientId: UUID): ActorRef =
|
||||||
system.actorOf(
|
system.actorOf(
|
||||||
JsonConnectionController.props(
|
JsonConnectionController.props(
|
||||||
clientId,
|
connectionId = clientId,
|
||||||
mainComponent,
|
mainComponent = mainComponent,
|
||||||
bufferRegistry,
|
bufferRegistry = bufferRegistry,
|
||||||
capabilityRouter,
|
capabilityRouter = capabilityRouter,
|
||||||
fileManager,
|
fileManager = fileManager,
|
||||||
contentRootManager,
|
contentRootManager = contentRootManager,
|
||||||
contextRegistry,
|
contextRegistry = contextRegistry,
|
||||||
suggestionsHandler,
|
suggestionsHandler = suggestionsHandler,
|
||||||
stdOutController,
|
stdOutController = stdOutController,
|
||||||
stdErrController,
|
stdErrController = stdErrController,
|
||||||
stdInController,
|
stdInController = stdInController,
|
||||||
runtimeConnector,
|
runtimeConnector = runtimeConnector,
|
||||||
idlenessMonitor,
|
idlenessMonitor = idlenessMonitor,
|
||||||
config
|
projectSettingsManager = projectSettingsManager,
|
||||||
|
localLibraryManager = localLibraryManager,
|
||||||
|
editionReferenceResolver = editionReferenceResolver,
|
||||||
|
editionManager = editionManager,
|
||||||
|
languageServerConfig = config
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
package org.enso.languageserver.protocol.json
|
package org.enso.languageserver.protocol.json
|
||||||
|
|
||||||
import io.circe.generic.auto._
|
import io.circe.generic.auto._
|
||||||
|
import org.enso.cli.task.notifications.TaskNotificationApi.{
|
||||||
|
TaskFinished,
|
||||||
|
TaskProgressUpdate,
|
||||||
|
TaskStarted
|
||||||
|
}
|
||||||
import org.enso.jsonrpc.Protocol
|
import org.enso.jsonrpc.Protocol
|
||||||
import org.enso.languageserver.capability.CapabilityApi.{
|
import org.enso.languageserver.capability.CapabilityApi.{
|
||||||
AcquireCapability,
|
AcquireCapability,
|
||||||
@ -17,6 +22,7 @@ import org.enso.languageserver.search.SearchApi._
|
|||||||
import org.enso.languageserver.runtime.VisualisationApi._
|
import org.enso.languageserver.runtime.VisualisationApi._
|
||||||
import org.enso.languageserver.session.SessionApi.InitProtocolConnection
|
import org.enso.languageserver.session.SessionApi.InitProtocolConnection
|
||||||
import org.enso.languageserver.text.TextApi._
|
import org.enso.languageserver.text.TextApi._
|
||||||
|
import org.enso.languageserver.libraries.LibraryApi._
|
||||||
import org.enso.languageserver.workspace.WorkspaceApi.ProjectInfo
|
import org.enso.languageserver.workspace.WorkspaceApi.ProjectInfo
|
||||||
|
|
||||||
object JsonRpc {
|
object JsonRpc {
|
||||||
@ -65,6 +71,21 @@ object JsonRpc {
|
|||||||
.registerRequest(Import)
|
.registerRequest(Import)
|
||||||
.registerRequest(RenameProject)
|
.registerRequest(RenameProject)
|
||||||
.registerRequest(ProjectInfo)
|
.registerRequest(ProjectInfo)
|
||||||
|
.registerRequest(EditionsListAvailable)
|
||||||
|
.registerRequest(EditionsResolve)
|
||||||
|
.registerRequest(EditionsGetProjectSettings)
|
||||||
|
.registerRequest(EditionsSetParentEdition)
|
||||||
|
.registerRequest(EditionsSetLocalLibrariesPreference)
|
||||||
|
.registerRequest(EditionsListDefinedLibraries)
|
||||||
|
.registerRequest(LibraryListLocal)
|
||||||
|
.registerRequest(LibraryCreate)
|
||||||
|
.registerRequest(LibraryGetMetadata)
|
||||||
|
.registerRequest(LibrarySetMetadata)
|
||||||
|
.registerRequest(LibraryPublish)
|
||||||
|
.registerRequest(LibraryPreinstall)
|
||||||
|
.registerNotification(TaskStarted)
|
||||||
|
.registerNotification(TaskProgressUpdate)
|
||||||
|
.registerNotification(TaskFinished)
|
||||||
.registerNotification(ForceReleaseCapability)
|
.registerNotification(ForceReleaseCapability)
|
||||||
.registerNotification(GrantCapability)
|
.registerNotification(GrantCapability)
|
||||||
.registerNotification(TextDidChange)
|
.registerNotification(TextDidChange)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
license: APLv2
|
license: APLv2
|
||||||
name: Standard
|
name: Standard
|
||||||
enso-version: default
|
|
||||||
version: "0.1.0"
|
version: "0.1.0"
|
||||||
author: "Enso Team <contact@enso.org>"
|
author: "Enso Team <contact@enso.org>"
|
||||||
maintainer: "Enso Team <contact@enso.org>"
|
maintainer: "Enso Team <contact@enso.org>"
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package org.enso.languageserver.libraries
|
||||||
|
|
||||||
|
import io.circe.syntax._
|
||||||
|
import org.enso.languageserver.libraries.EditionReference.{
|
||||||
|
CurrentProjectEdition,
|
||||||
|
NamedEdition
|
||||||
|
}
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpec
|
||||||
|
|
||||||
|
class EditionNameSerializationSpec extends AnyWordSpec with Matchers {
|
||||||
|
"EditionName" should {
|
||||||
|
"serialize and deserialize to the same thing" in {
|
||||||
|
val edition1: EditionReference = CurrentProjectEdition
|
||||||
|
edition1.asJson.as[EditionReference] shouldEqual Right(edition1)
|
||||||
|
|
||||||
|
val edition2: EditionReference = NamedEdition("Foo-Bar")
|
||||||
|
edition2.asJson.as[EditionReference] shouldEqual Right(edition2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package org.enso.languageserver.libraries
|
||||||
|
|
||||||
|
import io.circe.syntax._
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpec
|
||||||
|
|
||||||
|
class LibraryEntrySerializationSpec extends AnyWordSpec with Matchers {
|
||||||
|
"LibraryEntry" should {
|
||||||
|
"serialize and deserialize to the same thing" in {
|
||||||
|
val entry1 = LibraryEntry("Foo", "Bar", LibraryEntry.LocalLibraryVersion)
|
||||||
|
entry1.asJson.as[LibraryEntry] shouldEqual Right(entry1)
|
||||||
|
|
||||||
|
val entry2 = LibraryEntry(
|
||||||
|
"Foo",
|
||||||
|
"Bar",
|
||||||
|
LibraryEntry.PublishedLibraryVersion("1.2.3", "https://example.com/")
|
||||||
|
)
|
||||||
|
entry2.asJson.as[LibraryEntry] shouldEqual Right(entry2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,15 @@
|
|||||||
package org.enso.languageserver.websocket.json
|
package org.enso.languageserver.websocket.json
|
||||||
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
import akka.testkit.TestProbe
|
import akka.testkit.TestProbe
|
||||||
import io.circe.literal._
|
import io.circe.literal._
|
||||||
import io.circe.parser.parse
|
import io.circe.parser.parse
|
||||||
import io.circe.syntax.EncoderOps
|
import io.circe.syntax.EncoderOps
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
import org.enso.distribution.{DistributionManager, EditionManager, LanguageHome}
|
||||||
|
import org.enso.editions.EditionResolver
|
||||||
import org.enso.jsonrpc.test.JsonRpcServerTestKit
|
import org.enso.jsonrpc.test.JsonRpcServerTestKit
|
||||||
import org.enso.jsonrpc.{ClientControllerFactory, Protocol}
|
import org.enso.jsonrpc.{ClientControllerFactory, Protocol}
|
||||||
|
import org.enso.languageserver.TestClock
|
||||||
import org.enso.languageserver.boot.resource.{
|
import org.enso.languageserver.boot.resource.{
|
||||||
DirectoriesInitialization,
|
DirectoriesInitialization,
|
||||||
RepoInitialization,
|
RepoInitialization,
|
||||||
@ -21,6 +21,11 @@ import org.enso.languageserver.effect.ZioExec
|
|||||||
import org.enso.languageserver.event.InitializedEvent
|
import org.enso.languageserver.event.InitializedEvent
|
||||||
import org.enso.languageserver.filemanager._
|
import org.enso.languageserver.filemanager._
|
||||||
import org.enso.languageserver.io._
|
import org.enso.languageserver.io._
|
||||||
|
import org.enso.languageserver.libraries.{
|
||||||
|
EditionReferenceResolver,
|
||||||
|
LocalLibraryManager,
|
||||||
|
ProjectSettingsManager
|
||||||
|
}
|
||||||
import org.enso.languageserver.monitoring.IdlenessMonitor
|
import org.enso.languageserver.monitoring.IdlenessMonitor
|
||||||
import org.enso.languageserver.protocol.json.{
|
import org.enso.languageserver.protocol.json.{
|
||||||
JsonConnectionControllerFactory,
|
JsonConnectionControllerFactory,
|
||||||
@ -30,22 +35,28 @@ import org.enso.languageserver.refactoring.ProjectNameChangedEvent
|
|||||||
import org.enso.languageserver.runtime.{ContextRegistry, RuntimeFailureMapper}
|
import org.enso.languageserver.runtime.{ContextRegistry, RuntimeFailureMapper}
|
||||||
import org.enso.languageserver.search.SuggestionsHandler
|
import org.enso.languageserver.search.SuggestionsHandler
|
||||||
import org.enso.languageserver.session.SessionRouter
|
import org.enso.languageserver.session.SessionRouter
|
||||||
import org.enso.languageserver.TestClock
|
|
||||||
import org.enso.languageserver.text.BufferRegistry
|
import org.enso.languageserver.text.BufferRegistry
|
||||||
|
import org.enso.pkg.PackageManager
|
||||||
import org.enso.polyglot.data.TypeGraph
|
import org.enso.polyglot.data.TypeGraph
|
||||||
import org.enso.polyglot.runtime.Runtime.Api
|
import org.enso.polyglot.runtime.Runtime.Api
|
||||||
|
import org.enso.runtimeversionmanager.test.{FakeEnvironment, HasTestDirectory}
|
||||||
import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo, SqlVersionsRepo}
|
import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo, SqlVersionsRepo}
|
||||||
import org.enso.testkit.EitherValue
|
import org.enso.testkit.EitherValue
|
||||||
import org.enso.text.Sha3_224VersionCalculator
|
import org.enso.text.Sha3_224VersionCalculator
|
||||||
import org.scalatest.OptionValues
|
import org.scalatest.OptionValues
|
||||||
|
|
||||||
|
import java.nio.file
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.util.UUID
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
class BaseServerTest
|
class BaseServerTest
|
||||||
extends JsonRpcServerTestKit
|
extends JsonRpcServerTestKit
|
||||||
with EitherValue
|
with EitherValue
|
||||||
with OptionValues {
|
with OptionValues
|
||||||
|
with HasTestDirectory
|
||||||
|
with FakeEnvironment {
|
||||||
|
|
||||||
import system.dispatcher
|
import system.dispatcher
|
||||||
|
|
||||||
@ -68,7 +79,12 @@ class BaseServerTest
|
|||||||
graph
|
graph
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val testDirectory =
|
||||||
|
Files.createTempDirectory("enso-test").toRealPath()
|
||||||
|
override def getTestDirectory: file.Path = testDirectory
|
||||||
|
|
||||||
sys.addShutdownHook(FileUtils.deleteQuietly(testContentRoot.file))
|
sys.addShutdownHook(FileUtils.deleteQuietly(testContentRoot.file))
|
||||||
|
sys.addShutdownHook(FileUtils.deleteQuietly(testDirectory.toFile))
|
||||||
|
|
||||||
def mkConfig: Config =
|
def mkConfig: Config =
|
||||||
Config(
|
Config(
|
||||||
@ -204,30 +220,93 @@ class BaseServerTest
|
|||||||
Api.VerifyModulesIndexResponse(Seq())
|
Api.VerifyModulesIndexResponse(Seq())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
locally {
|
||||||
|
val dataRoot = getTestDirectory.resolve("test_data")
|
||||||
|
val editions = dataRoot.resolve("editions")
|
||||||
|
Files.createDirectories(editions)
|
||||||
|
val distribution = file.Path.of("distribution")
|
||||||
|
val currentEdition = buildinfo.Info.currentEdition + ".yaml"
|
||||||
|
val dest = editions.resolve(currentEdition)
|
||||||
|
if (Files.notExists(dest)) {
|
||||||
|
Files.copy(
|
||||||
|
distribution.resolve("editions").resolve(currentEdition),
|
||||||
|
dest
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val environment = fakeInstalledEnvironment()
|
||||||
|
val languageHome = LanguageHome.detectFromExecutableLocation(environment)
|
||||||
|
val distributionManager = new DistributionManager(environment)
|
||||||
|
|
||||||
|
val editionProvider =
|
||||||
|
EditionManager.makeEditionProvider(
|
||||||
|
distributionManager,
|
||||||
|
Some(languageHome)
|
||||||
|
)
|
||||||
|
val editionResolver = EditionResolver(editionProvider)
|
||||||
|
val editionReferenceResolver = new EditionReferenceResolver(
|
||||||
|
config.projectContentRoot.file,
|
||||||
|
editionProvider,
|
||||||
|
editionResolver
|
||||||
|
)
|
||||||
|
val editionManager = EditionManager(distributionManager, Some(languageHome))
|
||||||
|
|
||||||
|
val projectSettingsManager = system.actorOf(
|
||||||
|
ProjectSettingsManager.props(
|
||||||
|
config.projectContentRoot.file,
|
||||||
|
editionResolver
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val localLibraryManager = system.actorOf(
|
||||||
|
LocalLibraryManager.props(
|
||||||
|
config.projectContentRoot.file,
|
||||||
|
distributionManager
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
new JsonConnectionControllerFactory(
|
new JsonConnectionControllerFactory(
|
||||||
initializationComponent,
|
mainComponent = initializationComponent,
|
||||||
bufferRegistry,
|
bufferRegistry = bufferRegistry,
|
||||||
capabilityRouter,
|
capabilityRouter = capabilityRouter,
|
||||||
fileManager,
|
fileManager = fileManager,
|
||||||
contentRootManagerActor,
|
contentRootManager = contentRootManagerActor,
|
||||||
contextRegistry,
|
contextRegistry = contextRegistry,
|
||||||
suggestionsHandler,
|
suggestionsHandler = suggestionsHandler,
|
||||||
stdOutController,
|
stdOutController = stdOutController,
|
||||||
stdErrController,
|
stdErrController = stdErrController,
|
||||||
stdInController,
|
stdInController = stdInController,
|
||||||
runtimeConnectorProbe.ref,
|
runtimeConnector = runtimeConnectorProbe.ref,
|
||||||
idlenessMonitor,
|
idlenessMonitor = idlenessMonitor,
|
||||||
config
|
projectSettingsManager = projectSettingsManager,
|
||||||
|
localLibraryManager = localLibraryManager,
|
||||||
|
editionReferenceResolver = editionReferenceResolver,
|
||||||
|
editionManager = editionManager,
|
||||||
|
config = config
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def getInitialisedWsClient(): WsTestClient = {
|
/** Specifies if the `package.yaml` at project root should be auto-created. */
|
||||||
val client = new WsTestClient(address)
|
protected def initializeProjectPackage: Boolean = true
|
||||||
|
|
||||||
|
lazy val initPackage: Unit = {
|
||||||
|
if (initializeProjectPackage) {
|
||||||
|
PackageManager.Default.create(
|
||||||
|
config.projectContentRoot.file,
|
||||||
|
name = "TestProject"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def getInitialisedWsClient(debug: Boolean = false): WsTestClient = {
|
||||||
|
val client = new WsTestClient(address, debugMessages = debug)
|
||||||
initSession(client)
|
initSession(client)
|
||||||
client
|
client
|
||||||
}
|
}
|
||||||
|
|
||||||
private def initSession(client: WsTestClient): UUID = {
|
private def initSession(client: WsTestClient): UUID = {
|
||||||
|
initPackage
|
||||||
val clientId = UUID.randomUUID()
|
val clientId = UUID.randomUUID()
|
||||||
client.send(json"""
|
client.send(json"""
|
||||||
{ "jsonrpc": "2.0",
|
{ "jsonrpc": "2.0",
|
||||||
@ -252,5 +331,4 @@ class BaseServerTest
|
|||||||
)
|
)
|
||||||
clientId
|
clientId
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,245 @@
|
|||||||
|
package org.enso.languageserver.websocket.json
|
||||||
|
|
||||||
|
import io.circe.literal._
|
||||||
|
import io.circe.{Json, JsonObject}
|
||||||
|
import org.enso.languageserver.libraries.LibraryEntry
|
||||||
|
import org.enso.languageserver.libraries.LibraryEntry.PublishedLibraryVersion
|
||||||
|
|
||||||
|
class LibrariesTest extends BaseServerTest {
|
||||||
|
"LocalLibraryManager" should {
|
||||||
|
"create a library project and include it on the list of local projects" in {
|
||||||
|
val client = getInitialisedWsClient()
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "library/listLocal",
|
||||||
|
"id": 0
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 0,
|
||||||
|
"result": {
|
||||||
|
"localLibraries": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "library/create",
|
||||||
|
"id": 1,
|
||||||
|
"params": {
|
||||||
|
"namespace": "User",
|
||||||
|
"name": "MyLocalLib",
|
||||||
|
"authors": [],
|
||||||
|
"maintainers": [],
|
||||||
|
"license": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"result": null
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "library/listLocal",
|
||||||
|
"id": 2
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 2,
|
||||||
|
"result": {
|
||||||
|
"localLibraries": [
|
||||||
|
{
|
||||||
|
"namespace": "User",
|
||||||
|
"name": "MyLocalLib",
|
||||||
|
"version": {
|
||||||
|
"type": "LocalLibraryVersion"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail with LibraryAlreadyExists when creating a library that already " +
|
||||||
|
"existed" ignore {
|
||||||
|
// TODO [RW] error handling (#1877)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"mocked library/preinstall" should {
|
||||||
|
"send progress notifications" in {
|
||||||
|
val client = getInitialisedWsClient()
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "library/preinstall",
|
||||||
|
"id": 0,
|
||||||
|
"params": {
|
||||||
|
"namespace": "Foo",
|
||||||
|
"name": "Test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
val messages =
|
||||||
|
for (_ <- 0 to 3) yield {
|
||||||
|
val msg = client.expectSomeJson().asObject.value
|
||||||
|
val method = msg("method").map(_.asString.value).getOrElse("error")
|
||||||
|
val params =
|
||||||
|
msg("params").map(_.asObject.value).getOrElse(JsonObject())
|
||||||
|
(method, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
val taskStart = messages.find(_._1 == "task/started").value
|
||||||
|
val taskId = taskStart._2("taskId").value.asString.value
|
||||||
|
taskStart
|
||||||
|
._2("relatedOperation")
|
||||||
|
.value
|
||||||
|
.asString
|
||||||
|
.value shouldEqual "library/preinstall"
|
||||||
|
|
||||||
|
taskStart._2("unit").value.asString.value shouldEqual "Bytes"
|
||||||
|
|
||||||
|
val updates = messages.filter { case (method, params) =>
|
||||||
|
method == "task/progress-update" &&
|
||||||
|
params("taskId").value.asString.value == taskId
|
||||||
|
}
|
||||||
|
|
||||||
|
updates should not be empty
|
||||||
|
updates.head
|
||||||
|
._2("message")
|
||||||
|
.value
|
||||||
|
.asString
|
||||||
|
.value shouldEqual "Download Test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"editions/listAvailable" should {
|
||||||
|
"list editions on the search path" in {
|
||||||
|
val client = getInitialisedWsClient()
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "editions/listAvailable",
|
||||||
|
"id": 0,
|
||||||
|
"params": {
|
||||||
|
"update": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 0,
|
||||||
|
"result": {
|
||||||
|
"editionNames": [
|
||||||
|
${buildinfo.Info.currentEdition}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
"update the list of editions if requested" ignore {
|
||||||
|
// TODO [RW] updating editions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"editions/listDefinedLibraries" should {
|
||||||
|
"include Standard.Base in the list" in {
|
||||||
|
def containsBase(response: Json): Unit = {
|
||||||
|
val result = response.asObject.value("result").value
|
||||||
|
val libs = result.asObject.value("availableLibraries").value
|
||||||
|
val parsed = libs.asArray.value.map(_.as[LibraryEntry])
|
||||||
|
val bases = parsed.collect {
|
||||||
|
case Right(
|
||||||
|
LibraryEntry("Standard", "Base", PublishedLibraryVersion(_, _))
|
||||||
|
) =>
|
||||||
|
()
|
||||||
|
}
|
||||||
|
bases should have size 1
|
||||||
|
}
|
||||||
|
|
||||||
|
val client = getInitialisedWsClient()
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "editions/listDefinedLibraries",
|
||||||
|
"id": 0,
|
||||||
|
"params": {
|
||||||
|
"edition": {
|
||||||
|
"type": "CurrentProjectEdition"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
containsBase(client.expectSomeJson())
|
||||||
|
|
||||||
|
val currentEditionName = buildinfo.Info.currentEdition
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "editions/listDefinedLibraries",
|
||||||
|
"id": 0,
|
||||||
|
"params": {
|
||||||
|
"edition": {
|
||||||
|
"type": "NamedEdition",
|
||||||
|
"editionName": $currentEditionName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
containsBase(client.expectSomeJson())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"editions/resolve" should {
|
||||||
|
"resolve the engine version associated with an edition" in {
|
||||||
|
val currentVersion = buildinfo.Info.ensoVersion
|
||||||
|
|
||||||
|
val client = getInitialisedWsClient()
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "editions/resolve",
|
||||||
|
"id": 0,
|
||||||
|
"params": {
|
||||||
|
"edition": {
|
||||||
|
"type": "CurrentProjectEdition"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 0,
|
||||||
|
"result": {
|
||||||
|
"engineVersion": $currentVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "editions/resolve",
|
||||||
|
"id": 1,
|
||||||
|
"params": {
|
||||||
|
"edition": {
|
||||||
|
"type": "NamedEdition",
|
||||||
|
"editionName": ${buildinfo.Info.currentEdition}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"result": {
|
||||||
|
"engineVersion": $currentVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
package org.enso.languageserver.websocket.json
|
||||||
|
|
||||||
|
import io.circe.literal._
|
||||||
|
import org.enso.distribution.FileSystem
|
||||||
|
|
||||||
|
import java.nio.file.Files
|
||||||
|
|
||||||
|
class ProjectSettingsManagerTest extends BaseServerTest {
|
||||||
|
override def beforeAll(): Unit = {
|
||||||
|
super.beforeAll()
|
||||||
|
|
||||||
|
val editionsDir = getTestDirectory.resolve("test_data").resolve("editions")
|
||||||
|
Files.createDirectories(editionsDir)
|
||||||
|
FileSystem.writeTextFile(
|
||||||
|
editionsDir.resolve("some-edition.yaml"),
|
||||||
|
"""engine-version: 1.2.3
|
||||||
|
|""".stripMargin
|
||||||
|
)
|
||||||
|
|
||||||
|
FileSystem.writeTextFile(
|
||||||
|
editionsDir.resolve("broken.yaml"),
|
||||||
|
"""extends: non-existent
|
||||||
|
|""".stripMargin
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
"ProjectSettingsManager" should {
|
||||||
|
"get default settings" in {
|
||||||
|
val client = getInitialisedWsClient()
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "editions/getProjectSettings",
|
||||||
|
"id": 0
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 0,
|
||||||
|
"result": {
|
||||||
|
"parentEdition": ${buildinfo.Info.currentEdition},
|
||||||
|
"preferLocalLibraries": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
"allow to set local libraries preference and parent edition and reflect " +
|
||||||
|
"these changes" in {
|
||||||
|
val client = getInitialisedWsClient()
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "editions/setProjectLocalLibrariesPreference",
|
||||||
|
"id": 0,
|
||||||
|
"params": {
|
||||||
|
"preferLocalLibraries": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 0,
|
||||||
|
"result": {
|
||||||
|
"needsRestart": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "editions/setParentEdition",
|
||||||
|
"id": 1,
|
||||||
|
"params": {
|
||||||
|
"newEditionName": "some-edition"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"result": {
|
||||||
|
"needsRestart": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "editions/getProjectSettings",
|
||||||
|
"id": 2
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 2,
|
||||||
|
"result": {
|
||||||
|
"parentEdition": "some-edition",
|
||||||
|
"preferLocalLibraries": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail if the provided parent edition is not resolvable" ignore {}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,8 @@ import java.io.{File, FileOutputStream}
|
|||||||
|
|
||||||
class WorkspaceOperationsTest extends BaseServerTest with FlakySpec {
|
class WorkspaceOperationsTest extends BaseServerTest with FlakySpec {
|
||||||
|
|
||||||
|
override def initializeProjectPackage: Boolean = false
|
||||||
|
|
||||||
"workspace/projectInfo" must {
|
"workspace/projectInfo" must {
|
||||||
val packageConfigName = Config.ensoPackageConfigName
|
val packageConfigName = Config.ensoPackageConfigName
|
||||||
val testYamlPath = new File(testContentRoot.file, packageConfigName)
|
val testYamlPath = new File(testContentRoot.file, packageConfigName)
|
||||||
|
@ -46,9 +46,7 @@ case class Launcher(cliOptions: GlobalCLIOptions) {
|
|||||||
}
|
}
|
||||||
private lazy val configurationManager =
|
private lazy val configurationManager =
|
||||||
new GlobalConfigurationManager(componentsManager, distributionManager)
|
new GlobalConfigurationManager(componentsManager, distributionManager)
|
||||||
private lazy val editionManager = new EditionManager(
|
private lazy val editionManager = EditionManager(distributionManager)
|
||||||
distributionManager.paths.editionSearchPaths.toList
|
|
||||||
)
|
|
||||||
private lazy val projectManager = new ProjectManager
|
private lazy val projectManager = new ProjectManager
|
||||||
private lazy val runner =
|
private lazy val runner =
|
||||||
new LauncherRunner(
|
new LauncherRunner(
|
||||||
|
@ -31,9 +31,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
|
|||||||
new GlobalConfigurationManager(componentsManager, distributionManager) {
|
new GlobalConfigurationManager(componentsManager, distributionManager) {
|
||||||
override def defaultVersion: SemVer = defaultEngineVersion
|
override def defaultVersion: SemVer = defaultEngineVersion
|
||||||
}
|
}
|
||||||
val editionManager = new EditionManager(
|
val editionManager = EditionManager(distributionManager)
|
||||||
distributionManager.paths.editionSearchPaths.toList
|
|
||||||
)
|
|
||||||
val projectManager = new ProjectManager()
|
val projectManager = new ProjectManager()
|
||||||
val cwd = cwdOverride.getOrElse(getTestDirectory)
|
val cwd = cwdOverride.getOrElse(getTestDirectory)
|
||||||
val runner =
|
val runner =
|
||||||
|
@ -209,6 +209,10 @@ object Runtime {
|
|||||||
new JsonSubTypes.Type(
|
new JsonSubTypes.Type(
|
||||||
value = classOf[Api.LibraryLoaded],
|
value = classOf[Api.LibraryLoaded],
|
||||||
name = "libraryLoaded"
|
name = "libraryLoaded"
|
||||||
|
),
|
||||||
|
new JsonSubTypes.Type(
|
||||||
|
value = classOf[Api.ProgressNotification],
|
||||||
|
name = "progressNotification"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1350,6 +1354,40 @@ object Runtime {
|
|||||||
location: File
|
location: File
|
||||||
) extends ApiNotification
|
) extends ApiNotification
|
||||||
|
|
||||||
|
/** A notification containing updates on the progress of long-running tasks.
|
||||||
|
*
|
||||||
|
* @param payload the actual update contained within this notification
|
||||||
|
*/
|
||||||
|
case class ProgressNotification(
|
||||||
|
payload: ProgressNotification.NotificationType
|
||||||
|
) extends ApiNotification
|
||||||
|
|
||||||
|
object ProgressNotification {
|
||||||
|
sealed trait NotificationType
|
||||||
|
|
||||||
|
/** Indicates that a new task has been started. */
|
||||||
|
case class TaskStarted(
|
||||||
|
taskId: UUID,
|
||||||
|
relatedOperation: String,
|
||||||
|
unit: String,
|
||||||
|
total: Option[Long]
|
||||||
|
) extends NotificationType
|
||||||
|
|
||||||
|
/** Indicates that the task has progressed. */
|
||||||
|
case class TaskProgressUpdate(
|
||||||
|
taskId: UUID,
|
||||||
|
message: Option[String],
|
||||||
|
done: Long
|
||||||
|
) extends NotificationType
|
||||||
|
|
||||||
|
/** Indicates that the task has been finished. */
|
||||||
|
case class TaskFinished(
|
||||||
|
taskId: UUID,
|
||||||
|
message: Option[String],
|
||||||
|
success: Boolean
|
||||||
|
) extends NotificationType
|
||||||
|
}
|
||||||
|
|
||||||
private lazy val mapper = {
|
private lazy val mapper = {
|
||||||
val factory = new CBORFactory()
|
val factory = new CBORFactory()
|
||||||
val mapper = new ObjectMapper(factory) with ScalaObjectMapper
|
val mapper = new ObjectMapper(factory) with ScalaObjectMapper
|
||||||
|
@ -67,7 +67,7 @@ object PackageRepository {
|
|||||||
object Error {
|
object Error {
|
||||||
|
|
||||||
/** Indicates that a resolution error has happened, for example the package
|
/** Indicates that a resolution error has happened, for example the package
|
||||||
* was not defined in the selected edition.
|
* was not defined in the selected edition.
|
||||||
*/
|
*/
|
||||||
case class PackageCouldNotBeResolved(cause: Throwable) extends Error {
|
case class PackageCouldNotBeResolved(cause: Throwable) extends Error {
|
||||||
override def toString: String =
|
override def toString: String =
|
||||||
@ -95,11 +95,11 @@ object PackageRepository {
|
|||||||
|
|
||||||
/** The default [[PackageRepository]] implementation.
|
/** The default [[PackageRepository]] implementation.
|
||||||
*
|
*
|
||||||
* @param libraryProvider the [[ResolvingLibraryProvider]] which resolves
|
* @param libraryProvider the [[ResolvingLibraryProvider]] which resolves
|
||||||
* which library version should be imported and
|
* which library version should be imported and
|
||||||
* locates them (or downloads if they are missing)
|
* locates them (or downloads if they are missing)
|
||||||
* @param context the language context
|
* @param context the language context
|
||||||
* @param builtins the builtins module
|
* @param builtins the builtins module
|
||||||
* @param notificationHandler a notification handler
|
* @param notificationHandler a notification handler
|
||||||
*/
|
*/
|
||||||
class Default(
|
class Default(
|
||||||
@ -315,12 +315,12 @@ object PackageRepository {
|
|||||||
* Edition and library search paths are based on the distribution and
|
* Edition and library search paths are based on the distribution and
|
||||||
* language home (if it is provided).
|
* language home (if it is provided).
|
||||||
*
|
*
|
||||||
* @param projectPackage the package of the current project (if ran inside of a project)
|
* @param projectPackage the package of the current project (if ran inside of a project)
|
||||||
* @param languageHome the language home (if set)
|
* @param languageHome the language home (if set)
|
||||||
* @param distributionManager the distribution manager
|
* @param distributionManager the distribution manager
|
||||||
* @param context the context reference, needed to add polyglot libraries to
|
* @param context the context reference, needed to add polyglot libraries to
|
||||||
* the classpath
|
* the classpath
|
||||||
* @param builtins the builtins that are always preloaded
|
* @param builtins the builtins that are always preloaded
|
||||||
* @param notificationHandler a handler for library addition and progress
|
* @param notificationHandler a handler for library addition and progress
|
||||||
* notifications
|
* notifications
|
||||||
* @return an initialized [[PackageRepository]]
|
* @return an initialized [[PackageRepository]]
|
||||||
@ -337,11 +337,8 @@ object PackageRepository {
|
|||||||
.flatMap(_.config.edition)
|
.flatMap(_.config.edition)
|
||||||
.getOrElse(DefaultEdition.getDefaultEdition)
|
.getOrElse(DefaultEdition.getDefaultEdition)
|
||||||
|
|
||||||
val homeManager = languageHome.map { home => LanguageHome(Path.of(home)) }
|
val homeManager = languageHome.map { home => LanguageHome(Path.of(home)) }
|
||||||
val editionSearchPaths =
|
val editionManager = EditionManager(distributionManager, homeManager)
|
||||||
homeManager.map(_.editions).toList ++
|
|
||||||
distributionManager.paths.editionSearchPaths
|
|
||||||
val editionManager = new EditionManager(editionSearchPaths)
|
|
||||||
val edition = editionManager.resolveEdition(rawEdition).get
|
val edition = editionManager.resolveEdition(rawEdition).get
|
||||||
|
|
||||||
val resolvingLibraryProvider =
|
val resolvingLibraryProvider =
|
||||||
|
@ -2,7 +2,12 @@ package org.enso.interpreter.instrument
|
|||||||
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import org.enso.cli.ProgressBar
|
import org.enso.cli.ProgressBar
|
||||||
import org.enso.cli.task.{ProgressReporter, TaskProgress}
|
import org.enso.cli.task.{
|
||||||
|
ProgressNotification,
|
||||||
|
ProgressNotificationForwarder,
|
||||||
|
ProgressReporter,
|
||||||
|
TaskProgress
|
||||||
|
}
|
||||||
import org.enso.editions.{LibraryName, LibraryVersion}
|
import org.enso.editions.{LibraryName, LibraryVersion}
|
||||||
import org.enso.polyglot.runtime.Runtime.{Api, ApiResponse}
|
import org.enso.polyglot.runtime.Runtime.{Api, ApiResponse}
|
||||||
|
|
||||||
@ -15,9 +20,9 @@ trait NotificationHandler extends ProgressReporter {
|
|||||||
|
|
||||||
/** Called when a library has been loaded.
|
/** Called when a library has been loaded.
|
||||||
*
|
*
|
||||||
* @param libraryName name of the added library
|
* @param libraryName name of the added library
|
||||||
* @param libraryVersion selected version
|
* @param libraryVersion selected version
|
||||||
* @param location path to the location from which the library is loaded
|
* @param location path to the location from which the library is loaded
|
||||||
*/
|
*/
|
||||||
def addedLibrary(
|
def addedLibrary(
|
||||||
libraryName: LibraryName,
|
libraryName: LibraryName,
|
||||||
@ -80,7 +85,9 @@ object NotificationHandler {
|
|||||||
* notifications to the Language Server, which then should forward them to
|
* notifications to the Language Server, which then should forward them to
|
||||||
* the IDE.
|
* the IDE.
|
||||||
*/
|
*/
|
||||||
class InteractiveMode(endpoint: Endpoint) extends NotificationHandler {
|
class InteractiveMode(endpoint: Endpoint)
|
||||||
|
extends NotificationHandler
|
||||||
|
with ProgressNotificationForwarder {
|
||||||
private val logger = Logger[InteractiveMode]
|
private val logger = Logger[InteractiveMode]
|
||||||
|
|
||||||
private def sendMessage(message: ApiResponse): Unit = {
|
private def sendMessage(message: ApiResponse): Unit = {
|
||||||
@ -105,7 +112,16 @@ object NotificationHandler {
|
|||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
override def trackProgress(message: String, task: TaskProgress[_]): Unit = {
|
override def trackProgress(message: String, task: TaskProgress[_]): Unit = {
|
||||||
logger.info(message)
|
logger.info(message)
|
||||||
// TODO [RW] this should be implemented once progress tracking is used by downloads
|
super.trackProgress(message, task)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def sendProgressNotification(
|
||||||
|
notification: ProgressNotification
|
||||||
|
): Unit = sendMessage(
|
||||||
|
ProgressNotificationTranslator.translate(
|
||||||
|
"compiler/downloadingDependencies",
|
||||||
|
notification
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
package org.enso.interpreter.instrument
|
||||||
|
|
||||||
|
import org.enso.cli.task.{
|
||||||
|
ProgressUnit,
|
||||||
|
ProgressNotification => TaskProgressNotification
|
||||||
|
}
|
||||||
|
import org.enso.polyglot.runtime.Runtime.Api.ProgressNotification
|
||||||
|
import org.enso.polyglot.runtime.Runtime.Api.ProgressNotification._
|
||||||
|
import org.enso.polyglot.runtime.Runtime.ApiResponse
|
||||||
|
|
||||||
|
/** A helper for translating notification formats. */
|
||||||
|
object ProgressNotificationTranslator {
|
||||||
|
|
||||||
|
/** Translates a notification as defined in the CLI module into the format
|
||||||
|
* that is used in the API of the runtime connector, so that it can be
|
||||||
|
* forwarded to the Language Server.
|
||||||
|
*
|
||||||
|
* @param relatedOperationName name of a related operation; these were
|
||||||
|
* originally tied to Project Manager or Language
|
||||||
|
* Server operations, but they can also be based
|
||||||
|
* on internal compiler operations
|
||||||
|
* @param progressNotification the notification to translate
|
||||||
|
*/
|
||||||
|
def translate(
|
||||||
|
relatedOperationName: String,
|
||||||
|
progressNotification: TaskProgressNotification
|
||||||
|
): ApiResponse = {
|
||||||
|
val payload = progressNotification match {
|
||||||
|
case TaskProgressNotification.TaskStarted(taskId, total, unit) =>
|
||||||
|
TaskStarted(
|
||||||
|
taskId = taskId,
|
||||||
|
relatedOperation = relatedOperationName,
|
||||||
|
unit = ProgressUnit.toString(unit),
|
||||||
|
total = total
|
||||||
|
)
|
||||||
|
case TaskProgressNotification.TaskUpdate(taskId, message, done) =>
|
||||||
|
TaskProgressUpdate(taskId, message, done)
|
||||||
|
case TaskProgressNotification.TaskSuccess(taskId) =>
|
||||||
|
TaskFinished(taskId, message = None, success = true)
|
||||||
|
case TaskProgressNotification.TaskFailure(taskId, throwable) =>
|
||||||
|
TaskFinished(
|
||||||
|
taskId,
|
||||||
|
message = Option(throwable.getMessage),
|
||||||
|
success = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ProgressNotification(payload)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package org.enso.cli.task
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
/** Internal representation of progress notifications. */
|
||||||
|
sealed trait ProgressNotification
|
||||||
|
object ProgressNotification {
|
||||||
|
|
||||||
|
/** Singals that a new task with progress has been started.
|
||||||
|
*
|
||||||
|
* @param taskId a unique id of the task
|
||||||
|
* @param total the total amount of units that the task is expected to take
|
||||||
|
* @param unit unit of that the progress is reported in
|
||||||
|
*/
|
||||||
|
case class TaskStarted(
|
||||||
|
taskId: UUID,
|
||||||
|
total: Option[Long],
|
||||||
|
unit: ProgressUnit
|
||||||
|
) extends ProgressNotification
|
||||||
|
|
||||||
|
/** Signals an update to task's progress.
|
||||||
|
*
|
||||||
|
* @param taskId the task id
|
||||||
|
* @param message an optional message to display
|
||||||
|
* @param done indication of how much progress has been done since the task
|
||||||
|
* started
|
||||||
|
*/
|
||||||
|
case class TaskUpdate(taskId: UUID, message: Option[String], done: Long)
|
||||||
|
extends ProgressNotification
|
||||||
|
|
||||||
|
/** Signals that a task has been finished successfully.
|
||||||
|
*
|
||||||
|
* @param taskId the task id
|
||||||
|
*/
|
||||||
|
case class TaskSuccess(taskId: UUID) extends ProgressNotification
|
||||||
|
|
||||||
|
/** Signals that a task has failed.
|
||||||
|
*
|
||||||
|
* @param taskId the task id
|
||||||
|
* @param throwable an exception associated with the failure
|
||||||
|
*/
|
||||||
|
case class TaskFailure(taskId: UUID, throwable: Throwable)
|
||||||
|
extends ProgressNotification
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package org.enso.cli.task
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
/** A [[ProgressReporter]] implementation that tracks tasks and sends
|
||||||
|
* [[ProgressNotification]]s using a generic interface.
|
||||||
|
*/
|
||||||
|
trait ProgressNotificationForwarder extends ProgressReporter {
|
||||||
|
|
||||||
|
/** The callback that is used to send the progress notification. */
|
||||||
|
def sendProgressNotification(notification: ProgressNotification): Unit
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def trackProgress(message: String, task: TaskProgress[_]): Unit = {
|
||||||
|
var uuid: Option[UUID] = None
|
||||||
|
|
||||||
|
/** Initializes the task on first invocation and just returns the
|
||||||
|
* generated UUID on further invocations.
|
||||||
|
*/
|
||||||
|
def initializeTask(total: Option[Long]): UUID = uuid match {
|
||||||
|
case Some(value) => value
|
||||||
|
case None =>
|
||||||
|
val generated = UUID.randomUUID()
|
||||||
|
uuid = Some(generated)
|
||||||
|
sendProgressNotification(
|
||||||
|
ProgressNotification.TaskStarted(
|
||||||
|
generated,
|
||||||
|
total,
|
||||||
|
task.unit
|
||||||
|
)
|
||||||
|
)
|
||||||
|
generated
|
||||||
|
}
|
||||||
|
|
||||||
|
task.addProgressListener(new ProgressListener[Any] {
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def progressUpdate(
|
||||||
|
done: Long,
|
||||||
|
total: Option[Long]
|
||||||
|
): Unit = {
|
||||||
|
val uuid = initializeTask(total)
|
||||||
|
sendProgressNotification(
|
||||||
|
ProgressNotification.TaskUpdate(
|
||||||
|
uuid,
|
||||||
|
Some(message),
|
||||||
|
done
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def done(result: Try[Any]): Unit = result match {
|
||||||
|
case Failure(exception) =>
|
||||||
|
val uuid = initializeTask(None)
|
||||||
|
sendProgressNotification(
|
||||||
|
ProgressNotification.TaskFailure(uuid, exception)
|
||||||
|
)
|
||||||
|
case Success(_) =>
|
||||||
|
val uuid = initializeTask(None)
|
||||||
|
sendProgressNotification(ProgressNotification.TaskSuccess(uuid))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -6,8 +6,21 @@ sealed trait ProgressUnit
|
|||||||
object ProgressUnit {
|
object ProgressUnit {
|
||||||
|
|
||||||
/** Specifies that progress amount is measured in bytes. */
|
/** Specifies that progress amount is measured in bytes. */
|
||||||
case object Bytes extends ProgressUnit
|
case object Bytes extends ProgressUnit {
|
||||||
|
override val toString: String = "bytes"
|
||||||
|
}
|
||||||
|
|
||||||
/** Does not specify a particular progress unit. */
|
/** Does not specify a particular progress unit. */
|
||||||
case object Unspecified extends ProgressUnit
|
case object Unspecified extends ProgressUnit {
|
||||||
|
override val toString: String = "unspecified"
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts a unit to its string representation. */
|
||||||
|
def toString(unit: ProgressUnit): String = unit.toString
|
||||||
|
|
||||||
|
/** Creates a unit from its string representation, falling back to
|
||||||
|
* [[Unspecified]] if it cannot be recognized.
|
||||||
|
*/
|
||||||
|
def fromString(str: String): ProgressUnit =
|
||||||
|
if (str == Bytes.toString) Bytes else Unspecified
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,24 @@
|
|||||||
package org.enso.distribution
|
package org.enso.distribution
|
||||||
|
|
||||||
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.editions
|
import org.enso.editions
|
||||||
import org.enso.editions.provider.FileSystemEditionProvider
|
import org.enso.editions.provider.{EditionProvider, FileSystemEditionProvider}
|
||||||
import org.enso.editions.{
|
import org.enso.editions.{EditionResolver, Editions}
|
||||||
DefaultEnsoVersion,
|
|
||||||
EditionResolver,
|
|
||||||
Editions,
|
|
||||||
EnsoVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import scala.util.{Success, Try}
|
import scala.annotation.unused
|
||||||
|
import scala.util.Try
|
||||||
/** A helper class for resolving editions. */
|
|
||||||
class EditionManager(searchPaths: List[Path]) {
|
|
||||||
private val editionProvider = FileSystemEditionProvider(searchPaths)
|
|
||||||
|
|
||||||
|
/** A helper class for resolving editions.
|
||||||
|
*
|
||||||
|
* @param primaryCachePath will be used for updating editions
|
||||||
|
* @param searchPaths all paths to search for editions, should include
|
||||||
|
* [[primaryCachePath]]
|
||||||
|
*/
|
||||||
|
class EditionManager(@unused primaryCachePath: Path, searchPaths: List[Path]) {
|
||||||
|
private val editionProvider = new FileSystemEditionProvider(
|
||||||
|
searchPaths
|
||||||
|
)
|
||||||
private val editionResolver = EditionResolver(editionProvider)
|
private val editionResolver = EditionResolver(editionProvider)
|
||||||
private val engineVersionResolver =
|
private val engineVersionResolver =
|
||||||
editions.EngineVersionResolver(editionProvider)
|
editions.EngineVersionResolver(editionProvider)
|
||||||
@ -38,10 +41,46 @@ class EditionManager(searchPaths: List[Path]) {
|
|||||||
* engine version
|
* engine version
|
||||||
* @return the resolved engine version
|
* @return the resolved engine version
|
||||||
*/
|
*/
|
||||||
def resolveEngineVersion(
|
def resolveEngineVersion(edition: Editions.RawEdition): Try[SemVer] =
|
||||||
edition: Option[Editions.RawEdition]
|
engineVersionResolver.resolveEnsoVersion(edition).toTry
|
||||||
): Try[EnsoVersion] =
|
|
||||||
edition
|
// TODO [RW] download edition updates, part of #1772
|
||||||
.map(engineVersionResolver.resolveEnsoVersion(_).toTry)
|
|
||||||
.getOrElse(Success(DefaultEnsoVersion))
|
/** Find all editions available in the [[searchPaths]]. */
|
||||||
|
def findAllAvailableEditions(): Seq[String] =
|
||||||
|
editionProvider.findAvailableEditions()
|
||||||
|
}
|
||||||
|
|
||||||
|
object EditionManager {
|
||||||
|
|
||||||
|
/** Create an [[EditionProvider]] that can locate editions from the
|
||||||
|
* distribution and the language home.
|
||||||
|
*/
|
||||||
|
def makeEditionProvider(
|
||||||
|
distributionManager: DistributionManager,
|
||||||
|
languageHome: Option[LanguageHome]
|
||||||
|
): EditionProvider = new FileSystemEditionProvider(
|
||||||
|
getSearchPaths(distributionManager, languageHome)
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Get search paths associated with the distribution and language home. */
|
||||||
|
private def getSearchPaths(
|
||||||
|
distributionManager: DistributionManager,
|
||||||
|
languageHome: Option[LanguageHome]
|
||||||
|
): List[Path] = {
|
||||||
|
val paths = languageHome.map(_.editions).toList ++
|
||||||
|
distributionManager.paths.editionSearchPaths
|
||||||
|
paths.distinct
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create an [[EditionManager]] that can locate editions from the
|
||||||
|
* distribution and the language home.
|
||||||
|
*/
|
||||||
|
def apply(
|
||||||
|
distributionManager: DistributionManager,
|
||||||
|
languageHome: Option[LanguageHome] = None
|
||||||
|
): EditionManager = new EditionManager(
|
||||||
|
distributionManager.paths.cachedEditions,
|
||||||
|
getSearchPaths(distributionManager, languageHome)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -20,3 +20,15 @@ case class LanguageHome(languageHome: Path) {
|
|||||||
def libraries: Path =
|
def libraries: Path =
|
||||||
rootPath.resolve(DistributionManager.LIBRARIES_DIRECTORY)
|
rootPath.resolve(DistributionManager.LIBRARIES_DIRECTORY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object LanguageHome {
|
||||||
|
|
||||||
|
/** Finds the [[LanguageHome]] based on the path of the runner JAR.
|
||||||
|
*
|
||||||
|
* Only guaranteed to work properly if used in a component that is started by the `engine-runner`.
|
||||||
|
*/
|
||||||
|
def detectFromExecutableLocation(environment: Environment): LanguageHome = {
|
||||||
|
val homePath = environment.getPathToRunningExecutable.getParent
|
||||||
|
LanguageHome(homePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -29,7 +29,7 @@ object EditionResolutionError {
|
|||||||
* reference is invalid.
|
* reference is invalid.
|
||||||
*/
|
*/
|
||||||
case class LibraryReferencesUndefinedRepository(
|
case class LibraryReferencesUndefinedRepository(
|
||||||
libraryName: String,
|
libraryName: LibraryName,
|
||||||
repositoryName: String
|
repositoryName: String
|
||||||
) extends EditionResolutionError(
|
) extends EditionResolutionError(
|
||||||
s"A library `$libraryName` references a repository `$repositoryName` " +
|
s"A library `$libraryName` references a repository `$repositoryName` " +
|
||||||
|
@ -62,16 +62,16 @@ case class EditionResolver(provider: EditionProvider) {
|
|||||||
* a mapping of resolved libraries
|
* a mapping of resolved libraries
|
||||||
*/
|
*/
|
||||||
private def resolveLibraries(
|
private def resolveLibraries(
|
||||||
libraries: Map[String, Editions.Raw.Library],
|
libraries: Map[LibraryName, Editions.Raw.Library],
|
||||||
currentRepositories: Map[String, Editions.Repository],
|
currentRepositories: Map[String, Editions.Repository],
|
||||||
parent: Option[ResolvedEdition]
|
parent: Option[ResolvedEdition]
|
||||||
): Either[
|
): Either[
|
||||||
LibraryReferencesUndefinedRepository,
|
LibraryReferencesUndefinedRepository,
|
||||||
Map[String, Editions.Resolved.Library]
|
Map[LibraryName, Editions.Resolved.Library]
|
||||||
] = {
|
] = {
|
||||||
val resolvedPairs: Either[
|
val resolvedPairs: Either[
|
||||||
LibraryReferencesUndefinedRepository,
|
LibraryReferencesUndefinedRepository,
|
||||||
List[(String, Editions.Resolved.Library)]
|
List[(LibraryName, Editions.Resolved.Library)]
|
||||||
] =
|
] =
|
||||||
libraries.toList.traverse { case (name, library) =>
|
libraries.toList.traverse { case (name, library) =>
|
||||||
val resolved = resolveLibrary(library, currentRepositories, parent)
|
val resolved = resolveLibrary(library, currentRepositories, parent)
|
||||||
@ -122,7 +122,7 @@ case class EditionResolver(provider: EditionProvider) {
|
|||||||
case (None, None) =>
|
case (None, None) =>
|
||||||
Left(
|
Left(
|
||||||
LibraryReferencesUndefinedRepository(
|
LibraryReferencesUndefinedRepository(
|
||||||
libraryName = library.qualifiedName,
|
libraryName = library.name,
|
||||||
repositoryName = repositoryName
|
repositoryName = repositoryName
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -48,7 +48,7 @@ object EditionSerialization {
|
|||||||
implicit val editionDecoder: Decoder[Raw.Edition] = { json =>
|
implicit val editionDecoder: Decoder[Raw.Edition] = { json =>
|
||||||
for {
|
for {
|
||||||
parent <- json.get[Option[EditionName]](Fields.parent)
|
parent <- json.get[Option[EditionName]](Fields.parent)
|
||||||
engineVersion <- json.get[Option[EnsoVersion]](Fields.engineVersion)
|
engineVersion <- json.get[Option[SemVer]](Fields.engineVersion)
|
||||||
_ <-
|
_ <-
|
||||||
if (parent.isEmpty && engineVersion.isEmpty)
|
if (parent.isEmpty && engineVersion.isEmpty)
|
||||||
Left(
|
Left(
|
||||||
@ -64,7 +64,7 @@ object EditionSerialization {
|
|||||||
libraries <- json.getOrElse[Seq[Raw.Library]](Fields.libraries)(Seq())
|
libraries <- json.getOrElse[Seq[Raw.Library]](Fields.libraries)(Seq())
|
||||||
res <- {
|
res <- {
|
||||||
val repositoryMap = Map.from(repositories.map(r => (r.name, r)))
|
val repositoryMap = Map.from(repositories.map(r => (r.name, r)))
|
||||||
val libraryMap = Map.from(libraries.map(l => (l.qualifiedName, l)))
|
val libraryMap = Map.from(libraries.map(l => (l.name, l)))
|
||||||
if (libraryMap.size != libraries.size)
|
if (libraryMap.size != libraries.size)
|
||||||
Left(
|
Left(
|
||||||
DecodingFailure(
|
DecodingFailure(
|
||||||
@ -157,7 +157,11 @@ object EditionSerialization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
implicit private val libraryDecoder: Decoder[Raw.Library] = { json =>
|
implicit private val libraryDecoder: Decoder[Raw.Library] = { json =>
|
||||||
def makeLibrary(name: String, repository: String, version: Option[SemVer]) =
|
def makeLibrary(
|
||||||
|
name: LibraryName,
|
||||||
|
repository: String,
|
||||||
|
version: Option[SemVer]
|
||||||
|
) =
|
||||||
if (repository == Fields.localRepositoryName)
|
if (repository == Fields.localRepositoryName)
|
||||||
if (version.isDefined)
|
if (version.isDefined)
|
||||||
Left(
|
Left(
|
||||||
@ -181,7 +185,7 @@ object EditionSerialization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
name <- json.get[String](Fields.name)
|
name <- json.get[LibraryName](Fields.name)
|
||||||
repository <- json.get[String](Fields.repository)
|
repository <- json.get[String](Fields.repository)
|
||||||
version <- json.get[Option[SemVer]](Fields.version)
|
version <- json.get[Option[SemVer]](Fields.version)
|
||||||
res <- makeLibrary(name, repository, version)
|
res <- makeLibrary(name, repository, version)
|
||||||
|
@ -42,22 +42,22 @@ trait Editions {
|
|||||||
* It should consist of a prefix followed by a dot an the library name, for
|
* It should consist of a prefix followed by a dot an the library name, for
|
||||||
* example `Prefix.Library_Name`.
|
* example `Prefix.Library_Name`.
|
||||||
*/
|
*/
|
||||||
def qualifiedName: String
|
def name: LibraryName
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents a local library. */
|
/** Represents a local library. */
|
||||||
case class LocalLibrary(override val qualifiedName: String) extends Library
|
case class LocalLibrary(override val name: LibraryName) extends Library
|
||||||
|
|
||||||
/** Represents a specific version of the library that is published in a
|
/** Represents a specific version of the library that is published in a
|
||||||
* repository.
|
* repository.
|
||||||
*
|
*
|
||||||
* @param qualifiedName the qualified name of the library
|
* @param name the qualified name of the library
|
||||||
* @param version the exact version of the library that should be used
|
* @param version the exact version of the library that should be used
|
||||||
* @param repository the recommended repository to download the library from,
|
* @param repository the recommended repository to download the library from,
|
||||||
* if it is not yet cached
|
* if it is not yet cached
|
||||||
*/
|
*/
|
||||||
case class PublishedLibrary(
|
case class PublishedLibrary(
|
||||||
override val qualifiedName: String,
|
override val name: LibraryName,
|
||||||
version: SemVer,
|
version: SemVer,
|
||||||
repository: LibraryRepositoryType
|
repository: LibraryRepositoryType
|
||||||
) extends Library
|
) extends Library
|
||||||
@ -75,9 +75,9 @@ trait Editions {
|
|||||||
*/
|
*/
|
||||||
case class Edition(
|
case class Edition(
|
||||||
parent: Option[NestedEditionType] = None,
|
parent: Option[NestedEditionType] = None,
|
||||||
engineVersion: Option[EnsoVersion] = None,
|
engineVersion: Option[SemVer] = None,
|
||||||
repositories: Map[String, Editions.Repository] = Map.empty,
|
repositories: Map[String, Editions.Repository] = Map.empty,
|
||||||
libraries: Map[String, Library] = Map.empty
|
libraries: Map[LibraryName, Library] = Map.empty
|
||||||
) {
|
) {
|
||||||
if (parent.isEmpty && engineVersion.isEmpty)
|
if (parent.isEmpty && engineVersion.isEmpty)
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
@ -134,7 +134,7 @@ object Editions {
|
|||||||
* is either the version override directly specified in the edition or the
|
* is either the version override directly specified in the edition or the
|
||||||
* version implied by its parent.
|
* version implied by its parent.
|
||||||
*/
|
*/
|
||||||
def getEngineVersion: EnsoVersion = edition.engineVersion.getOrElse {
|
def getEngineVersion: SemVer = edition.engineVersion.getOrElse {
|
||||||
val parent = edition.parent.getOrElse {
|
val parent = edition.parent.getOrElse {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Internal error: Resolved edition does not imply an engine version."
|
"Internal error: Resolved edition does not imply an engine version."
|
||||||
@ -142,6 +142,23 @@ object Editions {
|
|||||||
}
|
}
|
||||||
parent.getEngineVersion
|
parent.getEngineVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns a mapping of all libraries defined in the edition, including any
|
||||||
|
* libraries defined in parent editions (also taking into account the
|
||||||
|
* overrides).
|
||||||
|
*/
|
||||||
|
def getAllDefinedLibraries: Map[LibraryName, LibraryVersion] = {
|
||||||
|
val parent =
|
||||||
|
edition.parent.map(_.getAllDefinedLibraries).getOrElse(Map.empty)
|
||||||
|
edition.libraries.foldLeft(parent) { case (map, (name, lib)) =>
|
||||||
|
val version = lib match {
|
||||||
|
case Resolved.LocalLibrary(_) => LibraryVersion.Local
|
||||||
|
case Resolved.PublishedLibrary(_, version, repository) =>
|
||||||
|
LibraryVersion.Published(version, repository)
|
||||||
|
}
|
||||||
|
map.updated(name, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Syntax helpers for a raw edition. */
|
/** Syntax helpers for a raw edition. */
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.enso.editions
|
package org.enso.editions
|
||||||
|
|
||||||
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.editions.Editions.RawEdition
|
import org.enso.editions.Editions.RawEdition
|
||||||
import org.enso.editions.provider.EditionProvider
|
import org.enso.editions.provider.EditionProvider
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ case class EngineVersionResolver(editionProvider: EditionProvider) {
|
|||||||
*/
|
*/
|
||||||
def resolveEnsoVersion(
|
def resolveEnsoVersion(
|
||||||
edition: RawEdition
|
edition: RawEdition
|
||||||
): Either[EditionResolutionError, EnsoVersion] = {
|
): Either[EditionResolutionError, SemVer] = {
|
||||||
for {
|
for {
|
||||||
edition <- editionResolver.resolve(edition)
|
edition <- editionResolver.resolve(edition)
|
||||||
} yield edition.getEngineVersion
|
} yield edition.getEngineVersion
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.enso.editions
|
package org.enso.editions
|
||||||
|
|
||||||
import io.circe.{Decoder, DecodingFailure}
|
import io.circe.syntax.EncoderOps
|
||||||
|
import io.circe.{Decoder, DecodingFailure, Encoder}
|
||||||
|
|
||||||
/** Represents a library name that should uniquely identify the library.
|
/** Represents a library name that should uniquely identify the library.
|
||||||
*
|
*
|
||||||
@ -31,6 +32,10 @@ object LibraryName {
|
|||||||
} yield name
|
} yield name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
implicit val encoder: Encoder[LibraryName] = { libraryName =>
|
||||||
|
libraryName.toString.asJson
|
||||||
|
}
|
||||||
|
|
||||||
private val separator = '.'
|
private val separator = '.'
|
||||||
|
|
||||||
/** Creates a [[LibraryName]] from its string representation.
|
/** Creates a [[LibraryName]] from its string representation.
|
||||||
|
@ -5,12 +5,14 @@ import org.enso.editions.{EditionSerialization, Editions}
|
|||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.nio.file.{Files, Path}
|
import java.nio.file.{Files, Path}
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.util.{Failure, Success, Try}
|
import scala.collection.Factory
|
||||||
|
import scala.jdk.StreamConverters.StreamHasToScala
|
||||||
|
import scala.util.{Failure, Success, Try, Using}
|
||||||
|
|
||||||
/** An implementation of [[EditionProvider]] that looks for the edition files in
|
/** An implementation of [[EditionProvider]] that looks for the edition files in
|
||||||
* a list of filesystem paths.
|
* a list of filesystem paths.
|
||||||
*/
|
*/
|
||||||
case class FileSystemEditionProvider(searchPaths: List[Path])
|
class FileSystemEditionProvider(searchPaths: List[Path])
|
||||||
extends EditionProvider {
|
extends EditionProvider {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
@ -60,4 +62,23 @@ case class FileSystemEditionProvider(searchPaths: List[Path])
|
|||||||
.map(EditionReadError)
|
.map(EditionReadError)
|
||||||
} else Left(EditionNotFound)
|
} else Left(EditionNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Finds all editions available on the [[searchPaths]]. */
|
||||||
|
def findAvailableEditions(): Seq[String] =
|
||||||
|
searchPaths.flatMap(findEditionsAt).distinct
|
||||||
|
|
||||||
|
private def findEditionName(path: Path): Option[String] = {
|
||||||
|
val name = path.getFileName.toString
|
||||||
|
if (name.endsWith(editionSuffix)) {
|
||||||
|
Some(name.stripSuffix(editionSuffix))
|
||||||
|
} else None
|
||||||
|
}
|
||||||
|
|
||||||
|
private def findEditionsAt(path: Path): Seq[String] =
|
||||||
|
listDir(path).filter(Files.isRegularFile(_)).flatMap(findEditionName)
|
||||||
|
|
||||||
|
private def listDir(dir: Path): Seq[Path] =
|
||||||
|
if (Files.exists(dir))
|
||||||
|
Using(Files.list(dir))(_.toScala(Factory.arrayFactory).toSeq).get
|
||||||
|
else Seq()
|
||||||
}
|
}
|
||||||
|
@ -20,24 +20,28 @@ class EditionResolverSpec
|
|||||||
val editions: Map[String, Editions.RawEdition] = Map(
|
val editions: Map[String, Editions.RawEdition] = Map(
|
||||||
"2021.0" -> Editions.Raw.Edition(
|
"2021.0" -> Editions.Raw.Edition(
|
||||||
parent = None,
|
parent = None,
|
||||||
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
engineVersion = Some(SemVer(1, 2, 3)),
|
||||||
repositories = Map(
|
repositories = Map(
|
||||||
"main" -> mainRepo
|
"main" -> mainRepo
|
||||||
),
|
),
|
||||||
libraries = Map(
|
libraries = Map(
|
||||||
"Standard.Base" -> Editions.Raw
|
LibraryName("Standard", "Base") -> Editions.Raw
|
||||||
.PublishedLibrary("Standard.Base", SemVer(1, 2, 3), "main")
|
.PublishedLibrary(
|
||||||
|
LibraryName("Standard", "Base"),
|
||||||
|
SemVer(1, 2, 3),
|
||||||
|
"main"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"cycleA" -> Editions.Raw.Edition(
|
"cycleA" -> Editions.Raw.Edition(
|
||||||
parent = Some("cycleB"),
|
parent = Some("cycleB"),
|
||||||
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
engineVersion = Some(SemVer(1, 2, 3)),
|
||||||
repositories = Map(),
|
repositories = Map(),
|
||||||
libraries = Map()
|
libraries = Map()
|
||||||
),
|
),
|
||||||
"cycleB" -> Editions.Raw.Edition(
|
"cycleB" -> Editions.Raw.Edition(
|
||||||
parent = Some("cycleA"),
|
parent = Some("cycleA"),
|
||||||
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
engineVersion = Some(SemVer(1, 2, 3)),
|
||||||
repositories = Map(),
|
repositories = Map(),
|
||||||
libraries = Map()
|
libraries = Map()
|
||||||
)
|
)
|
||||||
@ -59,12 +63,14 @@ class EditionResolverSpec
|
|||||||
val repo = Repository.make("foo", "http://example.com").get
|
val repo = Repository.make("foo", "http://example.com").get
|
||||||
val edition = Editions.Raw.Edition(
|
val edition = Editions.Raw.Edition(
|
||||||
parent = None,
|
parent = None,
|
||||||
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
engineVersion = Some(SemVer(1, 2, 3)),
|
||||||
repositories = Map("foo" -> repo),
|
repositories = Map("foo" -> repo),
|
||||||
libraries = Map(
|
libraries = Map(
|
||||||
"bar.baz" -> Editions.Raw.LocalLibrary("bar.baz"),
|
LibraryName("bar", "baz") -> Editions.Raw.LocalLibrary(
|
||||||
"foo.bar" -> Editions.Raw
|
LibraryName("bar", "baz")
|
||||||
.PublishedLibrary("foo.bar", SemVer(1, 2, 3), "foo")
|
),
|
||||||
|
LibraryName("foo", "bar") -> Editions.Raw
|
||||||
|
.PublishedLibrary(LibraryName("foo", "bar"), SemVer(1, 2, 3), "foo")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -73,33 +79,47 @@ class EditionResolverSpec
|
|||||||
resolved.parent should be(empty)
|
resolved.parent should be(empty)
|
||||||
resolved.repositories shouldEqual edition.repositories
|
resolved.repositories shouldEqual edition.repositories
|
||||||
resolved.libraries should have size 2
|
resolved.libraries should have size 2
|
||||||
resolved.libraries("bar.baz") shouldEqual Editions.Resolved
|
resolved.libraries(
|
||||||
.LocalLibrary("bar.baz")
|
LibraryName("bar", "baz")
|
||||||
resolved.libraries("foo.bar") shouldEqual Editions.Resolved
|
) shouldEqual Editions.Resolved
|
||||||
.PublishedLibrary("foo.bar", SemVer(1, 2, 3), repo)
|
.LocalLibrary(LibraryName("bar", "baz"))
|
||||||
|
resolved.libraries(
|
||||||
|
LibraryName("foo", "bar")
|
||||||
|
) shouldEqual Editions.Resolved
|
||||||
|
.PublishedLibrary(LibraryName("foo", "bar"), SemVer(1, 2, 3), repo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"resolve a nested edition" in {
|
"resolve a nested edition" in {
|
||||||
val edition = Editions.Raw.Edition(
|
val edition = Editions.Raw.Edition(
|
||||||
parent = Some("2021.0"),
|
parent = Some("2021.0"),
|
||||||
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
engineVersion = Some(SemVer(1, 2, 3)),
|
||||||
repositories = Map(),
|
repositories = Map(),
|
||||||
libraries = Map(
|
libraries = Map(
|
||||||
"bar.baz" -> Editions.Raw.LocalLibrary("bar.baz"),
|
LibraryName("bar", "baz") -> Editions.Raw.LocalLibrary(
|
||||||
"foo.bar" -> Editions.Raw
|
LibraryName("bar", "baz")
|
||||||
.PublishedLibrary("foo.bar", SemVer(1, 2, 3), "main")
|
),
|
||||||
|
LibraryName("foo", "bar") -> Editions.Raw
|
||||||
|
.PublishedLibrary(
|
||||||
|
LibraryName("foo", "bar"),
|
||||||
|
SemVer(1, 2, 3),
|
||||||
|
"main"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
inside(resolver.resolve(edition)) { case Right(resolved) =>
|
inside(resolver.resolve(edition)) { case Right(resolved) =>
|
||||||
resolved.parent should be(defined)
|
resolved.parent should be(defined)
|
||||||
resolved.libraries should have size 2
|
resolved.libraries should have size 2
|
||||||
resolved.libraries("bar.baz") shouldEqual Editions.Resolved
|
resolved.libraries(
|
||||||
.LocalLibrary("bar.baz")
|
LibraryName("bar", "baz")
|
||||||
resolved.libraries("foo.bar") shouldEqual Editions.Resolved
|
) shouldEqual Editions.Resolved
|
||||||
|
.LocalLibrary(LibraryName("bar", "baz"))
|
||||||
|
resolved.libraries(
|
||||||
|
LibraryName("foo", "bar")
|
||||||
|
) shouldEqual Editions.Resolved
|
||||||
.PublishedLibrary(
|
.PublishedLibrary(
|
||||||
"foo.bar",
|
LibraryName("foo", "bar"),
|
||||||
SemVer(1, 2, 3),
|
SemVer(1, 2, 3),
|
||||||
FakeEditionProvider.mainRepo
|
FakeEditionProvider.mainRepo
|
||||||
)
|
)
|
||||||
@ -116,24 +136,30 @@ class EditionResolverSpec
|
|||||||
engineVersion = None,
|
engineVersion = None,
|
||||||
repositories = Map("main" -> localRepo),
|
repositories = Map("main" -> localRepo),
|
||||||
libraries = Map(
|
libraries = Map(
|
||||||
"foo.bar" -> Editions.Raw
|
LibraryName("foo", "bar") -> Editions.Raw
|
||||||
.PublishedLibrary("foo.bar", SemVer(1, 2, 3), "main")
|
.PublishedLibrary(
|
||||||
|
LibraryName("foo", "bar"),
|
||||||
|
SemVer(1, 2, 3),
|
||||||
|
"main"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
inside(resolver.resolve(edition)) { case Right(resolved) =>
|
inside(resolver.resolve(edition)) { case Right(resolved) =>
|
||||||
resolved.parent should be(defined)
|
resolved.parent should be(defined)
|
||||||
resolved.libraries should have size 1
|
resolved.libraries should have size 1
|
||||||
resolved.libraries("foo.bar") shouldEqual
|
resolved.libraries(LibraryName("foo", "bar")) shouldEqual
|
||||||
Editions.Resolved.PublishedLibrary(
|
Editions.Resolved.PublishedLibrary(
|
||||||
"foo.bar",
|
LibraryName("foo", "bar"),
|
||||||
SemVer(1, 2, 3),
|
SemVer(1, 2, 3),
|
||||||
localRepo
|
localRepo
|
||||||
)
|
)
|
||||||
|
|
||||||
resolved.parent.value.libraries("Standard.Base") shouldEqual
|
resolved.parent.value.libraries(
|
||||||
|
LibraryName("Standard", "Base")
|
||||||
|
) shouldEqual
|
||||||
Editions.Resolved.PublishedLibrary(
|
Editions.Resolved.PublishedLibrary(
|
||||||
"Standard.Base",
|
LibraryName("Standard", "Base"),
|
||||||
SemVer(1, 2, 3),
|
SemVer(1, 2, 3),
|
||||||
FakeEditionProvider.mainRepo
|
FakeEditionProvider.mainRepo
|
||||||
)
|
)
|
||||||
@ -143,7 +169,7 @@ class EditionResolverSpec
|
|||||||
"avoid cycles in the resolution" in {
|
"avoid cycles in the resolution" in {
|
||||||
val edition = Editions.Raw.Edition(
|
val edition = Editions.Raw.Edition(
|
||||||
parent = Some("cycleA"),
|
parent = Some("cycleA"),
|
||||||
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
engineVersion = Some(SemVer(1, 2, 3)),
|
||||||
repositories = Map(),
|
repositories = Map(),
|
||||||
libraries = Map()
|
libraries = Map()
|
||||||
)
|
)
|
||||||
|
@ -54,13 +54,16 @@ class EditionSerializationSpec extends AnyWordSpec with Matchers with Inside {
|
|||||||
.url shouldEqual "http://127.0.0.1:8080/root"
|
.url shouldEqual "http://127.0.0.1:8080/root"
|
||||||
|
|
||||||
edition.libraries.values should contain theSameElementsAs Seq(
|
edition.libraries.values should contain theSameElementsAs Seq(
|
||||||
Editions.Raw.LocalLibrary("Foo.Local"),
|
Editions.Raw.LocalLibrary(LibraryName("Foo", "Local")),
|
||||||
Editions.Raw.PublishedLibrary("Bar.Baz", SemVer(0, 0, 0), "example"),
|
Editions.Raw.PublishedLibrary(
|
||||||
Editions.Raw.PublishedLibrary("A.B", SemVer(1, 0, 1), "bar")
|
LibraryName("Bar", "Baz"),
|
||||||
)
|
SemVer(0, 0, 0),
|
||||||
edition.engineVersion should contain(
|
"example"
|
||||||
SemVerEnsoVersion(SemVer(1, 2, 3, Some("SNAPSHOT")))
|
),
|
||||||
|
Editions.Raw
|
||||||
|
.PublishedLibrary(LibraryName("A", "B"), SemVer(1, 0, 1), "bar")
|
||||||
)
|
)
|
||||||
|
edition.engineVersion should contain(SemVer(1, 2, 3, Some("SNAPSHOT")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +85,7 @@ class EditionSerializationSpec extends AnyWordSpec with Matchers with Inside {
|
|||||||
val parsed = EditionSerialization.parseYamlString(
|
val parsed = EditionSerialization.parseYamlString(
|
||||||
"""extends: foo
|
"""extends: foo
|
||||||
|libraries:
|
|libraries:
|
||||||
|- name: bar
|
|- name: bar.baz
|
||||||
| repository: local
|
| repository: local
|
||||||
| version: 1.2.3-SHOULD-NOT-BE-HERE
|
| version: 1.2.3-SHOULD-NOT-BE-HERE
|
||||||
|""".stripMargin
|
|""".stripMargin
|
||||||
@ -94,7 +97,7 @@ class EditionSerializationSpec extends AnyWordSpec with Matchers with Inside {
|
|||||||
val parsed2 = EditionSerialization.parseYamlString(
|
val parsed2 = EditionSerialization.parseYamlString(
|
||||||
"""extends: foo
|
"""extends: foo
|
||||||
|libraries:
|
|libraries:
|
||||||
|- name: bar
|
|- name: bar.baz
|
||||||
| repository: something
|
| repository: something
|
||||||
|""".stripMargin
|
|""".stripMargin
|
||||||
)
|
)
|
||||||
|
@ -118,6 +118,11 @@ abstract class JsonRpcServerTestKit
|
|||||||
parsed shouldEqual Right(json)
|
parsed shouldEqual Right(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def expectSomeJson(timeout: FiniteDuration = 5.seconds.dilated): Json = {
|
||||||
|
val parsed = parse(expectMessage(timeout))
|
||||||
|
inside(parsed) { case Right(json) => json }
|
||||||
|
}
|
||||||
|
|
||||||
def expectNoMessage(): Unit = outActor.expectNoMessage()
|
def expectNoMessage(): Unit = outActor.expectNoMessage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ case class LibraryResolver(
|
|||||||
): Either[LibraryResolutionError, LibraryVersion] = {
|
): Either[LibraryResolutionError, LibraryVersion] = {
|
||||||
import Editions.Resolved._
|
import Editions.Resolved._
|
||||||
val immediateResult =
|
val immediateResult =
|
||||||
edition.libraries.get(libraryName.qualifiedName).map {
|
edition.libraries.get(libraryName).map {
|
||||||
case LocalLibrary(_) =>
|
case LocalLibrary(_) =>
|
||||||
Right(LibraryVersion.Local)
|
Right(LibraryVersion.Local)
|
||||||
case PublishedLibrary(_, version, repository) =>
|
case PublishedLibrary(_, version, repository) =>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package org.enso.librarymanager.local
|
package org.enso.librarymanager.local
|
||||||
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import org.enso.distribution.FileSystem.PathSyntax
|
|
||||||
import org.enso.editions.LibraryName
|
import org.enso.editions.LibraryName
|
||||||
import org.enso.logger.masking.MaskedPath
|
import org.enso.logger.masking.MaskedPath
|
||||||
|
|
||||||
@ -31,7 +30,8 @@ class DefaultLocalLibraryProvider(searchPaths: List[Path])
|
|||||||
searchPaths: List[Path]
|
searchPaths: List[Path]
|
||||||
): Option[Path] = searchPaths match {
|
): Option[Path] = searchPaths match {
|
||||||
case head :: tail =>
|
case head :: tail =>
|
||||||
val potentialPath = head / libraryName.namespace / libraryName.name
|
val potentialPath =
|
||||||
|
LocalLibraryProvider.resolveLibraryPath(head, libraryName)
|
||||||
if (Files.exists(potentialPath) && Files.isDirectory(potentialPath)) {
|
if (Files.exists(potentialPath) && Files.isDirectory(potentialPath)) {
|
||||||
logger.trace(
|
logger.trace(
|
||||||
s"Found a local $libraryName at " +
|
s"Found a local $libraryName at " +
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.enso.librarymanager.local
|
package org.enso.librarymanager.local
|
||||||
|
|
||||||
|
import org.enso.distribution.FileSystem.PathSyntax
|
||||||
import org.enso.editions.LibraryName
|
import org.enso.editions.LibraryName
|
||||||
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -12,3 +13,12 @@ trait LocalLibraryProvider {
|
|||||||
*/
|
*/
|
||||||
def findLibrary(libraryName: LibraryName): Option[Path]
|
def findLibrary(libraryName: LibraryName): Option[Path]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object LocalLibraryProvider {
|
||||||
|
|
||||||
|
/** Resolve a path to the package root of a particular library located in one
|
||||||
|
* of the local library roots.
|
||||||
|
*/
|
||||||
|
def resolveLibraryPath(root: Path, libraryName: LibraryName): Path =
|
||||||
|
root / libraryName.namespace / libraryName.name
|
||||||
|
}
|
||||||
|
@ -2,12 +2,7 @@ package org.enso.librarymanager
|
|||||||
|
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.editions.Editions.Repository
|
import org.enso.editions.Editions.Repository
|
||||||
import org.enso.editions.{
|
import org.enso.editions.{Editions, LibraryName, LibraryVersion}
|
||||||
DefaultEnsoVersion,
|
|
||||||
Editions,
|
|
||||||
LibraryName,
|
|
||||||
LibraryVersion
|
|
||||||
}
|
|
||||||
import org.enso.librarymanager.local.LocalLibraryProvider
|
import org.enso.librarymanager.local.LocalLibraryProvider
|
||||||
import org.enso.testkit.EitherValue
|
import org.enso.testkit.EitherValue
|
||||||
import org.scalatest.Inside
|
import org.scalatest.Inside
|
||||||
@ -25,11 +20,15 @@ class LibraryResolverSpec
|
|||||||
val mainRepo = Repository.make("main", "https://example.com/main").get
|
val mainRepo = Repository.make("main", "https://example.com/main").get
|
||||||
val parentEdition = Editions.Resolved.Edition(
|
val parentEdition = Editions.Resolved.Edition(
|
||||||
parent = None,
|
parent = None,
|
||||||
engineVersion = Some(DefaultEnsoVersion),
|
engineVersion = Some(SemVer(0, 0, 0)),
|
||||||
repositories = Map("main" -> mainRepo),
|
repositories = Map("main" -> mainRepo),
|
||||||
libraries = Map(
|
libraries = Map(
|
||||||
"Standard.Base" -> Editions.Resolved
|
LibraryName("Standard", "Base") -> Editions.Resolved
|
||||||
.PublishedLibrary("Standard.Base", SemVer(4, 5, 6), mainRepo)
|
.PublishedLibrary(
|
||||||
|
LibraryName("Standard", "Base"),
|
||||||
|
SemVer(4, 5, 6),
|
||||||
|
mainRepo
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val customRepo = Repository.make("custom", "https://example.com/custom").get
|
val customRepo = Repository.make("custom", "https://example.com/custom").get
|
||||||
@ -38,24 +37,34 @@ class LibraryResolverSpec
|
|||||||
engineVersion = None,
|
engineVersion = None,
|
||||||
repositories = Map("custom" -> customRepo),
|
repositories = Map("custom" -> customRepo),
|
||||||
libraries = Map(
|
libraries = Map(
|
||||||
"Foo.Main" -> Editions.Resolved
|
LibraryName("Foo", "Main") -> Editions.Resolved
|
||||||
.PublishedLibrary("Foo.Main", SemVer(1, 0, 0), mainRepo),
|
.PublishedLibrary(
|
||||||
"Foo.My" -> Editions.Resolved
|
LibraryName("Foo", "Main"),
|
||||||
.PublishedLibrary("Foo.My", SemVer(2, 0, 0), customRepo),
|
SemVer(1, 0, 0),
|
||||||
"Foo.Local" -> Editions.Resolved.LocalLibrary("Foo.Local")
|
mainRepo
|
||||||
|
),
|
||||||
|
LibraryName("Foo", "My") -> Editions.Resolved
|
||||||
|
.PublishedLibrary(
|
||||||
|
LibraryName("Foo", "My"),
|
||||||
|
SemVer(2, 0, 0),
|
||||||
|
customRepo
|
||||||
|
),
|
||||||
|
LibraryName("Foo", "Local") -> Editions.Resolved.LocalLibrary(
|
||||||
|
LibraryName("Foo", "Local")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
case class FakeLocalLibraryProvider(fixtures: Map[String, Path])
|
case class FakeLocalLibraryProvider(fixtures: Map[LibraryName, Path])
|
||||||
extends LocalLibraryProvider {
|
extends LocalLibraryProvider {
|
||||||
override def findLibrary(libraryName: LibraryName): Option[Path] =
|
override def findLibrary(libraryName: LibraryName): Option[Path] =
|
||||||
fixtures.get(libraryName.qualifiedName)
|
fixtures.get(libraryName)
|
||||||
}
|
}
|
||||||
|
|
||||||
val localLibraries = Map(
|
val localLibraries = Map(
|
||||||
"Foo.My" -> Path.of("./Foo/My"),
|
LibraryName("Foo", "My") -> Path.of("./Foo/My"),
|
||||||
"Foo.Local" -> Path.of("./Foo/Local"),
|
LibraryName("Foo", "Local") -> Path.of("./Foo/Local"),
|
||||||
"Standard.Base" -> Path.of("./Standard/Base")
|
LibraryName("Standard", "Base") -> Path.of("./Standard/Base")
|
||||||
)
|
)
|
||||||
|
|
||||||
val resolver = LibraryResolver(FakeLocalLibraryProvider(localLibraries))
|
val resolver = LibraryResolver(FakeLocalLibraryProvider(localLibraries))
|
||||||
|
@ -205,7 +205,26 @@ object Config {
|
|||||||
val overridesObject = JsonObject(
|
val overridesObject = JsonObject(
|
||||||
overrides ++ preferLocalOverride: _*
|
overrides ++ preferLocalOverride: _*
|
||||||
)
|
)
|
||||||
originals.remove(JsonFields.ensoVersion).deepMerge(overridesObject).asJson
|
|
||||||
|
/** Fields that should not be inherited from the original set of fields.
|
||||||
|
*
|
||||||
|
* `ensoVersion` is dropped, because due to migration it is overridden by
|
||||||
|
* `edition` and we don't want to have both to avoid inconsistency.
|
||||||
|
*
|
||||||
|
* `prefer-local-libraries` cannot be inherited, because if it was set to
|
||||||
|
* `true` and we have changed it to `false`, overrides will not include it,
|
||||||
|
* because, as `false` is its default value, we just ignore the field. But
|
||||||
|
* if we inherit it from original fields, we would get `true` back. If the
|
||||||
|
* setting is still set to true, it will be included in the overrides, so
|
||||||
|
* it does not have to be inherited either.
|
||||||
|
*/
|
||||||
|
val fieldsToRemoveFromOriginals =
|
||||||
|
Seq(JsonFields.ensoVersion, JsonFields.preferLocalLibraries)
|
||||||
|
|
||||||
|
val removed = fieldsToRemoveFromOriginals.foldLeft(originals) {
|
||||||
|
case (obj, key) => obj.remove(key)
|
||||||
|
}
|
||||||
|
removed.deepMerge(overridesObject).asJson
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Tries to parse the [[Config]] from a YAML string. */
|
/** Tries to parse the [[Config]] from a YAML string. */
|
||||||
@ -229,7 +248,7 @@ object Config {
|
|||||||
ensoVersion: SemVer
|
ensoVersion: SemVer
|
||||||
): Editions.RawEdition = Editions.Raw.Edition(
|
): Editions.RawEdition = Editions.Raw.Edition(
|
||||||
parent = None,
|
parent = None,
|
||||||
engineVersion = Some(SemVerEnsoVersion(ensoVersion)),
|
engineVersion = Some(ensoVersion),
|
||||||
repositories = Map(),
|
repositories = Map(),
|
||||||
libraries = Map()
|
libraries = Map()
|
||||||
)
|
)
|
||||||
|
@ -48,11 +48,13 @@ case class Package[F](
|
|||||||
/** Stores the package metadata on the hard drive. If the package does not exist,
|
/** Stores the package metadata on the hard drive. If the package does not exist,
|
||||||
* creates the required directory structure.
|
* creates the required directory structure.
|
||||||
*/
|
*/
|
||||||
def save(): Unit = {
|
def save(): Try[Unit] = for {
|
||||||
if (!root.exists) createDirectories()
|
_ <- Try {
|
||||||
if (!sourceDir.exists) createSourceDir()
|
if (!root.exists) createDirectories()
|
||||||
saveConfig()
|
if (!sourceDir.exists) createSourceDir()
|
||||||
}
|
}
|
||||||
|
_ <- saveConfig()
|
||||||
|
} yield ()
|
||||||
|
|
||||||
/** Creates the package directory structure.
|
/** Creates the package directory structure.
|
||||||
*/
|
*/
|
||||||
@ -97,11 +99,10 @@ case class Package[F](
|
|||||||
|
|
||||||
/** Saves the config metadata into the package configuration file.
|
/** Saves the config metadata into the package configuration file.
|
||||||
*/
|
*/
|
||||||
def saveConfig(): Unit = {
|
def saveConfig(): Try[Unit] =
|
||||||
val writer = configFile.newBufferedWriter
|
Using(configFile.newBufferedWriter) { writer =>
|
||||||
Try(writer.write(config.toYaml))
|
writer.write(config.toYaml)
|
||||||
writer.close()
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the location of the package's Main file.
|
/** Gets the location of the package's Main file.
|
||||||
*
|
*
|
||||||
@ -208,13 +209,14 @@ class PackageManager[F](implicit val fileSystem: FileSystem[F]) {
|
|||||||
version: String = "0.0.1",
|
version: String = "0.0.1",
|
||||||
edition: Option[Editions.RawEdition] = None,
|
edition: Option[Editions.RawEdition] = None,
|
||||||
authors: List[Contact] = List(),
|
authors: List[Contact] = List(),
|
||||||
maintainers: List[Contact] = List()
|
maintainers: List[Contact] = List(),
|
||||||
|
license: String = ""
|
||||||
): Package[F] = {
|
): Package[F] = {
|
||||||
val config = Config(
|
val config = Config(
|
||||||
name = NameValidation.normalizeName(name),
|
name = NameValidation.normalizeName(name),
|
||||||
namespace = namespace,
|
namespace = namespace,
|
||||||
version = version,
|
version = version,
|
||||||
license = "",
|
license = license,
|
||||||
authors = authors,
|
authors = authors,
|
||||||
edition = edition,
|
edition = edition,
|
||||||
preferLocalLibraries = true,
|
preferLocalLibraries = true,
|
||||||
|
@ -2,7 +2,6 @@ package org.enso.pkg
|
|||||||
|
|
||||||
import io.circe.{Json, JsonObject}
|
import io.circe.{Json, JsonObject}
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.editions.SemVerEnsoVersion
|
|
||||||
import org.scalatest.matchers.should.Matchers
|
import org.scalatest.matchers.should.Matchers
|
||||||
import org.scalatest.wordspec.AnyWordSpec
|
import org.scalatest.wordspec.AnyWordSpec
|
||||||
import org.scalatest.{Inside, OptionValues}
|
import org.scalatest.{Inside, OptionValues}
|
||||||
@ -64,9 +63,7 @@ class ConfigSpec
|
|||||||
|""".stripMargin
|
|""".stripMargin
|
||||||
val parsed = Config.fromYaml(oldFormat).get
|
val parsed = Config.fromYaml(oldFormat).get
|
||||||
|
|
||||||
parsed.edition.get.engineVersion should contain(
|
parsed.edition.get.engineVersion should contain(SemVer(1, 2, 3))
|
||||||
SemVerEnsoVersion(SemVer(1, 2, 3))
|
|
||||||
)
|
|
||||||
|
|
||||||
val serialized = parsed.toYaml
|
val serialized = parsed.toYaml
|
||||||
val parsedAgain = Config.fromYaml(serialized).get
|
val parsedAgain = Config.fromYaml(serialized).get
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
package org.enso.projectmanager.data
|
|
||||||
|
|
||||||
import enumeratum._
|
|
||||||
import org.enso.cli.task.{TaskProgress, ProgressUnit => TaskProgressUnit}
|
|
||||||
|
|
||||||
/** Represents the unit used by progress updates. */
|
|
||||||
sealed trait ProgressUnit extends EnumEntry
|
|
||||||
object ProgressUnit extends Enum[ProgressUnit] with CirceEnum[ProgressUnit] {
|
|
||||||
|
|
||||||
/** Indicates that progress is measured by amount of bytes processed. */
|
|
||||||
case object Bytes extends ProgressUnit
|
|
||||||
|
|
||||||
/** Indicates that progress is measured by some other unit or it is not
|
|
||||||
* measured at all.
|
|
||||||
*/
|
|
||||||
case object Other extends ProgressUnit
|
|
||||||
|
|
||||||
override val values = findValues
|
|
||||||
|
|
||||||
/** Creates a [[ProgressUnit]] from the unit associated with [[TaskProgress]].
|
|
||||||
*/
|
|
||||||
def fromTask(task: TaskProgress[_]): ProgressUnit = task.unit match {
|
|
||||||
case TaskProgressUnit.Bytes => Bytes
|
|
||||||
case TaskProgressUnit.Unspecified => Other
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,6 +3,7 @@ package org.enso.projectmanager.protocol
|
|||||||
import io.circe.generic.auto._
|
import io.circe.generic.auto._
|
||||||
import org.enso.jsonrpc.Protocol
|
import org.enso.jsonrpc.Protocol
|
||||||
import org.enso.projectmanager.protocol.ProjectManagementApi._
|
import org.enso.projectmanager.protocol.ProjectManagementApi._
|
||||||
|
import org.enso.cli.task.notifications.TaskNotificationApi._
|
||||||
|
|
||||||
/** Implicits from this module are required for correct serialization.
|
/** Implicits from this module are required for correct serialization.
|
||||||
*
|
*
|
||||||
|
@ -9,7 +9,6 @@ import org.enso.jsonrpc.{Error, HasParams, HasResult, Method, Unused}
|
|||||||
import org.enso.projectmanager.data.{
|
import org.enso.projectmanager.data.{
|
||||||
EngineVersion,
|
EngineVersion,
|
||||||
MissingComponentAction,
|
MissingComponentAction,
|
||||||
ProgressUnit,
|
|
||||||
ProjectMetadata,
|
ProjectMetadata,
|
||||||
Socket
|
Socket
|
||||||
}
|
}
|
||||||
@ -117,46 +116,6 @@ object ProjectManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case object TaskStarted extends Method("task/started") {
|
|
||||||
|
|
||||||
case class Params(
|
|
||||||
taskId: UUID,
|
|
||||||
relatedOperation: String,
|
|
||||||
unit: ProgressUnit,
|
|
||||||
total: Option[Long]
|
|
||||||
)
|
|
||||||
|
|
||||||
implicit val hasParams = new HasParams[this.type] {
|
|
||||||
type Params = TaskStarted.Params
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case object TaskProgressUpdate extends Method("task/progress-update") {
|
|
||||||
|
|
||||||
case class Params(
|
|
||||||
taskId: UUID,
|
|
||||||
message: Option[String],
|
|
||||||
done: Long
|
|
||||||
)
|
|
||||||
|
|
||||||
implicit val hasParams = new HasParams[this.type] {
|
|
||||||
type Params = TaskProgressUpdate.Params
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case object TaskFinished extends Method("task/progress-update") {
|
|
||||||
|
|
||||||
case class Params(
|
|
||||||
taskId: UUID,
|
|
||||||
message: Option[String],
|
|
||||||
success: Boolean
|
|
||||||
)
|
|
||||||
|
|
||||||
implicit val hasParams = new HasParams[this.type] {
|
|
||||||
type Params = TaskFinished.Params
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case object EngineListInstalled extends Method("engine/list-installed") {
|
case object EngineListInstalled extends Method("engine/list-installed") {
|
||||||
|
|
||||||
case class Result(versions: Seq[EngineVersion])
|
case class Result(versions: Seq[EngineVersion])
|
||||||
|
@ -3,19 +3,11 @@ package org.enso.projectmanager.requesthandler
|
|||||||
import akka.actor.{Actor, ActorRef, Cancellable, Stash, Status}
|
import akka.actor.{Actor, ActorRef, Cancellable, Stash, Status}
|
||||||
import akka.pattern.pipe
|
import akka.pattern.pipe
|
||||||
import com.typesafe.scalalogging.{LazyLogging, Logger}
|
import com.typesafe.scalalogging.{LazyLogging, Logger}
|
||||||
|
import org.enso.cli.task.ProgressNotification
|
||||||
|
import org.enso.cli.task.notifications.ActorProgressNotificationForwarder
|
||||||
import org.enso.jsonrpc.Errors.ServiceError
|
import org.enso.jsonrpc.Errors.ServiceError
|
||||||
import org.enso.jsonrpc.{
|
import org.enso.jsonrpc._
|
||||||
HasParams,
|
|
||||||
HasResult,
|
|
||||||
Id,
|
|
||||||
Method,
|
|
||||||
Request,
|
|
||||||
ResponseError,
|
|
||||||
ResponseResult
|
|
||||||
}
|
|
||||||
import org.enso.projectmanager.control.effect.Exec
|
import org.enso.projectmanager.control.effect.Exec
|
||||||
import org.enso.projectmanager.service.versionmanagement.ProgressNotification
|
|
||||||
import org.enso.projectmanager.service.versionmanagement.ProgressNotification.translateProgressNotification
|
|
||||||
import org.enso.projectmanager.util.UnhandledLogging
|
import org.enso.projectmanager.util.UnhandledLogging
|
||||||
|
|
||||||
import scala.annotation.unused
|
import scala.annotation.unused
|
||||||
@ -118,7 +110,8 @@ abstract class RequestHandler[
|
|||||||
abandonTimeout(id, replyTo, timeoutCancellable)
|
abandonTimeout(id, replyTo, timeoutCancellable)
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
replyTo ! translateProgressNotification(method.name, notification)
|
replyTo ! ActorProgressNotificationForwarder
|
||||||
|
.translateProgressNotification(method.name, notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cancels the timeout operation.
|
/** Cancels the timeout operation.
|
||||||
|
@ -4,7 +4,7 @@ import akka.actor.ActorRef
|
|||||||
import cats.MonadError
|
import cats.MonadError
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.editions.EnsoVersion
|
import org.enso.editions.DefaultEdition
|
||||||
import org.enso.pkg.Config
|
import org.enso.pkg.Config
|
||||||
import org.enso.projectmanager.control.core.syntax._
|
import org.enso.projectmanager.control.core.syntax._
|
||||||
import org.enso.projectmanager.control.core.{
|
import org.enso.projectmanager.control.core.{
|
||||||
@ -44,7 +44,6 @@ import org.enso.projectmanager.service.ValidationFailure.{
|
|||||||
NameShouldStartWithCapitalLetter
|
NameShouldStartWithCapitalLetter
|
||||||
}
|
}
|
||||||
import org.enso.projectmanager.service.config.GlobalConfigServiceApi
|
import org.enso.projectmanager.service.config.GlobalConfigServiceApi
|
||||||
import org.enso.projectmanager.service.config.GlobalConfigServiceFailure.ConfigurationFileAccessFailure
|
|
||||||
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerErrorRecoverySyntax._
|
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerErrorRecoverySyntax._
|
||||||
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerFactory
|
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerFactory
|
||||||
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
|
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
|
||||||
@ -306,15 +305,7 @@ class ProjectService[
|
|||||||
missingComponentAction: MissingComponentAction
|
missingComponentAction: MissingComponentAction
|
||||||
): F[ProjectServiceFailure, RunningLanguageServerInfo] = for {
|
): F[ProjectServiceFailure, RunningLanguageServerInfo] = for {
|
||||||
version <- resolveProjectVersion(project)
|
version <- resolveProjectVersion(project)
|
||||||
version <- configurationService
|
_ <- preinstallEngine(progressTracker, version, missingComponentAction)
|
||||||
.resolveEnsoVersion(version)
|
|
||||||
.mapError { case ConfigurationFileAccessFailure(message) =>
|
|
||||||
ProjectOpenFailed(
|
|
||||||
"Could not deduce the default version to use for the project: " +
|
|
||||||
message
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ <- preinstallEngine(progressTracker, version, missingComponentAction)
|
|
||||||
sockets <- languageServerGateway
|
sockets <- languageServerGateway
|
||||||
.start(progressTracker, clientId, project, version)
|
.start(progressTracker, clientId, project, version)
|
||||||
.mapError {
|
.mapError {
|
||||||
@ -371,18 +362,7 @@ class ProjectService[
|
|||||||
private def resolveProjectMetadata(
|
private def resolveProjectMetadata(
|
||||||
project: Project
|
project: Project
|
||||||
): F[ProjectServiceFailure, ProjectMetadata] = {
|
): F[ProjectServiceFailure, ProjectMetadata] = {
|
||||||
val version = for {
|
val version = resolveProjectVersion(project)
|
||||||
version <- resolveProjectVersion(project)
|
|
||||||
version <- configurationService
|
|
||||||
.resolveEnsoVersion(version)
|
|
||||||
.mapError { case ConfigurationFileAccessFailure(message) =>
|
|
||||||
GlobalConfigurationAccessFailure(
|
|
||||||
"Could not deduce the default version to use for the project: " +
|
|
||||||
message
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} yield version
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
version <- version.map(Some(_)).recover { error =>
|
version <- version.map(Some(_)).recover { error =>
|
||||||
// TODO [RW] We may consider sending this warning to the IDE once
|
// TODO [RW] We may consider sending this warning to the IDE once
|
||||||
@ -483,11 +463,16 @@ class ProjectService[
|
|||||||
|
|
||||||
private def resolveProjectVersion(
|
private def resolveProjectVersion(
|
||||||
project: Project
|
project: Project
|
||||||
): F[ProjectServiceFailure, EnsoVersion] =
|
): F[ProjectServiceFailure, SemVer] =
|
||||||
Sync[F]
|
Sync[F]
|
||||||
.blockingOp {
|
.blockingOp {
|
||||||
|
// TODO [RW] at some point we will need to use the configuration service to get the actual default version, see #1864
|
||||||
|
val _ = configurationService
|
||||||
|
|
||||||
|
val edition =
|
||||||
|
project.edition.getOrElse(DefaultEdition.getDefaultEdition)
|
||||||
distributionConfiguration.editionManager
|
distributionConfiguration.editionManager
|
||||||
.resolveEngineVersion(project.edition)
|
.resolveEngineVersion(edition)
|
||||||
.get
|
.get
|
||||||
}
|
}
|
||||||
.mapError { error =>
|
.mapError { error =>
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
package org.enso.projectmanager.service.versionmanagement
|
package org.enso.projectmanager.service.versionmanagement
|
||||||
|
|
||||||
import java.util.UUID
|
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.cli.task.{ProgressListener, TaskProgress}
|
import org.enso.cli.task.{
|
||||||
|
ProgressNotification,
|
||||||
|
ProgressNotificationForwarder,
|
||||||
|
ProgressUnit
|
||||||
|
}
|
||||||
import org.enso.distribution.locking.Resource
|
import org.enso.distribution.locking.Resource
|
||||||
import org.enso.projectmanager.data.ProgressUnit
|
|
||||||
import org.enso.runtimeversionmanager.components.{
|
import org.enso.runtimeversionmanager.components.{
|
||||||
GraalVMVersion,
|
GraalVMVersion,
|
||||||
RuntimeVersionManagementUserInterface
|
RuntimeVersionManagementUserInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
import scala.util.{Failure, Success, Try}
|
import java.util.UUID
|
||||||
|
|
||||||
/** A [[RuntimeVersionManagementUserInterface]] that sends
|
/** A [[RuntimeVersionManagementUserInterface]] that sends
|
||||||
* [[ProgressNotification]] to the specified actors (both for usual tasks and
|
* [[ProgressNotification]] to the specified actors (both for usual tasks and
|
||||||
@ -27,54 +29,8 @@ class ControllerInterface(
|
|||||||
progressTracker: ActorRef,
|
progressTracker: ActorRef,
|
||||||
allowMissingComponents: Boolean,
|
allowMissingComponents: Boolean,
|
||||||
allowBrokenComponents: Boolean
|
allowBrokenComponents: Boolean
|
||||||
) extends RuntimeVersionManagementUserInterface {
|
) extends RuntimeVersionManagementUserInterface
|
||||||
|
with ProgressNotificationForwarder {
|
||||||
/** @inheritdoc */
|
|
||||||
override def trackProgress(message: String, task: TaskProgress[_]): Unit = {
|
|
||||||
var uuid: Option[UUID] = None
|
|
||||||
|
|
||||||
/** Initializes the task on first invocation and just returns the
|
|
||||||
* generated UUID on further invocations.
|
|
||||||
*/
|
|
||||||
def initializeTask(total: Option[Long]): UUID = uuid match {
|
|
||||||
case Some(value) => value
|
|
||||||
case None =>
|
|
||||||
val generated = UUID.randomUUID()
|
|
||||||
uuid = Some(generated)
|
|
||||||
val unit = ProgressUnit.fromTask(task)
|
|
||||||
progressTracker ! ProgressNotification.TaskStarted(
|
|
||||||
generated,
|
|
||||||
total,
|
|
||||||
unit
|
|
||||||
)
|
|
||||||
generated
|
|
||||||
}
|
|
||||||
task.addProgressListener(new ProgressListener[Any] {
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
override def progressUpdate(
|
|
||||||
done: Long,
|
|
||||||
total: Option[Long]
|
|
||||||
): Unit = {
|
|
||||||
val uuid = initializeTask(total)
|
|
||||||
progressTracker ! ProgressNotification.TaskUpdate(
|
|
||||||
uuid,
|
|
||||||
Some(message),
|
|
||||||
done
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
|
||||||
override def done(result: Try[Any]): Unit = result match {
|
|
||||||
case Failure(exception) =>
|
|
||||||
val uuid = initializeTask(None)
|
|
||||||
progressTracker ! ProgressNotification.TaskFailure(uuid, exception)
|
|
||||||
case Success(_) =>
|
|
||||||
val uuid = initializeTask(None)
|
|
||||||
progressTracker ! ProgressNotification.TaskSuccess(uuid)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
override def shouldInstallMissingEngine(version: SemVer): Boolean =
|
override def shouldInstallMissingEngine(version: SemVer): Boolean =
|
||||||
@ -101,7 +57,7 @@ class ControllerInterface(
|
|||||||
progressTracker ! ProgressNotification.TaskStarted(
|
progressTracker ! ProgressNotification.TaskStarted(
|
||||||
uuid,
|
uuid,
|
||||||
None,
|
None,
|
||||||
ProgressUnit.Other
|
ProgressUnit.Unspecified
|
||||||
)
|
)
|
||||||
progressTracker ! ProgressNotification.TaskUpdate(
|
progressTracker ! ProgressNotification.TaskUpdate(
|
||||||
uuid,
|
uuid,
|
||||||
@ -117,4 +73,10 @@ class ControllerInterface(
|
|||||||
progressTracker ! ProgressNotification.TaskSuccess(uuid)
|
progressTracker ! ProgressNotification.TaskSuccess(uuid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def sendProgressNotification(
|
||||||
|
notification: ProgressNotification
|
||||||
|
): Unit =
|
||||||
|
progressTracker ! notification
|
||||||
}
|
}
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
package org.enso.projectmanager.service.versionmanagement
|
|
||||||
|
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
import org.enso.jsonrpc.Notification
|
|
||||||
import org.enso.projectmanager.data.ProgressUnit
|
|
||||||
import org.enso.projectmanager.protocol.ProjectManagementApi
|
|
||||||
|
|
||||||
/** Internal representation of progress notifications that are sent by the
|
|
||||||
* [[ControllerInterface]].
|
|
||||||
*
|
|
||||||
* They are translated by the [[RequestHandler]] into protocol progress
|
|
||||||
* notifications.
|
|
||||||
*/
|
|
||||||
sealed trait ProgressNotification
|
|
||||||
object ProgressNotification {
|
|
||||||
|
|
||||||
/** Singals that a new task with progress has been started. */
|
|
||||||
case class TaskStarted(
|
|
||||||
taskId: UUID,
|
|
||||||
total: Option[Long],
|
|
||||||
unit: ProgressUnit
|
|
||||||
) extends ProgressNotification
|
|
||||||
|
|
||||||
/** Singals an update to task's progress. */
|
|
||||||
case class TaskUpdate(taskId: UUID, message: Option[String], done: Long)
|
|
||||||
extends ProgressNotification
|
|
||||||
|
|
||||||
/** Singals that a task has been finished successfully. */
|
|
||||||
case class TaskSuccess(taskId: UUID) extends ProgressNotification
|
|
||||||
|
|
||||||
/** Singals that a task has failed. */
|
|
||||||
case class TaskFailure(taskId: UUID, throwable: Throwable)
|
|
||||||
extends ProgressNotification
|
|
||||||
|
|
||||||
/** Translates a [[ProgressNotification]] into a protocol message. */
|
|
||||||
def translateProgressNotification(
|
|
||||||
relatedOperationName: String,
|
|
||||||
progressNotification: ProgressNotification
|
|
||||||
): Notification[_, _] = progressNotification match {
|
|
||||||
case TaskStarted(taskId, total, unit) =>
|
|
||||||
Notification(
|
|
||||||
ProjectManagementApi.TaskStarted,
|
|
||||||
ProjectManagementApi.TaskStarted.Params(
|
|
||||||
taskId = taskId,
|
|
||||||
relatedOperation = relatedOperationName,
|
|
||||||
unit = unit,
|
|
||||||
total = total
|
|
||||||
)
|
|
||||||
)
|
|
||||||
case TaskUpdate(taskId, message, done) =>
|
|
||||||
Notification(
|
|
||||||
ProjectManagementApi.TaskProgressUpdate,
|
|
||||||
ProjectManagementApi.TaskProgressUpdate.Params(taskId, message, done)
|
|
||||||
)
|
|
||||||
case TaskSuccess(taskId) =>
|
|
||||||
Notification(
|
|
||||||
ProjectManagementApi.TaskFinished,
|
|
||||||
ProjectManagementApi.TaskFinished.Params(taskId, None, success = true)
|
|
||||||
)
|
|
||||||
case TaskFailure(taskId, throwable) =>
|
|
||||||
Notification(
|
|
||||||
ProjectManagementApi.TaskFinished,
|
|
||||||
ProjectManagementApi.TaskFinished
|
|
||||||
.Params(taskId, Some(throwable.getMessage), success = false)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -49,9 +49,7 @@ object DefaultDistributionConfiguration
|
|||||||
lazy val resourceManager = new ResourceManager(lockManager)
|
lazy val resourceManager = new ResourceManager(lockManager)
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
lazy val editionManager = new EditionManager(
|
lazy val editionManager = EditionManager(distributionManager)
|
||||||
distributionManager.paths.editionSearchPaths.toList
|
|
||||||
)
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
lazy val temporaryDirectoryManager =
|
lazy val temporaryDirectoryManager =
|
||||||
|
@ -64,9 +64,7 @@ class TestDistributionConfiguration(
|
|||||||
|
|
||||||
lazy val resourceManager = new ResourceManager(lockManager)
|
lazy val resourceManager = new ResourceManager(lockManager)
|
||||||
|
|
||||||
lazy val editionManager: EditionManager = new EditionManager(
|
lazy val editionManager: EditionManager = EditionManager(distributionManager)
|
||||||
distributionManager.paths.editionSearchPaths.toList
|
|
||||||
)
|
|
||||||
|
|
||||||
lazy val temporaryDirectoryManager =
|
lazy val temporaryDirectoryManager =
|
||||||
new TemporaryDirectoryManager(distributionManager, resourceManager)
|
new TemporaryDirectoryManager(distributionManager, resourceManager)
|
||||||
|
@ -4,7 +4,6 @@ import akka.testkit.TestActors.blackholeProps
|
|||||||
import io.circe.Json
|
import io.circe.Json
|
||||||
import io.circe.literal.JsonStringContext
|
import io.circe.literal.JsonStringContext
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.editions.SemVerEnsoVersion
|
|
||||||
import org.enso.projectmanager.data.MissingComponentAction
|
import org.enso.projectmanager.data.MissingComponentAction
|
||||||
import org.enso.projectmanager.{BaseServerSpec, ProjectManagementOps}
|
import org.enso.projectmanager.{BaseServerSpec, ProjectManagementOps}
|
||||||
import org.enso.testkit.RetrySpec
|
import org.enso.testkit.RetrySpec
|
||||||
@ -51,7 +50,7 @@ abstract class ProjectOpenSpecBase
|
|||||||
val edition = config.edition.get
|
val edition = config.edition.get
|
||||||
config.copy(edition =
|
config.copy(edition =
|
||||||
Some(
|
Some(
|
||||||
edition.copy(engineVersion = Some(SemVerEnsoVersion(brokenVersion)))
|
edition.copy(engineVersion = Some(brokenVersion))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -44,11 +44,13 @@ trait FakeEnvironment { self: HasTestDirectory =>
|
|||||||
val configDir = getTestDirectory.resolve("test_config")
|
val configDir = getTestDirectory.resolve("test_config")
|
||||||
val binDir = getTestDirectory.resolve("test_bin")
|
val binDir = getTestDirectory.resolve("test_bin")
|
||||||
val runDir = getTestDirectory.resolve("test_run")
|
val runDir = getTestDirectory.resolve("test_run")
|
||||||
|
val homeDir = getTestDirectory.resolve("test_home")
|
||||||
val env = extraOverrides
|
val env = extraOverrides
|
||||||
.updated("ENSO_DATA_DIRECTORY", dataDir.toString)
|
.updated("ENSO_DATA_DIRECTORY", dataDir.toString)
|
||||||
.updated("ENSO_CONFIG_DIRECTORY", configDir.toString)
|
.updated("ENSO_CONFIG_DIRECTORY", configDir.toString)
|
||||||
.updated("ENSO_BIN_DIRECTORY", binDir.toString)
|
.updated("ENSO_BIN_DIRECTORY", binDir.toString)
|
||||||
.updated("ENSO_RUNTIME_DIRECTORY", runDir.toString)
|
.updated("ENSO_RUNTIME_DIRECTORY", runDir.toString)
|
||||||
|
.updated("ENSO_HOME", homeDir.toString)
|
||||||
val fakeEnvironment = new Environment {
|
val fakeEnvironment = new Environment {
|
||||||
override def getPathToRunningExecutable: Path = executable
|
override def getPathToRunningExecutable: Path = executable
|
||||||
|
|
||||||
|
@ -249,8 +249,11 @@ class Runner(
|
|||||||
): SemVer = versionOverride.getOrElse {
|
): SemVer = versionOverride.getOrElse {
|
||||||
project match {
|
project match {
|
||||||
case Some(project) =>
|
case Some(project) =>
|
||||||
val edition = project.edition
|
// TODO [RW] properly get the default edition, see #1864
|
||||||
val version = editionManager.resolveEngineVersion(edition).get
|
val version = project.edition
|
||||||
|
.map(edition => editionManager.resolveEngineVersion(edition).get)
|
||||||
|
.map(SemVerEnsoVersion)
|
||||||
|
.getOrElse(DefaultEnsoVersion)
|
||||||
version match {
|
version match {
|
||||||
case DefaultEnsoVersion =>
|
case DefaultEnsoVersion =>
|
||||||
globalConfigurationManager.defaultVersion
|
globalConfigurationManager.defaultVersion
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
package org.enso.cli.task.notifications
|
||||||
|
|
||||||
|
import akka.actor.ActorRef
|
||||||
|
import org.enso.cli.task.{
|
||||||
|
ProgressNotification,
|
||||||
|
ProgressNotificationForwarder,
|
||||||
|
ProgressReporter
|
||||||
|
}
|
||||||
|
import org.enso.cli.task.ProgressNotification.{
|
||||||
|
TaskFailure,
|
||||||
|
TaskStarted,
|
||||||
|
TaskSuccess,
|
||||||
|
TaskUpdate
|
||||||
|
}
|
||||||
|
import org.enso.jsonrpc.Notification
|
||||||
|
|
||||||
|
object ActorProgressNotificationForwarder {
|
||||||
|
def translateAndForward(
|
||||||
|
relatedOperationName: String,
|
||||||
|
recipient: ActorRef
|
||||||
|
): ProgressReporter =
|
||||||
|
new ProgressNotificationForwarder {
|
||||||
|
override def sendProgressNotification(
|
||||||
|
notification: ProgressNotification
|
||||||
|
): Unit = {
|
||||||
|
val translated: Notification[_, _] =
|
||||||
|
translateProgressNotification(relatedOperationName, notification)
|
||||||
|
recipient ! translated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Translates a [[ProgressNotification]] into a protocol message. */
|
||||||
|
def translateProgressNotification(
|
||||||
|
relatedOperationName: String,
|
||||||
|
progressNotification: ProgressNotification
|
||||||
|
): Notification[_, _] = progressNotification match {
|
||||||
|
case TaskStarted(taskId, total, unit) =>
|
||||||
|
Notification(
|
||||||
|
TaskNotificationApi.TaskStarted,
|
||||||
|
TaskNotificationApi.TaskStarted.Params(
|
||||||
|
taskId = taskId,
|
||||||
|
relatedOperation = relatedOperationName,
|
||||||
|
unit = unit,
|
||||||
|
total = total
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case TaskUpdate(taskId, message, done) =>
|
||||||
|
Notification(
|
||||||
|
TaskNotificationApi.TaskProgressUpdate,
|
||||||
|
TaskNotificationApi.TaskProgressUpdate.Params(taskId, message, done)
|
||||||
|
)
|
||||||
|
case TaskSuccess(taskId) =>
|
||||||
|
Notification(
|
||||||
|
TaskNotificationApi.TaskFinished,
|
||||||
|
TaskNotificationApi.TaskFinished.Params(taskId, None, success = true)
|
||||||
|
)
|
||||||
|
case TaskFailure(taskId, throwable) =>
|
||||||
|
Notification(
|
||||||
|
TaskNotificationApi.TaskFinished,
|
||||||
|
TaskNotificationApi.TaskFinished
|
||||||
|
.Params(taskId, Option(throwable.getMessage), success = false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package org.enso.cli.task.notifications
|
||||||
|
|
||||||
|
import enumeratum._
|
||||||
|
import org.enso.cli.task.{ProgressUnit => TaskProgressUnit}
|
||||||
|
|
||||||
|
/** Represents the unit used by progress updates. */
|
||||||
|
sealed trait SerializableProgressUnit extends EnumEntry
|
||||||
|
object SerializableProgressUnit
|
||||||
|
extends Enum[SerializableProgressUnit]
|
||||||
|
with CirceEnum[SerializableProgressUnit] {
|
||||||
|
|
||||||
|
/** Indicates that progress is measured by amount of bytes processed. */
|
||||||
|
case object Bytes extends SerializableProgressUnit
|
||||||
|
|
||||||
|
/** Indicates that progress is measured by some other unit or it is not
|
||||||
|
* measured at all.
|
||||||
|
*/
|
||||||
|
case object Other extends SerializableProgressUnit
|
||||||
|
|
||||||
|
override val values = findValues
|
||||||
|
|
||||||
|
/** Converts a [[TaskProgressUnit]] to [[SerializableProgressUnit]].
|
||||||
|
*/
|
||||||
|
implicit def fromUnit(unit: TaskProgressUnit): SerializableProgressUnit =
|
||||||
|
unit match {
|
||||||
|
case TaskProgressUnit.Bytes => Bytes
|
||||||
|
case TaskProgressUnit.Unspecified => Other
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package org.enso.cli.task.notifications
|
||||||
|
|
||||||
|
import org.enso.jsonrpc.{HasParams, Method}
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
object TaskNotificationApi {
|
||||||
|
|
||||||
|
case object TaskStarted extends Method("task/started") {
|
||||||
|
|
||||||
|
case class Params(
|
||||||
|
taskId: UUID,
|
||||||
|
relatedOperation: String,
|
||||||
|
unit: SerializableProgressUnit,
|
||||||
|
total: Option[Long]
|
||||||
|
)
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = TaskStarted.Params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object TaskProgressUpdate extends Method("task/progress-update") {
|
||||||
|
|
||||||
|
case class Params(
|
||||||
|
taskId: UUID,
|
||||||
|
message: Option[String],
|
||||||
|
done: Long
|
||||||
|
)
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = TaskProgressUpdate.Params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object TaskFinished extends Method("task/finished") {
|
||||||
|
|
||||||
|
case class Params(
|
||||||
|
taskId: UUID,
|
||||||
|
message: Option[String],
|
||||||
|
success: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = TaskFinished.Params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,3 @@
|
|||||||
E4A61C4649AD6FB148D4779D9B20B395AEAB73475EE13D20EFB55C163724A77B
|
C03EC922F039EB5CC96F93A489453ADDD4FA6EBBF75713B05FE67B48CFF6ACBF
|
||||||
64FE86A276F737CE2B4D352D8F35F790CA0DA18F329A48A467F34FC9C3CAF07D
|
64FE86A276F737CE2B4D352D8F35F790CA0DA18F329A48A467F34FC9C3CAF07D
|
||||||
0
|
0
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
0BA0D3694722E724BABC3D4D860FA5D11DA541B20632F18175E1F45BF44DE717
|
0AED341E22D16C5722BF722FD5F92039464E9FE3CCD3288D3643F05E09AF62FB
|
||||||
B7948AB0E996317E7F073260BA28A63D14215436079C09E793F803CF999D607C
|
B7948AB0E996317E7F073260BA28A63D14215436079C09E793F803CF999D607C
|
||||||
0
|
0
|
||||||
|
Loading…
Reference in New Issue
Block a user