Skip to content

Commit

Permalink
Metals support (scalacenter#655)
Browse files Browse the repository at this point in the history
* initlial commmit of metals server

* fix tests, add tests and refactor them

* metals add documentation

* metals add css, status indicator and refactor the code

* add deployment script for metals runner

* add configuration files

* update CONTRIBUTING.md

* fix sbtmatrix scala version

* fix cache, blocking and mobile ui
  • Loading branch information
rochala authored Nov 22, 2022
1 parent 4e41b82 commit ef463cf
Show file tree
Hide file tree
Showing 80 changed files with 2,482 additions and 287 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,4 @@ metals.sbt
.bloop
.metals
.vscode
project/project
dist
client/dist
37 changes: 33 additions & 4 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,14 +1,43 @@
version = 1.1.0
version = 3.5.8

style = default
maxColumn = 140
maxColumn = 120

runner.dialect = scala213
fileOverride {
"glob:**/metals-runner/src/**" {
runner.dialect = scala3
}
}

docstrings = JavaDoc
docstrings.blankFirstLine = true
assumeStandardLibraryStripMargin = true
project.git = true
danglingParentheses = true
lineEndings = unix
encoding = UTF-8
importSelectors = singleLine
project.excludeFilters = [
instrumentation/src/test/resources
storage/src/test/resources
demo/
]
]
align.preset = more

rewrite.rules = [Imports]
rewrite.imports.sort = original
rewrite.imports.groups = [
["java\\..*", "scala\\..*"],
]

newlines.beforeMultiline = fold
newlines.topLevelStatements = [before, after]
newlines.topLevelStatementsMinBreaks = 2
newlines.implicitParamListModifierForce = [before]

continuationIndent.defnSite = 2
continuationIndent.extendSite = 2

rewrite.imports.expand = false
rewrite.trailingCommas.style = "never"

