Add BSP buildTarget/scalaMainClasses endpoint

This commit is contained in:
Adrien Piquerez 2020-09-14 13:58:51 +02:00
parent 3bee2cff9a
commit 92fb69f370
15 changed files with 380 additions and 4 deletions

View File

@ -400,6 +400,8 @@ object Keys {
val bspBuildTargetCompileItem = taskKey[Int]("").withRank(DTask)
val bspBuildTargetScalacOptions = inputKey[Unit]("").withRank(DTask)
val bspBuildTargetScalacOptionsItem = taskKey[ScalacOptionsItem]("").withRank(DTask)
val bspScalaMainClasses = inputKey[Unit]("Corresponds to buildTarget/scalaMainClasses request").withRank(DTask)
val bspScalaMainClassesItem = taskKey[ScalaMainClassesItem]("").withRank(DTask)
val useCoursier = settingKey[Boolean]("Use Coursier for dependency resolution.").withRank(BSetting)
val csrCacheDirectory = settingKey[File]("Coursier cache directory. Uses -Dsbt.coursier.home or Coursier's default.").withRank(CSetting)

View File

@ -138,7 +138,18 @@ object BuildServerProtocol {
s.respondEvent(result)
}
}.evaluated,
bspBuildTargetScalacOptions / aggregate := false
bspBuildTargetScalacOptions / aggregate := false,
bspScalaMainClasses := Def.inputTaskDyn {
val s = state.value
val workspace = bspWorkspace.value
val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri)))
val filter = ScopeFilter.in(targets.map(workspace))
Def.task {
val items = bspScalaMainClassesItem.all(filter).value
val result = ScalaMainClassesResult(items.toVector, None)
s.respondEvent(result)
}
}.evaluated
)
// This will be scoped to Compile, Test, IntegrationTest etc
@ -164,7 +175,8 @@ object BuildServerProtocol {
bspBuildTargetDependencySourcesItem := dependencySourcesItemTask.value,
bspBuildTargetCompileItem := bspCompileTask.value,
bspBuildTargetScalacOptionsItem := scalacOptionsTask.value,
bspInternalDependencyConfigurations := internalDependencyConfigurationsSetting.value
bspInternalDependencyConfigurations := internalDependencyConfigurationsSetting.value,
bspScalaMainClassesItem := scalaMainClassesTask.value
)
def handler(
@ -222,6 +234,12 @@ object BuildServerProtocol {
val targets = param.targets.map(_.uri).mkString(" ")
val command = Keys.bspBuildTargetScalacOptions.key
val _ = callback.appendExec(s"$command $targets", Some(r.id))
case r: JsonRpcRequestMessage if r.method == "buildTarget/scalaMainClasses" =>
val param = Converter.fromJson[ScalacOptionsParams](json(r)).get
val targets = param.targets.map(_.uri).mkString(" ")
val command = Keys.bspScalaMainClasses.key
val _ = callback.appendExec(s"$command $targets", Some(r.id))
},
onResponse = PartialFunction.empty,
onNotification = PartialFunction.empty,
@ -403,6 +421,17 @@ object BuildServerProtocol {
}
}
private def scalaMainClassesTask: Initialize[Task[ScalaMainClassesItem]] = Def.task {
val jvmOptions = Keys.javaOptions.value.toVector
val mainClasses = Keys.discoveredMainClasses.value.map(
ScalaMainClass(_, Vector(), jvmOptions)
)
ScalaMainClassesItem(
bspTargetIdentifier.value,
mainClasses.toVector
)
}
private def toId(ref: ProjectReference, config: Configuration): BuildTargetIdentifier =
ref match {
case ProjectRef(build, project) =>

View File

@ -0,0 +1,45 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/**
* @param class The main class to run.
* @param arguments The user arguments to the main entrypoint.
* @param jvmOptions The jvm options for the application.
*/
final class ScalaMainClass private (
val `class`: String,
val arguments: Vector[String],
val jvmOptions: Vector[String]) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: ScalaMainClass => (this.`class` == x.`class`) && (this.arguments == x.arguments) && (this.jvmOptions == x.jvmOptions)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaMainClass".##) + `class`.##) + arguments.##) + jvmOptions.##)
}
override def toString: String = {
"ScalaMainClass(" + `class` + ", " + arguments + ", " + jvmOptions + ")"
}
private[this] def copy(`class`: String = `class`, arguments: Vector[String] = arguments, jvmOptions: Vector[String] = jvmOptions): ScalaMainClass = {
new ScalaMainClass(`class`, arguments, jvmOptions)
}
def withClass(`class`: String): ScalaMainClass = {
copy(`class` = `class`)
}
def withArguments(arguments: Vector[String]): ScalaMainClass = {
copy(arguments = arguments)
}
def withJvmOptions(jvmOptions: Vector[String]): ScalaMainClass = {
copy(jvmOptions = jvmOptions)
}
}
object ScalaMainClass {
def apply(`class`: String, arguments: Vector[String], jvmOptions: Vector[String]): ScalaMainClass = new ScalaMainClass(`class`, arguments, jvmOptions)
}

