mirror of https://github.com/sbt/sbt.git
Merge pull request #5618 from adpi2/topic/metals-support
Add suggestion about semanticdb when Metals connect to sbt
This commit is contained in:
commit
778264a319
|
|
@ -208,7 +208,8 @@ object Defaults extends BuildCommon {
|
|||
fullServerHandlers := {
|
||||
Seq(
|
||||
LanguageServerProtocol.handler(fileConverter.value),
|
||||
BuildServerProtocol.handler(sbtVersion.value)
|
||||
BuildServerProtocol
|
||||
.handler(sbtVersion.value, semanticdbEnabled.value, semanticdbVersion.value)
|
||||
) ++ serverHandlers.value :+ ServerHandler.fallback
|
||||
},
|
||||
uncachedStamper := Stamps.uncachedStamps(fileConverter.value),
|
||||
|
|
|
|||
|
|
@ -20,9 +20,12 @@ import sbt.internal.bsp._
|
|||
import sbt.internal.langserver.ErrorCodes
|
||||
import sbt.internal.protocol.JsonRpcRequestMessage
|
||||
import sbt.librarymanagement.Configuration
|
||||
import sbt.util.Logger
|
||||
import sjsonnew.shaded.scalajson.ast.unsafe.JValue
|
||||
import sjsonnew.support.scalajson.unsafe.Converter
|
||||
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
object BuildServerProtocol {
|
||||
import sbt.internal.bsp.codec.JsonProtocol._
|
||||
private val capabilities = BuildServerCapabilities(
|
||||
|
|
@ -123,11 +126,17 @@ object BuildServerProtocol {
|
|||
bspInternalDependencyConfigurations := internalDependencyConfigurationsSetting.value
|
||||
)
|
||||
|
||||
def handler(sbtVersion: String): ServerHandler = ServerHandler { callback =>
|
||||
def handler(
|
||||
sbtVersion: String,
|
||||
semanticdbEnabled: Boolean,
|
||||
semanticdbVersion: String
|
||||
): ServerHandler = ServerHandler { callback =>
|
||||
ServerIntent(
|
||||
{
|
||||
case r: JsonRpcRequestMessage if r.method == "build/initialize" =>
|
||||
val _ = Converter.fromJson[InitializeBuildParams](json(r)).get
|
||||
val params = Converter.fromJson[InitializeBuildParams](json(r)).get
|
||||
checkMetalsCompatibility(semanticdbEnabled, semanticdbVersion, params, callback.log)
|
||||
|
||||
val response = InitializeBuildResult(
|
||||
"sbt",
|
||||
sbtVersion,
|
||||
|
|
@ -175,6 +184,39 @@ object BuildServerProtocol {
|
|||
)
|
||||
}
|
||||
|
||||
private def checkMetalsCompatibility(
|
||||
semanticdbEnabled: Boolean,
|
||||
semanticdbVersion: String,
|
||||
params: InitializeBuildParams,
|
||||
log: Logger
|
||||
): Unit = {
|
||||
for {
|
||||
data <- params.data
|
||||
// try parse metadata as MetalsMetadata
|
||||
metalsMetadata <- Converter.fromJson[MetalsMetadata](data).toOption
|
||||
} {
|
||||
if (!semanticdbEnabled) {
|
||||
log.warn(s"${params.displayName} requires the semanticdb compiler plugin")
|
||||
log.warn(
|
||||
s"consider setting 'Global / semanticdbEnabled := true' in your global sbt settings ($$HOME/.sbt/1.0)"
|
||||
)
|
||||
}
|
||||
|
||||
for {
|
||||
requiredVersion <- SemanticVersion.tryParse(metalsMetadata.semanticdbVersion)
|
||||
currentVersion <- SemanticVersion.tryParse(semanticdbVersion)
|
||||
if requiredVersion > currentVersion
|
||||
} {
|
||||
log.warn(
|
||||
s"${params.displayName} requires semanticdb version ${metalsMetadata.semanticdbVersion}, current version is $semanticdbVersion"
|
||||
)
|
||||
log.warn(
|
||||
s"""consider setting 'Global / semanticdbVersion := "${metalsMetadata.semanticdbVersion}"' in your global sbt settings ($$HOME/.sbt/1.0)"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def json(r: JsonRpcRequestMessage): JValue =
|
||||
r.params.getOrElse(
|
||||
throw LangServerError(
|
||||
|
|
@ -315,4 +357,22 @@ object BuildServerProtocol {
|
|||
BuildTargetIdentifier(new URI(s"$build#$project/${config.id}"))
|
||||
case _ => sys.error(s"unexpected $ref")
|
||||
}
|
||||
|
||||
private case class SemanticVersion(major: Int, minor: Int) extends Ordered[SemanticVersion] {
|
||||
override def compare(that: SemanticVersion): Int = {
|
||||
if (that.major != major) major.compare(that.major)
|
||||
else minor.compare(minor)
|
||||
}
|
||||
}
|
||||
|
||||
private object SemanticVersion {
|
||||
def tryParse(versionStr: String): Option[SemanticVersion] = {
|
||||
try {
|
||||
val parts = versionStr.split('.')
|
||||
Some(SemanticVersion(parts(0).toInt, parts(1).toInt))
|
||||
} catch {
|
||||
case NonFatal(_) => None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,28 +11,30 @@ package sbt.internal.bsp
|
|||
* @param bspVersion The BSP version that the client speaks
|
||||
* @param rootUri The rootUri of the workspace
|
||||
* @param capabilities The capabilities of the client
|
||||
* @param data Additional metadata about 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 {
|
||||
val capabilities: sbt.internal.bsp.BuildClientCapabilities,
|
||||
val data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]) 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 x: InitializeBuildParams => (this.displayName == x.displayName) && (this.version == x.version) && (this.bspVersion == x.bspVersion) && (this.rootUri == x.rootUri) && (this.capabilities == x.capabilities) && (this.data == x.data)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.InitializeBuildParams".##) + displayName.##) + version.##) + bspVersion.##) + rootUri.##) + capabilities.##)
|
||||
37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.InitializeBuildParams".##) + displayName.##) + version.##) + bspVersion.##) + rootUri.##) + capabilities.##) + data.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"InitializeBuildParams(" + displayName + ", " + version + ", " + bspVersion + ", " + rootUri + ", " + capabilities + ")"
|
||||
"InitializeBuildParams(" + displayName + ", " + version + ", " + bspVersion + ", " + rootUri + ", " + capabilities + ", " + data + ")"
|
||||
}
|
||||
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)
|
||||
private[this] def copy(displayName: String = displayName, version: String = version, bspVersion: String = bspVersion, rootUri: java.net.URI = rootUri, capabilities: sbt.internal.bsp.BuildClientCapabilities = capabilities, data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue] = data): InitializeBuildParams = {
|
||||
new InitializeBuildParams(displayName, version, bspVersion, rootUri, capabilities, data)
|
||||
}
|
||||
def withDisplayName(displayName: String): InitializeBuildParams = {
|
||||
copy(displayName = displayName)
|
||||
|
|
@ -49,8 +51,15 @@ final class InitializeBuildParams private (
|
|||
def withCapabilities(capabilities: sbt.internal.bsp.BuildClientCapabilities): InitializeBuildParams = {
|
||||
copy(capabilities = capabilities)
|
||||
}
|
||||
def withData(data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]): InitializeBuildParams = {
|
||||
copy(data = data)
|
||||
}
|
||||
def withData(data: sjsonnew.shaded.scalajson.ast.unsafe.JValue): InitializeBuildParams = {
|
||||
copy(data = Option(data))
|
||||
}
|
||||
}
|
||||
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)
|
||||
def apply(displayName: String, version: String, bspVersion: String, rootUri: java.net.URI, capabilities: sbt.internal.bsp.BuildClientCapabilities, data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]): InitializeBuildParams = new InitializeBuildParams(displayName, version, bspVersion, rootUri, capabilities, data)
|
||||
def apply(displayName: String, version: String, bspVersion: String, rootUri: java.net.URI, capabilities: sbt.internal.bsp.BuildClientCapabilities, data: sjsonnew.shaded.scalajson.ast.unsafe.JValue): InitializeBuildParams = new InitializeBuildParams(displayName, version, bspVersion, rootUri, capabilities, Option(data))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.bsp
|
||||
/**
|
||||
* Metals metadata in the initialization request
|
||||
* @param semanticdbVersion The semanticdb plugin version that should be enabled for Metals code navigation
|
||||
* @param supportedScalaVersions The list of scala versions that are supported by Metals
|
||||
*/
|
||||
final class MetalsMetadata private (
|
||||
val semanticdbVersion: String,
|
||||
val supportedScalaVersions: Vector[String]) extends Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: MetalsMetadata => (this.semanticdbVersion == x.semanticdbVersion) && (this.supportedScalaVersions == x.supportedScalaVersions)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (17 + "sbt.internal.bsp.MetalsMetadata".##) + semanticdbVersion.##) + supportedScalaVersions.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"MetalsMetadata(" + semanticdbVersion + ", " + supportedScalaVersions + ")"
|
||||
}
|
||||
private[this] def copy(semanticdbVersion: String = semanticdbVersion, supportedScalaVersions: Vector[String] = supportedScalaVersions): MetalsMetadata = {
|
||||
new MetalsMetadata(semanticdbVersion, supportedScalaVersions)
|
||||
}
|
||||
def withSemanticdbVersion(semanticdbVersion: String): MetalsMetadata = {
|
||||
copy(semanticdbVersion = semanticdbVersion)
|
||||
}
|
||||
def withSupportedScalaVersions(supportedScalaVersions: Vector[String]): MetalsMetadata = {
|
||||
copy(supportedScalaVersions = supportedScalaVersions)
|
||||
}
|
||||
}
|
||||
object MetalsMetadata {
|
||||
|
||||
def apply(semanticdbVersion: String, supportedScalaVersions: Vector[String]): MetalsMetadata = new MetalsMetadata(semanticdbVersion, supportedScalaVersions)
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
// 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 =>
|
||||
trait InitializeBuildParamsFormats { self: sbt.internal.bsp.codec.BuildClientCapabilitiesFormats with sbt.internal.util.codec.JValueFormats 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 {
|
||||
|
|
@ -16,8 +16,9 @@ implicit lazy val InitializeBuildParamsFormat: JsonFormat[sbt.internal.bsp.Initi
|
|||
val bspVersion = unbuilder.readField[String]("bspVersion")
|
||||
val rootUri = unbuilder.readField[java.net.URI]("rootUri")
|
||||
val capabilities = unbuilder.readField[sbt.internal.bsp.BuildClientCapabilities]("capabilities")
|
||||
val data = unbuilder.readField[Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]]("data")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.bsp.InitializeBuildParams(displayName, version, bspVersion, rootUri, capabilities)
|
||||
sbt.internal.bsp.InitializeBuildParams(displayName, version, bspVersion, rootUri, capabilities, data)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
|
|
@ -29,6 +30,7 @@ implicit lazy val InitializeBuildParamsFormat: JsonFormat[sbt.internal.bsp.Initi
|
|||
builder.addField("bspVersion", obj.bspVersion)
|
||||
builder.addField("rootUri", obj.rootUri)
|
||||
builder.addField("capabilities", obj.capabilities)
|
||||
builder.addField("data", obj.data)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,4 +40,5 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
|||
with sbt.internal.bsp.codec.ScalacOptionsItemFormats
|
||||
with sbt.internal.bsp.codec.ScalacOptionsResultFormats
|
||||
with sbt.internal.bsp.codec.BspConnectionDetailsFormats
|
||||
with sbt.internal.bsp.codec.MetalsMetadataFormats
|
||||
object JsonProtocol extends JsonProtocol
|
||||
29
protocol/src/main/contraband-scala/sbt/internal/bsp/codec/MetalsMetadataFormats.scala
generated
Normal file
29
protocol/src/main/contraband-scala/sbt/internal/bsp/codec/MetalsMetadataFormats.scala
generated
Normal file
|
|
@ -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 MetalsMetadataFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val MetalsMetadataFormat: JsonFormat[sbt.internal.bsp.MetalsMetadata] = new JsonFormat[sbt.internal.bsp.MetalsMetadata] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.MetalsMetadata = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
val semanticdbVersion = unbuilder.readField[String]("semanticdbVersion")
|
||||
val supportedScalaVersions = unbuilder.readField[Vector[String]]("supportedScalaVersions")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.bsp.MetalsMetadata(semanticdbVersion, supportedScalaVersions)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.internal.bsp.MetalsMetadata, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("semanticdbVersion", obj.semanticdbVersion)
|
||||
builder.addField("supportedScalaVersions", obj.supportedScalaVersions)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -135,8 +135,8 @@ type InitializeBuildParams {
|
|||
## The capabilities of the client
|
||||
capabilities: sbt.internal.bsp.BuildClientCapabilities!
|
||||
|
||||
# Additional metadata about the client
|
||||
# data: any
|
||||
## Additional metadata about the client
|
||||
data: sjsonnew.shaded.scalajson.ast.unsafe.JValue
|
||||
}
|
||||
|
||||
type BuildClientCapabilities {
|
||||
|
|
@ -457,3 +457,12 @@ type BspConnectionDetails {
|
|||
## Command arguments runnable via system processes to start a BSP server
|
||||
argv: [String]
|
||||
}
|
||||
|
||||
## Metals metadata in the initialization request
|
||||
type MetalsMetadata {
|
||||
## The semanticdb plugin version that should be enabled for Metals code navigation
|
||||
semanticdbVersion: String!
|
||||
|
||||
## The list of scala versions that are supported by Metals
|
||||
supportedScalaVersions: [String]
|
||||
}
|
||||
Loading…
Reference in New Issue