diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 683c526a5..9021301a9 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -342,7 +342,7 @@ object Keys { val buildTargetIdentifier = settingKey[BuildTargetIdentifier]("Id for BSP build target.").withRank(DSetting) val bspWorkspace = taskKey[Map[BuildTargetIdentifier, Scope]]("Mapping of BSP build targets to sbt scopes").withRank(DTask) val bspWorkspaceBuildTargets = taskKey[Seq[BuildTarget]]("List all the BSP build targets").withRank(DTask) - val bspBuildTarget = settingKey[BuildTarget]("Description of the BSP build target").withRank(DSetting) + val bspBuildTarget = taskKey[BuildTarget]("Description of the BSP build target").withRank(DTask) val bspBuildTargetSources = inputKey[Unit]("").withRank(DTask) val bspBuildTargetSourcesItem = taskKey[SourcesItem]("").withRank(DTask) val bspBuildTargetCompile = inputKey[Unit]("").withRank(DTask) diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala index 740f39ce0..ddf9662b6 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -21,7 +21,6 @@ import sbt.internal.langserver.ErrorCodes import sbt.internal.protocol.JsonRpcRequestMessage import sbt.librarymanagement.Configuration import sbt.librarymanagement.Configurations.{ Compile, Test } -import sbt.std.TaskExtra._ import sjsonnew.shaded.scalajson.ast.unsafe.JValue import sjsonnew.support.scalajson.unsafe.Converter @@ -36,9 +35,11 @@ object BuildServerProtocol { val scopes: Seq[Scope] = structure.allProjectRefs.flatMap { ref => Seq(Scope.Global.in(ref, Compile), Scope.Global.in(ref, Test)) } - scopes - .map(scope => (scope / Keys.buildTargetIdentifier).toTask) - .joinWith(tasks => joinTasks(tasks).join.map(_.zip(scopes).toMap)) + Def.task { + val targetIds = scopes.map(_ / Keys.buildTargetIdentifier).join.value + targetIds.zip(scopes).toMap + } + }.value, bspWorkspaceBuildTargets := Def.taskDyn { val workspace = Keys.bspWorkspace.value @@ -98,7 +99,7 @@ object BuildServerProtocol { val c = configuration.value toId(ref, c) }, - bspBuildTarget := bspBuildTargetSetting.value, + bspBuildTarget := bspBuildTargetTask.value, bspBuildTargetSourcesItem := { val id = buildTargetIdentifier.value val dirs = unmanagedSourceDirectories.value @@ -172,44 +173,47 @@ object BuildServerProtocol { ) ) - private def bspBuildTargetSetting: Def.Initialize[BuildTarget] = Def.settingDyn { + private def bspBuildTargetTask: Def.Initialize[Task[BuildTarget]] = Def.taskDyn { import sbt.internal.bsp.codec.JsonProtocol._ val buildTargetIdentifier = Keys.buildTargetIdentifier.value val thisProject = Keys.thisProject.value val thisProjectRef = Keys.thisProjectRef.value + val scalaJars = Keys.scalaInstance.value.allJars.map(_.toURI.toString) val compileData = ScalaBuildTarget( scalaOrganization = scalaOrganization.value, scalaVersion = scalaVersion.value, scalaBinaryVersion = scalaBinaryVersion.value, platform = ScalaPlatform.JVM, - jars = Vector("scala-library") + jars = scalaJars.toVector ) val configuration = Keys.configuration.value val displayName = configuration.name match { case "compile" => thisProject.id - case configName => s"${thisProject.id} $configName" + case configName => s"${thisProject.id}-$configName" } val baseDirectory = Keys.baseDirectory.value.toURI - val internalDependencies = configuration.name match { + val allDependencies = configuration.name match { case "test" => thisProject.dependencies :+ ResolvedClasspathDependency(thisProjectRef, Some("test->compile")) case _ => thisProject.dependencies } - val dependencies = Initialize.join { - for { - dependencyRef <- internalDependencies - dependencyId <- dependencyTargetKeys(dependencyRef, configuration) - } yield dependencyId - } - Def.setting { + val capabilities = BuildTargetCapabilities(canCompile = true, canTest = false, canRun = false) + val tags = BuildTargetTag.fromConfig(configuration.name) + Def.task { + val allDepIds = allDependencies + .flatMap(dependencyTargetKeys(_, configuration)) + .join + .value + BuildTarget( buildTargetIdentifier, Some(displayName), Some(baseDirectory), - tags = Vector.empty, + tags, + capabilities, languageIds = Vector("scala"), - dependencies = dependencies.value.toVector, + dependencies = allDepIds.toVector, dataKind = Some("scala"), data = Some(Converter.toJsonUnsafe(compileData)), ) diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTarget.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTarget.scala index 6732350c6..d1d7e3cbe 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTarget.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTarget.scala @@ -32,6 +32,7 @@ final class BuildTarget private ( val displayName: Option[String], val baseDirectory: Option[java.net.URI], val tags: Vector[String], + val capabilities: sbt.internal.bsp.BuildTargetCapabilities, val languageIds: Vector[String], val dependencies: Vector[sbt.internal.bsp.BuildTargetIdentifier], val dataKind: Option[String], @@ -40,17 +41,17 @@ final class BuildTarget private ( override def equals(o: Any): Boolean = o match { - case x: BuildTarget => (this.id == x.id) && (this.displayName == x.displayName) && (this.baseDirectory == x.baseDirectory) && (this.tags == x.tags) && (this.languageIds == x.languageIds) && (this.dependencies == x.dependencies) && (this.dataKind == x.dataKind) && (this.data == x.data) + case x: BuildTarget => (this.id == x.id) && (this.displayName == x.displayName) && (this.baseDirectory == x.baseDirectory) && (this.tags == x.tags) && (this.capabilities == x.capabilities) && (this.languageIds == x.languageIds) && (this.dependencies == x.dependencies) && (this.dataKind == x.dataKind) && (this.data == x.data) case _ => false } override def hashCode: Int = { - 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.BuildTarget".##) + id.##) + displayName.##) + baseDirectory.##) + tags.##) + languageIds.##) + dependencies.##) + dataKind.##) + data.##) + 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.BuildTarget".##) + id.##) + displayName.##) + baseDirectory.##) + tags.##) + capabilities.##) + languageIds.##) + dependencies.##) + dataKind.##) + data.##) } override def toString: String = { - "BuildTarget(" + id + ", " + displayName + ", " + baseDirectory + ", " + tags + ", " + languageIds + ", " + dependencies + ", " + dataKind + ", " + data + ")" + "BuildTarget(" + id + ", " + displayName + ", " + baseDirectory + ", " + tags + ", " + capabilities + ", " + languageIds + ", " + dependencies + ", " + dataKind + ", " + data + ")" } - private[this] def copy(id: sbt.internal.bsp.BuildTargetIdentifier = id, displayName: Option[String] = displayName, baseDirectory: Option[java.net.URI] = baseDirectory, tags: Vector[String] = tags, languageIds: Vector[String] = languageIds, dependencies: Vector[sbt.internal.bsp.BuildTargetIdentifier] = dependencies, dataKind: Option[String] = dataKind, data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue] = data): BuildTarget = { - new BuildTarget(id, displayName, baseDirectory, tags, languageIds, dependencies, dataKind, data) + private[this] def copy(id: sbt.internal.bsp.BuildTargetIdentifier = id, displayName: Option[String] = displayName, baseDirectory: Option[java.net.URI] = baseDirectory, tags: Vector[String] = tags, capabilities: sbt.internal.bsp.BuildTargetCapabilities = capabilities, languageIds: Vector[String] = languageIds, dependencies: Vector[sbt.internal.bsp.BuildTargetIdentifier] = dependencies, dataKind: Option[String] = dataKind, data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue] = data): BuildTarget = { + new BuildTarget(id, displayName, baseDirectory, tags, capabilities, languageIds, dependencies, dataKind, data) } def withId(id: sbt.internal.bsp.BuildTargetIdentifier): BuildTarget = { copy(id = id) @@ -70,6 +71,9 @@ final class BuildTarget private ( def withTags(tags: Vector[String]): BuildTarget = { copy(tags = tags) } + def withCapabilities(capabilities: sbt.internal.bsp.BuildTargetCapabilities): BuildTarget = { + copy(capabilities = capabilities) + } def withLanguageIds(languageIds: Vector[String]): BuildTarget = { copy(languageIds = languageIds) } @@ -91,6 +95,6 @@ final class BuildTarget private ( } object BuildTarget { - def apply(id: sbt.internal.bsp.BuildTargetIdentifier, displayName: Option[String], baseDirectory: Option[java.net.URI], tags: Vector[String], languageIds: Vector[String], dependencies: Vector[sbt.internal.bsp.BuildTargetIdentifier], dataKind: Option[String], data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]): BuildTarget = new BuildTarget(id, displayName, baseDirectory, tags, languageIds, dependencies, dataKind, data) - def apply(id: sbt.internal.bsp.BuildTargetIdentifier, displayName: String, baseDirectory: java.net.URI, tags: Vector[String], languageIds: Vector[String], dependencies: Vector[sbt.internal.bsp.BuildTargetIdentifier], dataKind: String, data: sjsonnew.shaded.scalajson.ast.unsafe.JValue): BuildTarget = new BuildTarget(id, Option(displayName), Option(baseDirectory), tags, languageIds, dependencies, Option(dataKind), Option(data)) + def apply(id: sbt.internal.bsp.BuildTargetIdentifier, displayName: Option[String], baseDirectory: Option[java.net.URI], tags: Vector[String], capabilities: sbt.internal.bsp.BuildTargetCapabilities, languageIds: Vector[String], dependencies: Vector[sbt.internal.bsp.BuildTargetIdentifier], dataKind: Option[String], data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]): BuildTarget = new BuildTarget(id, displayName, baseDirectory, tags, capabilities, languageIds, dependencies, dataKind, data) + def apply(id: sbt.internal.bsp.BuildTargetIdentifier, displayName: String, baseDirectory: java.net.URI, tags: Vector[String], capabilities: sbt.internal.bsp.BuildTargetCapabilities, languageIds: Vector[String], dependencies: Vector[sbt.internal.bsp.BuildTargetIdentifier], dataKind: String, data: sjsonnew.shaded.scalajson.ast.unsafe.JValue): BuildTarget = new BuildTarget(id, Option(displayName), Option(baseDirectory), tags, capabilities, languageIds, dependencies, Option(dataKind), Option(data)) } diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTargetCapabilities.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTargetCapabilities.scala new file mode 100644 index 000000000..e5ad9ed90 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTargetCapabilities.scala @@ -0,0 +1,45 @@ +/** + * This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** + * @param canCompile This target can be compiled by the BSP server. + * @param canTest This target can be tested by the BSP server. + * @param canRun This target can be run by the BSP server. + */ +final class BuildTargetCapabilities private ( + val canCompile: Boolean, + val canTest: Boolean, + val canRun: Boolean) extends Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: BuildTargetCapabilities => (this.canCompile == x.canCompile) && (this.canTest == x.canTest) && (this.canRun == x.canRun) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.BuildTargetCapabilities".##) + canCompile.##) + canTest.##) + canRun.##) + } + override def toString: String = { + "BuildTargetCapabilities(" + canCompile + ", " + canTest + ", " + canRun + ")" + } + private[this] def copy(canCompile: Boolean = canCompile, canTest: Boolean = canTest, canRun: Boolean = canRun): BuildTargetCapabilities = { + new BuildTargetCapabilities(canCompile, canTest, canRun) + } + def withCanCompile(canCompile: Boolean): BuildTargetCapabilities = { + copy(canCompile = canCompile) + } + def withCanTest(canTest: Boolean): BuildTargetCapabilities = { + copy(canTest = canTest) + } + def withCanRun(canRun: Boolean): BuildTargetCapabilities = { + copy(canRun = canRun) + } +} +object BuildTargetCapabilities { + + def apply(canCompile: Boolean, canTest: Boolean, canRun: Boolean): BuildTargetCapabilities = new BuildTargetCapabilities(canCompile, canTest, canRun) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetCapabilitiesFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetCapabilitiesFormats.scala new file mode 100644 index 000000000..61ecb1ba7 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetCapabilitiesFormats.scala @@ -0,0 +1,31 @@ +/** + * This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp.codec +import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +trait BuildTargetCapabilitiesFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val BuildTargetCapabilitiesFormat: JsonFormat[sbt.internal.bsp.BuildTargetCapabilities] = new JsonFormat[sbt.internal.bsp.BuildTargetCapabilities] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.BuildTargetCapabilities = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val canCompile = unbuilder.readField[Boolean]("canCompile") + val canTest = unbuilder.readField[Boolean]("canTest") + val canRun = unbuilder.readField[Boolean]("canRun") + unbuilder.endObject() + sbt.internal.bsp.BuildTargetCapabilities(canCompile, canTest, canRun) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.BuildTargetCapabilities, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("canCompile", obj.canCompile) + builder.addField("canTest", obj.canTest) + builder.addField("canRun", obj.canRun) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetFormats.scala index 58a6342a3..8515f37ee 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait BuildTargetFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol => +trait BuildTargetFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.bsp.codec.BuildTargetCapabilitiesFormats with sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol => implicit lazy val BuildTargetFormat: JsonFormat[sbt.internal.bsp.BuildTarget] = new JsonFormat[sbt.internal.bsp.BuildTarget] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.BuildTarget = { __jsOpt match { @@ -15,12 +15,13 @@ implicit lazy val BuildTargetFormat: JsonFormat[sbt.internal.bsp.BuildTarget] = val displayName = unbuilder.readField[Option[String]]("displayName") val baseDirectory = unbuilder.readField[Option[java.net.URI]]("baseDirectory") val tags = unbuilder.readField[Vector[String]]("tags") + val capabilities = unbuilder.readField[sbt.internal.bsp.BuildTargetCapabilities]("capabilities") val languageIds = unbuilder.readField[Vector[String]]("languageIds") val dependencies = unbuilder.readField[Vector[sbt.internal.bsp.BuildTargetIdentifier]]("dependencies") val dataKind = unbuilder.readField[Option[String]]("dataKind") val data = unbuilder.readField[Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]]("data") unbuilder.endObject() - sbt.internal.bsp.BuildTarget(id, displayName, baseDirectory, tags, languageIds, dependencies, dataKind, data) + sbt.internal.bsp.BuildTarget(id, displayName, baseDirectory, tags, capabilities, languageIds, dependencies, dataKind, data) case None => deserializationError("Expected JsObject but found None") } @@ -31,6 +32,7 @@ implicit lazy val BuildTargetFormat: JsonFormat[sbt.internal.bsp.BuildTarget] = builder.addField("displayName", obj.displayName) builder.addField("baseDirectory", obj.baseDirectory) builder.addField("tags", obj.tags) + builder.addField("capabilities", obj.capabilities) builder.addField("languageIds", obj.languageIds) builder.addField("dependencies", obj.dependencies) builder.addField("dataKind", obj.dataKind) diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JsonProtocol.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JsonProtocol.scala index a25912842..ccef86a85 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JsonProtocol.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JsonProtocol.scala @@ -6,6 +6,7 @@ package sbt.internal.bsp.codec trait JsonProtocol extends sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.BuildTargetIdentifierFormats + with sbt.internal.bsp.codec.BuildTargetCapabilitiesFormats with sbt.internal.util.codec.JValueFormats with sbt.internal.bsp.codec.BuildTargetFormats with sbt.internal.bsp.codec.TaskIdFormats diff --git a/protocol/src/main/contraband/bsp.contra b/protocol/src/main/contraband/bsp.contra index 4ca6eeac9..a61b1581a 100644 --- a/protocol/src/main/contraband/bsp.contra +++ b/protocol/src/main/contraband/bsp.contra @@ -29,7 +29,7 @@ type BuildTarget { tags: [String] # The capabilities of this build target. - # capabilities: BuildTargetCapabilities + capabilities: sbt.internal.bsp.BuildTargetCapabilities! ## The set of languages that this target contains. ## The ID string for each language is defined in the LSP. @@ -52,6 +52,17 @@ type BuildTargetIdentifier { uri: java.net.URI! } +type BuildTargetCapabilities { + ## This target can be compiled by the BSP server. + canCompile: Boolean! + + ## This target can be tested by the BSP server. + canTest: Boolean! + + ## This target can be run by the BSP server. + canRun: Boolean! +} + type TaskId { ## A unique identifier id: String! diff --git a/protocol/src/main/scala/sbt/internal/bsp/BuildTargetTag.scala b/protocol/src/main/scala/sbt/internal/bsp/BuildTargetTag.scala new file mode 100644 index 000000000..d4785fa01 --- /dev/null +++ b/protocol/src/main/scala/sbt/internal/bsp/BuildTargetTag.scala @@ -0,0 +1,17 @@ +package sbt.internal.bsp + +// https://build-server-protocol.github.io/docs/specification.html#build-target +object BuildTargetTag { + val test: String = "test" + val application: String = "application" + val library: String = "library" + val integrationTest: String = "integration-test" + val benchmark: String = "benchmark" + val noIDE: String = "no-ide" + + def fromConfig(config: String): Vector[String] = config match { + case "test" => Vector(test) + case "compile" => Vector(library) + case _ => Vector.empty + } +}