View File

@ -0,0 +1,40 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/**
* @param target The build target that contains the test classes.
* @param classes The main class items
*/
final class ScalaMainClassesItem private (
val target: sbt.internal.bsp.BuildTargetIdentifier,
val classes: Vector[sbt.internal.bsp.ScalaMainClass]) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: ScalaMainClassesItem => (this.target == x.target) && (this.classes == x.classes)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaMainClassesItem".##) + target.##) + classes.##)
}
override def toString: String = {
"ScalaMainClassesItem(" + target + ", " + classes + ")"
}
private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, classes: Vector[sbt.internal.bsp.ScalaMainClass] = classes): ScalaMainClassesItem = {
new ScalaMainClassesItem(target, classes)
}
def withTarget(target: sbt.internal.bsp.BuildTargetIdentifier): ScalaMainClassesItem = {
copy(target = target)
}
def withClasses(classes: Vector[sbt.internal.bsp.ScalaMainClass]): ScalaMainClassesItem = {
copy(classes = classes)
}
}
object ScalaMainClassesItem {
def apply(target: sbt.internal.bsp.BuildTargetIdentifier, classes: Vector[sbt.internal.bsp.ScalaMainClass]): ScalaMainClassesItem = new ScalaMainClassesItem(target, classes)
}

View File

@ -0,0 +1,45 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/**
* The build target main classes request is sent from the client to the server
* to query for the list of main classes that can be fed as arguments to buildTarget/run.
* @param originId An optional number uniquely identifying a client request.
*/
final class ScalaMainClassesParams private (
val targets: Vector[sbt.internal.bsp.BuildTargetIdentifier],
val originId: Option[String]) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: ScalaMainClassesParams => (this.targets == x.targets) && (this.originId == x.originId)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaMainClassesParams".##) + targets.##) + originId.##)
}
override def toString: String = {
"ScalaMainClassesParams(" + targets + ", " + originId + ")"
}
private[this] def copy(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier] = targets, originId: Option[String] = originId): ScalaMainClassesParams = {
new ScalaMainClassesParams(targets, originId)
}
def withTargets(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): ScalaMainClassesParams = {
copy(targets = targets)
}
def withOriginId(originId: Option[String]): ScalaMainClassesParams = {
copy(originId = originId)
}
def withOriginId(originId: String): ScalaMainClassesParams = {
copy(originId = Option(originId))
}
}
object ScalaMainClassesParams {
def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier], originId: Option[String]): ScalaMainClassesParams = new ScalaMainClassesParams(targets, originId)
def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier], originId: String): ScalaMainClassesParams = new ScalaMainClassesParams(targets, Option(originId))
}

View File

@ -0,0 +1,41 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/** @param originId An optional id of the request that triggered this result. */
final class ScalaMainClassesResult private (
val items: Vector[sbt.internal.bsp.ScalaMainClassesItem],
val originId: Option[String]) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: ScalaMainClassesResult => (this.items == x.items) && (this.originId == x.originId)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaMainClassesResult".##) + items.##) + originId.##)
}
override def toString: String = {
"ScalaMainClassesResult(" + items + ", " + originId + ")"
}
private[this] def copy(items: Vector[sbt.internal.bsp.ScalaMainClassesItem] = items, originId: Option[String] = originId): ScalaMainClassesResult = {
new ScalaMainClassesResult(items, originId)
}
def withItems(items: Vector[sbt.internal.bsp.ScalaMainClassesItem]): ScalaMainClassesResult = {
copy(items = items)
}
def withOriginId(originId: Option[String]): ScalaMainClassesResult = {
copy(originId = originId)
}
def withOriginId(originId: String): ScalaMainClassesResult = {
copy(originId = Option(originId))
}
}
object ScalaMainClassesResult {
def apply(items: Vector[sbt.internal.bsp.ScalaMainClassesItem], originId: Option[String]): ScalaMainClassesResult = new ScalaMainClassesResult(items, originId)
def apply(items: Vector[sbt.internal.bsp.ScalaMainClassesItem], originId: String): ScalaMainClassesResult = new ScalaMainClassesResult(items, Option(originId))
}

View File

@ -41,4 +41,8 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.internal.bsp.codec.ScalacOptionsResultFormats
with sbt.internal.bsp.codec.BspConnectionDetailsFormats
with sbt.internal.bsp.codec.MetalsMetadataFormats
with sbt.internal.bsp.codec.ScalaMainClassesParamsFormats
with sbt.internal.bsp.codec.ScalaMainClassFormats
with sbt.internal.bsp.codec.ScalaMainClassesItemFormats
with sbt.internal.bsp.codec.ScalaMainClassesResultFormats
object JsonProtocol extends JsonProtocol

View File

