Merge pull request #5838 from adpi2/bsp-reload

Add BSP workspace/reload
This commit is contained in:
eugene yokota 2020-09-14 08:08:14 -04:00 committed by GitHub
commit 75da49556c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 79 additions and 15 deletions

View File

@ -23,9 +23,8 @@ import sbt.internal.CommandStrings.BootCommand
import sbt.internal._
import sbt.internal.client.BspClient
import sbt.internal.inc.ScalaInstance
import sbt.internal.io.Retry
import sbt.internal.nio.{ CheckBuildSources, FileTreeRepository }
import sbt.internal.server.NetworkChannel
import sbt.internal.server.{ BuildServerProtocol, NetworkChannel }
import sbt.internal.util.Types.{ const, idFun }
import sbt.internal.util._
import sbt.internal.util.complete.{ Parser, SizeParser }
@ -317,7 +316,10 @@ object BuiltinCommands {
NetworkChannel.disconnect,
waitCmd,
promptChannel,
) ++ allBasicCommands ++ ContinuousCommands.value
) ++
allBasicCommands ++
ContinuousCommands.value ++
BuildServerProtocol.commands
def DefaultBootCommands: Seq[String] =
WriteSbtVersion :: LoadProject :: NotifyUsersAboutShell :: s"$IfLast $Shell" :: Nil

View File

@ -17,11 +17,13 @@ import sbt.Def._
import sbt.Keys._
import sbt.ScopeFilter.Make._
import sbt.SlashSyntax0._
import sbt.StandardMain.exchange
import sbt.internal.bsp._
import sbt.internal.langserver.ErrorCodes
import sbt.internal.protocol.JsonRpcRequestMessage
import sbt.librarymanagement.Configuration
import sbt.util.Logger
import sjsonnew.shaded.scalajson.ast.unsafe.JNull
import sjsonnew.shaded.scalajson.ast.unsafe.JValue
import sjsonnew.support.scalajson.unsafe.Converter
@ -29,9 +31,44 @@ import scala.util.control.NonFatal
object BuildServerProtocol {
import sbt.internal.bsp.codec.JsonProtocol._
private val capabilities = BuildServerCapabilities(
CompileProvider(BuildServerConnection.languages),
dependencySourcesProvider = true
dependencySourcesProvider = true,
canReload = true
)
private val bspReload = "bspReload"
private val bspReloadFailed = "bspReloadFailed"
private val bspReloadSucceed = "bspReloadSucceed"
lazy val commands: Seq[Command] = Seq(
Command.single(bspReload) { (state, reqId) =>
import sbt.BasicCommandStrings._
import sbt.internal.CommandStrings._
val result = List(
StashOnFailure,
s"$OnFailure $bspReloadFailed $reqId",
LoadProjectImpl,
s"$bspReloadSucceed $reqId",
PopOnFailure,
FailureWall
) ::: state
result
},
Command.single(bspReloadFailed) { (state, reqId) =>
exchange.respondError(
ErrorCodes.InternalError,
"reload failed",
Some(reqId),
state.source
)
state
},
Command.single(bspReloadSucceed) { (state, reqId) =>
exchange.respondEvent(JNull, Some(reqId), state.source)
state
}
)
lazy val globalSettings: Seq[Def.Setting[_]] = Seq(
@ -59,7 +96,6 @@ object BuildServerProtocol {
val filter = ScopeFilter.in(targets.map(workspace))
// run the worker task concurrently
Def.task {
import sbt.internal.bsp.codec.JsonProtocol._
val items = bspBuildTargetSourcesItem.all(filter).value
val result = SourcesResult(items.toVector)
s.respondEvent(result)
@ -86,7 +122,6 @@ object BuildServerProtocol {
val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri)))
val filter = ScopeFilter.in(targets.map(workspace))
Def.task {
import sbt.internal.bsp.codec.JsonProtocol._
val statusCode = Keys.bspBuildTargetCompileItem.all(filter).value.max
s.respondEvent(BspCompileResult(None, statusCode))
}
@ -155,6 +190,9 @@ object BuildServerProtocol {
case r: JsonRpcRequestMessage if r.method == "workspace/buildTargets" =>
val _ = callback.appendExec(Keys.bspWorkspaceBuildTargets.key.toString, Some(r.id))
case r: JsonRpcRequestMessage if r.method == "workspace/reload" =>
val _ = callback.appendExec(s"$bspReload ${r.id}", None)
case r: JsonRpcRequestMessage if r.method == "build/shutdown" =>
()

View File

@ -8,25 +8,27 @@ package sbt.internal.bsp
* @param compileProvider The languages the server supports compilation via method buildTarget/compile.
* @param dependencySourcesProvider The server provides sources for library dependencies
via method buildTarget/dependencySources
* @param canReload Reloading the workspace state through workspace/reload is supported
*/
final class BuildServerCapabilities private (
val compileProvider: Option[sbt.internal.bsp.CompileProvider],
val dependencySourcesProvider: Option[Boolean]) extends Serializable {
val dependencySourcesProvider: Option[Boolean],
val canReload: Option[Boolean]) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: BuildServerCapabilities => (this.compileProvider == x.compileProvider) && (this.dependencySourcesProvider == x.dependencySourcesProvider)
case x: BuildServerCapabilities => (this.compileProvider == x.compileProvider) && (this.dependencySourcesProvider == x.dependencySourcesProvider) && (this.canReload == x.canReload)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.bsp.BuildServerCapabilities".##) + compileProvider.##) + dependencySourcesProvider.##)
37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.BuildServerCapabilities".##) + compileProvider.##) + dependencySourcesProvider.##) + canReload.##)
}
override def toString: String = {
"BuildServerCapabilities(" + compileProvider + ", " + dependencySourcesProvider + ")"
"BuildServerCapabilities(" + compileProvider + ", " + dependencySourcesProvider + ", " + canReload + ")"
}
private[this] def copy(compileProvider: Option[sbt.internal.bsp.CompileProvider] = compileProvider, dependencySourcesProvider: Option[Boolean] = dependencySourcesProvider): BuildServerCapabilities = {
new BuildServerCapabilities(compileProvider, dependencySourcesProvider)
private[this] def copy(compileProvider: Option[sbt.internal.bsp.CompileProvider] = compileProvider, dependencySourcesProvider: Option[Boolean] = dependencySourcesProvider, canReload: Option[Boolean] = canReload): BuildServerCapabilities = {
new BuildServerCapabilities(compileProvider, dependencySourcesProvider, canReload)
}
def withCompileProvider(compileProvider: Option[sbt.internal.bsp.CompileProvider]): BuildServerCapabilities = {
copy(compileProvider = compileProvider)
@ -40,9 +42,15 @@ final class BuildServerCapabilities private (
def withDependencySourcesProvider(dependencySourcesProvider: Boolean): BuildServerCapabilities = {
copy(dependencySourcesProvider = Option(dependencySourcesProvider))
}
def withCanReload(canReload: Option[Boolean]): BuildServerCapabilities = {
copy(canReload = canReload)
}
def withCanReload(canReload: Boolean): BuildServerCapabilities = {
copy(canReload = Option(canReload))
}
}
object BuildServerCapabilities {
def apply(compileProvider: Option[sbt.internal.bsp.CompileProvider], dependencySourcesProvider: Option[Boolean]): BuildServerCapabilities = new BuildServerCapabilities(compileProvider, dependencySourcesProvider)
def apply(compileProvider: sbt.internal.bsp.CompileProvider, dependencySourcesProvider: Boolean): BuildServerCapabilities = new BuildServerCapabilities(Option(compileProvider), Option(dependencySourcesProvider))
def apply(compileProvider: Option[sbt.internal.bsp.CompileProvider], dependencySourcesProvider: Option[Boolean], canReload: Option[Boolean]): BuildServerCapabilities = new BuildServerCapabilities(compileProvider, dependencySourcesProvider, canReload)
def apply(compileProvider: sbt.internal.bsp.CompileProvider, dependencySourcesProvider: Boolean, canReload: Boolean): BuildServerCapabilities = new BuildServerCapabilities(Option(compileProvider), Option(dependencySourcesProvider), Option(canReload))
}

View File

@ -13,8 +13,9 @@ implicit lazy val BuildServerCapabilitiesFormat: JsonFormat[sbt.internal.bsp.Bui
unbuilder.beginObject(__js)
val compileProvider = unbuilder.readField[Option[sbt.internal.bsp.CompileProvider]]("compileProvider")
val dependencySourcesProvider = unbuilder.readField[Option[Boolean]]("dependencySourcesProvider")
val canReload = unbuilder.readField[Option[Boolean]]("canReload")
unbuilder.endObject()
sbt.internal.bsp.BuildServerCapabilities(compileProvider, dependencySourcesProvider)
sbt.internal.bsp.BuildServerCapabilities(compileProvider, dependencySourcesProvider, canReload)
case None =>
deserializationError("Expected JsObject but found None")
}
@ -23,6 +24,7 @@ implicit lazy val BuildServerCapabilitiesFormat: JsonFormat[sbt.internal.bsp.Bui
builder.beginObject()
builder.addField("compileProvider", obj.compileProvider)
builder.addField("dependencySourcesProvider", obj.dependencySourcesProvider)
builder.addField("canReload", obj.canReload)
builder.endObject()
}
}

View File

@ -186,6 +186,9 @@ type BuildServerCapabilities {
# via method buildTarget/resources
# resourcesProvider: Boolean
## Reloading the workspace state through workspace/reload is supported
canReload: Boolean
# The server sends notifications to the client on build
# target change events via buildTarget/didChange
# buildTargetChangedProvider: Boolean

View File

@ -74,6 +74,17 @@ object BuildServerTest extends AbstractServerTest {
})
}
test("workspace/reload") { _ =>
svr.sendJsonRpc(
"""{ "jsonrpc": "2.0", "id": "15", "method": "workspace/reload"}"""
)
assert(svr.waitForString(10.seconds) { s =>
println(s)
(s contains """"id":"15"""") &&
(s contains """"result":null""")
})
}
def initializeRequest(): Unit = {
svr.sendJsonRpc(
"""{ "jsonrpc": "2.0", "id": "10", "method": "build/initialize",