diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index b271fbc1e9..4883a5f5f2 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -402,7 +402,7 @@ object Keys { val bspTargetIdentifier = settingKey[BuildTargetIdentifier]("Build target identifier of a project and configuration.").withRank(DSetting) val bspWorkspace = settingKey[Map[BuildTargetIdentifier, Scope]]("Mapping of BSP build targets to sbt scopes").withRank(DSetting) private[sbt] val bspFullWorkspace = settingKey[BspFullWorkspace]("Mapping of BSP build targets to sbt scopes and meta-targets for the SBT build itself").withRank(DSetting) - val bspInternalDependencyConfigurations = settingKey[Seq[(ProjectRef, Set[ConfigKey])]]("The project configurations that this configuration depends on, possibly transitivly").withRank(DSetting) + val bspInternalDependencyConfigurations = settingKey[Seq[(ProjectRef, Set[ConfigKey])]]("The project configurations that this configuration depends on, possibly transitively").withRank(DSetting) val bspWorkspaceBuildTargets = taskKey[Seq[BuildTarget]]("List all the BSP build targets").withRank(DTask) val bspBuildTarget = taskKey[BuildTarget]("Description of the BSP build targets").withRank(DTask) val bspBuildTargetSources = inputKey[Unit]("").withRank(DTask) @@ -415,6 +415,7 @@ object Keys { val bspBuildTargetCompileItem = taskKey[Int]("").withRank(DTask) val bspBuildTargetTest = inputKey[Unit]("Corresponds to buildTarget/test request").withRank(DTask) val bspBuildTargetRun = inputKey[Unit]("Corresponds to buildTarget/run request").withRank(DTask) + val bspBuildTargetCleanCache = inputKey[Unit]("Corresponds to buildTarget/cleanCache request").withRank(DTask) val bspBuildTargetScalacOptions = inputKey[Unit]("").withRank(DTask) val bspBuildTargetScalacOptionsItem = taskKey[ScalacOptionsItem]("").withRank(DTask) val bspScalaTestClasses = inputKey[Unit]("Corresponds to buildTarget/scalaTestClasses request").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 e5417b92f7..8d9b3c4b56 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -186,6 +186,23 @@ object BuildServerProtocol { bspBuildTargetCompile / aggregate := false, bspBuildTargetTest := bspTestTask.evaluated, bspBuildTargetTest / aggregate := false, + bspBuildTargetCleanCache := Def.inputTaskDyn { + val s: State = state.value + val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri))) + val workspace = bspFullWorkspace.value.filter(targets) + workspace.warnIfBuildsNonEmpty(Method.Compile, s.log) +// val filter = ScopeFilter.in(workspace.scopes.values.toList) + Def.task { +// val statusCodes = Keys.bspBuildTargetCleanCache.key.all(filter).value +// val statusCode = allOrThrow(statusCodes) match { +// case Seq() => false +// case other => true +// } + s.respondEvent(CleanCacheResult(None, true)) + } + }.evaluated, + bspBuildTargetCleanCache / aggregate := false, + bspBuildTargetTest / aggregate := false, bspBuildTargetScalacOptions := Def.inputTaskDyn { val s = state.value @@ -310,6 +327,7 @@ object BuildServerProtocol { final val Compile = "buildTarget/compile" final val Test = "buildTarget/test" final val Run = "buildTarget/run" + final val CleanCache = "buildTarget/cleanCache" final val ScalacOptions = "buildTarget/scalacOptions" final val ScalaTestClasses = "buildTarget/scalaTestClasses" final val ScalaMainClasses = "buildTarget/scalaMainClasses" @@ -397,6 +415,12 @@ object BuildServerProtocol { Some(r.id) ) + case r if r.method == Method.CleanCache => + val param = Converter.fromJson[CleanCacheParams](json(r)).get + val targets = param.targets.map(_.uri).mkString(" ") + val command = Keys.bspBuildTargetCleanCache.key + val _ = callback.appendExec(s"$command $targets", Some(r.id)) + case r if r.method == Method.ScalacOptions => val param = Converter.fromJson[ScalacOptionsParams](json(r)).get val targets = param.targets.map(_.uri).mkString(" ") diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/CleanCacheParams.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/CleanCacheParams.scala new file mode 100644 index 0000000000..afd5b5ccda --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/CleanCacheParams.scala @@ -0,0 +1,36 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** + * Clean Cache Request + * @param targets A sequence of build targets to clean + */ +final class CleanCacheParams private ( + val targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: CleanCacheParams => (this.targets == x.targets) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (17 + "sbt.internal.bsp.CleanCacheParams".##) + targets.##) + } + override def toString: String = { + "CleanCacheParams(" + targets + ")" + } + private[this] def copy(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier] = targets): CleanCacheParams = { + new CleanCacheParams(targets) + } + def withTargets(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): CleanCacheParams = { + copy(targets = targets) + } +} +object CleanCacheParams { + + def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): CleanCacheParams = new CleanCacheParams(targets) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/CleanCacheResult.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/CleanCacheResult.scala new file mode 100644 index 0000000000..4a427ef293 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/CleanCacheResult.scala @@ -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 +/** + * Clean Cache Response + * @param message Optional message to display to the user + * @param cleaned Indicates whether the clean cache request was performed or not + */ +final class CleanCacheResult private ( + val message: Option[String], + val cleaned: Boolean) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: CleanCacheResult => (this.message == x.message) && (this.cleaned == x.cleaned) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (17 + "sbt.internal.bsp.CleanCacheResult".##) + message.##) + cleaned.##) + } + override def toString: String = { + "CleanCacheResult(" + message + ", " + cleaned + ")" + } + private[this] def copy(message: Option[String] = message, cleaned: Boolean = cleaned): CleanCacheResult = { + new CleanCacheResult(message, cleaned) + } + def withMessage(message: Option[String]): CleanCacheResult = { + copy(message = message) + } + def withMessage(message: String): CleanCacheResult = { + copy(message = Option(message)) + } + def withCleaned(cleaned: Boolean): CleanCacheResult = { + copy(cleaned = cleaned) + } +} +object CleanCacheResult { + + def apply(message: Option[String], cleaned: Boolean): CleanCacheResult = new CleanCacheResult(message, cleaned) + def apply(message: String, cleaned: Boolean): CleanCacheResult = new CleanCacheResult(Option(message), cleaned) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/CleanCacheParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/CleanCacheParamsFormats.scala new file mode 100644 index 0000000000..6716acccde --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/CleanCacheParamsFormats.scala @@ -0,0 +1,27 @@ +/** + * 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 CleanCacheParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val CleanCacheParamsFormat: JsonFormat[sbt.internal.bsp.CleanCacheParams] = new JsonFormat[sbt.internal.bsp.CleanCacheParams] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.CleanCacheParams = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val targets = unbuilder.readField[Vector[sbt.internal.bsp.BuildTargetIdentifier]]("targets") + unbuilder.endObject() + sbt.internal.bsp.CleanCacheParams(targets) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.CleanCacheParams, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("targets", obj.targets) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/CleanCacheResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/CleanCacheResultFormats.scala new file mode 100644 index 0000000000..01e532bce8 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/CleanCacheResultFormats.scala @@ -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 CleanCacheResultFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val CleanCacheResultFormat: JsonFormat[sbt.internal.bsp.CleanCacheResult] = new JsonFormat[sbt.internal.bsp.CleanCacheResult] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.CleanCacheResult = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val message = unbuilder.readField[Option[String]]("message") + val cleaned = unbuilder.readField[Boolean]("cleaned") + unbuilder.endObject() + sbt.internal.bsp.CleanCacheResult(message, cleaned) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.CleanCacheResult, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("message", obj.message) + builder.addField("cleaned", obj.cleaned) + builder.endObject() + } +} +} 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 6f606599ff..3b74469219 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 @@ -36,6 +36,8 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.TaskFinishParamsFormats with sbt.internal.bsp.codec.CompileParamsFormats with sbt.internal.bsp.codec.BspCompileResultFormats + with sbt.internal.bsp.codec.CleanCacheParamsFormats + with sbt.internal.bsp.codec.CleanCacheResultFormats with sbt.internal.bsp.codec.CompileTaskFormats with sbt.internal.bsp.codec.CompileReportFormats with sbt.internal.bsp.codec.TestParamsFormats diff --git a/protocol/src/main/contraband/bsp.contra b/protocol/src/main/contraband/bsp.contra index 02b0626bbf..5f423babce 100644 --- a/protocol/src/main/contraband/bsp.contra +++ b/protocol/src/main/contraband/bsp.contra @@ -368,6 +368,21 @@ type BspCompileResult { # data: any } +## Clean Cache Request +type CleanCacheParams { + ## A sequence of build targets to clean + targets: [sbt.internal.bsp.BuildTargetIdentifier] +} + +## Clean Cache Response +type CleanCacheResult { + ## Optional message to display to the user + message: String + + ## Indicates whether the clean cache request was performed or not + cleaned: Boolean! +} + ## Compile Notifications type CompileTask { diff --git a/server-test/src/test/scala/testpkg/BuildServerTest.scala b/server-test/src/test/scala/testpkg/BuildServerTest.scala index c7ec754e70..9bf0082292 100644 --- a/server-test/src/test/scala/testpkg/BuildServerTest.scala +++ b/server-test/src/test/scala/testpkg/BuildServerTest.scala @@ -7,7 +7,7 @@ package testpkg -import sbt.internal.bsp.{ BspCompileResult, SourcesResult, StatusCode, WorkspaceBuildTargetsResult } +import sbt.internal.bsp._ import sbt.internal.langserver.ErrorCodes import sbt.IO @@ -108,6 +108,18 @@ object BuildServerTest extends AbstractServerTest { }) } + test("buildTarget/cleanCache") { _ => + val buildTarget = buildTargetUri("util", "Compile") + svr.sendJsonRpc( + s"""{ "jsonrpc": "2.0", "id": "32", "method": "buildTarget/cleanCache", "params": { + | "targets": [{ "uri": "$buildTarget" }] + |} }""".stripMargin + ) + assert(processing("buildTarget/cleanCache")) + val res = svr.waitFor[CleanCacheResult](10.seconds) + assert(res.cleaned) + } + test("workspace/reload") { _ => svr.sendJsonRpc( """{ "jsonrpc": "2.0", "id": "48", "method": "workspace/reload"}"""