From cb93d20492bdbfe155e7a634a393b4f1ad1061b7 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sun, 16 Feb 2020 18:01:45 -0500 Subject: [PATCH] build server protocol Initial draft for bsp support. This shows two communication pattern around BSP. First, if the request can be handled with the build knowledge is readily available in `NetworkChannel` we can reply immediately. `BuildServerImpl#onBspBuildTargets` is an example for that. Second, if the request requires `State`, then we can forward the parameter into a custom command, and reply back from a command. `BuildServerProtocol.bspBuildTargetSources` is an example of that since it needs to invoke tasks to generate sources. --- .../sbt/internal/server/ServerHandler.scala | 3 + main/src/main/scala/sbt/Main.scala | 2 +- .../internal/server/BuildServerProtocol.scala | 149 ++++++++++++++++++ .../server/LanguageServerProtocol.scala | 38 +++++ .../sbt/internal/server/NetworkChannel.scala | 17 +- .../bsp/BuildClientCapabilities.scala | 36 +++++ .../sbt/internal/bsp/BuildTarget.scala | 87 ++++++++++ .../internal/bsp/BuildTargetIdentifier.scala | 36 +++++ .../internal/bsp/InitializeBuildParams.scala | 56 +++++++ .../internal/bsp/InitializeBuildResult.scala | 50 ++++++ .../sbt/internal/bsp/SourceItem.scala | 49 ++++++ .../sbt/internal/bsp/SourcesItem.scala | 37 +++++ .../sbt/internal/bsp/SourcesParams.scala | 33 ++++ .../sbt/internal/bsp/SourcesResult.scala | 33 ++++ .../bsp/WorkspaceBuildTargetsResult.scala | 37 +++++ .../BuildClientCapabilitiesFormats.scala | 27 ++++ .../bsp/codec/BuildTargetFormats.scala | 39 +++++ .../codec/BuildTargetIdentiferFormats.scala | 27 ++++ .../codec/BuildTargetIdentifierFormats.scala | 27 ++++ .../codec/InitializeBuildParamsFormats.scala | 35 ++++ .../codec/InitializeBuildResultFormats.scala | 33 ++++ .../sbt/internal/bsp/codec/JsonProtocol.scala | 18 +++ .../bsp/codec/SourceItemFormats.scala | 31 ++++ .../bsp/codec/SourcesItemFormats.scala | 29 ++++ .../bsp/codec/SourcesParamsFormats.scala | 27 ++++ .../bsp/codec/SourcesResultFormats.scala | 27 ++++ .../WorkspaceBuildTargetsResultFormats.scala | 27 ++++ protocol/src/main/contraband/bsp.contra | 136 ++++++++++++++++ .../sbt/internal/bsp/SourceItemKind.scala | 19 +++ .../src/server-test/buildserver/build.sbt | 11 ++ .../test/scala/testpkg/BuildServerTest.scala | 61 +++++++ .../src/test/scala/testpkg/TestServer.scala | 12 +- 32 files changed, 1245 insertions(+), 4 deletions(-) create mode 100644 main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/BuildClientCapabilities.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTarget.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTargetIdentifier.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/InitializeBuildParams.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/InitializeBuildResult.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/SourceItem.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/SourcesItem.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/SourcesParams.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/SourcesResult.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/WorkspaceBuildTargetsResult.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildClientCapabilitiesFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetIdentiferFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetIdentifierFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildParamsFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildResultFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JsonProtocol.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourceItemFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesItemFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesParamsFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesResultFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/WorkspaceBuildTargetsResultFormats.scala create mode 100644 protocol/src/main/contraband/bsp.contra create mode 100644 protocol/src/main/scala/sbt/internal/bsp/SourceItemKind.scala create mode 100644 server-test/src/server-test/buildserver/build.sbt create mode 100644 server-test/src/test/scala/testpkg/BuildServerTest.scala diff --git a/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala b/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala index 2edf6c357..addcbb041 100644 --- a/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala +++ b/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala @@ -14,6 +14,7 @@ import sbt.internal.protocol._ import sbt.util.Logger import sbt.protocol.{ SettingQuery => Q, CompletionParams => CP } import sbt.internal.langserver.{ CancelRequestParams => CRP } +import sbt.internal.bsp._ /** * ServerHandler allows plugins to extend sbt server. @@ -73,4 +74,6 @@ trait ServerCallback { private[sbt] def onSettingQuery(execId: Option[String], req: Q): Unit private[sbt] def onCompletionRequest(execId: Option[String], cp: CP): Unit private[sbt] def onCancellationRequest(execId: Option[String], crp: CRP): Unit + private[sbt] def onBspInitialize(execId: Option[String], param: InitializeBuildParams): Unit + private[sbt] def onBspBuildTargets(execId: Option[String]): Unit } diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 23a6e5b36..8aa5b32fa 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -242,7 +242,7 @@ object BuiltinCommands { act, continuous, clearCaches, - ) ++ allBasicCommands + ) ++ allBasicCommands ++ server.BuildServerProtocol.commands def DefaultBootCommands: Seq[String] = WriteSbtVersion :: LoadProject :: NotifyUsersAboutShell :: s"$IfLast $Shell" :: Nil diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala new file mode 100644 index 000000000..c6030fff3 --- /dev/null +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -0,0 +1,149 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt +package internal +package server + +import java.net.URI +import sbt.internal.bsp._ +import sbt.librarymanagement.{ Configuration, Configurations } +import Configurations.{ Compile, Test } +import sbt.SlashSyntax0._ +import sbt.BuildSyntax._ +import scala.collection.mutable + +object BuildServerProtocol { + private[sbt] val idMap: mutable.Map[BuildTargetIdentifier, (ProjectRef, Configuration)] = + mutable.Map.empty + val BspBuildTargetSource = "bspBuildTargetSources" + + def commands: List[Command] = List(bspBuildTargetSources) + + /** + * Command that expects list of URIs. + * https://github.com/build-server-protocol/build-server-protocol/blob/master/docs/specification.md#build-target-sources-request + */ + def bspBuildTargetSources: Command = Command.args(BspBuildTargetSource, "") { + (s0: State, args: Seq[String]) => + import sbt.internal.bsp.codec.JsonProtocol._ + var s: State = s0 + val items = args map { arg => + val id = BuildTargetIdentifier(new URI(arg)) + val pair = idMap(id) + println(pair.toString) + val dirs = s0.setting(pair._1 / pair._2 / Keys.unmanagedSourceDirectories) + val (next, managed) = s.unsafeRunTask(pair._1 / pair._2 / Keys.managedSources) + s = next + val items = (dirs.toVector map { dir => + SourceItem(dir.toURI, SourceItemKind.Directory, false) + }) ++ + (managed.toVector map { x => + SourceItem(x.toURI, SourceItemKind.File, true) + }) + SourcesItem(id, items) + } + val result = SourcesResult(items.toVector) + s0.respondEvent(result) + s + } + + // def json(s: String): JValue = Parser.parseUnsafe(s) + + def toId(ref: ProjectReference, config: Configuration): BuildTargetIdentifier = + ref match { + case ProjectRef(build, project) => + BuildTargetIdentifier(new URI(s"$build#$project/${config.id}")) + case _ => sys.error(s"unexpected $ref") + } + + def idForConfig( + ref: ClasspathDep[ProjectRef], + from: Configuration + ): Seq[BuildTargetIdentifier] = { + val configStr = ref.configuration.getOrElse("compile") + val configExprs0 = configStr.split(",").toList + val configExprs1 = configExprs0 map { expr => + if (expr.contains("->")) { + val xs = expr.split("->") + (xs(0), xs(1)) + } else ("compile", expr) + } + configExprs1 flatMap { + case (fr, "compile") if fr == from.name => Some(toId(ref.project, Compile)) + case (fr, "test") if fr == from.name => Some(toId(ref.project, Test)) + case _ => None + } + } +} + +// This is mixed into NetworkChannel +trait BuildServerImpl { self: LanguageServerProtocol with NetworkChannel => + import BuildServerProtocol._ + + protected def structure: BuildStructure + protected def getSetting[A](key: SettingKey[A]): Option[A] + protected def setting[A](key: SettingKey[A]): A + + protected def onBspInitialize(execId: Option[String], param: InitializeBuildParams): Unit = { + import sbt.internal.bsp.codec.JsonProtocol._ + val bspVersion = "2.0.0-M5" + // https://microsoft.github.io/language-server-protocol/specification#textDocumentItem + val languageIds = Vector("scala") + val sbtV = setting(Keys.sbtVersion) + val response = + InitializeBuildResult("sbt", sbtV, bspVersion, BuildClientCapabilities(languageIds)) + respondResult(response, execId) + } + + /** + * https://github.com/build-server-protocol/build-server-protocol/blob/master/docs/specification.md#workspace-build-targets-request + */ + protected def onBspBuildTargets(execId: Option[String]): Unit = { + import sbt.internal.bsp.codec.JsonProtocol._ + val targets = buildTargets + respondResult(targets, execId) + } + + def buildTargets: WorkspaceBuildTargetsResult = { + val allProjectPairs = structure.allProjectPairs + val ts = allProjectPairs flatMap { + case (p, ref) => + val baseOpt = getSetting(ref / Keys.baseDirectory).map(_.toURI) + val internalCompileDeps = p.dependencies.flatMap(idForConfig(_, Compile)).toVector + val compileId = toId(ref, Compile) + idMap(compileId) = (ref, Compile) + val t0 = BuildTarget( + compileId, + displayName = Some(p.id), + baseDirectory = baseOpt, + tags = Vector.empty, + languageIds = Vector("scala"), + dependencies = internalCompileDeps, + dataKind = None + ) + val testId = toId(ref, Test) + idMap(testId) = (ref, Test) + // encode Test extending Compile + val internalTestDeps = + (p.dependencies ++ Seq(ResolvedClasspathDependency(ref, Some("test->compile")))) + .flatMap(idForConfig(_, Test)) + .toVector + val t1 = BuildTarget( + testId, + displayName = Some(p.id + " test"), + baseDirectory = baseOpt, + tags = Vector.empty, + languageIds = Vector("scala"), + dependencies = internalTestDeps, + dataKind = None + ) + Seq(t0, t1) + } + WorkspaceBuildTargetsResult(ts.toVector) + } +} diff --git a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala index 8e68dc5a0..36c181a51 100644 --- a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala @@ -14,6 +14,7 @@ import sjsonnew.support.scalajson.unsafe.Converter import sbt.protocol.Serialization import sbt.protocol.{ CompletionParams => CP, SettingQuery => Q } import sbt.internal.langserver.{ CancelRequestParams => CRP } +import sbt.internal.bsp._ import sbt.internal.protocol._ import sbt.internal.protocol.codec._ import sbt.internal.langserver._ @@ -44,6 +45,7 @@ private[sbt] object LanguageServerProtocol { { import sbt.internal.langserver.codec.JsonProtocol._ import internalJsonProtocol._ + import sbt.internal.bsp.codec.JsonProtocol._ def json(r: JsonRpcRequestMessage) = r.params.getOrElse( throw LangServerError( @@ -91,6 +93,36 @@ private[sbt] object LanguageServerProtocol { import sbt.protocol.codec.JsonProtocol._ val param = Converter.fromJson[CP](json(r)).get onCompletionRequest(Option(r.id), param) + case r: JsonRpcRequestMessage if r.method == "build/initialize" => + import sbt.protocol.codec.JsonProtocol._ + val param = Converter.fromJson[InitializeBuildParams](json(r)).get + onBspInitialize(Option(r.id), param) + case r: JsonRpcRequestMessage if r.method == "workspace/buildTargets" => + onBspBuildTargets(Option(r.id)) + case r: JsonRpcRequestMessage if r.method == "buildTarget/sources" => + import sbt.internal.bsp.codec.JsonProtocol._ + val param = Converter.fromJson[SourcesParams](json(r)).get + appendExec( + Exec( + s"""${BuildServerProtocol.BspBuildTargetSource} ${param.targets + .map(_.uri) + .mkString(" ")}""", + Option(r.id), + Some(CommandSource(name)) + ) + ) + () + case r: JsonRpcRequestMessage if r.method == "sbt/setting" => + import sbt.protocol.codec.JsonProtocol._ + val param = Converter.fromJson[Q](json(r)).get + onSettingQuery(Option(r.id), param) + case r: JsonRpcRequestMessage if r.method == "sbt/cancelRequest" => + val param = Converter.fromJson[CRP](json(r)).get + onCancellationRequest(Option(r.id), param) + case r: JsonRpcRequestMessage if r.method == "sbt/completion" => + import sbt.protocol.codec.JsonProtocol._ + val param = Converter.fromJson[CP](json(r)).get + onCompletionRequest(Option(r.id), param) } }, { case n: JsonRpcNotificationMessage if n.method == "textDocument/didSave" => @@ -113,6 +145,8 @@ private[sbt] trait LanguageServerProtocol { self: NetworkChannel => protected def onSettingQuery(execId: Option[String], req: Q): Unit protected def onCompletionRequest(execId: Option[String], cp: CP): Unit protected def onCancellationRequest(execId: Option[String], crp: CRP): Unit + protected def onBspInitialize(execId: Option[String], param: InitializeBuildParams): Unit + protected def onBspBuildTargets(execId: Option[String]): Unit protected lazy val callbackImpl: ServerCallback = new ServerCallback { def jsonRpcRespond[A: JsonFormat](event: A, execId: Option[String]): Unit = @@ -136,6 +170,10 @@ private[sbt] trait LanguageServerProtocol { self: NetworkChannel => self.onCompletionRequest(execId, cp) private[sbt] def onCancellationRequest(execId: Option[String], crp: CancelRequestParams): Unit = self.onCancellationRequest(execId, crp) + private[sbt] def onBspInitialize(execId: Option[String], param: InitializeBuildParams): Unit = + self.onBspInitialize(execId, param) + private[sbt] def onBspBuildTargets(execId: Option[String]): Unit = + self.onBspBuildTargets(execId) } /** diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index fe44bffcf..5adae6af2 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -34,13 +34,14 @@ import scala.util.control.NonFatal final class NetworkChannel( val name: String, connection: Socket, - structure: BuildStructure, + protected val structure: BuildStructure, auth: Set[ServerAuthentication], instance: ServerInstance, handlers: Seq[ServerHandler], val log: Logger ) extends CommandChannel - with LanguageServerProtocol { + with LanguageServerProtocol + with BuildServerImpl { import NetworkChannel._ private val running = new AtomicBoolean(true) @@ -395,6 +396,18 @@ final class NetworkChannel( } } + protected def getSetting[A](key: SettingKey[A]): Option[A] = + structure.data.get(key.scope, key.key) + + protected def getTaskValue[A](key: TaskKey[A]): Option[Task[A]] = + structure.data.get(key.scope, key.key) + + protected def setting[A](key: SettingKey[A]): A = + getSetting(key).getOrElse(sys.error(s"key ${Def.displayFull(key.scopedKey)} is not defined")) + + protected def taskValue[A](key: TaskKey[A]): Task[A] = + getTaskValue(key).getOrElse(sys.error(s"key ${Def.displayFull(key.scopedKey)} is not defined")) + private def onExecCommand(cmd: ExecCommand) = { if (initialized) { append( diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildClientCapabilities.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildClientCapabilities.scala new file mode 100644 index 000000000..f1895ddef --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildClientCapabilities.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 +/** @param languageIds The languages that this client supports. +The ID strings for each language is defined in the LSP. +The server must never respond with build targets for other +languages than those that appear in this list. */ +final class BuildClientCapabilities private ( + val languageIds: Vector[String]) extends Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: BuildClientCapabilities => (this.languageIds == x.languageIds) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (17 + "sbt.internal.bsp.BuildClientCapabilities".##) + languageIds.##) + } + override def toString: String = { + "BuildClientCapabilities(" + languageIds + ")" + } + private[this] def copy(languageIds: Vector[String] = languageIds): BuildClientCapabilities = { + new BuildClientCapabilities(languageIds) + } + def withLanguageIds(languageIds: Vector[String]): BuildClientCapabilities = { + copy(languageIds = languageIds) + } +} +object BuildClientCapabilities { + + def apply(languageIds: Vector[String]): BuildClientCapabilities = new BuildClientCapabilities(languageIds) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTarget.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTarget.scala new file mode 100644 index 000000000..c728f85d0 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTarget.scala @@ -0,0 +1,87 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** + * Build target + * @param id The target’s unique identifier + * @param displayName A human readable name for this target. + May be presented in the user interface. + Should be unique if possible. + The id.uri is used if None. + * @param baseDirectory The directory where this target belongs to. Multiple build targets are allowed to map + to the same base directory, and a build target is not required to have a base directory. + A base directory does not determine the sources of a target, see buildTarget/sources. + * @param tags Free-form string tags to categorize or label this build target. + For example, can be used by the client to: + - customize how the target should be translated into the client's project model. + - group together different but related targets in the user interface. + - display icons or colors in the user interface. + Pre-defined tags are listed in `BuildTargetTag` but clients and servers + are free to define new tags for custom purposes. + * @param languageIds The set of languages that this target contains. + The ID string for each language is defined in the LSP. + * @param dataKind Kind of data to expect in the `data` field. If this field is not set, the kind of data is not specified. + */ +final class BuildTarget private ( + val id: sbt.internal.bsp.BuildTargetIdentifier, + val displayName: Option[String], + val baseDirectory: Option[java.net.URI], + val tags: Vector[String], + val languageIds: Vector[String], + val dependencies: Vector[sbt.internal.bsp.BuildTargetIdentifier], + val dataKind: Option[String]) extends Serializable { + + + + 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) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.BuildTarget".##) + id.##) + displayName.##) + baseDirectory.##) + tags.##) + languageIds.##) + dependencies.##) + dataKind.##) + } + override def toString: String = { + "BuildTarget(" + id + ", " + displayName + ", " + baseDirectory + ", " + tags + ", " + languageIds + ", " + dependencies + ", " + dataKind + ")" + } + 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): BuildTarget = { + new BuildTarget(id, displayName, baseDirectory, tags, languageIds, dependencies, dataKind) + } + def withId(id: sbt.internal.bsp.BuildTargetIdentifier): BuildTarget = { + copy(id = id) + } + def withDisplayName(displayName: Option[String]): BuildTarget = { + copy(displayName = displayName) + } + def withDisplayName(displayName: String): BuildTarget = { + copy(displayName = Option(displayName)) + } + def withBaseDirectory(baseDirectory: Option[java.net.URI]): BuildTarget = { + copy(baseDirectory = baseDirectory) + } + def withBaseDirectory(baseDirectory: java.net.URI): BuildTarget = { + copy(baseDirectory = Option(baseDirectory)) + } + def withTags(tags: Vector[String]): BuildTarget = { + copy(tags = tags) + } + def withLanguageIds(languageIds: Vector[String]): BuildTarget = { + copy(languageIds = languageIds) + } + def withDependencies(dependencies: Vector[sbt.internal.bsp.BuildTargetIdentifier]): BuildTarget = { + copy(dependencies = dependencies) + } + def withDataKind(dataKind: Option[String]): BuildTarget = { + copy(dataKind = dataKind) + } + def withDataKind(dataKind: String): BuildTarget = { + copy(dataKind = Option(dataKind)) + } +} +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]): BuildTarget = new BuildTarget(id, displayName, baseDirectory, tags, languageIds, dependencies, dataKind) + 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): BuildTarget = new BuildTarget(id, Option(displayName), Option(baseDirectory), tags, languageIds, dependencies, Option(dataKind)) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTargetIdentifier.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTargetIdentifier.scala new file mode 100644 index 000000000..9a2d2e7f9 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildTargetIdentifier.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 +/** + * Build Target Identifier + * @param uri The target's Uri + */ +final class BuildTargetIdentifier private ( + val uri: java.net.URI) extends Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: BuildTargetIdentifier => (this.uri == x.uri) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (17 + "sbt.internal.bsp.BuildTargetIdentifier".##) + uri.##) + } + override def toString: String = { + "BuildTargetIdentifier(" + uri + ")" + } + private[this] def copy(uri: java.net.URI = uri): BuildTargetIdentifier = { + new BuildTargetIdentifier(uri) + } + def withUri(uri: java.net.URI): BuildTargetIdentifier = { + copy(uri = uri) + } +} +object BuildTargetIdentifier { + + def apply(uri: java.net.URI): BuildTargetIdentifier = new BuildTargetIdentifier(uri) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/InitializeBuildParams.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/InitializeBuildParams.scala new file mode 100644 index 000000000..e81805c2a --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/InitializeBuildParams.scala @@ -0,0 +1,56 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** + * Initialize Build Request + * @param displayName Name of the client + * @param version The version of the client + * @param bspVersion The BSP version that the client speaks + * @param rootUri The rootUri of the workspace + * @param capabilities The capabilities of the client + */ +final class InitializeBuildParams private ( + val displayName: String, + val version: String, + val bspVersion: String, + val rootUri: java.net.URI, + val capabilities: sbt.internal.bsp.BuildClientCapabilities) extends Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: InitializeBuildParams => (this.displayName == x.displayName) && (this.version == x.version) && (this.bspVersion == x.bspVersion) && (this.rootUri == x.rootUri) && (this.capabilities == x.capabilities) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.InitializeBuildParams".##) + displayName.##) + version.##) + bspVersion.##) + rootUri.##) + capabilities.##) + } + override def toString: String = { + "InitializeBuildParams(" + displayName + ", " + version + ", " + bspVersion + ", " + rootUri + ", " + capabilities + ")" + } + private[this] def copy(displayName: String = displayName, version: String = version, bspVersion: String = bspVersion, rootUri: java.net.URI = rootUri, capabilities: sbt.internal.bsp.BuildClientCapabilities = capabilities): InitializeBuildParams = { + new InitializeBuildParams(displayName, version, bspVersion, rootUri, capabilities) + } + def withDisplayName(displayName: String): InitializeBuildParams = { + copy(displayName = displayName) + } + def withVersion(version: String): InitializeBuildParams = { + copy(version = version) + } + def withBspVersion(bspVersion: String): InitializeBuildParams = { + copy(bspVersion = bspVersion) + } + def withRootUri(rootUri: java.net.URI): InitializeBuildParams = { + copy(rootUri = rootUri) + } + def withCapabilities(capabilities: sbt.internal.bsp.BuildClientCapabilities): InitializeBuildParams = { + copy(capabilities = capabilities) + } +} +object InitializeBuildParams { + + def apply(displayName: String, version: String, bspVersion: String, rootUri: java.net.URI, capabilities: sbt.internal.bsp.BuildClientCapabilities): InitializeBuildParams = new InitializeBuildParams(displayName, version, bspVersion, rootUri, capabilities) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/InitializeBuildResult.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/InitializeBuildResult.scala new file mode 100644 index 000000000..63e6e1a0d --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/InitializeBuildResult.scala @@ -0,0 +1,50 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** + * @param displayName Name of the server + * @param version The version of the server + * @param bspVersion The BSP version that the server speaks + * @param capabilities The capabilities of the build server + */ +final class InitializeBuildResult private ( + val displayName: String, + val version: String, + val bspVersion: String, + val capabilities: sbt.internal.bsp.BuildClientCapabilities) extends Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: InitializeBuildResult => (this.displayName == x.displayName) && (this.version == x.version) && (this.bspVersion == x.bspVersion) && (this.capabilities == x.capabilities) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.InitializeBuildResult".##) + displayName.##) + version.##) + bspVersion.##) + capabilities.##) + } + override def toString: String = { + "InitializeBuildResult(" + displayName + ", " + version + ", " + bspVersion + ", " + capabilities + ")" + } + private[this] def copy(displayName: String = displayName, version: String = version, bspVersion: String = bspVersion, capabilities: sbt.internal.bsp.BuildClientCapabilities = capabilities): InitializeBuildResult = { + new InitializeBuildResult(displayName, version, bspVersion, capabilities) + } + def withDisplayName(displayName: String): InitializeBuildResult = { + copy(displayName = displayName) + } + def withVersion(version: String): InitializeBuildResult = { + copy(version = version) + } + def withBspVersion(bspVersion: String): InitializeBuildResult = { + copy(bspVersion = bspVersion) + } + def withCapabilities(capabilities: sbt.internal.bsp.BuildClientCapabilities): InitializeBuildResult = { + copy(capabilities = capabilities) + } +} +object InitializeBuildResult { + + def apply(displayName: String, version: String, bspVersion: String, capabilities: sbt.internal.bsp.BuildClientCapabilities): InitializeBuildResult = new InitializeBuildResult(displayName, version, bspVersion, capabilities) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/SourceItem.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/SourceItem.scala new file mode 100644 index 000000000..8a0ffdebf --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/SourceItem.scala @@ -0,0 +1,49 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** + * @param uri Either a text document or a directory. A directory entry must end with a forward + slash "/" and a directory entry implies that every nested text document within the + directory belongs to this source item. + + * @param kind Type of file of the source item, such as whether it is file or directory. + * @param generated Indicates if this source is automatically generated by the build and is not + intended to be manually edited by the user. + */ +final class SourceItem private ( + val uri: java.net.URI, + val kind: Int, + val generated: Boolean) extends Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: SourceItem => (this.uri == x.uri) && (this.kind == x.kind) && (this.generated == x.generated) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.SourceItem".##) + uri.##) + kind.##) + generated.##) + } + override def toString: String = { + "SourceItem(" + uri + ", " + kind + ", " + generated + ")" + } + private[this] def copy(uri: java.net.URI = uri, kind: Int = kind, generated: Boolean = generated): SourceItem = { + new SourceItem(uri, kind, generated) + } + def withUri(uri: java.net.URI): SourceItem = { + copy(uri = uri) + } + def withKind(kind: Int): SourceItem = { + copy(kind = kind) + } + def withGenerated(generated: Boolean): SourceItem = { + copy(generated = generated) + } +} +object SourceItem { + + def apply(uri: java.net.URI, kind: Int, generated: Boolean): SourceItem = new SourceItem(uri, kind, generated) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/SourcesItem.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/SourcesItem.scala new file mode 100644 index 000000000..c58dadb76 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/SourcesItem.scala @@ -0,0 +1,37 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** @param sources The text documents or and directories that belong to this build target. */ +final class SourcesItem private ( + val target: sbt.internal.bsp.BuildTargetIdentifier, + val sources: Vector[sbt.internal.bsp.SourceItem]) extends Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: SourcesItem => (this.target == x.target) && (this.sources == x.sources) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (37 * (17 + "sbt.internal.bsp.SourcesItem".##) + target.##) + sources.##) + } + override def toString: String = { + "SourcesItem(" + target + ", " + sources + ")" + } + private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, sources: Vector[sbt.internal.bsp.SourceItem] = sources): SourcesItem = { + new SourcesItem(target, sources) + } + def withTarget(target: sbt.internal.bsp.BuildTargetIdentifier): SourcesItem = { + copy(target = target) + } + def withSources(sources: Vector[sbt.internal.bsp.SourceItem]): SourcesItem = { + copy(sources = sources) + } +} +object SourcesItem { + + def apply(target: sbt.internal.bsp.BuildTargetIdentifier, sources: Vector[sbt.internal.bsp.SourceItem]): SourcesItem = new SourcesItem(target, sources) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/SourcesParams.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/SourcesParams.scala new file mode 100644 index 000000000..6e88b185c --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/SourcesParams.scala @@ -0,0 +1,33 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** Build Target Sources Request */ +final class SourcesParams private ( + val targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]) extends Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: SourcesParams => (this.targets == x.targets) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (17 + "sbt.internal.bsp.SourcesParams".##) + targets.##) + } + override def toString: String = { + "SourcesParams(" + targets + ")" + } + private[this] def copy(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier] = targets): SourcesParams = { + new SourcesParams(targets) + } + def withTargets(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): SourcesParams = { + copy(targets = targets) + } +} +object SourcesParams { + + def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): SourcesParams = new SourcesParams(targets) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/SourcesResult.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/SourcesResult.scala new file mode 100644 index 000000000..36c2a60d2 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/SourcesResult.scala @@ -0,0 +1,33 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** Build Target Sources response */ +final class SourcesResult private ( + val items: Vector[sbt.internal.bsp.SourcesItem]) extends Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: SourcesResult => (this.items == x.items) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (17 + "sbt.internal.bsp.SourcesResult".##) + items.##) + } + override def toString: String = { + "SourcesResult(" + items + ")" + } + private[this] def copy(items: Vector[sbt.internal.bsp.SourcesItem] = items): SourcesResult = { + new SourcesResult(items) + } + def withItems(items: Vector[sbt.internal.bsp.SourcesItem]): SourcesResult = { + copy(items = items) + } +} +object SourcesResult { + + def apply(items: Vector[sbt.internal.bsp.SourcesItem]): SourcesResult = new SourcesResult(items) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/WorkspaceBuildTargetsResult.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/WorkspaceBuildTargetsResult.scala new file mode 100644 index 000000000..6497b9aa6 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/WorkspaceBuildTargetsResult.scala @@ -0,0 +1,37 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** + * Workspace Build Targets response + * @param targets The build targets in this workspace that + contain sources with the given language ids. + */ +final class WorkspaceBuildTargetsResult private ( + val targets: Vector[sbt.internal.bsp.BuildTarget]) extends Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: WorkspaceBuildTargetsResult => (this.targets == x.targets) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (17 + "sbt.internal.bsp.WorkspaceBuildTargetsResult".##) + targets.##) + } + override def toString: String = { + "WorkspaceBuildTargetsResult(" + targets + ")" + } + private[this] def copy(targets: Vector[sbt.internal.bsp.BuildTarget] = targets): WorkspaceBuildTargetsResult = { + new WorkspaceBuildTargetsResult(targets) + } + def withTargets(targets: Vector[sbt.internal.bsp.BuildTarget]): WorkspaceBuildTargetsResult = { + copy(targets = targets) + } +} +object WorkspaceBuildTargetsResult { + + def apply(targets: Vector[sbt.internal.bsp.BuildTarget]): WorkspaceBuildTargetsResult = new WorkspaceBuildTargetsResult(targets) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildClientCapabilitiesFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildClientCapabilitiesFormats.scala new file mode 100644 index 000000000..0e4602229 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildClientCapabilitiesFormats.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 BuildClientCapabilitiesFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val BuildClientCapabilitiesFormat: JsonFormat[sbt.internal.bsp.BuildClientCapabilities] = new JsonFormat[sbt.internal.bsp.BuildClientCapabilities] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.BuildClientCapabilities = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val languageIds = unbuilder.readField[Vector[String]]("languageIds") + unbuilder.endObject() + sbt.internal.bsp.BuildClientCapabilities(languageIds) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.BuildClientCapabilities, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("languageIds", obj.languageIds) + 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 new file mode 100644 index 000000000..72306879a --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetFormats.scala @@ -0,0 +1,39 @@ +/** + * 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 BuildTargetFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats 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 { + case Some(__js) => + unbuilder.beginObject(__js) + val id = unbuilder.readField[sbt.internal.bsp.BuildTargetIdentifier]("id") + val displayName = unbuilder.readField[Option[String]]("displayName") + val baseDirectory = unbuilder.readField[Option[java.net.URI]]("baseDirectory") + val tags = unbuilder.readField[Vector[String]]("tags") + val languageIds = unbuilder.readField[Vector[String]]("languageIds") + val dependencies = unbuilder.readField[Vector[sbt.internal.bsp.BuildTargetIdentifier]]("dependencies") + val dataKind = unbuilder.readField[Option[String]]("dataKind") + unbuilder.endObject() + sbt.internal.bsp.BuildTarget(id, displayName, baseDirectory, tags, languageIds, dependencies, dataKind) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.BuildTarget, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("id", obj.id) + builder.addField("displayName", obj.displayName) + builder.addField("baseDirectory", obj.baseDirectory) + builder.addField("tags", obj.tags) + builder.addField("languageIds", obj.languageIds) + builder.addField("dependencies", obj.dependencies) + builder.addField("dataKind", obj.dataKind) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetIdentiferFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetIdentiferFormats.scala new file mode 100644 index 000000000..9e4b98301 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetIdentiferFormats.scala @@ -0,0 +1,27 @@ +/** + * 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 BuildTargetIdentiferFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val BuildTargetIdentiferFormat: JsonFormat[sbt.internal.bsp.BuildTargetIdentifer] = new JsonFormat[sbt.internal.bsp.BuildTargetIdentifer] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.BuildTargetIdentifer = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val uri = unbuilder.readField[java.net.URI]("uri") + unbuilder.endObject() + sbt.internal.bsp.BuildTargetIdentifer(uri) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.BuildTargetIdentifer, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("uri", obj.uri) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetIdentifierFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetIdentifierFormats.scala new file mode 100644 index 000000000..7e5c447bb --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetIdentifierFormats.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 BuildTargetIdentifierFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val BuildTargetIdentifierFormat: JsonFormat[sbt.internal.bsp.BuildTargetIdentifier] = new JsonFormat[sbt.internal.bsp.BuildTargetIdentifier] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.BuildTargetIdentifier = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val uri = unbuilder.readField[java.net.URI]("uri") + unbuilder.endObject() + sbt.internal.bsp.BuildTargetIdentifier(uri) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.BuildTargetIdentifier, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("uri", obj.uri) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildParamsFormats.scala new file mode 100644 index 000000000..60e7bb731 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildParamsFormats.scala @@ -0,0 +1,35 @@ +/** + * 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 InitializeBuildParamsFormats { self: sbt.internal.bsp.codec.BuildClientCapabilitiesFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val InitializeBuildParamsFormat: JsonFormat[sbt.internal.bsp.InitializeBuildParams] = new JsonFormat[sbt.internal.bsp.InitializeBuildParams] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.InitializeBuildParams = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val displayName = unbuilder.readField[String]("displayName") + val version = unbuilder.readField[String]("version") + val bspVersion = unbuilder.readField[String]("bspVersion") + val rootUri = unbuilder.readField[java.net.URI]("rootUri") + val capabilities = unbuilder.readField[sbt.internal.bsp.BuildClientCapabilities]("capabilities") + unbuilder.endObject() + sbt.internal.bsp.InitializeBuildParams(displayName, version, bspVersion, rootUri, capabilities) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.InitializeBuildParams, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("displayName", obj.displayName) + builder.addField("version", obj.version) + builder.addField("bspVersion", obj.bspVersion) + builder.addField("rootUri", obj.rootUri) + builder.addField("capabilities", obj.capabilities) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildResultFormats.scala new file mode 100644 index 000000000..d02958ba6 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildResultFormats.scala @@ -0,0 +1,33 @@ +/** + * 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 InitializeBuildResultFormats { self: sbt.internal.bsp.codec.BuildClientCapabilitiesFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val InitializeBuildResultFormat: JsonFormat[sbt.internal.bsp.InitializeBuildResult] = new JsonFormat[sbt.internal.bsp.InitializeBuildResult] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.InitializeBuildResult = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val displayName = unbuilder.readField[String]("displayName") + val version = unbuilder.readField[String]("version") + val bspVersion = unbuilder.readField[String]("bspVersion") + val capabilities = unbuilder.readField[sbt.internal.bsp.BuildClientCapabilities]("capabilities") + unbuilder.endObject() + sbt.internal.bsp.InitializeBuildResult(displayName, version, bspVersion, capabilities) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.InitializeBuildResult, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("displayName", obj.displayName) + builder.addField("version", obj.version) + builder.addField("bspVersion", obj.bspVersion) + builder.addField("capabilities", obj.capabilities) + 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 new file mode 100644 index 000000000..96247518f --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JsonProtocol.scala @@ -0,0 +1,18 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp.codec +trait JsonProtocol extends sjsonnew.BasicJsonProtocol + with sbt.internal.bsp.codec.BuildTargetIdentifierFormats + with sbt.internal.bsp.codec.BuildTargetFormats + with sbt.internal.bsp.codec.BuildClientCapabilitiesFormats + with sbt.internal.bsp.codec.InitializeBuildParamsFormats + with sbt.internal.bsp.codec.InitializeBuildResultFormats + with sbt.internal.bsp.codec.WorkspaceBuildTargetsResultFormats + with sbt.internal.bsp.codec.SourcesParamsFormats + with sbt.internal.bsp.codec.SourceItemFormats + with sbt.internal.bsp.codec.SourcesItemFormats + with sbt.internal.bsp.codec.SourcesResultFormats +object JsonProtocol extends JsonProtocol \ No newline at end of file diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourceItemFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourceItemFormats.scala new file mode 100644 index 000000000..63e843b7e --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourceItemFormats.scala @@ -0,0 +1,31 @@ +/** + * 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 SourceItemFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val SourceItemFormat: JsonFormat[sbt.internal.bsp.SourceItem] = new JsonFormat[sbt.internal.bsp.SourceItem] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.SourceItem = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val uri = unbuilder.readField[java.net.URI]("uri") + val kind = unbuilder.readField[Int]("kind") + val generated = unbuilder.readField[Boolean]("generated") + unbuilder.endObject() + sbt.internal.bsp.SourceItem(uri, kind, generated) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.SourceItem, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("uri", obj.uri) + builder.addField("kind", obj.kind) + builder.addField("generated", obj.generated) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesItemFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesItemFormats.scala new file mode 100644 index 000000000..6862a8575 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesItemFormats.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 SourcesItemFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.bsp.codec.SourceItemFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val SourcesItemFormat: JsonFormat[sbt.internal.bsp.SourcesItem] = new JsonFormat[sbt.internal.bsp.SourcesItem] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.SourcesItem = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val target = unbuilder.readField[sbt.internal.bsp.BuildTargetIdentifier]("target") + val sources = unbuilder.readField[Vector[sbt.internal.bsp.SourceItem]]("sources") + unbuilder.endObject() + sbt.internal.bsp.SourcesItem(target, sources) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.SourcesItem, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("target", obj.target) + builder.addField("sources", obj.sources) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesParamsFormats.scala new file mode 100644 index 000000000..99322474d --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesParamsFormats.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 SourcesParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val SourcesParamsFormat: JsonFormat[sbt.internal.bsp.SourcesParams] = new JsonFormat[sbt.internal.bsp.SourcesParams] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.SourcesParams = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val targets = unbuilder.readField[Vector[sbt.internal.bsp.BuildTargetIdentifier]]("targets") + unbuilder.endObject() + sbt.internal.bsp.SourcesParams(targets) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.SourcesParams, 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/SourcesResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesResultFormats.scala new file mode 100644 index 000000000..d021e1e4f --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesResultFormats.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 SourcesResultFormats { self: sbt.internal.bsp.codec.SourcesItemFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val SourcesResultFormat: JsonFormat[sbt.internal.bsp.SourcesResult] = new JsonFormat[sbt.internal.bsp.SourcesResult] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.SourcesResult = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val items = unbuilder.readField[Vector[sbt.internal.bsp.SourcesItem]]("items") + unbuilder.endObject() + sbt.internal.bsp.SourcesResult(items) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.SourcesResult, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("items", obj.items) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/WorkspaceBuildTargetsResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/WorkspaceBuildTargetsResultFormats.scala new file mode 100644 index 000000000..d21532d72 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/WorkspaceBuildTargetsResultFormats.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 WorkspaceBuildTargetsResultFormats { self: sbt.internal.bsp.codec.BuildTargetFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val WorkspaceBuildTargetsResultFormat: JsonFormat[sbt.internal.bsp.WorkspaceBuildTargetsResult] = new JsonFormat[sbt.internal.bsp.WorkspaceBuildTargetsResult] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.WorkspaceBuildTargetsResult = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val targets = unbuilder.readField[Vector[sbt.internal.bsp.BuildTarget]]("targets") + unbuilder.endObject() + sbt.internal.bsp.WorkspaceBuildTargetsResult(targets) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.WorkspaceBuildTargetsResult, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("targets", obj.targets) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband/bsp.contra b/protocol/src/main/contraband/bsp.contra new file mode 100644 index 000000000..ed9642087 --- /dev/null +++ b/protocol/src/main/contraband/bsp.contra @@ -0,0 +1,136 @@ +package sbt.internal.bsp +@target(Scala) +@codecPackage("sbt.internal.bsp.codec") +@fullCodec("JsonProtocol") + +## Build target +type BuildTarget { + ## The target’s unique identifier + id: sbt.internal.bsp.BuildTargetIdentifier! + + ## A human readable name for this target. + ## May be presented in the user interface. + ## Should be unique if possible. + ## The id.uri is used if None. + displayName: String + + ## The directory where this target belongs to. Multiple build targets are allowed to map + ## to the same base directory, and a build target is not required to have a base directory. + ## A base directory does not determine the sources of a target, see buildTarget/sources. + baseDirectory: java.net.URI + + ## Free-form string tags to categorize or label this build target. + ## For example, can be used by the client to: + ## - customize how the target should be translated into the client's project model. + ## - group together different but related targets in the user interface. + ## - display icons or colors in the user interface. + ## Pre-defined tags are listed in `BuildTargetTag` but clients and servers + ## are free to define new tags for custom purposes. + tags: [String] + + # The capabilities of this build target. + # capabilities: BuildTargetCapabilities + + ## The set of languages that this target contains. + ## The ID string for each language is defined in the LSP. + languageIds: [String] + + # The direct upstream build target dependencies of this build target + dependencies: [sbt.internal.bsp.BuildTargetIdentifier] + + ## Kind of data to expect in the `data` field. If this field is not set, the kind of data is not specified. + dataKind: String + + # Language-specific metadata about this target. + # data: any +} + +## Build Target Identifier +type BuildTargetIdentifier { + ## The target's Uri + uri: java.net.URI! +} + +## Initialize Build Request +type InitializeBuildParams { + ## Name of the client + displayName: String! + + ## The version of the client + version: String! + + ## The BSP version that the client speaks + bspVersion: String! + + ## The rootUri of the workspace + rootUri: java.net.URI! + + ## The capabilities of the client + capabilities: sbt.internal.bsp.BuildClientCapabilities! + + # Additional metadata about the client + # data: any +} + +type BuildClientCapabilities { + ## The languages that this client supports. + ## The ID strings for each language is defined in the LSP. + ## The server must never respond with build targets for other + ## languages than those that appear in this list. + languageIds: [String] +} + +type InitializeBuildResult { + ## Name of the server + displayName: String! + + ## The version of the server + version: String! + + ## The BSP version that the server speaks + bspVersion: String! + + ## The capabilities of the build server + capabilities: sbt.internal.bsp.BuildClientCapabilities! + + # Additional metadata about the server + # data: any +} + +## Workspace Build Targets response +type WorkspaceBuildTargetsResult { + ## The build targets in this workspace that + ## contain sources with the given language ids. + targets: [sbt.internal.bsp.BuildTarget] +} + +## Build Target Sources Request +type SourcesParams { + targets: [sbt.internal.bsp.BuildTargetIdentifier] +} + +## Build Target Sources response +type SourcesResult { + items: [sbt.internal.bsp.SourcesItem] +} + +type SourcesItem { + target: sbt.internal.bsp.BuildTargetIdentifier! + ## The text documents or and directories that belong to this build target. + sources: [sbt.internal.bsp.SourceItem] +} + +type SourceItem { + ## Either a text document or a directory. A directory entry must end with a forward + ## slash "/" and a directory entry implies that every nested text document within the + ## directory belongs to this source item. + ## + uri: java.net.URI! + + ## Type of file of the source item, such as whether it is file or directory. + kind: Int! + + ## Indicates if this source is automatically generated by the build and is not + ## intended to be manually edited by the user. + generated: Boolean! +} diff --git a/protocol/src/main/scala/sbt/internal/bsp/SourceItemKind.scala b/protocol/src/main/scala/sbt/internal/bsp/SourceItemKind.scala new file mode 100644 index 000000000..f9dd7e6b3 --- /dev/null +++ b/protocol/src/main/scala/sbt/internal/bsp/SourceItemKind.scala @@ -0,0 +1,19 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt +package internal +package bsp + +object SourceItemKind { + + /** The source item references a normal file. */ + val File: Int = 1 + + /** The source item references a directory. */ + val Directory: Int = 2 +} diff --git a/server-test/src/server-test/buildserver/build.sbt b/server-test/src/server-test/buildserver/build.sbt new file mode 100644 index 000000000..754c9a2b8 --- /dev/null +++ b/server-test/src/server-test/buildserver/build.sbt @@ -0,0 +1,11 @@ +ThisBuild / scalaVersion := "2.13.1" + +Global / serverLog / logLevel := Level.Debug + +lazy val root = (project in file(".")) + .aggregate(foo, util) + +lazy val foo = project + .dependsOn(util) + +lazy val util = project diff --git a/server-test/src/test/scala/testpkg/BuildServerTest.scala b/server-test/src/test/scala/testpkg/BuildServerTest.scala new file mode 100644 index 000000000..be41f7800 --- /dev/null +++ b/server-test/src/test/scala/testpkg/BuildServerTest.scala @@ -0,0 +1,61 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package testpkg + +import scala.concurrent.duration._ + +// starts svr using server-test/buildserver and perform custom server tests +object BuildServerTest extends AbstractServerTest { + override val testDirectory: String = "buildserver" + + test("build/initialize") { _ => + initializeRequest() + assert(svr.waitForString(10.seconds) { s => + println(s) + (s contains """"id":"10"""") + }) + } + + test("workspace/buildTargets") { _ => + svr.sendJsonRpc( + """{ "jsonrpc": "2.0", "id": "11", "method": "workspace/buildTargets", "params": {} }""" + ) + assert(svr.waitForString(10.seconds) { s => + println(s) + (s contains """"id":"11"""") && + (s contains """"displayName":"util"""") + }) + } + + test("buildTarget/sources") { _ => + val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#util/Compile" + svr.sendJsonRpc( + s"""{ "jsonrpc": "2.0", "id": "12", "method": "buildTarget/sources", "params": { + | "targets": [{ "uri": "$x" }] + |} }""".stripMargin + ) + assert(svr.waitForString(10.seconds) { s => + println(s) + (s contains """"id":"12"""") + }) + } + + def initializeRequest(): Unit = { + svr.sendJsonRpc( + """{ "jsonrpc": "2.0", "id": "10", "method": "build/initialize", + | "params": { + | "displayName": "test client", + | "version": "1.0.0", + | "bspVersion": "2.0.0-M5", + | "rootUri": "file://root/", + | "capabilities": { "languageIds": ["scala"] } + | } + |}""".stripMargin + ) + } +} diff --git a/server-test/src/test/scala/testpkg/TestServer.scala b/server-test/src/test/scala/testpkg/TestServer.scala index e9a5faa64..dc4170560 100644 --- a/server-test/src/test/scala/testpkg/TestServer.scala +++ b/server-test/src/test/scala/testpkg/TestServer.scala @@ -26,8 +26,18 @@ trait AbstractServerTest extends TestSuite[Unit] { var svr: TestServer = _ def testDirectory: String + private val targetDir: File = { + val p0 = new File("..").getAbsoluteFile.getCanonicalFile / "target" + val p1 = new File("target").getAbsoluteFile + if (p0.exists) p0 + else p1 + } + override def setupSuite(): Unit = { - temp = IO.createTemporaryDirectory + temp = targetDir / "test-server" / testDirectory + if (temp.exists) { + IO.delete(temp) + } val classpath = sys.props.get("sbt.server.classpath") match { case Some(s: String) => s.split(java.io.File.pathSeparator).map(file) case _ => throw new IllegalStateException("No server classpath was specified.")