mirror of https://github.com/sbt/sbt.git
Merge pull request #6553 from retronym/topic/bsp-sbt-target
Support the SBT extension in BSP import
This commit is contained in:
commit
1497686e57
|
|
@ -164,7 +164,10 @@ def mimaSettingsSince(versions: Seq[String]): Seq[Def.Setting[_]] = Def settings
|
||||||
exclude[FinalClassProblem]("sbt.internal.*"),
|
exclude[FinalClassProblem]("sbt.internal.*"),
|
||||||
exclude[FinalMethodProblem]("sbt.internal.*"),
|
exclude[FinalMethodProblem]("sbt.internal.*"),
|
||||||
exclude[IncompatibleResultTypeProblem]("sbt.internal.*"),
|
exclude[IncompatibleResultTypeProblem]("sbt.internal.*"),
|
||||||
exclude[ReversedMissingMethodProblem]("sbt.internal.*")
|
exclude[ReversedMissingMethodProblem]("sbt.internal.*"),
|
||||||
|
exclude[DirectMissingMethodProblem]("sbt.PluginData.apply"),
|
||||||
|
exclude[DirectMissingMethodProblem]("sbt.PluginData.copy"),
|
||||||
|
exclude[DirectMissingMethodProblem]("sbt.PluginData.this"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -429,7 +429,7 @@ object Defaults extends BuildCommon {
|
||||||
LanguageServerProtocol.handler(fileConverter.value),
|
LanguageServerProtocol.handler(fileConverter.value),
|
||||||
BuildServerProtocol.handler(
|
BuildServerProtocol.handler(
|
||||||
loadedBuild.value,
|
loadedBuild.value,
|
||||||
bspWorkspace.value,
|
bspFullWorkspace.value,
|
||||||
sbtVersion.value,
|
sbtVersion.value,
|
||||||
semanticdbEnabled.value,
|
semanticdbEnabled.value,
|
||||||
semanticdbVersion.value
|
semanticdbVersion.value
|
||||||
|
|
|
||||||
|
|
@ -143,14 +143,18 @@ final case class PluginData(
|
||||||
definitionClasspath: Seq[Attributed[File]],
|
definitionClasspath: Seq[Attributed[File]],
|
||||||
resolvers: Option[Vector[Resolver]],
|
resolvers: Option[Vector[Resolver]],
|
||||||
report: Option[UpdateReport],
|
report: Option[UpdateReport],
|
||||||
scalacOptions: Seq[String]
|
scalacOptions: Seq[String],
|
||||||
|
unmanagedSourceDirectories: Seq[File],
|
||||||
|
unmanagedSources: Seq[File],
|
||||||
|
managedSourceDirectories: Seq[File],
|
||||||
|
managedSources: Seq[File]
|
||||||
) {
|
) {
|
||||||
val classpath: Seq[Attributed[File]] = definitionClasspath ++ dependencyClasspath
|
val classpath: Seq[Attributed[File]] = definitionClasspath ++ dependencyClasspath
|
||||||
}
|
}
|
||||||
|
|
||||||
object PluginData {
|
object PluginData {
|
||||||
private[sbt] def apply(dependencyClasspath: Def.Classpath): PluginData =
|
private[sbt] def apply(dependencyClasspath: Def.Classpath): PluginData =
|
||||||
PluginData(dependencyClasspath, Nil, None, None, Nil)
|
PluginData(dependencyClasspath, Nil, None, None, Nil, Nil, Nil, Nil, Nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
object EvaluateTask {
|
object EvaluateTask {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ package sbt
|
||||||
import java.nio.file.{ Path => NioPath }
|
import java.nio.file.{ Path => NioPath }
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
import lmcoursier.definitions.{ CacheLogger, ModuleMatchers, Reconciliation }
|
import lmcoursier.definitions.{ CacheLogger, ModuleMatchers, Reconciliation }
|
||||||
import lmcoursier.{ CoursierConfiguration, FallbackDependency }
|
import lmcoursier.{ CoursierConfiguration, FallbackDependency }
|
||||||
import org.apache.ivy.core.module.descriptor.ModuleDescriptor
|
import org.apache.ivy.core.module.descriptor.ModuleDescriptor
|
||||||
|
|
@ -26,6 +25,7 @@ import sbt.internal.inc.ScalaInstance
|
||||||
import sbt.internal.io.WatchState
|
import sbt.internal.io.WatchState
|
||||||
import sbt.internal.librarymanagement.{ CompatibilityWarningOptions, IvySbt }
|
import sbt.internal.librarymanagement.{ CompatibilityWarningOptions, IvySbt }
|
||||||
import sbt.internal.remotecache.RemoteCacheArtifact
|
import sbt.internal.remotecache.RemoteCacheArtifact
|
||||||
|
import sbt.internal.server.BuildServerProtocol.BspFullWorkspace
|
||||||
import sbt.internal.server.{ BuildServerReporter, ServerHandler }
|
import sbt.internal.server.{ BuildServerReporter, ServerHandler }
|
||||||
import sbt.internal.util.{ AttributeKey, ProgressState, SourcePosition }
|
import sbt.internal.util.{ AttributeKey, ProgressState, SourcePosition }
|
||||||
import sbt.io._
|
import sbt.io._
|
||||||
|
|
@ -398,8 +398,10 @@ object Keys {
|
||||||
|
|
||||||
val bspConfig = taskKey[Unit]("Create or update the BSP connection files").withRank(DSetting)
|
val bspConfig = taskKey[Unit]("Create or update the BSP connection files").withRank(DSetting)
|
||||||
val bspEnabled = SettingKey[Boolean](BasicKeys.bspEnabled)
|
val bspEnabled = SettingKey[Boolean](BasicKeys.bspEnabled)
|
||||||
|
val bspSbtEnabled = settingKey[Boolean]("Should BSP export meta-targets for the SBT build itself?")
|
||||||
val bspTargetIdentifier = settingKey[BuildTargetIdentifier]("Build target identifier of a project and configuration.").withRank(DSetting)
|
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)
|
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 transitivly").withRank(DSetting)
|
||||||
val bspWorkspaceBuildTargets = taskKey[Seq[BuildTarget]]("List all the BSP build targets").withRank(DTask)
|
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 bspBuildTarget = taskKey[BuildTarget]("Description of the BSP build targets").withRank(DTask)
|
||||||
|
|
|
||||||
|
|
@ -1164,12 +1164,20 @@ private[sbt] object Load {
|
||||||
val prod = (Configurations.Runtime / exportedProducts).value
|
val prod = (Configurations.Runtime / exportedProducts).value
|
||||||
val cp = (Configurations.Runtime / fullClasspath).value
|
val cp = (Configurations.Runtime / fullClasspath).value
|
||||||
val opts = (Configurations.Compile / scalacOptions).value
|
val opts = (Configurations.Compile / scalacOptions).value
|
||||||
|
val unmanagedSrcDirs = (Configurations.Compile / unmanagedSourceDirectories).value
|
||||||
|
val unmanagedSrcs = (Configurations.Compile / unmanagedSources).value
|
||||||
|
val managedSrcDirs = (Configurations.Compile / managedSourceDirectories).value
|
||||||
|
val managedSrcs = (Configurations.Compile / managedSources).value
|
||||||
PluginData(
|
PluginData(
|
||||||
removeEntries(cp, prod),
|
removeEntries(cp, prod),
|
||||||
prod,
|
prod,
|
||||||
Some(fullResolvers.value.toVector),
|
Some(fullResolvers.value.toVector),
|
||||||
Some(update.value),
|
Some(update.value),
|
||||||
opts
|
opts,
|
||||||
|
unmanagedSrcDirs,
|
||||||
|
unmanagedSrcs,
|
||||||
|
managedSrcDirs,
|
||||||
|
managedSrcs,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
scalacOptions += "-Wconf:cat=unused-nowarn:s",
|
scalacOptions += "-Wconf:cat=unused-nowarn:s",
|
||||||
|
|
@ -1225,7 +1233,7 @@ private[sbt] object Load {
|
||||||
loadPluginDefinition(
|
loadPluginDefinition(
|
||||||
dir,
|
dir,
|
||||||
config,
|
config,
|
||||||
PluginData(config.globalPluginClasspath, Nil, None, None, Nil)
|
PluginData(config.globalPluginClasspath, Nil, None, None, Nil, Nil, Nil, Nil, Nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
def buildPlugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins =
|
def buildPlugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins =
|
||||||
|
|
@ -1417,6 +1425,10 @@ final case class LoadBuildConfiguration(
|
||||||
data.internalClasspath,
|
data.internalClasspath,
|
||||||
Some(data.resolvers),
|
Some(data.resolvers),
|
||||||
Some(data.updateReport),
|
Some(data.updateReport),
|
||||||
|
Nil,
|
||||||
|
Nil,
|
||||||
|
Nil,
|
||||||
|
Nil,
|
||||||
Nil
|
Nil
|
||||||
)
|
)
|
||||||
case None => PluginData(globalPluginClasspath)
|
case None => PluginData(globalPluginClasspath)
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,13 @@ package internal
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
import sbt.BuildPaths.{ configurationSources, projectStandard }
|
||||||
import sbt.BuildSyntax._
|
import sbt.BuildSyntax._
|
||||||
import sbt.Def._
|
import sbt.Def._
|
||||||
import sbt.Keys._
|
import sbt.Keys._
|
||||||
import sbt.Project._
|
import sbt.Project._
|
||||||
import sbt.ScopeFilter.Make._
|
import sbt.ScopeFilter.Make._
|
||||||
|
import sbt.Scoped.richTaskSeq
|
||||||
import sbt.SlashSyntax0._
|
import sbt.SlashSyntax0._
|
||||||
import sbt.StandardMain.exchange
|
import sbt.StandardMain.exchange
|
||||||
import sbt.internal.bsp._
|
import sbt.internal.bsp._
|
||||||
|
|
@ -22,13 +24,17 @@ import sbt.internal.langserver.ErrorCodes
|
||||||
import sbt.internal.protocol.JsonRpcRequestMessage
|
import sbt.internal.protocol.JsonRpcRequestMessage
|
||||||
import sbt.internal.util.Attributed
|
import sbt.internal.util.Attributed
|
||||||
import sbt.internal.util.complete.{ Parser, Parsers }
|
import sbt.internal.util.complete.{ Parser, Parsers }
|
||||||
import sbt.librarymanagement.Configuration
|
import sbt.librarymanagement.CrossVersion.binaryScalaVersion
|
||||||
|
import sbt.librarymanagement.{ Configuration, ScalaArtifacts }
|
||||||
import sbt.std.TaskExtra
|
import sbt.std.TaskExtra
|
||||||
import sbt.util.Logger
|
import sbt.util.Logger
|
||||||
import sjsonnew.shaded.scalajson.ast.unsafe.{ JNull, JValue }
|
import sjsonnew.shaded.scalajson.ast.unsafe.{ JNull, JValue }
|
||||||
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter, Parser => JsonParser }
|
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter, Parser => JsonParser }
|
||||||
import xsbti.CompileFailed
|
import xsbti.CompileFailed
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import scala.collection.mutable
|
||||||
|
|
||||||
// import scala.annotation.nowarn
|
// import scala.annotation.nowarn
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import scala.util.{ Failure, Success, Try }
|
import scala.util.{ Failure, Success, Try }
|
||||||
|
|
@ -93,36 +99,66 @@ object BuildServerProtocol {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
bspEnabled := true,
|
bspEnabled := true,
|
||||||
bspWorkspace := bspWorkspaceSetting.value,
|
bspSbtEnabled := true,
|
||||||
|
bspFullWorkspace := bspFullWorkspaceSetting.value,
|
||||||
|
bspWorkspace := bspFullWorkspace.value.scopes,
|
||||||
bspWorkspaceBuildTargets := Def.taskDyn {
|
bspWorkspaceBuildTargets := Def.taskDyn {
|
||||||
val workspace = Keys.bspWorkspace.value
|
val workspace = Keys.bspFullWorkspace.value
|
||||||
val state = Keys.state.value
|
val state = Keys.state.value
|
||||||
val allTargets = ScopeFilter.in(workspace.values.toSeq)
|
val allTargets = ScopeFilter.in(workspace.scopes.values.toSeq)
|
||||||
|
val sbtTargets: List[Def.Initialize[Task[BuildTarget]]] = workspace.builds.map {
|
||||||
|
case (buildTargetIdentifier, loadedBuildUnit) =>
|
||||||
|
val buildFor = workspace.buildToScope.getOrElse(buildTargetIdentifier, Nil)
|
||||||
|
sbtBuildTarget(loadedBuildUnit, buildTargetIdentifier, buildFor)
|
||||||
|
}.toList
|
||||||
Def.task {
|
Def.task {
|
||||||
val buildTargets = Keys.bspBuildTarget.all(allTargets).value.toVector
|
val buildTargets = Keys.bspBuildTarget.all(allTargets).value.toVector
|
||||||
state.respondEvent(WorkspaceBuildTargetsResult(buildTargets))
|
val allBuildTargets = buildTargets ++ sbtTargets.join.value
|
||||||
buildTargets
|
state.respondEvent(WorkspaceBuildTargetsResult(allBuildTargets))
|
||||||
|
allBuildTargets
|
||||||
}
|
}
|
||||||
}.value,
|
}.value,
|
||||||
// https://github.com/build-server-protocol/build-server-protocol/blob/master/docs/specification.md#build-target-sources-request
|
// https://github.com/build-server-protocol/build-server-protocol/blob/master/docs/specification.md#build-target-sources-request
|
||||||
bspBuildTargetSources := Def.inputTaskDyn {
|
bspBuildTargetSources := Def.inputTaskDyn {
|
||||||
val s = state.value
|
val s = state.value
|
||||||
val workspace = bspWorkspace.value
|
|
||||||
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 workspace = bspFullWorkspace.value.filter(targets)
|
||||||
|
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||||
// run the worker task concurrently
|
// run the worker task concurrently
|
||||||
Def.task {
|
Def.task {
|
||||||
val items = bspBuildTargetSourcesItem.all(filter).value
|
val items = bspBuildTargetSourcesItem.all(filter).value
|
||||||
val result = SourcesResult(items.toVector)
|
val buildItems = workspace.builds.toVector.map {
|
||||||
|
case (id, loadedBuildUnit) =>
|
||||||
|
val base = loadedBuildUnit.localBase
|
||||||
|
val sbtFiles = configurationSources(base)
|
||||||
|
val pluginData = loadedBuildUnit.unit.plugins.pluginData
|
||||||
|
val all = Vector.newBuilder[SourceItem]
|
||||||
|
def add(fs: Seq[File], sourceItemKind: Int, generated: Boolean): Unit = {
|
||||||
|
fs.foreach(f => all += (SourceItem(f.toURI, sourceItemKind, generated = generated)))
|
||||||
|
}
|
||||||
|
all += (SourceItem(
|
||||||
|
loadedBuildUnit.unit.plugins.base.toURI,
|
||||||
|
SourceItemKind.Directory,
|
||||||
|
generated = false
|
||||||
|
))
|
||||||
|
add(pluginData.unmanagedSourceDirectories, SourceItemKind.Directory, generated = false)
|
||||||
|
add(pluginData.unmanagedSources, SourceItemKind.File, generated = false)
|
||||||
|
add(pluginData.managedSourceDirectories, SourceItemKind.Directory, generated = true)
|
||||||
|
add(pluginData.managedSources, SourceItemKind.File, generated = true)
|
||||||
|
add(sbtFiles, SourceItemKind.File, generated = false)
|
||||||
|
SourcesItem(id, all.result())
|
||||||
|
}
|
||||||
|
val result = SourcesResult((items ++ buildItems).toVector)
|
||||||
s.respondEvent(result)
|
s.respondEvent(result)
|
||||||
}
|
}
|
||||||
}.evaluated,
|
}.evaluated,
|
||||||
bspBuildTargetSources / aggregate := false,
|
bspBuildTargetSources / aggregate := false,
|
||||||
bspBuildTargetResources := Def.inputTaskDyn {
|
bspBuildTargetResources := Def.inputTaskDyn {
|
||||||
val s = state.value
|
val s = state.value
|
||||||
val workspace = bspWorkspace.value
|
|
||||||
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 workspace = bspFullWorkspace.value.filter(targets)
|
||||||
|
workspace.warnIfBuildsNonEmpty(Method.Resources, s.log)
|
||||||
|
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||||
// run the worker task concurrently
|
// run the worker task concurrently
|
||||||
Def.task {
|
Def.task {
|
||||||
val items = bspBuildTargetResourcesItem.all(filter).value
|
val items = bspBuildTargetResourcesItem.all(filter).value
|
||||||
|
|
@ -133,9 +169,9 @@ object BuildServerProtocol {
|
||||||
bspBuildTargetResources / aggregate := false,
|
bspBuildTargetResources / aggregate := false,
|
||||||
bspBuildTargetDependencySources := Def.inputTaskDyn {
|
bspBuildTargetDependencySources := Def.inputTaskDyn {
|
||||||
val s = state.value
|
val s = state.value
|
||||||
val workspace = bspWorkspace.value
|
|
||||||
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 workspace = bspFullWorkspace.value.filter(targets)
|
||||||
|
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||||
// run the worker task concurrently
|
// run the worker task concurrently
|
||||||
Def.task {
|
Def.task {
|
||||||
import sbt.internal.bsp.codec.JsonProtocol._
|
import sbt.internal.bsp.codec.JsonProtocol._
|
||||||
|
|
@ -147,9 +183,10 @@ object BuildServerProtocol {
|
||||||
bspBuildTargetDependencySources / aggregate := false,
|
bspBuildTargetDependencySources / aggregate := false,
|
||||||
bspBuildTargetCompile := Def.inputTaskDyn {
|
bspBuildTargetCompile := Def.inputTaskDyn {
|
||||||
val s: State = state.value
|
val s: State = state.value
|
||||||
val workspace = bspWorkspace.value
|
|
||||||
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 workspace = bspFullWorkspace.value.filter(targets)
|
||||||
|
workspace.warnIfBuildsNonEmpty(Method.Compile, s.log)
|
||||||
|
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||||
Def.task {
|
Def.task {
|
||||||
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))
|
||||||
|
|
@ -160,21 +197,40 @@ object BuildServerProtocol {
|
||||||
bspBuildTargetTest / aggregate := false,
|
bspBuildTargetTest / aggregate := false,
|
||||||
bspBuildTargetScalacOptions := Def.inputTaskDyn {
|
bspBuildTargetScalacOptions := Def.inputTaskDyn {
|
||||||
val s = state.value
|
val s = state.value
|
||||||
val workspace = bspWorkspace.value
|
|
||||||
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 workspace = bspFullWorkspace.value.filter(targets)
|
||||||
|
val builds = workspace.builds
|
||||||
|
|
||||||
|
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||||
Def.task {
|
Def.task {
|
||||||
val items = bspBuildTargetScalacOptionsItem.all(filter).value
|
val items = bspBuildTargetScalacOptionsItem.all(filter).value
|
||||||
val result = ScalacOptionsResult(items.toVector)
|
val appProvider = appConfiguration.value.provider()
|
||||||
|
val sbtJars = appProvider.mainClasspath()
|
||||||
|
val buildItems = builds.map {
|
||||||
|
build =>
|
||||||
|
val plugins: LoadedPlugins = build._2.unit.plugins
|
||||||
|
val scalacOptions = plugins.pluginData.scalacOptions
|
||||||
|
val pluginClassPath = plugins.classpath
|
||||||
|
val classpath = (pluginClassPath ++ sbtJars).map(_.toURI).toVector
|
||||||
|
ScalacOptionsItem(
|
||||||
|
build._1,
|
||||||
|
scalacOptions.toVector,
|
||||||
|
classpath,
|
||||||
|
new File(build._2.localBase, "project/target").toURI
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val result = ScalacOptionsResult((items ++ buildItems).toVector)
|
||||||
s.respondEvent(result)
|
s.respondEvent(result)
|
||||||
}
|
}
|
||||||
}.evaluated,
|
}.evaluated,
|
||||||
bspBuildTargetScalacOptions / aggregate := false,
|
bspBuildTargetScalacOptions / aggregate := false,
|
||||||
bspScalaTestClasses := Def.inputTaskDyn {
|
bspScalaTestClasses := Def.inputTaskDyn {
|
||||||
val s = state.value
|
val s = state.value
|
||||||
val workspace = bspWorkspace.value
|
|
||||||
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 workspace = bspFullWorkspace.value.filter(targets)
|
||||||
|
workspace.warnIfBuildsNonEmpty(Method.ScalaTestClasses, s.log)
|
||||||
|
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||||
Def.task {
|
Def.task {
|
||||||
val items = bspScalaTestClassesItem.all(filter).value
|
val items = bspScalaTestClassesItem.all(filter).value
|
||||||
val result = ScalaTestClassesResult(items.toVector, None)
|
val result = ScalaTestClassesResult(items.toVector, None)
|
||||||
|
|
@ -183,9 +239,10 @@ object BuildServerProtocol {
|
||||||
}.evaluated,
|
}.evaluated,
|
||||||
bspScalaMainClasses := Def.inputTaskDyn {
|
bspScalaMainClasses := Def.inputTaskDyn {
|
||||||
val s = state.value
|
val s = state.value
|
||||||
val workspace = bspWorkspace.value
|
|
||||||
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 workspace = bspFullWorkspace.value.filter(targets)
|
||||||
|
workspace.warnIfBuildsNonEmpty(Method.ScalaMainClasses, s.log)
|
||||||
|
val filter = ScopeFilter.in(workspace.scopes.values.toList)
|
||||||
Def.task {
|
Def.task {
|
||||||
val items = bspScalaMainClassesItem.all(filter).value
|
val items = bspScalaMainClassesItem.all(filter).value
|
||||||
val result = ScalaMainClassesResult(items.toVector, None)
|
val result = ScalaMainClassesResult(items.toVector, None)
|
||||||
|
|
@ -247,10 +304,27 @@ object BuildServerProtocol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
private object Method {
|
||||||
|
final val Initialize = "build/initialize"
|
||||||
|
final val BuildTargets = "workspace/buildTargets"
|
||||||
|
final val Reload = "workspace/reload"
|
||||||
|
final val Shutdown = "build/shutdown"
|
||||||
|
final val Sources = "buildTarget/sources"
|
||||||
|
final val Resources = "buildTarget/resources"
|
||||||
|
final val DependencySources = "buildTarget/dependencySources"
|
||||||
|
final val Compile = "buildTarget/compile"
|
||||||
|
final val Test = "buildTarget/test"
|
||||||
|
final val Run = "buildTarget/run"
|
||||||
|
final val ScalacOptions = "buildTarget/scalacOptions"
|
||||||
|
final val ScalaTestClasses = "buildTarget/scalaTestClasses"
|
||||||
|
final val ScalaMainClasses = "buildTarget/scalaMainClasses"
|
||||||
|
final val Exit = "build/exit"
|
||||||
|
}
|
||||||
|
identity(Method) // silence spurious "private object Method in object BuildServerProtocol is never used" warning!
|
||||||
|
|
||||||
def handler(
|
def handler(
|
||||||
loadedBuild: LoadedBuild,
|
loadedBuild: LoadedBuild,
|
||||||
workspace: Map[BuildTargetIdentifier, Scope],
|
workspace: BspFullWorkspace,
|
||||||
sbtVersion: String,
|
sbtVersion: String,
|
||||||
semanticdbEnabled: Boolean,
|
semanticdbEnabled: Boolean,
|
||||||
semanticdbVersion: String
|
semanticdbVersion: String
|
||||||
|
|
@ -264,7 +338,7 @@ object BuildServerProtocol {
|
||||||
ServerHandler { callback =>
|
ServerHandler { callback =>
|
||||||
ServerIntent(
|
ServerIntent(
|
||||||
onRequest = {
|
onRequest = {
|
||||||
case r if r.method == "build/initialize" =>
|
case r if r.method == Method.Initialize =>
|
||||||
val params = Converter.fromJson[InitializeBuildParams](json(r)).get
|
val params = Converter.fromJson[InitializeBuildParams](json(r)).get
|
||||||
checkMetalsCompatibility(semanticdbEnabled, semanticdbVersion, params, callback.log)
|
checkMetalsCompatibility(semanticdbEnabled, semanticdbVersion, params, callback.log)
|
||||||
|
|
||||||
|
|
@ -277,42 +351,42 @@ object BuildServerProtocol {
|
||||||
)
|
)
|
||||||
callback.jsonRpcRespond(response, Some(r.id)); ()
|
callback.jsonRpcRespond(response, Some(r.id)); ()
|
||||||
|
|
||||||
case r if r.method == "workspace/buildTargets" =>
|
case r if r.method == Method.BuildTargets =>
|
||||||
val _ = callback.appendExec(Keys.bspWorkspaceBuildTargets.key.toString, Some(r.id))
|
val _ = callback.appendExec(Keys.bspWorkspaceBuildTargets.key.toString, Some(r.id))
|
||||||
|
|
||||||
case r if r.method == "workspace/reload" =>
|
case r if r.method == Method.Reload =>
|
||||||
val _ = callback.appendExec(s"$bspReload ${r.id}", Some(r.id))
|
val _ = callback.appendExec(s"$bspReload ${r.id}", Some(r.id))
|
||||||
|
|
||||||
case r if r.method == "build/shutdown" =>
|
case r if r.method == Method.Shutdown =>
|
||||||
callback.jsonRpcRespond(JNull, Some(r.id))
|
callback.jsonRpcRespond(JNull, Some(r.id))
|
||||||
|
|
||||||
case r if r.method == "buildTarget/sources" =>
|
case r if r.method == Method.Sources =>
|
||||||
val param = Converter.fromJson[SourcesParams](json(r)).get
|
val param = Converter.fromJson[SourcesParams](json(r)).get
|
||||||
val targets = param.targets.map(_.uri).mkString(" ")
|
val targets = param.targets.map(_.uri).mkString(" ")
|
||||||
val command = Keys.bspBuildTargetSources.key
|
val command = Keys.bspBuildTargetSources.key
|
||||||
val _ = callback.appendExec(s"$command $targets", Some(r.id))
|
val _ = callback.appendExec(s"$command $targets", Some(r.id))
|
||||||
|
|
||||||
case r if r.method == "buildTarget/dependencySources" =>
|
case r if r.method == Method.DependencySources =>
|
||||||
val param = Converter.fromJson[DependencySourcesParams](json(r)).get
|
val param = Converter.fromJson[DependencySourcesParams](json(r)).get
|
||||||
val targets = param.targets.map(_.uri).mkString(" ")
|
val targets = param.targets.map(_.uri).mkString(" ")
|
||||||
val command = Keys.bspBuildTargetDependencySources.key
|
val command = Keys.bspBuildTargetDependencySources.key
|
||||||
val _ = callback.appendExec(s"$command $targets", Some(r.id))
|
val _ = callback.appendExec(s"$command $targets", Some(r.id))
|
||||||
|
|
||||||
case r if r.method == "buildTarget/compile" =>
|
case r if r.method == Method.Compile =>
|
||||||
val param = Converter.fromJson[CompileParams](json(r)).get
|
val param = Converter.fromJson[CompileParams](json(r)).get
|
||||||
val targets = param.targets.map(_.uri).mkString(" ")
|
val targets = param.targets.map(_.uri).mkString(" ")
|
||||||
val command = Keys.bspBuildTargetCompile.key
|
val command = Keys.bspBuildTargetCompile.key
|
||||||
val _ = callback.appendExec(s"$command $targets", Some(r.id))
|
val _ = callback.appendExec(s"$command $targets", Some(r.id))
|
||||||
|
|
||||||
case r: JsonRpcRequestMessage if r.method == "buildTarget/test" =>
|
case r: JsonRpcRequestMessage if r.method == Method.Test =>
|
||||||
val task = bspBuildTargetTest.key
|
val task = bspBuildTargetTest.key
|
||||||
val paramStr = CompactPrinter(json(r))
|
val paramStr = CompactPrinter(json(r))
|
||||||
val _ = callback.appendExec(s"$task $paramStr", Some(r.id))
|
val _ = callback.appendExec(s"$task $paramStr", Some(r.id))
|
||||||
|
|
||||||
case r if r.method == "buildTarget/run" =>
|
case r if r.method == Method.Run =>
|
||||||
val paramJson = json(r)
|
val paramJson = json(r)
|
||||||
val param = Converter.fromJson[RunParams](json(r)).get
|
val param = Converter.fromJson[RunParams](json(r)).get
|
||||||
val scope = workspace.getOrElse(
|
val scope = workspace.scopes.getOrElse(
|
||||||
param.target,
|
param.target,
|
||||||
throw LangServerError(
|
throw LangServerError(
|
||||||
ErrorCodes.InvalidParams,
|
ErrorCodes.InvalidParams,
|
||||||
|
|
@ -328,25 +402,25 @@ object BuildServerProtocol {
|
||||||
Some(r.id)
|
Some(r.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
case r if r.method == "buildTarget/scalacOptions" =>
|
case r if r.method == Method.ScalacOptions =>
|
||||||
val param = Converter.fromJson[ScalacOptionsParams](json(r)).get
|
val param = Converter.fromJson[ScalacOptionsParams](json(r)).get
|
||||||
val targets = param.targets.map(_.uri).mkString(" ")
|
val targets = param.targets.map(_.uri).mkString(" ")
|
||||||
val command = Keys.bspBuildTargetScalacOptions.key
|
val command = Keys.bspBuildTargetScalacOptions.key
|
||||||
val _ = callback.appendExec(s"$command $targets", Some(r.id))
|
val _ = callback.appendExec(s"$command $targets", Some(r.id))
|
||||||
|
|
||||||
case r if r.method == "buildTarget/scalaTestClasses" =>
|
case r if r.method == Method.ScalaTestClasses =>
|
||||||
val param = Converter.fromJson[ScalaTestClassesParams](json(r)).get
|
val param = Converter.fromJson[ScalaTestClassesParams](json(r)).get
|
||||||
val targets = param.targets.map(_.uri).mkString(" ")
|
val targets = param.targets.map(_.uri).mkString(" ")
|
||||||
val command = Keys.bspScalaTestClasses.key
|
val command = Keys.bspScalaTestClasses.key
|
||||||
val _ = callback.appendExec(s"$command $targets", Some(r.id))
|
val _ = callback.appendExec(s"$command $targets", Some(r.id))
|
||||||
|
|
||||||
case r if r.method == "buildTarget/scalaMainClasses" =>
|
case r if r.method == Method.ScalaMainClasses =>
|
||||||
val param = Converter.fromJson[ScalaMainClassesParams](json(r)).get
|
val param = Converter.fromJson[ScalaMainClassesParams](json(r)).get
|
||||||
val targets = param.targets.map(_.uri).mkString(" ")
|
val targets = param.targets.map(_.uri).mkString(" ")
|
||||||
val command = Keys.bspScalaMainClasses.key
|
val command = Keys.bspScalaMainClasses.key
|
||||||
val _ = callback.appendExec(s"$command $targets", Some(r.id))
|
val _ = callback.appendExec(s"$command $targets", Some(r.id))
|
||||||
|
|
||||||
case r if r.method == "buildTarget/resources" =>
|
case r if r.method == Method.Resources =>
|
||||||
val param = Converter.fromJson[ResourcesParams](json(r)).get
|
val param = Converter.fromJson[ResourcesParams](json(r)).get
|
||||||
val targets = param.targets.map(_.uri).mkString(" ")
|
val targets = param.targets.map(_.uri).mkString(" ")
|
||||||
val command = Keys.bspBuildTargetResources.key
|
val command = Keys.bspBuildTargetResources.key
|
||||||
|
|
@ -354,7 +428,7 @@ object BuildServerProtocol {
|
||||||
},
|
},
|
||||||
onResponse = PartialFunction.empty,
|
onResponse = PartialFunction.empty,
|
||||||
onNotification = {
|
onNotification = {
|
||||||
case r if r.method == "build/exit" =>
|
case r if r.method == Method.Exit =>
|
||||||
val _ = callback.appendExec(BasicCommandStrings.TerminateAction, None)
|
val _ = callback.appendExec(BasicCommandStrings.TerminateAction, None)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -403,7 +477,7 @@ object BuildServerProtocol {
|
||||||
)
|
)
|
||||||
|
|
||||||
@nowarn
|
@nowarn
|
||||||
private def bspWorkspaceSetting: Def.Initialize[Map[BuildTargetIdentifier, Scope]] =
|
private def bspFullWorkspaceSetting: Def.Initialize[BspFullWorkspace] =
|
||||||
Def.settingDyn {
|
Def.settingDyn {
|
||||||
val loadedBuild = Keys.loadedBuild.value
|
val loadedBuild = Keys.loadedBuild.value
|
||||||
|
|
||||||
|
|
@ -423,11 +497,32 @@ object BuildServerProtocol {
|
||||||
.map(_ / Keys.bspEnabled)
|
.map(_ / Keys.bspEnabled)
|
||||||
.join
|
.join
|
||||||
.value
|
.value
|
||||||
val result = for {
|
val buildsMap =
|
||||||
|
mutable.HashMap[BuildTargetIdentifier, mutable.ListBuffer[BuildTargetIdentifier]]()
|
||||||
|
|
||||||
|
val scopeMap = for {
|
||||||
(targetId, scope, bspEnabled) <- (targetIds, scopes, bspEnabled).zipped
|
(targetId, scope, bspEnabled) <- (targetIds, scopes, bspEnabled).zipped
|
||||||
if bspEnabled
|
if bspEnabled
|
||||||
} yield targetId -> scope
|
} yield {
|
||||||
result.toMap
|
scope.project.toOption match {
|
||||||
|
case Some(ProjectRef(buildUri, _)) =>
|
||||||
|
val loadedBuildUnit = loadedBuild.units(buildUri)
|
||||||
|
buildsMap.getOrElseUpdate(
|
||||||
|
toSbtTargetId(loadedBuildUnit),
|
||||||
|
new mutable.ListBuffer
|
||||||
|
) += targetId
|
||||||
|
}
|
||||||
|
targetId -> scope
|
||||||
|
}
|
||||||
|
val buildMap = if (bspSbtEnabled.value) {
|
||||||
|
for (loadedBuildUnit <- loadedBuild.units.values) yield {
|
||||||
|
val rootProjectId = loadedBuildUnit.root
|
||||||
|
toSbtTargetId(loadedBuildUnit) -> loadedBuildUnit
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
BspFullWorkspace(scopeMap.toMap, buildMap.toMap, buildsMap.mapValues(_.result()).toMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -469,6 +564,43 @@ object BuildServerProtocol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def sbtBuildTarget(
|
||||||
|
loadedUnit: LoadedBuildUnit,
|
||||||
|
buildTargetIdentifier: BuildTargetIdentifier,
|
||||||
|
buildFor: Seq[BuildTargetIdentifier]
|
||||||
|
): Def.Initialize[Task[BuildTarget]] = Def.task {
|
||||||
|
val scalaProvider = appConfiguration.value.provider().scalaProvider()
|
||||||
|
appConfiguration.value.provider().mainClasspath()
|
||||||
|
val scalaJars = scalaProvider.jars()
|
||||||
|
val compileData = ScalaBuildTarget(
|
||||||
|
scalaOrganization = ScalaArtifacts.Organization,
|
||||||
|
scalaVersion = scalaProvider.version(),
|
||||||
|
scalaBinaryVersion = binaryScalaVersion(scalaProvider.version()),
|
||||||
|
platform = ScalaPlatform.JVM,
|
||||||
|
jars = scalaJars.toVector.map(_.toURI.toString)
|
||||||
|
)
|
||||||
|
val sbtVersionValue = sbtVersion.value
|
||||||
|
val sbtData = SbtBuildTarget(
|
||||||
|
sbtVersionValue,
|
||||||
|
loadedUnit.imports.toVector,
|
||||||
|
compileData,
|
||||||
|
None,
|
||||||
|
buildFor.toVector
|
||||||
|
)
|
||||||
|
|
||||||
|
BuildTarget(
|
||||||
|
buildTargetIdentifier,
|
||||||
|
toSbtTargetIdName(loadedUnit),
|
||||||
|
projectStandard(loadedUnit.unit.localBase).toURI,
|
||||||
|
Vector(),
|
||||||
|
BuildTargetCapabilities(canCompile = false, canTest = false, canRun = false),
|
||||||
|
BuildServerConnection.languages,
|
||||||
|
Vector(),
|
||||||
|
"sbt",
|
||||||
|
data = Converter.toJsonUnsafe(sbtData),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private def scalacOptionsTask: Def.Initialize[Task[ScalacOptionsItem]] = Def.taskDyn {
|
private def scalacOptionsTask: Def.Initialize[Task[ScalacOptionsItem]] = Def.taskDyn {
|
||||||
val target = Keys.bspTargetIdentifier.value
|
val target = Keys.bspTargetIdentifier.value
|
||||||
val scalacOptions = Keys.scalacOptions.value
|
val scalacOptions = Keys.scalacOptions.value
|
||||||
|
|
@ -571,7 +703,7 @@ object BuildServerProtocol {
|
||||||
.map(_.flatMap(json => Converter.fromJson[TestParams](json)))
|
.map(_.flatMap(json => Converter.fromJson[TestParams](json)))
|
||||||
.parsed
|
.parsed
|
||||||
.get
|
.get
|
||||||
val workspace = bspWorkspace.value
|
val workspace = bspFullWorkspace.value
|
||||||
|
|
||||||
val resultTask: Def.Initialize[Task[Result[Seq[Unit]]]] = testParams.dataKind match {
|
val resultTask: Def.Initialize[Task[Result[Seq[Unit]]]] = testParams.dataKind match {
|
||||||
case Some("scala-test") =>
|
case Some("scala-test") =>
|
||||||
|
|
@ -582,7 +714,7 @@ object BuildServerProtocol {
|
||||||
case Success(value) => value.testClasses
|
case Success(value) => value.testClasses
|
||||||
}
|
}
|
||||||
val testTasks: Seq[Def.Initialize[Task[Unit]]] = items.map { item =>
|
val testTasks: Seq[Def.Initialize[Task[Unit]]] = items.map { item =>
|
||||||
val scope = workspace(item.target)
|
val scope = workspace.scopes(item.target)
|
||||||
item.classes.toList match {
|
item.classes.toList match {
|
||||||
case Nil => Def.task(())
|
case Nil => Def.task(())
|
||||||
case classes =>
|
case classes =>
|
||||||
|
|
@ -599,7 +731,7 @@ object BuildServerProtocol {
|
||||||
|
|
||||||
case None =>
|
case None =>
|
||||||
// run allTests in testParams.targets
|
// run allTests in testParams.targets
|
||||||
val filter = ScopeFilter.in(testParams.targets.map(workspace))
|
val filter = ScopeFilter.in(testParams.targets.map(workspace.scopes))
|
||||||
test.all(filter).result
|
test.all(filter).result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -644,7 +776,7 @@ object BuildServerProtocol {
|
||||||
|
|
||||||
@nowarn
|
@nowarn
|
||||||
private def internalDependencyConfigurationsSetting = Def.settingDyn {
|
private def internalDependencyConfigurationsSetting = Def.settingDyn {
|
||||||
val allScopes = bspWorkspace.value.map { case (_, scope) => scope }.toSet
|
val allScopes = bspFullWorkspace.value.scopes.map { case (_, scope) => scope }.toSet
|
||||||
val directDependencies = Keys.internalDependencyConfigurations.value
|
val directDependencies = Keys.internalDependencyConfigurations.value
|
||||||
.map {
|
.map {
|
||||||
case (project, rawConfigs) =>
|
case (project, rawConfigs) =>
|
||||||
|
|
@ -705,6 +837,20 @@ object BuildServerProtocol {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// naming convention still seems like the only reliable way to get IntelliJ to import this correctly
|
||||||
|
// https://github.com/JetBrains/intellij-scala/blob/a54c2a7c157236f35957049cbfd8c10587c9e60c/scala/scala-impl/src/org/jetbrains/sbt/language/SbtFileImpl.scala#L82-L84
|
||||||
|
private def toSbtTargetIdName(ref: LoadedBuildUnit): String = {
|
||||||
|
ref.root + "-build"
|
||||||
|
}
|
||||||
|
private def toSbtTargetId(ref: LoadedBuildUnit): BuildTargetIdentifier = {
|
||||||
|
val name = toSbtTargetIdName(ref)
|
||||||
|
val build = ref.unit.uri
|
||||||
|
val sanitized = build.toString.indexOf("#") match {
|
||||||
|
case i if i > 0 => build.toString.take(i)
|
||||||
|
case _ => build.toString
|
||||||
|
}
|
||||||
|
BuildTargetIdentifier(new URI(sanitized + "#" + name))
|
||||||
|
}
|
||||||
private def toId(ref: ProjectReference, config: Configuration): BuildTargetIdentifier =
|
private def toId(ref: ProjectReference, config: Configuration): BuildTargetIdentifier =
|
||||||
ref match {
|
ref match {
|
||||||
case ProjectRef(build, project) =>
|
case ProjectRef(build, project) =>
|
||||||
|
|
@ -733,4 +879,23 @@ object BuildServerProtocol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The regular targets for each scope and meta-targets for the SBT build. */
|
||||||
|
private[sbt] final case class BspFullWorkspace(
|
||||||
|
scopes: Map[BuildTargetIdentifier, Scope],
|
||||||
|
builds: Map[BuildTargetIdentifier, LoadedBuildUnit],
|
||||||
|
buildToScope: Map[BuildTargetIdentifier, Seq[BuildTargetIdentifier]]
|
||||||
|
) {
|
||||||
|
def filter(targets: Seq[BuildTargetIdentifier]): BspFullWorkspace = {
|
||||||
|
val set = targets.toSet
|
||||||
|
def filterMap[T](map: Map[BuildTargetIdentifier, T]) = map.filter(x => set.contains(x._1))
|
||||||
|
BspFullWorkspace(filterMap(scopes), filterMap(builds), buildToScope)
|
||||||
|
}
|
||||||
|
def warnIfBuildsNonEmpty(method: String, log: Logger): Unit = {
|
||||||
|
if (builds.nonEmpty)
|
||||||
|
log.warn(
|
||||||
|
s"$method is a no-op for build.sbt targets: ${builds.keys.mkString("[", ",", "]")}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ object FakeState {
|
||||||
Nil
|
Nil
|
||||||
)
|
)
|
||||||
|
|
||||||
val pluginData = PluginData(Nil, Nil, None, None, Nil)
|
val pluginData = PluginData(Nil, Nil, None, None, Nil, Nil, Nil, Nil, Nil)
|
||||||
val builds: DetectedModules[BuildDef] = new DetectedModules[BuildDef](Nil)
|
val builds: DetectedModules[BuildDef] = new DetectedModules[BuildDef](Nil)
|
||||||
|
|
||||||
val detectedAutoPlugins: Seq[DetectedAutoPlugin] =
|
val detectedAutoPlugins: Seq[DetectedAutoPlugin] =
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,18 @@
|
||||||
|
|
||||||
package testpkg
|
package testpkg
|
||||||
|
|
||||||
|
import sbt.internal.bsp.SourcesResult
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import sbt.internal.bsp.WorkspaceBuildTargetsResult
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
// starts svr using server-test/buildserver and perform custom server tests
|
// starts svr using server-test/buildserver and perform custom server tests
|
||||||
object BuildServerTest extends AbstractServerTest {
|
object BuildServerTest extends AbstractServerTest {
|
||||||
|
|
||||||
|
import sbt.internal.bsp.codec.JsonProtocol._
|
||||||
|
|
||||||
override val testDirectory: String = "buildserver"
|
override val testDirectory: String = "buildserver"
|
||||||
|
|
||||||
test("build/initialize") { _ =>
|
test("build/initialize") { _ =>
|
||||||
|
|
@ -26,12 +34,12 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
"""{ "jsonrpc": "2.0", "id": "16", "method": "workspace/buildTargets", "params": {} }"""
|
"""{ "jsonrpc": "2.0", "id": "16", "method": "workspace/buildTargets", "params": {} }"""
|
||||||
)
|
)
|
||||||
assert(processing("workspace/buildTargets"))
|
assert(processing("workspace/buildTargets"))
|
||||||
assert {
|
val result = svr.waitFor[WorkspaceBuildTargetsResult](10.seconds)
|
||||||
svr.waitForString(10.seconds) { s =>
|
val utilTarget = result.targets.find(_.displayName.contains("util")).get
|
||||||
(s contains """"id":"16"""") &&
|
assert(utilTarget.id.uri.toString.endsWith("#util/Compile"))
|
||||||
(s contains """"displayName":"util"""")
|
val buildServerBuildTarget =
|
||||||
}
|
result.targets.find(_.displayName.contains("buildserver-build")).get
|
||||||
}
|
assert(buildServerBuildTarget.id.uri.toString.endsWith("#buildserver-build"))
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/sources") { _ =>
|
test("buildTarget/sources") { _ =>
|
||||||
|
|
@ -42,10 +50,33 @@ object BuildServerTest extends AbstractServerTest {
|
||||||
|} }""".stripMargin
|
|} }""".stripMargin
|
||||||
)
|
)
|
||||||
assert(processing("buildTarget/sources"))
|
assert(processing("buildTarget/sources"))
|
||||||
assert(svr.waitForString(10.seconds) { s =>
|
val s = svr.waitFor[SourcesResult](10.seconds)
|
||||||
(s contains """"id":"24"""") &&
|
val sources = s.items.head.sources.map(_.uri)
|
||||||
(s contains "util/src/main/scala")
|
assert(sources.contains(new File(svr.baseDirectory, "util/src/main/scala").toURI))
|
||||||
})
|
}
|
||||||
|
test("buildTarget/sources SBT") { _ =>
|
||||||
|
val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#buildserver-build"
|
||||||
|
svr.sendJsonRpc(
|
||||||
|
s"""{ "jsonrpc": "2.0", "id": "25", "method": "buildTarget/sources", "params": {
|
||||||
|
| "targets": [{ "uri": "$x" }]
|
||||||
|
|} }""".stripMargin
|
||||||
|
)
|
||||||
|
assert(processing("buildTarget/sources"))
|
||||||
|
val s = svr.waitFor[SourcesResult](10.seconds)
|
||||||
|
val sources = s.items.head.sources.map(_.uri).sorted
|
||||||
|
val expectedSources = Vector(
|
||||||
|
"build.sbt",
|
||||||
|
"project/",
|
||||||
|
"project/A.scala",
|
||||||
|
"project/src/main/java",
|
||||||
|
"project/src/main/scala-2",
|
||||||
|
"project/src/main/scala-2.12",
|
||||||
|
"project/src/main/scala-sbt-1.0",
|
||||||
|
"project/src/main/scala/",
|
||||||
|
"project/src/main/scala/B.scala",
|
||||||
|
"project/target/scala-2.12/sbt-1.0/src_managed/main"
|
||||||
|
).map(rel => new File(svr.baseDirectory.getAbsoluteFile, rel).toURI).sorted
|
||||||
|
assert(sources == expectedSources)
|
||||||
}
|
}
|
||||||
|
|
||||||
test("buildTarget/compile") { _ =>
|
test("buildTarget/compile") { _ =>
|
||||||
|
|
|
||||||
|
|
@ -12,17 +12,18 @@ import java.net.Socket
|
||||||
import java.nio.file.{ Files, Path }
|
import java.nio.file.{ Files, Path }
|
||||||
import java.util.concurrent.{ LinkedBlockingQueue, TimeUnit }
|
import java.util.concurrent.{ LinkedBlockingQueue, TimeUnit }
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
import verify._
|
import verify._
|
||||||
import sbt.{ ForkOptions, OutputStrategy, RunFromSourceMain }
|
import sbt.{ ForkOptions, OutputStrategy, RunFromSourceMain }
|
||||||
import sbt.io.IO
|
import sbt.io.IO
|
||||||
import sbt.io.syntax._
|
import sbt.io.syntax._
|
||||||
import sbt.protocol.ClientSocket
|
import sbt.protocol.ClientSocket
|
||||||
|
import sjsonnew.JsonReader
|
||||||
|
import sjsonnew.support.scalajson.unsafe.{ Converter, Parser }
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.concurrent._
|
import scala.concurrent._
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.util.{ Success, Try }
|
import scala.util.{ Failure, Success, Try }
|
||||||
|
|
||||||
trait AbstractServerTest extends TestSuite[Unit] {
|
trait AbstractServerTest extends TestSuite[Unit] {
|
||||||
private var temp: File = _
|
private var temp: File = _
|
||||||
|
|
@ -293,6 +294,57 @@ case class TestServer(
|
||||||
}
|
}
|
||||||
impl()
|
impl()
|
||||||
}
|
}
|
||||||
|
final def waitFor[T: JsonReader](duration: FiniteDuration): T = {
|
||||||
|
val deadline = duration.fromNow
|
||||||
|
var lastEx: Throwable = null
|
||||||
|
@tailrec def impl(): T =
|
||||||
|
lines.poll(deadline.timeLeft.toMillis, TimeUnit.MILLISECONDS) match {
|
||||||
|
case null =>
|
||||||
|
if (lastEx != null) throw lastEx
|
||||||
|
else throw new TimeoutException
|
||||||
|
case s =>
|
||||||
|
Parser
|
||||||
|
.parseFromString(s)
|
||||||
|
.flatMap(
|
||||||
|
jvalue =>
|
||||||
|
Converter.fromJson[T](
|
||||||
|
jvalue.toStandard
|
||||||
|
.asInstanceOf[sjsonnew.shaded.scalajson.ast.JObject]
|
||||||
|
.value("result")
|
||||||
|
.toUnsafe
|
||||||
|
)
|
||||||
|
) match {
|
||||||
|
case Success(value) =>
|
||||||
|
value
|
||||||
|
case Failure(exception) =>
|
||||||
|
if (deadline.isOverdue) {
|
||||||
|
val ex = new TimeoutException()
|
||||||
|
ex.initCause(exception)
|
||||||
|
throw ex
|
||||||
|
} else {
|
||||||
|
lastEx = exception
|
||||||
|
impl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl()
|
||||||
|
}
|
||||||
|
final def waitForResponse(duration: FiniteDuration, id: Int): String = {
|
||||||
|
val deadline = duration.fromNow
|
||||||
|
@tailrec def impl(): String =
|
||||||
|
lines.poll(deadline.timeLeft.toMillis, TimeUnit.MILLISECONDS) match {
|
||||||
|
case null =>
|
||||||
|
throw new TimeoutException()
|
||||||
|
case s =>
|
||||||
|
val s1 = s
|
||||||
|
val correctId = s1.contains("\"id\":\"" + id + "\"")
|
||||||
|
if (!correctId && !deadline.isOverdue) impl()
|
||||||
|
else if (deadline.isOverdue)
|
||||||
|
throw new TimeoutException()
|
||||||
|
else s
|
||||||
|
}
|
||||||
|
impl()
|
||||||
|
}
|
||||||
|
|
||||||
final def neverReceive(duration: FiniteDuration)(f: String => Boolean): Boolean = {
|
final def neverReceive(duration: FiniteDuration)(f: String => Boolean): Boolean = {
|
||||||
val deadline = duration.fromNow
|
val deadline = duration.fromNow
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue