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

View File

@ -17,11 +17,13 @@ import sbt.Def._
import sbt.Keys._ import sbt.Keys._
import sbt.ScopeFilter.Make._ import sbt.ScopeFilter.Make._
import sbt.SlashSyntax0._ import sbt.SlashSyntax0._
import sbt.StandardMain.exchange
import sbt.internal.bsp._ import sbt.internal.bsp._
import sbt.internal.langserver.ErrorCodes import sbt.internal.langserver.ErrorCodes
import sbt.internal.protocol.JsonRpcRequestMessage import sbt.internal.protocol.JsonRpcRequestMessage
import sbt.librarymanagement.Configuration import sbt.librarymanagement.Configuration
import sbt.util.Logger import sbt.util.Logger
import sjsonnew.shaded.scalajson.ast.unsafe.JNull
import sjsonnew.shaded.scalajson.ast.unsafe.JValue import sjsonnew.shaded.scalajson.ast.unsafe.JValue
import sjsonnew.support.scalajson.unsafe.Converter import sjsonnew.support.scalajson.unsafe.Converter
@ -29,9 +31,44 @@ import scala.util.control.NonFatal
object BuildServerProtocol { object BuildServerProtocol {
import sbt.internal.bsp.codec.JsonProtocol._ import sbt.internal.bsp.codec.JsonProtocol._
private val capabilities = BuildServerCapabilities( private val capabilities = BuildServerCapabilities(
CompileProvider(BuildServerConnection.languages), 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( lazy val globalSettings: Seq[Def.Setting[_]] = Seq(
@ -59,7 +96,6 @@ object BuildServerProtocol {
val filter = ScopeFilter.in(targets.map(workspace)) val filter = ScopeFilter.in(targets.map(workspace))
// run the worker task concurrently // run the worker task concurrently
Def.task { Def.task {
import sbt.internal.bsp.codec.JsonProtocol._
val items = bspBuildTargetSourcesItem.all(filter).value val items = bspBuildTargetSourcesItem.all(filter).value
val result = SourcesResult(items.toVector) val result = SourcesResult(items.toVector)
s.respondEvent(result) s.respondEvent(result)
@ -86,7 +122,6 @@ object BuildServerProtocol {
val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri))) val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri)))
val filter = ScopeFilter.in(targets.map(workspace)) val filter = ScopeFilter.in(targets.map(workspace))
Def.task { Def.task {
import sbt.internal.bsp.codec.JsonProtocol._
val statusCode = Keys.bspBuildTargetCompileItem.all(filter).value.max val statusCode = Keys.bspBuildTargetCompileItem.all(filter).value.max
s.respondEvent(BspCompileResult(None, statusCode)) s.respondEvent(BspCompileResult(None, statusCode))
} }
@ -155,6 +190,9 @@ object BuildServerProtocol {
case r: JsonRpcRequestMessage if r.method == "workspace/buildTargets" => case r: JsonRpcRequestMessage if r.method == "workspace/buildTargets" =>
val _ = callback.appendExec(Keys.bspWorkspaceBuildTargets.key.toString, Some(r.id)) 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" => 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 compileProvider The languages the server supports compilation via method buildTarget/compile.
* @param dependencySourcesProvider The server provides sources for library dependencies * @param dependencySourcesProvider The server provides sources for library dependencies
via method buildTarget/dependencySources via method buildTarget/dependencySources
* @param canReload Reloading the workspace state through workspace/reload is supported
*/ */
final class BuildServerCapabilities private ( final class BuildServerCapabilities private (
val compileProvider: Option[sbt.internal.bsp.CompileProvider], 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 { 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 case _ => false
} }
override def hashCode: Int = { 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 = { 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 = { private[this] def copy(compileProvider: Option[sbt.internal.bsp.CompileProvider] = compileProvider, dependencySourcesProvider: Option[Boolean] = dependencySourcesProvider, canReload: Option[Boolean] = canReload): BuildServerCapabilities = {
new BuildServerCapabilities(compileProvider, dependencySourcesProvider) new BuildServerCapabilities(compileProvider, dependencySourcesProvider, canReload)
} }
def withCompileProvider(compileProvider: Option[sbt.internal.bsp.CompileProvider]): BuildServerCapabilities = { def withCompileProvider(compileProvider: Option[sbt.internal.bsp.CompileProvider]): BuildServerCapabilities = {
copy(compileProvider = compileProvider) copy(compileProvider = compileProvider)
@ -40,9 +42,15 @@ final class BuildServerCapabilities private (
def withDependencySourcesProvider(dependencySourcesProvider: Boolean): BuildServerCapabilities = { def withDependencySourcesProvider(dependencySourcesProvider: Boolean): BuildServerCapabilities = {
copy(dependencySourcesProvider = Option(dependencySourcesProvider)) copy(dependencySourcesProvider = Option(dependencySourcesProvider))
} }
def withCanReload(canReload: Option[Boolean]): BuildServerCapabilities = {
copy(canReload = canReload)
}
def withCanReload(canReload: Boolean): BuildServerCapabilities = {
copy(canReload = Option(canReload))
}
} }
object BuildServerCapabilities { object BuildServerCapabilities {
def apply(compileProvider: Option[sbt.internal.bsp.CompileProvider], dependencySourcesProvider: Option[Boolean]): BuildServerCapabilities = new BuildServerCapabilities(compileProvider, 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): BuildServerCapabilities = new BuildServerCapabilities(Option(compileProvider), Option(dependencySourcesProvider)) 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) unbuilder.beginObject(__js)
val compileProvider = unbuilder.readField[Option[sbt.internal.bsp.CompileProvider]]("compileProvider") val compileProvider = unbuilder.readField[Option[sbt.internal.bsp.CompileProvider]]("compileProvider")
val dependencySourcesProvider = unbuilder.readField[Option[Boolean]]("dependencySourcesProvider") val dependencySourcesProvider = unbuilder.readField[Option[Boolean]]("dependencySourcesProvider")
val canReload = unbuilder.readField[Option[Boolean]]("canReload")
unbuilder.endObject() unbuilder.endObject()
sbt.internal.bsp.BuildServerCapabilities(compileProvider, dependencySourcesProvider) sbt.internal.bsp.BuildServerCapabilities(compileProvider, dependencySourcesProvider, canReload)
case None => case None =>
deserializationError("Expected JsObject but found None") deserializationError("Expected JsObject but found None")
} }
@ -23,6 +24,7 @@ implicit lazy val BuildServerCapabilitiesFormat: JsonFormat[sbt.internal.bsp.Bui
builder.beginObject() builder.beginObject()
builder.addField("compileProvider", obj.compileProvider) builder.addField("compileProvider", obj.compileProvider)
builder.addField("dependencySourcesProvider", obj.dependencySourcesProvider) builder.addField("dependencySourcesProvider", obj.dependencySourcesProvider)
builder.addField("canReload", obj.canReload)
builder.endObject() builder.endObject()
} }
} }

View File

@ -186,6 +186,9 @@ type BuildServerCapabilities {
# via method buildTarget/resources # via method buildTarget/resources
# resourcesProvider: Boolean # resourcesProvider: Boolean
## Reloading the workspace state through workspace/reload is supported
canReload: Boolean
# The server sends notifications to the client on build # The server sends notifications to the client on build
# target change events via buildTarget/didChange # target change events via buildTarget/didChange
# buildTargetChangedProvider: Boolean # 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 = { def initializeRequest(): Unit = {
svr.sendJsonRpc( svr.sendJsonRpc(
"""{ "jsonrpc": "2.0", "id": "10", "method": "build/initialize", """{ "jsonrpc": "2.0", "id": "10", "method": "build/initialize",