@ -0,0 +1,31 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait ScalaMainClassFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val ScalaMainClassFormat: JsonFormat[sbt.internal.bsp.ScalaMainClass] = new JsonFormat[sbt.internal.bsp.ScalaMainClass] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaMainClass = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val `class` = unbuilder.readField[String]("class")
val arguments = unbuilder.readField[Vector[String]]("arguments")
val jvmOptions = unbuilder.readField[Vector[String]]("jvmOptions")
unbuilder.endObject()
sbt.internal.bsp.ScalaMainClass(`class`, arguments, jvmOptions)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.ScalaMainClass, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("class", obj.`class`)
builder.addField("arguments", obj.arguments)
builder.addField("jvmOptions", obj.jvmOptions)
builder.endObject()
}
}
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait ScalaMainClassesItemFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.bsp.codec.ScalaMainClassFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val ScalaMainClassesItemFormat: JsonFormat[sbt.internal.bsp.ScalaMainClassesItem] = new JsonFormat[sbt.internal.bsp.ScalaMainClassesItem] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaMainClassesItem = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val target = unbuilder.readField[sbt.internal.bsp.BuildTargetIdentifier]("target")
val classes = unbuilder.readField[Vector[sbt.internal.bsp.ScalaMainClass]]("classes")
unbuilder.endObject()
sbt.internal.bsp.ScalaMainClassesItem(target, classes)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.ScalaMainClassesItem, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("target", obj.target)
builder.addField("classes", obj.classes)
builder.endObject()
}
}
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait ScalaMainClassesParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val ScalaMainClassesParamsFormat: JsonFormat[sbt.internal.bsp.ScalaMainClassesParams] = new JsonFormat[sbt.internal.bsp.ScalaMainClassesParams] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaMainClassesParams = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val targets = unbuilder.readField[Vector[sbt.internal.bsp.BuildTargetIdentifier]]("targets")
val originId = unbuilder.readField[Option[String]]("originId")
unbuilder.endObject()
sbt.internal.bsp.ScalaMainClassesParams(targets, originId)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.ScalaMainClassesParams, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("targets", obj.targets)
builder.addField("originId", obj.originId)
builder.endObject()
}
}
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait ScalaMainClassesResultFormats { self: sbt.internal.bsp.codec.ScalaMainClassesItemFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val ScalaMainClassesResultFormat: JsonFormat[sbt.internal.bsp.ScalaMainClassesResult] = new JsonFormat[sbt.internal.bsp.ScalaMainClassesResult] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaMainClassesResult = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val items = unbuilder.readField[Vector[sbt.internal.bsp.ScalaMainClassesItem]]("items")
val originId = unbuilder.readField[Option[String]]("originId")
unbuilder.endObject()
sbt.internal.bsp.ScalaMainClassesResult(items, originId)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.ScalaMainClassesResult, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("items", obj.items)
builder.addField("originId", obj.originId)
builder.endObject()
}
}
}

View File

@ -468,4 +468,39 @@ type MetalsMetadata {
## The list of scala versions that are supported by Metals
supportedScalaVersions: [String]
}
}
## The build target main classes request is sent from the client to the server
## to query for the list of main classes that can be fed as arguments to buildTarget/run.
type ScalaMainClassesParams {
targets: [sbt.internal.bsp.BuildTargetIdentifier]
## An optional number uniquely identifying a client request.
originId: String
}
type ScalaMainClassesResult {
items: [sbt.internal.bsp.ScalaMainClassesItem]
## An optional id of the request that triggered this result.
originId: String
}
type ScalaMainClassesItem {
## The build target that contains the test classes.
target: sbt.internal.bsp.BuildTargetIdentifier!
## The main class items
classes: [sbt.internal.bsp.ScalaMainClass]
}
type ScalaMainClass {
## The main class to run.
class: String!
## The user arguments to the main entrypoint.
arguments: [String]
## The jvm options for the application.
jvmOptions: [String]
}

View File

@ -5,7 +5,7 @@ Global / serverLog / logLevel := Level.Debug
lazy val root = (project in file("."))
.aggregate(foo, util)
lazy val foo = project
lazy val foo = project.in(file("foo"))
.dependsOn(util)
lazy val util = project

View File

@ -0,0 +1,3 @@
package foo
object FooMain extends App

View File

@ -85,6 +85,20 @@ object BuildServerTest extends AbstractServerTest {
})
}
test("buildTarget/scalaMainClasses") { _ =>
val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#foo/Compile"
svr.sendJsonRpc(
s"""{ "jsonrpc": "2.0", "id": "16", "method": "buildTarget/scalaMainClasses", "params": {
| "targets": [{ "uri": "$x" }]
|} }""".stripMargin
)
assert(svr.waitForString(10.seconds) { s =>
println(s)
(s contains """"id":"16"""") &&
(s contains """"class":"foo.FooMain"""")
})
}
def initializeRequest(): Unit = {
svr.sendJsonRpc(
"""{ "jsonrpc": "2.0", "id": "10", "method": "build/initialize",