51 changes: 35 additions & 16 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ You can install a pre-commit hook with `bin/hooks.sh`
├── runtime-scala | methods exposed inside scastie
├── sbt-runner | remote actor communicating with sbt instance over I/O streams
├── sbt-scastie | sbt plugin to report errors and console output with the `sbt-api` model
├── metals-runner | server responsible for managing metals instances to provide interactive features
├── server | web server
└── utils | read/writte files
```
Expand All @@ -91,22 +92,39 @@ You can install a pre-commit hook with `bin/hooks.sh`


```
Scala.js Client run/save/format +-------------------------------------------+
+-----------------+ AutowireApi +---------------------+ +-------------------------------------------+|
| ScastieBackend | (HTTP) | +------------+ | akka+remote +-------------------------------------------+||
| +--------+ +-----------------> | |LoadBalancer| <------------------+ | SbtActor Sbt(Proccess) |||
| | | | | +------------+ | | | +----------+ +-----------+ |||
| | | | | | +---> | | <-----> |sbt|scastie| ||+
| | | | | | | +----+-----+ I/O Stream +-----------+ |+
| | | | | | +-------------------------------------------+
| | | | | |
| | | | | |
| | | | | |
| | | | | SnippetContainer(DB)|
| | | | | |
| | | | <-----------------+ oauth |
| +--------+ | SnippetProgress | static ressources |
+-----------------+ (sse/websocket) +---------------------+
Scala.js Client run/save/format +-------------------------------------------+
+---------------------+ AutowireApi +---------------------+ +-------------------------------------------+|
| ScastieBackend | (HTTP) | +------------+ | akka+remote +-------------------------------------------+||
| +--------+ +-----------------> | |LoadBalancer| <------------------+ | SbtActor Sbt(Proccess) |||
| | | | | +------------+ | | | +----------+ +-----------+ |||
| | | | | | +---> | | <-----> |sbt|scastie| ||+
| | | | | | | +----------+ I/O Stream +-----------+ ++
| | | | | | +-------------------------------------------+
| | | | | |
| | | | | |
| | | | | |
| | | | | SnippetContainer(DB)|
| | | | | |
| | | | <-----------------+ oauth |
| +--------+ | SnippetProgress | static ressources |
| | (sse/websocket) +---------------------+
| InteractiveProvider |
| +--------+ | +---------------------+
| | | | | |
| | | | | MetalsRunnerServer |
| | +-------------------> | |
| | | | (HTTP request) | |
| +--------+ | | |
| | | |
+---------------------+ +---------------------+
Editor: http://asciiflow.com/
Expand Down Expand Up @@ -180,6 +198,7 @@ ssh [email protected]
./sbt.sh
exit
./server.sh
./metalsRunner.sh
```

# Running with docker locally
Expand Down
100 changes: 100 additions & 0 deletions api/src/main/scala/com.olegych.scastie.api/ApiModels.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,25 @@ object FormatResponse {
}
}


object EitherFormat {
import play.api.libs.functional.syntax._
implicit object JsEither {

implicit def eitherReads[A, B](implicit A: Reads[A], B: Reads[B]): Reads[Either[A, B]] = {
(JsPath \ "Left" \ "value").read[A].map(Left(_)) or
(JsPath \ "Right" \ "value").read[B].map(Right(_))
}

implicit def eitherWrites[A, B](implicit A: Writes[A], B: Writes[B]): Writes[Either[A, B]] = Writes[Either[A, B]] {
case Left(value) => Json.obj("Left" -> Json.toJson(value))
case Right(value) => Json.obj("Right" -> Json.toJson(value))
}
}
}



case class FormatResponse(
result: Either[String, String]
)
Expand Down Expand Up @@ -108,6 +127,87 @@ case class ScalaDependency(
override def toString: String = target.renderSbt(this)
}

case class ScastieMetalsOptions(dependencies: Set[ScalaDependency], scalaTarget: ScalaTarget)

object ScastieMetalsOptions {
implicit val scastieMetalsOptions: OFormat[ScastieMetalsOptions] = Json.format[ScastieMetalsOptions]
}

case class ScastieOffsetParams(content: String, offset: Int, isWorksheetMode: Boolean)

sealed trait FailureType {
val msg: String
}

case class NoResult(msg: String) extends FailureType
case class PresentationCompilerFailure(msg: String) extends FailureType

object FailureType {
implicit val failureTypeFormat: OFormat[FailureType] = Json.format[FailureType]
}

object NoResult {
implicit val noResultFormat: OFormat[NoResult] = Json.format[NoResult]
}

object PresentationCompilerFailure {
implicit val presentationCompilerFailureFormat: OFormat[PresentationCompilerFailure] = Json.format[PresentationCompilerFailure]
}

object ScastieOffsetParams {
implicit val scastieOffsetParams: OFormat[ScastieOffsetParams] = Json.format[ScastieOffsetParams]
}

case class LSPRequestDTO(options: ScastieMetalsOptions, offsetParams: ScastieOffsetParams)
case class CompletionInfoRequest(options: ScastieMetalsOptions, completionItem: CompletionItemDTO)

object CompletionInfoRequest {
implicit val completionInfoRequestFormat: OFormat[CompletionInfoRequest] = Json.format[CompletionInfoRequest]
}

case class InsertInstructions(text: String, cursorMove: Int)
case class AdditionalInsertInstructions(text: String, startLine: Int, startChar: Int, endLine: Int, endChar: Int)

case class CompletionItemDTO(
label: String,
detail: String,
tpe: String,
order: Option[Int],
instructions: InsertInstructions,
additionalInsertInstructions: List[AdditionalInsertInstructions],
symbol: Option[String]
)


case class HoverDTO(from: Int, to: Int, content: String)

case class CompletionsDTO(items: Set[CompletionItemDTO])


object InsertInstructions {
implicit val insertInstructionsFormat: OFormat[InsertInstructions] = Json.format[InsertInstructions]
}

object AdditionalInsertInstructions {
implicit val additionalInsertInstructionsFormat: OFormat[AdditionalInsertInstructions] = Json.format[AdditionalInsertInstructions]
}

object CompletionItemDTO {
implicit val completionItemDTOFormat: OFormat[CompletionItemDTO] = Json.format[CompletionItemDTO]
}

object CompletionsDTO {
implicit val completionsDTOFormat: OFormat[CompletionsDTO] = Json.format[CompletionsDTO]
}

object LSPRequestDTO {
implicit val lspRequestDTOFormat: OFormat[LSPRequestDTO] = Json.format[LSPRequestDTO]
}

object HoverDTO {
implicit val hoverDTOFormat: OFormat[HoverDTO] = Json.format[HoverDTO]
}

object Project {
implicit val formatProject: OFormat[Project] =
Json.format[Project]
Expand Down
2 changes: 1 addition & 1 deletion bin/scalafmt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ if [ ! -f $HERE/.coursier ]; then
fi

if [ ! -f $HERE/.scalafmt ]; then
$HERE/.coursier bootstrap com.geirsson:scalafmt-cli_2.11:1.1.0 --main org.scalafmt.cli.Cli -o $HERE/.scalafmt
$HERE/.coursier bootstrap com.geirsson:scalafmt-cli_2.12:1.5.1 --main org.scalafmt.cli.Cli -o $HERE/.scalafmt
chmod +x $HERE/.scalafmt
fi

Expand Down
37 changes: 32 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ def akka(module: String) = "com.typesafe.akka" %% ("akka-" + module) % "2.6.19"

val akkaHttpVersion = "10.2.9"

addCommandAlias("startAll", "sbtRunner/reStart;server/reStart;client/fastLinkJS")
addCommandAlias("startAllProd", "sbtRunner/reStart;server/fullLinkJS/reStart")
addCommandAlias("startAll", "sbtRunner/reStart;server/reStart;metalsRunner/reStart;client/fastLinkJS")
addCommandAlias("startAllProd", "sbtRunner/reStart;metalsRunner/reStart;server/fullLinkJS/reStart")

val fastLinkOutputDir = taskKey[String]("output directory for `yarn dev`")
val fullLinkOutputDir = taskKey[String]("output directory for `yarn build`")
Expand All @@ -28,6 +28,7 @@ lazy val scastie = project
server,
storage,
utils,
metalsRunner,
).map(_.project)):_*
)
.settings(baseSettings)
Expand All @@ -38,7 +39,7 @@ lazy val scastie = project
val ___ = (server / Universal / packageBin).value
},
)
.settings(Deployment.settings(server, sbtRunner))
.settings(Deployment.settings(server, sbtRunner, metalsRunner))

lazy val testSettings =
Seq(
Expand Down Expand Up @@ -98,6 +99,29 @@ lazy val smallRunnerRuntimeDependenciesInTest = {
)
}

lazy val metalsRunner = project
.in(file("metals-runner"))
.settings(baseNoCrossSettings)
.settings(
fork := true,
maintainer := "scalacenter",
scalaVersion := ScalaVersions.stable3,
libraryDependencies ++= Seq(
"org.scalameta" % "metals" % "0.11.9" cross(CrossVersion.for3Use2_13),
"org.eclipse.lsp4j" % "org.eclipse.lsp4j" % "0.15.0",
"org.http4s" %% "http4s-ember-server" % "0.23.16",
"org.http4s" %% "http4s-ember-client" % "0.23.16",
"org.http4s" %% "http4s-dsl" % "0.23.16",
"org.http4s" %% "http4s-circe" % "0.23.16",
"io.circe" %% "circe-generic" % "0.14.2",
"org.scalameta" %% "munit" % "0.7.29" % Test,
"com.evolutiongaming" %% "scache" % "4.2.3",
"org.typelevel" %% "munit-cats-effect-3" % "1.0.6" % Test
)
)
.enablePlugins(JavaServerAppPackaging)
.dependsOn(api.jvm(ScalaVersions.old3))

lazy val dockerOrg = "scalacenter"

lazy val sbtRunner = project
Expand Down Expand Up @@ -219,11 +243,14 @@ lazy val client = project
baseDirectory.value.getParentFile
},
stFlavour := Flavour.ScalajsReact,
scalaJSLinkerConfig := {
Compile / fastLinkJS / scalaJSLinkerConfig := {
val dir = (Compile / fastLinkJS / scalaJSLinkerOutputDirectory).value.toURI()
scalaJSLinkerConfig.value.withModuleKind(ModuleKind.ESModule)
.withRelativizeSourceMapBase(Some(dir))
},
Compile / fullLinkJS / scalaJSLinkerConfig := {
scalaJSLinkerConfig.value.withModuleKind(ModuleKind.ESModule)
},
fastLinkOutputDir := linkerOutputDirectory((Compile / fastLinkJS).value).getAbsolutePath(),
fullLinkOutputDir := linkerOutputDirectory((Compile / fullLinkJS).value).getAbsolutePath(),
yarnBuild := {
Expand Down Expand Up @@ -257,7 +284,7 @@ lazy val instrumentation = project
.settings(loggingAndTest)
.settings(
libraryDependencies ++= Seq(
"org.scalameta" %% "scalameta" % "4.5.5",
"org.scalameta" %% "scalameta" % "4.6.0",
"com.googlecode.java-diff-utils" % "diffutils" % "1.3.0" % Test
)
)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion client/src/main/resources/prod/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { scastie } from '@linkOutputDir/main.js'
import '@resources/sass/app-main.scss'

import * as Sentry from "@sentry/browser";
import { BrowserTracing } from "@sentry/tracing";

Sentry.init({
dsn: "https://[email protected]/6778768",
Expand Down
3 changes: 3 additions & 0 deletions client/src/main/resources/sass/dark.scss
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ $cm-gutters-linenumber-color: #839496;
$cm-active-hint-background: #7dc8e5;
$cm-active-hint-text: #002b36;

// Tooltip
$code-background-color: #171717;

.scastie {
.dark {
.main-panel {
Expand Down
3 changes: 3 additions & 0 deletions client/src/main/resources/sass/light.scss
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ $cm-active-hint-background: #7dc8e5;
$cm-active-hint-text: #002b36;
$cm-cursor: #00acdb;

// Tooltip
$code-background-color: #e3e3ca;

.scastie {
.light {
.main-panel {
Expand Down
Loading

0 comments on commit ef463cf

Please sign in to comment.