mirror of
https://github.com/sbt/sbt.git
synced 2026-09-01 10:37:54 +02:00
Merge branch 'bport2/fix-lsp' into 2.0.x
This commit is contained in:
@@ -641,6 +641,7 @@ lazy val commandProj = (project in file("main-command"))
|
||||
contrabandSettings,
|
||||
mimaSettings,
|
||||
mimaBinaryIssueFilters ++= Vector(
|
||||
exclude[ReversedMissingMethodProblem]("sbt.internal.server.ServerCallback.*"),
|
||||
exclude[MissingClassProblem]("sbt.internal.util.JoinThread"),
|
||||
exclude[MissingClassProblem]("sbt.internal.util.JoinThread$"),
|
||||
exclude[MissingClassProblem]("sbt.internal.util.ReadJsonFromInputStream"),
|
||||
|
||||
@@ -78,6 +78,7 @@ trait ServerCallback {
|
||||
|
||||
private[sbt] def authOptions: Set[ServerAuthentication]
|
||||
private[sbt] def authenticate(token: String): Boolean
|
||||
private[sbt] def isAuthenticated: Boolean
|
||||
private[sbt] def setInitialized(value: Boolean): Unit
|
||||
private[sbt] def setInitializeOption(opts: InitializeOption): Unit
|
||||
private[sbt] def onSettingQuery(execId: Option[String], req: Q): Unit
|
||||
|
||||
@@ -47,6 +47,7 @@ object LintUnused {
|
||||
sbt.nio.Keys.outputFileStamper,
|
||||
sbt.nio.Keys.watchTriggers,
|
||||
serverConnectionType,
|
||||
serverPort,
|
||||
serverIdleTimeout,
|
||||
shellPrompt,
|
||||
sLog,
|
||||
|
||||
@@ -41,12 +41,22 @@ private[sbt] object LanguageServerProtocol {
|
||||
ServerCapabilities(
|
||||
textDocumentSync = TextDocumentSyncOptions(true, 0, false, false, SaveOptions(false)),
|
||||
hoverProvider = false,
|
||||
definitionProvider = true
|
||||
definitionProvider = false
|
||||
)
|
||||
}
|
||||
|
||||
def handler(converter: FileConverter): ServerHandler = ServerHandler { callback =>
|
||||
import callback.*
|
||||
|
||||
def checkAuthenticated(r: JsonRpcRequestMessage)(f: => Unit): Unit =
|
||||
if !isAuthenticated then
|
||||
jsonRpcRespondError(
|
||||
Some(r.id),
|
||||
ErrorCodes.InvalidRequest,
|
||||
s"'${r.method}' is not allowed before authentication."
|
||||
)
|
||||
else f
|
||||
|
||||
ServerIntent(
|
||||
onRequest = {
|
||||
case r: JsonRpcRequestMessage if r.method == "initialize" =>
|
||||
@@ -68,32 +78,37 @@ private[sbt] object LanguageServerProtocol {
|
||||
if (!opt.skipAnalysis.getOrElse(false)) appendExec("collectAnalyses", None)
|
||||
jsonRpcRespond(InitializeResult(serverCapabilities), Some(r.id))
|
||||
|
||||
case r: JsonRpcRequestMessage if r.method == "textDocument/definition" =>
|
||||
val _ = Definition.lspDefinition(json(r), r.id, CommandSource(name), converter, log)(using
|
||||
StandardMain.executionContext
|
||||
)
|
||||
|
||||
case r: JsonRpcRequestMessage if r.method == "sbt/exec" =>
|
||||
val param = Converter.fromJson[SbtExecParams](json(r)).get
|
||||
val _ = appendExec(param.commandLine, Some(r.id))
|
||||
checkAuthenticated(r) {
|
||||
val param = Converter.fromJson[SbtExecParams](json(r)).get
|
||||
val _ = appendExec(param.commandLine, Some(r.id))
|
||||
}
|
||||
|
||||
case r: JsonRpcRequestMessage if r.method == "sbt/setting" =>
|
||||
val param = Converter.fromJson[Q](json(r)).get
|
||||
onSettingQuery(Option(r.id), param)
|
||||
checkAuthenticated(r) {
|
||||
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[CancelRequestParams](json(r)).get
|
||||
onCancellationRequest(Option(r.id), param)
|
||||
checkAuthenticated(r) {
|
||||
val param = Converter.fromJson[CancelRequestParams](json(r)).get
|
||||
onCancellationRequest(Option(r.id), param)
|
||||
}
|
||||
|
||||
case r: JsonRpcRequestMessage if r.method == "sbt/completion" =>
|
||||
val param = Converter.fromJson[CP](json(r)).get
|
||||
onCompletionRequest(Option(r.id), param)
|
||||
checkAuthenticated(r) {
|
||||
val param = Converter.fromJson[CP](json(r)).get
|
||||
onCompletionRequest(Option(r.id), param)
|
||||
}
|
||||
|
||||
},
|
||||
onResponse = PartialFunction.empty,
|
||||
onNotification = {
|
||||
case n: JsonRpcNotificationMessage if n.method == "textDocument/didSave" =>
|
||||
val _ = appendExec(";Test/compile; collectAnalyses", None)
|
||||
if (isAuthenticated) {
|
||||
val _ = appendExec(";Test/compile; collectAnalyses", None)
|
||||
} else log.warn(s"ignoring '${n.method}' before authentication")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ final class NetworkChannel(
|
||||
private val delimiter: Byte = '\n'.toByte
|
||||
private val out = connection.getOutputStream
|
||||
private var initialized = false
|
||||
private var authenticated = false
|
||||
|
||||
/**
|
||||
* Reference to the client-side custom options
|
||||
@@ -144,6 +145,7 @@ final class NetworkChannel(
|
||||
def name: String = self.name
|
||||
private[sbt] def authOptions: Set[ServerAuthentication] = self.authOptions
|
||||
private[sbt] def authenticate(token: String): Boolean = self.authenticate(token)
|
||||
private[sbt] def isAuthenticated: Boolean = self.isAuthenticated
|
||||
private[sbt] def setInitialized(value: Boolean): Unit = self.setInitialized(value)
|
||||
private[sbt] def setInitializeOption(opts: InitializeOption): Unit =
|
||||
self.setInitializeOption(opts)
|
||||
@@ -168,7 +170,14 @@ final class NetworkChannel(
|
||||
private[sbt] def subscribeToAll: Boolean =
|
||||
Option(initializeOption.get).flatMap(_.subscribeToAll).getOrElse(false)
|
||||
|
||||
protected def authenticate(token: String): Boolean = instance.authenticate(token)
|
||||
protected def authenticate(token: String): Boolean = {
|
||||
val result = instance.authenticate(token)
|
||||
if result then authenticated = true
|
||||
result
|
||||
}
|
||||
|
||||
private[sbt] def isAuthenticated: Boolean =
|
||||
authenticated || authOptions.isEmpty
|
||||
|
||||
protected def setInitialized(value: Boolean): Unit = initialized = value
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ package server
|
||||
|
||||
import java.util.concurrent.{ ArrayBlockingQueue, ConcurrentHashMap }
|
||||
import java.util.UUID
|
||||
import sbt.internal.langserver.ErrorCodes
|
||||
import sbt.internal.protocol.{
|
||||
JsonRpcNotificationMessage,
|
||||
JsonRpcRequestMessage,
|
||||
@@ -176,13 +177,20 @@ object VirtualTerminal {
|
||||
private val requestHandler: Handler[JsonRpcRequestMessage] =
|
||||
callback => {
|
||||
case r if r.method == attach =>
|
||||
val isInteractive = r.params
|
||||
.flatMap(Converter.fromJson[Attach](_).toOption.map(_.interactive))
|
||||
.exists(identity)
|
||||
StandardMain.exchange.channelForName(callback.name) match {
|
||||
case Some(nc: NetworkChannel) => nc.setInteractive(r.id, isInteractive)
|
||||
case _ =>
|
||||
}
|
||||
if (callback.isAuthenticated) {
|
||||
val isInteractive = r.params
|
||||
.flatMap(Converter.fromJson[Attach](_).toOption.map(_.interactive))
|
||||
.exists(identity)
|
||||
StandardMain.exchange.channelForName(callback.name) match {
|
||||
case Some(nc: NetworkChannel) => nc.setInteractive(r.id, isInteractive)
|
||||
case _ =>
|
||||
}
|
||||
} else
|
||||
callback.jsonRpcRespondError(
|
||||
Some(r.id),
|
||||
ErrorCodes.InvalidRequest,
|
||||
s"'$attach' is not allowed before authentication."
|
||||
)
|
||||
}
|
||||
private val responseHandler: Handler[JsonRpcResponseMessage] =
|
||||
callback => {
|
||||
@@ -242,12 +250,14 @@ object VirtualTerminal {
|
||||
private val notificationHandler: Handler[JsonRpcNotificationMessage] =
|
||||
callback => {
|
||||
case n if n.method == systemIn =>
|
||||
import sjsonnew.BasicJsonProtocol.*
|
||||
n.params.flatMap(Converter.fromJson[Byte](_).toOption).foreach { byte =>
|
||||
StandardMain.exchange.channelForName(callback.name) match {
|
||||
case Some(nc: NetworkChannel) => nc.write(byte)
|
||||
case _ =>
|
||||
if (callback.isAuthenticated) {
|
||||
import sjsonnew.BasicJsonProtocol.*
|
||||
n.params.flatMap(Converter.fromJson[Byte](_).toOption).foreach { byte =>
|
||||
StandardMain.exchange.channelForName(callback.name) match {
|
||||
case Some(nc: NetworkChannel) => nc.write(byte)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
} else callback.log.warn(s"ignoring '$systemIn' before authentication")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
scalaVersion := "3.8.4"
|
||||
|
||||
Global / serverConnectionType := ConnectionType.Tcp
|
||||
Global / serverPort := 5002
|
||||
|
||||
lazy val root = (project in file("."))
|
||||
.settings(
|
||||
name := "tcp",
|
||||
)
|
||||
Reference in New Issue
Block a user