mirror of https://github.com/sbt/sbt.git
[sbt-server] LSP completions support
This commit is contained in:
parent
a23479dfb1
commit
34e0fc159c
|
|
@ -93,6 +93,7 @@ val mimaSettings = Def settings (
|
|||
exclude[FinalClassProblem]("sbt.internal.*"),
|
||||
exclude[FinalMethodProblem]("sbt.internal.*"),
|
||||
exclude[IncompatibleResultTypeProblem]("sbt.internal.*"),
|
||||
exclude[ReversedMissingMethodProblem]("sbt.internal.*")
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import sbt.internal.util.{
|
|||
ExitHooks,
|
||||
GlobalLogging
|
||||
}
|
||||
import sbt.internal.util.complete.HistoryCommands
|
||||
import sbt.internal.util.complete.{ HistoryCommands, Parser }
|
||||
import sbt.internal.inc.classpath.ClassLoaderCache
|
||||
|
||||
/**
|
||||
|
|
@ -54,6 +54,26 @@ final case class State(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data structure extracted form the State Machine for safe observability purposes.
|
||||
*
|
||||
* @param currentExecId provide the execId extracted from the original State.
|
||||
* @param combinedParser the parser extracted from the original State.
|
||||
*/
|
||||
private[sbt] final case class SafeState(
|
||||
currentExecId: Option[String],
|
||||
combinedParser: Parser[() => sbt.State]
|
||||
)
|
||||
|
||||
private[sbt] object SafeState {
|
||||
def apply(s: State) = {
|
||||
new SafeState(
|
||||
currentExecId = s.currentCommand.map(_.execId).flatten,
|
||||
combinedParser = s.combinedParser
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
trait Identity {
|
||||
override final def hashCode = super.hashCode
|
||||
override final def equals(a: Any) = super.equals(a)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ package server
|
|||
import sjsonnew.JsonFormat
|
||||
import sbt.internal.protocol._
|
||||
import sbt.util.Logger
|
||||
import sbt.protocol.{ SettingQuery => Q }
|
||||
import sbt.protocol.{ SettingQuery => Q, CompletionParams => CP }
|
||||
|
||||
/**
|
||||
* ServerHandler allows plugins to extend sbt server.
|
||||
|
|
@ -70,4 +70,5 @@ trait ServerCallback {
|
|||
private[sbt] def authenticate(token: String): Boolean
|
||||
private[sbt] def setInitialized(value: Boolean): Unit
|
||||
private[sbt] def onSettingQuery(execId: Option[String], req: Q): Unit
|
||||
private[sbt] def onCompletionRequest(execId: Option[String], cp: CP): Unit
|
||||
}
|
||||
|
|
|
|||
|
|
@ -383,6 +383,7 @@ object EvaluateTask {
|
|||
(dummyRoots, roots) :: (Def.dummyStreamsManager, streams) :: (dummyState, state) :: dummies
|
||||
)
|
||||
|
||||
val lastEvaluatedState: AtomicReference[SafeState] = new AtomicReference()
|
||||
val currentlyRunningEngine: AtomicReference[(State, RunningTaskEngine)] = new AtomicReference()
|
||||
|
||||
/**
|
||||
|
|
@ -452,6 +453,7 @@ object EvaluateTask {
|
|||
finally {
|
||||
strat.onTaskEngineFinish(cancelState)
|
||||
currentlyRunningEngine.set(null)
|
||||
lastEvaluatedState.set(SafeState(state))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import sjsonnew.JsonFormat
|
|||
import sjsonnew.shaded.scalajson.ast.unsafe.JValue
|
||||
import sjsonnew.support.scalajson.unsafe.Converter
|
||||
import sbt.protocol.Serialization
|
||||
import sbt.protocol.{ SettingQuery => Q, ExecStatusEvent }
|
||||
import sbt.protocol.{ SettingQuery => Q, ExecStatusEvent, CompletionParams => CP }
|
||||
import sbt.internal.protocol._
|
||||
import sbt.internal.protocol.codec._
|
||||
import sbt.internal.langserver._
|
||||
|
|
@ -135,6 +135,10 @@ private[sbt] object LanguageServerProtocol {
|
|||
case NonFatal(e) =>
|
||||
errorRespond("Cancel request failed")
|
||||
}
|
||||
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" =>
|
||||
|
|
@ -155,6 +159,7 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { self =>
|
|||
protected def setInitialized(value: Boolean): Unit
|
||||
protected def log: Logger
|
||||
protected def onSettingQuery(execId: Option[String], req: Q): Unit
|
||||
protected def onCompletionRequest(execId: Option[String], cp: CP): Unit
|
||||
|
||||
protected lazy val callbackImpl: ServerCallback = new ServerCallback {
|
||||
def jsonRpcRespond[A: JsonFormat](event: A, execId: Option[String]): Unit =
|
||||
|
|
@ -174,6 +179,8 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel { self =>
|
|||
private[sbt] def setInitialized(value: Boolean): Unit = self.setInitialized(value)
|
||||
private[sbt] def onSettingQuery(execId: Option[String], req: Q): Unit =
|
||||
self.onSettingQuery(execId, req)
|
||||
private[sbt] def onCompletionRequest(execId: Option[String], cp: CP): Unit =
|
||||
self.onCompletionRequest(execId, cp)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@ import scala.annotation.tailrec
|
|||
import sbt.protocol._
|
||||
import sbt.internal.langserver.ErrorCodes
|
||||
import sbt.internal.util.{ ObjectEvent, StringEvent }
|
||||
import sbt.internal.util.complete.Parser
|
||||
import sbt.internal.util.codec.JValueFormats
|
||||
import sbt.internal.protocol.{ JsonRpcRequestMessage, JsonRpcNotificationMessage }
|
||||
import sbt.util.Logger
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
final class NetworkChannel(
|
||||
val name: String,
|
||||
|
|
@ -364,6 +366,48 @@ final class NetworkChannel(
|
|||
}
|
||||
}
|
||||
|
||||
protected def onCompletionRequest(execId: Option[String], cp: CompletionParams) = {
|
||||
if (initialized) {
|
||||
try {
|
||||
Option(EvaluateTask.lastEvaluatedState.get) match {
|
||||
case Some(sstate) =>
|
||||
val completionItems =
|
||||
Parser
|
||||
.completions(sstate.combinedParser, cp.query, 9)
|
||||
.get
|
||||
.map(c => {
|
||||
if (!c.isEmpty) Some(c.append.replaceAll("\n", " "))
|
||||
else None
|
||||
})
|
||||
.flatten
|
||||
.map(c => cp.query + c.toString)
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
jsonRpcRespond(
|
||||
CompletionResponse(
|
||||
items = completionItems.toVector
|
||||
),
|
||||
execId
|
||||
)
|
||||
case _ =>
|
||||
jsonRpcRespondError(
|
||||
execId,
|
||||
ErrorCodes.UnknownError,
|
||||
"No available sbt state"
|
||||
)
|
||||
}
|
||||
} catch {
|
||||
case NonFatal(e) =>
|
||||
jsonRpcRespondError(
|
||||
execId,
|
||||
ErrorCodes.UnknownError,
|
||||
"Completions request failed"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
log.warn(s"ignoring completion request $cp before initialization")
|
||||
}
|
||||
}
|
||||
|
||||
def shutdown(): Unit = {
|
||||
log.info("Shutting down client connection")
|
||||
running.set(false)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver
|
||||
abstract class Command(
|
||||
val title: Option[String],
|
||||
val command: Option[String],
|
||||
val arguments: Vector[String]) extends Serializable {
|
||||
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: Command => (this.title == x.title) && (this.command == x.command) && (this.arguments == x.arguments)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (37 * (17 + "sbt.internal.langserver.Command".##) + title.##) + command.##) + arguments.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"Command(" + title + ", " + command + ", " + arguments + ")"
|
||||
}
|
||||
}
|
||||
object Command {
|
||||
|
||||
}
|
||||
40
protocol/src/main/contraband-scala/sbt/internal/langserver/CompletionContext.scala
generated
Normal file
40
protocol/src/main/contraband-scala/sbt/internal/langserver/CompletionContext.scala
generated
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver
|
||||
final class CompletionContext private (
|
||||
val triggerKind: Int,
|
||||
val triggerCharacter: Option[String]) extends Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: CompletionContext => (this.triggerKind == x.triggerKind) && (this.triggerCharacter == x.triggerCharacter)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (17 + "sbt.internal.langserver.CompletionContext".##) + triggerKind.##) + triggerCharacter.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"CompletionContext(" + triggerKind + ", " + triggerCharacter + ")"
|
||||
}
|
||||
private[this] def copy(triggerKind: Int = triggerKind, triggerCharacter: Option[String] = triggerCharacter): CompletionContext = {
|
||||
new CompletionContext(triggerKind, triggerCharacter)
|
||||
}
|
||||
def withTriggerKind(triggerKind: Int): CompletionContext = {
|
||||
copy(triggerKind = triggerKind)
|
||||
}
|
||||
def withTriggerCharacter(triggerCharacter: Option[String]): CompletionContext = {
|
||||
copy(triggerCharacter = triggerCharacter)
|
||||
}
|
||||
def withTriggerCharacter(triggerCharacter: String): CompletionContext = {
|
||||
copy(triggerCharacter = Option(triggerCharacter))
|
||||
}
|
||||
}
|
||||
object CompletionContext {
|
||||
|
||||
def apply(triggerKind: Int, triggerCharacter: Option[String]): CompletionContext = new CompletionContext(triggerKind, triggerCharacter)
|
||||
def apply(triggerKind: Int, triggerCharacter: String): CompletionContext = new CompletionContext(triggerKind, Option(triggerCharacter))
|
||||
}
|
||||
32
protocol/src/main/contraband-scala/sbt/internal/langserver/CompletionItem.scala
generated
Normal file
32
protocol/src/main/contraband-scala/sbt/internal/langserver/CompletionItem.scala
generated
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver
|
||||
final class CompletionItem private (
|
||||
val label: String) extends Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: CompletionItem => (this.label == x.label)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (17 + "sbt.internal.langserver.CompletionItem".##) + label.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"CompletionItem(" + label + ")"
|
||||
}
|
||||
private[this] def copy(label: String = label): CompletionItem = {
|
||||
new CompletionItem(label)
|
||||
}
|
||||
def withLabel(label: String): CompletionItem = {
|
||||
copy(label = label)
|
||||
}
|
||||
}
|
||||
object CompletionItem {
|
||||
|
||||
def apply(label: String): CompletionItem = new CompletionItem(label)
|
||||
}
|
||||
36
protocol/src/main/contraband-scala/sbt/internal/langserver/CompletionList.scala
generated
Normal file
36
protocol/src/main/contraband-scala/sbt/internal/langserver/CompletionList.scala
generated
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver
|
||||
final class CompletionList private (
|
||||
val isIncomplete: Boolean,
|
||||
val items: Vector[sbt.internal.langserver.CompletionItem]) extends Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: CompletionList => (this.isIncomplete == x.isIncomplete) && (this.items == x.items)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (17 + "sbt.internal.langserver.CompletionList".##) + isIncomplete.##) + items.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"CompletionList(" + isIncomplete + ", " + items + ")"
|
||||
}
|
||||
private[this] def copy(isIncomplete: Boolean = isIncomplete, items: Vector[sbt.internal.langserver.CompletionItem] = items): CompletionList = {
|
||||
new CompletionList(isIncomplete, items)
|
||||
}
|
||||
def withIsIncomplete(isIncomplete: Boolean): CompletionList = {
|
||||
copy(isIncomplete = isIncomplete)
|
||||
}
|
||||
def withItems(items: Vector[sbt.internal.langserver.CompletionItem]): CompletionList = {
|
||||
copy(items = items)
|
||||
}
|
||||
}
|
||||
object CompletionList {
|
||||
|
||||
def apply(isIncomplete: Boolean, items: Vector[sbt.internal.langserver.CompletionItem]): CompletionList = new CompletionList(isIncomplete, items)
|
||||
}
|
||||
50
protocol/src/main/contraband-scala/sbt/internal/langserver/CompletionParams.scala
generated
Normal file
50
protocol/src/main/contraband-scala/sbt/internal/langserver/CompletionParams.scala
generated
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver
|
||||
/**
|
||||
* Completion request interfaces
|
||||
* @param textDocument The text document.
|
||||
* @param position The position inside the text document.
|
||||
* @param context completion context
|
||||
*/
|
||||
final class CompletionParams private (
|
||||
textDocument: sbt.internal.langserver.TextDocumentIdentifier,
|
||||
position: sbt.internal.langserver.Position,
|
||||
val context: Option[sbt.internal.langserver.CompletionContext]) extends sbt.internal.langserver.TextDocumentPositionParamsInterface(textDocument, position) with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: CompletionParams => (this.textDocument == x.textDocument) && (this.position == x.position) && (this.context == x.context)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (37 * (17 + "sbt.internal.langserver.CompletionParams".##) + textDocument.##) + position.##) + context.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"CompletionParams(" + textDocument + ", " + position + ", " + context + ")"
|
||||
}
|
||||
private[this] def copy(textDocument: sbt.internal.langserver.TextDocumentIdentifier = textDocument, position: sbt.internal.langserver.Position = position, context: Option[sbt.internal.langserver.CompletionContext] = context): CompletionParams = {
|
||||
new CompletionParams(textDocument, position, context)
|
||||
}
|
||||
def withTextDocument(textDocument: sbt.internal.langserver.TextDocumentIdentifier): CompletionParams = {
|
||||
copy(textDocument = textDocument)
|
||||
}
|
||||
def withPosition(position: sbt.internal.langserver.Position): CompletionParams = {
|
||||
copy(position = position)
|
||||
}
|
||||
def withContext(context: Option[sbt.internal.langserver.CompletionContext]): CompletionParams = {
|
||||
copy(context = context)
|
||||
}
|
||||
def withContext(context: sbt.internal.langserver.CompletionContext): CompletionParams = {
|
||||
copy(context = Option(context))
|
||||
}
|
||||
}
|
||||
object CompletionParams {
|
||||
|
||||
def apply(textDocument: sbt.internal.langserver.TextDocumentIdentifier, position: sbt.internal.langserver.Position, context: Option[sbt.internal.langserver.CompletionContext]): CompletionParams = new CompletionParams(textDocument, position, context)
|
||||
def apply(textDocument: sbt.internal.langserver.TextDocumentIdentifier, position: sbt.internal.langserver.Position, context: sbt.internal.langserver.CompletionContext): CompletionParams = new CompletionParams(textDocument, position, Option(context))
|
||||
}
|
||||
|
|
@ -5,13 +5,12 @@
|
|||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver
|
||||
/**
|
||||
* Goto definition params model
|
||||
* @param textDocument The text document.
|
||||
* @param position The position inside the text document.
|
||||
*/
|
||||
final class TextDocumentPositionParams private (
|
||||
val textDocument: sbt.internal.langserver.TextDocumentIdentifier,
|
||||
val position: sbt.internal.langserver.Position) extends Serializable {
|
||||
textDocument: sbt.internal.langserver.TextDocumentIdentifier,
|
||||
position: sbt.internal.langserver.Position) extends sbt.internal.langserver.TextDocumentPositionParamsInterface(textDocument, position) with Serializable {
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
28
protocol/src/main/contraband-scala/sbt/internal/langserver/TextDocumentPositionParamsInterface.scala
generated
Normal file
28
protocol/src/main/contraband-scala/sbt/internal/langserver/TextDocumentPositionParamsInterface.scala
generated
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver
|
||||
/** Goto definition params model */
|
||||
abstract class TextDocumentPositionParamsInterface(
|
||||
val textDocument: sbt.internal.langserver.TextDocumentIdentifier,
|
||||
val position: sbt.internal.langserver.Position) extends Serializable {
|
||||
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: TextDocumentPositionParamsInterface => (this.textDocument == x.textDocument) && (this.position == x.position)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (17 + "sbt.internal.langserver.TextDocumentPositionParamsInterface".##) + textDocument.##) + position.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TextDocumentPositionParamsInterface(" + textDocument + ", " + position + ")"
|
||||
}
|
||||
}
|
||||
object TextDocumentPositionParamsInterface {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver
|
||||
final class TextEdit private (
|
||||
val range: sbt.internal.langserver.Range,
|
||||
val newText: String) extends Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: TextEdit => (this.range == x.range) && (this.newText == x.newText)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (17 + "sbt.internal.langserver.TextEdit".##) + range.##) + newText.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TextEdit(" + range + ", " + newText + ")"
|
||||
}
|
||||
private[this] def copy(range: sbt.internal.langserver.Range = range, newText: String = newText): TextEdit = {
|
||||
new TextEdit(range, newText)
|
||||
}
|
||||
def withRange(range: sbt.internal.langserver.Range): TextEdit = {
|
||||
copy(range = range)
|
||||
}
|
||||
def withNewText(newText: String): TextEdit = {
|
||||
copy(newText = newText)
|
||||
}
|
||||
}
|
||||
object TextEdit {
|
||||
|
||||
def apply(range: sbt.internal.langserver.Range, newText: String): TextEdit = new TextEdit(range, newText)
|
||||
}
|
||||
17
protocol/src/main/contraband-scala/sbt/internal/langserver/codec/CommandFormats.scala
generated
Normal file
17
protocol/src/main/contraband-scala/sbt/internal/langserver/codec/CommandFormats.scala
generated
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver.codec
|
||||
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
|
||||
trait CommandFormats {
|
||||
implicit lazy val CommandFormat: JsonFormat[sbt.internal.langserver.Command] = new JsonFormat[sbt.internal.langserver.Command] {
|
||||
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.langserver.Command = {
|
||||
deserializationError("No known implementation of Command.")
|
||||
}
|
||||
override def write[J](obj: sbt.internal.langserver.Command, builder: Builder[J]): Unit = {
|
||||
serializationError("No known implementation of Command.")
|
||||
}
|
||||
}
|
||||
}
|
||||
29
protocol/src/main/contraband-scala/sbt/internal/langserver/codec/CompletionContextFormats.scala
generated
Normal file
29
protocol/src/main/contraband-scala/sbt/internal/langserver/codec/CompletionContextFormats.scala
generated
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait CompletionContextFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val CompletionContextFormat: JsonFormat[sbt.internal.langserver.CompletionContext] = new JsonFormat[sbt.internal.langserver.CompletionContext] {
|
||||
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.langserver.CompletionContext = {
|
||||
jsOpt match {
|
||||
case Some(js) =>
|
||||
unbuilder.beginObject(js)
|
||||
val triggerKind = unbuilder.readField[Int]("triggerKind")
|
||||
val triggerCharacter = unbuilder.readField[Option[String]]("triggerCharacter")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.langserver.CompletionContext(triggerKind, triggerCharacter)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.internal.langserver.CompletionContext, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("triggerKind", obj.triggerKind)
|
||||
builder.addField("triggerCharacter", obj.triggerCharacter)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
27
protocol/src/main/contraband-scala/sbt/internal/langserver/codec/CompletionItemFormats.scala
generated
Normal file
27
protocol/src/main/contraband-scala/sbt/internal/langserver/codec/CompletionItemFormats.scala
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait CompletionItemFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val CompletionItemFormat: JsonFormat[sbt.internal.langserver.CompletionItem] = new JsonFormat[sbt.internal.langserver.CompletionItem] {
|
||||
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.langserver.CompletionItem = {
|
||||
jsOpt match {
|
||||
case Some(js) =>
|
||||
unbuilder.beginObject(js)
|
||||
val label = unbuilder.readField[String]("label")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.langserver.CompletionItem(label)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.internal.langserver.CompletionItem, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("label", obj.label)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
29
protocol/src/main/contraband-scala/sbt/internal/langserver/codec/CompletionListFormats.scala
generated
Normal file
29
protocol/src/main/contraband-scala/sbt/internal/langserver/codec/CompletionListFormats.scala
generated
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait CompletionListFormats { self: sbt.internal.langserver.codec.CompletionItemFormats with sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val CompletionListFormat: JsonFormat[sbt.internal.langserver.CompletionList] = new JsonFormat[sbt.internal.langserver.CompletionList] {
|
||||
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.langserver.CompletionList = {
|
||||
jsOpt match {
|
||||
case Some(js) =>
|
||||
unbuilder.beginObject(js)
|
||||
val isIncomplete = unbuilder.readField[Boolean]("isIncomplete")
|
||||
val items = unbuilder.readField[Vector[sbt.internal.langserver.CompletionItem]]("items")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.langserver.CompletionList(isIncomplete, items)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.internal.langserver.CompletionList, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("isIncomplete", obj.isIncomplete)
|
||||
builder.addField("items", obj.items)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
31
protocol/src/main/contraband-scala/sbt/internal/langserver/codec/CompletionParamsFormats.scala
generated
Normal file
31
protocol/src/main/contraband-scala/sbt/internal/langserver/codec/CompletionParamsFormats.scala
generated
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait CompletionParamsFormats { self: sbt.internal.langserver.codec.TextDocumentIdentifierFormats with sbt.internal.langserver.codec.PositionFormats with sbt.internal.langserver.codec.CompletionContextFormats with sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val CompletionParamsFormat: JsonFormat[sbt.internal.langserver.CompletionParams] = new JsonFormat[sbt.internal.langserver.CompletionParams] {
|
||||
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.langserver.CompletionParams = {
|
||||
jsOpt match {
|
||||
case Some(js) =>
|
||||
unbuilder.beginObject(js)
|
||||
val textDocument = unbuilder.readField[sbt.internal.langserver.TextDocumentIdentifier]("textDocument")
|
||||
val position = unbuilder.readField[sbt.internal.langserver.Position]("position")
|
||||
val context = unbuilder.readField[Option[sbt.internal.langserver.CompletionContext]]("context")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.langserver.CompletionParams(textDocument, position, context)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.internal.langserver.CompletionParams, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("textDocument", obj.textDocument)
|
||||
builder.addField("position", obj.position)
|
||||
builder.addField("context", obj.context)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,4 +22,5 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
|||
with sbt.internal.langserver.codec.CancelRequestParamsFormats
|
||||
with sbt.internal.langserver.codec.TextDocumentIdentifierFormats
|
||||
with sbt.internal.langserver.codec.TextDocumentPositionParamsFormats
|
||||
with sbt.internal.langserver.codec.TextDocumentPositionParamsInterfaceFormats
|
||||
object JsonProtocol extends JsonProtocol
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver.codec
|
||||
|
||||
import _root_.sjsonnew.JsonFormat
|
||||
trait TextDocumentPositionParamsInterfaceFormats { self: sbt.internal.langserver.codec.TextDocumentIdentifierFormats with sbt.internal.langserver.codec.PositionFormats with sjsonnew.BasicJsonProtocol with sbt.internal.langserver.codec.TextDocumentPositionParamsFormats =>
|
||||
implicit lazy val TextDocumentPositionParamsInterfaceFormat: JsonFormat[sbt.internal.langserver.TextDocumentPositionParamsInterface] = flatUnionFormat1[sbt.internal.langserver.TextDocumentPositionParamsInterface, sbt.internal.langserver.TextDocumentPositionParams]("type")
|
||||
}
|
||||
29
protocol/src/main/contraband-scala/sbt/internal/langserver/codec/TextEditFormats.scala
generated
Normal file
29
protocol/src/main/contraband-scala/sbt/internal/langserver/codec/TextEditFormats.scala
generated
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.langserver.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait TextEditFormats { self: sbt.internal.langserver.codec.RangeFormats with sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val TextEditFormat: JsonFormat[sbt.internal.langserver.TextEdit] = new JsonFormat[sbt.internal.langserver.TextEdit] {
|
||||
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.langserver.TextEdit = {
|
||||
jsOpt match {
|
||||
case Some(js) =>
|
||||
unbuilder.beginObject(js)
|
||||
val range = unbuilder.readField[sbt.internal.langserver.Range]("range")
|
||||
val newText = unbuilder.readField[String]("newText")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.langserver.TextEdit(range, newText)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.internal.langserver.TextEdit, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("range", obj.range)
|
||||
builder.addField("newText", obj.newText)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol
|
||||
final class CompletionParams private (
|
||||
val query: String) extends Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: CompletionParams => (this.query == x.query)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (17 + "sbt.protocol.CompletionParams".##) + query.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"CompletionParams(" + query + ")"
|
||||
}
|
||||
private[this] def copy(query: String = query): CompletionParams = {
|
||||
new CompletionParams(query)
|
||||
}
|
||||
def withQuery(query: String): CompletionParams = {
|
||||
copy(query = query)
|
||||
}
|
||||
}
|
||||
object CompletionParams {
|
||||
|
||||
def apply(query: String): CompletionParams = new CompletionParams(query)
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol
|
||||
final class CompletionResponse private (
|
||||
val items: Vector[String]) extends Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: CompletionResponse => (this.items == x.items)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (17 + "sbt.protocol.CompletionResponse".##) + items.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"CompletionResponse(" + items + ")"
|
||||
}
|
||||
private[this] def copy(items: Vector[String] = items): CompletionResponse = {
|
||||
new CompletionResponse(items)
|
||||
}
|
||||
def withItems(items: Vector[String]): CompletionResponse = {
|
||||
copy(items = items)
|
||||
}
|
||||
}
|
||||
object CompletionResponse {
|
||||
|
||||
def apply(items: Vector[String]): CompletionResponse = new CompletionResponse(items)
|
||||
}
|
||||
27
protocol/src/main/contraband-scala/sbt/protocol/codec/CompletionParamsFormats.scala
generated
Normal file
27
protocol/src/main/contraband-scala/sbt/protocol/codec/CompletionParamsFormats.scala
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait CompletionParamsFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val CompletionParamsFormat: JsonFormat[sbt.protocol.CompletionParams] = new JsonFormat[sbt.protocol.CompletionParams] {
|
||||
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.CompletionParams = {
|
||||
jsOpt match {
|
||||
case Some(js) =>
|
||||
unbuilder.beginObject(js)
|
||||
val query = unbuilder.readField[String]("query")
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.CompletionParams(query)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.protocol.CompletionParams, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("query", obj.query)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
27
protocol/src/main/contraband-scala/sbt/protocol/codec/CompletionResponseFormats.scala
generated
Normal file
27
protocol/src/main/contraband-scala/sbt/protocol/codec/CompletionResponseFormats.scala
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait CompletionResponseFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val CompletionResponseFormat: JsonFormat[sbt.protocol.CompletionResponse] = new JsonFormat[sbt.protocol.CompletionResponse] {
|
||||
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.CompletionResponse = {
|
||||
jsOpt match {
|
||||
case Some(js) =>
|
||||
unbuilder.beginObject(js)
|
||||
val items = unbuilder.readField[Vector[String]]("items")
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.CompletionResponse(items)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.protocol.CompletionResponse, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("items", obj.items)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
|||
with sbt.protocol.codec.ExecCommandFormats
|
||||
with sbt.protocol.codec.SettingQueryFormats
|
||||
with sbt.protocol.codec.CommandMessageFormats
|
||||
with sbt.protocol.codec.CompletionParamsFormats
|
||||
with sbt.protocol.codec.ChannelAcceptedEventFormats
|
||||
with sbt.protocol.codec.LogEventFormats
|
||||
with sbt.protocol.codec.ExecStatusEventFormats
|
||||
|
|
@ -17,5 +18,6 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
|||
with sbt.protocol.codec.SettingQueryFailureFormats
|
||||
with sbt.protocol.codec.EventMessageFormats
|
||||
with sbt.protocol.codec.SettingQueryResponseFormats
|
||||
with sbt.protocol.codec.CompletionResponseFormats
|
||||
with sbt.protocol.codec.ExecutionEventFormats
|
||||
object JsonProtocol extends JsonProtocol
|
||||
|
|
@ -137,7 +137,7 @@ type CancelRequestParams {
|
|||
}
|
||||
|
||||
## Goto definition params model
|
||||
type TextDocumentPositionParams {
|
||||
interface TextDocumentPositionParamsInterface {
|
||||
## The text document.
|
||||
textDocument: sbt.internal.langserver.TextDocumentIdentifier!
|
||||
|
||||
|
|
@ -145,6 +145,14 @@ type TextDocumentPositionParams {
|
|||
position: sbt.internal.langserver.Position!
|
||||
}
|
||||
|
||||
type TextDocumentPositionParams implements TextDocumentPositionParamsInterface {
|
||||
## The text document.
|
||||
textDocument: sbt.internal.langserver.TextDocumentIdentifier!
|
||||
|
||||
## The position inside the text document.
|
||||
position: sbt.internal.langserver.Position!
|
||||
}
|
||||
|
||||
## Text documents are identified using a URI. On the protocol level, URIs are passed as strings.
|
||||
type TextDocumentIdentifier {
|
||||
## The text document's URI.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ type SettingQuery implements CommandMessage {
|
|||
setting: String!
|
||||
}
|
||||
|
||||
type CompletionParams {
|
||||
query: String!
|
||||
}
|
||||
|
||||
## Message for events.
|
||||
interface EventMessage {
|
||||
|
|
@ -57,6 +60,10 @@ type SettingQueryFailure implements SettingQueryResponse {
|
|||
message: String!
|
||||
}
|
||||
|
||||
type CompletionResponse {
|
||||
items: [String]
|
||||
}
|
||||
|
||||
# enum Status {
|
||||
# Ready
|
||||
# Processing
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
val hello = taskKey[Unit]("Say hello")
|
||||
|
||||
hello := {}
|
||||
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5"
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package org.sbt
|
||||
|
||||
import org.scalatest.FlatSpec
|
||||
|
||||
class ExampleSpec extends FlatSpec {
|
||||
"a test" should "do something" in {
|
||||
assert(true == true)
|
||||
assert(false == false)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@ package testpkg
|
|||
|
||||
import org.scalatest._
|
||||
import scala.concurrent._
|
||||
import scala.annotation.tailrec
|
||||
import sbt.protocol.ClientSocket
|
||||
import scala.util.Try
|
||||
import TestServer.withTestServer
|
||||
|
|
@ -17,6 +16,7 @@ import java.io.File
|
|||
import sbt.io.syntax._
|
||||
import sbt.io.IO
|
||||
import sbt.RunFromSourceMain
|
||||
import scala.util.Try
|
||||
import scala.concurrent.ExecutionContext
|
||||
import java.util.concurrent.ForkJoinPool
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ class ServerSpec extends fixture.AsyncFreeSpec with fixture.AsyncTestDataFixture
|
|||
"server" - {
|
||||
"should start" in { implicit td =>
|
||||
withTestServer("handshake") { p =>
|
||||
p.writeLine(
|
||||
p.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "3", "method": "sbt/setting", "params": { "setting": "root/name" } }"""
|
||||
)
|
||||
assert(p.waitForString(10) { s =>
|
||||
|
|
@ -35,7 +35,7 @@ class ServerSpec extends fixture.AsyncFreeSpec with fixture.AsyncTestDataFixture
|
|||
|
||||
"return number id when number id is sent" in { implicit td =>
|
||||
withTestServer("handshake") { p =>
|
||||
p.writeLine(
|
||||
p.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": 3, "method": "sbt/setting", "params": { "setting": "root/name" } }"""
|
||||
)
|
||||
assert(p.waitForString(10) { s =>
|
||||
|
|
@ -46,7 +46,7 @@ class ServerSpec extends fixture.AsyncFreeSpec with fixture.AsyncTestDataFixture
|
|||
|
||||
"report task failures in case of exceptions" in { implicit td =>
|
||||
withTestServer("events") { p =>
|
||||
p.writeLine(
|
||||
p.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": 11, "method": "sbt/exec", "params": { "commandLine": "hello" } }"""
|
||||
)
|
||||
assert(p.waitForString(10) { s =>
|
||||
|
|
@ -57,10 +57,10 @@ class ServerSpec extends fixture.AsyncFreeSpec with fixture.AsyncTestDataFixture
|
|||
|
||||
"return error if cancelling non-matched task id" in { implicit td =>
|
||||
withTestServer("events") { p =>
|
||||
p.writeLine(
|
||||
p.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id":12, "method": "sbt/exec", "params": { "commandLine": "run" } }"""
|
||||
)
|
||||
p.writeLine(
|
||||
p.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id":13, "method": "sbt/cancelRequest", "params": { "id": "55" } }"""
|
||||
)
|
||||
|
||||
|
|
@ -72,12 +72,12 @@ class ServerSpec extends fixture.AsyncFreeSpec with fixture.AsyncTestDataFixture
|
|||
|
||||
"cancel on-going task with numeric id" in { implicit td =>
|
||||
withTestServer("events") { p =>
|
||||
p.writeLine(
|
||||
p.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id":12, "method": "sbt/exec", "params": { "commandLine": "run" } }"""
|
||||
)
|
||||
|
||||
assert(p.waitForString(60) { s =>
|
||||
p.writeLine(
|
||||
p.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id":13, "method": "sbt/cancelRequest", "params": { "id": "12" } }"""
|
||||
)
|
||||
s contains """"result":{"status":"Task cancelled""""
|
||||
|
|
@ -87,18 +87,65 @@ class ServerSpec extends fixture.AsyncFreeSpec with fixture.AsyncTestDataFixture
|
|||
|
||||
"cancel on-going task with string id" in { implicit td =>
|
||||
withTestServer("events") { p =>
|
||||
p.writeLine(
|
||||
p.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "foo", "method": "sbt/exec", "params": { "commandLine": "run" } }"""
|
||||
)
|
||||
|
||||
assert(p.waitForString(60) { s =>
|
||||
p.writeLine(
|
||||
p.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id": "bar", "method": "sbt/cancelRequest", "params": { "id": "foo" } }"""
|
||||
)
|
||||
s contains """"result":{"status":"Task cancelled""""
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
"return basic completions on request" in { implicit td =>
|
||||
withTestServer("completions") { p =>
|
||||
val completionStr = """{ "query": "" }"""
|
||||
p.sendJsonRpc(
|
||||
s"""{ "jsonrpc": "2.0", "id": 15, "method": "sbt/completion", "params": $completionStr }"""
|
||||
)
|
||||
|
||||
assert(p.waitForString(10) { s =>
|
||||
s contains """"result":{"items":["""
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
"return completion for custom tasks" in { implicit td =>
|
||||
withTestServer("completions") { p =>
|
||||
val completionStr = """{ "query": "hell" }"""
|
||||
p.sendJsonRpc(
|
||||
s"""{ "jsonrpc": "2.0", "id": 15, "method": "sbt/completion", "params": $completionStr }"""
|
||||
)
|
||||
|
||||
assert(p.waitForString(10) { s =>
|
||||
s contains """"result":{"items":["hello"]}"""
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
"return completions for user classes" in { implicit td =>
|
||||
withTestServer("completions") { p =>
|
||||
p.sendJsonRpc(
|
||||
"""{ "jsonrpc": "2.0", "id":12, "method": "sbt/exec", "params": { "commandLine": "test" } }"""
|
||||
)
|
||||
|
||||
p.waitForString(30) { s =>
|
||||
(s contains """"id":12,"result":{"status":"Done"""") && (s contains """"exitCode":0""")
|
||||
}
|
||||
|
||||
val completionStr = """{ "query": "testOnly org." }"""
|
||||
p.sendJsonRpc(
|
||||
s"""{ "jsonrpc": "2.0", "id": 15, "method": "sbt/completion", "params": $completionStr }"""
|
||||
)
|
||||
|
||||
assert(p.waitForString(30) { s =>
|
||||
s contains """"result":{"items":["testOnly org.sbt.ExampleSpec"]}"""
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -154,7 +201,7 @@ object TestServer {
|
|||
case class TestServer(baseDirectory: File)(implicit ec: ExecutionContext) {
|
||||
import TestServer.hostLog
|
||||
|
||||
val readBuffer = new Array[Byte](4096)
|
||||
val readBuffer = new Array[Byte](40960)
|
||||
var buffer: Vector[Byte] = Vector.empty
|
||||
var bytesRead = 0
|
||||
private val delimiter: Byte = '\n'.toByte
|
||||
|
|
@ -215,7 +262,7 @@ case class TestServer(baseDirectory: File)(implicit ec: ExecutionContext) {
|
|||
writeLine(message)
|
||||
}
|
||||
|
||||
def writeLine(s: String): Unit = {
|
||||
private def writeLine(s: String): Unit = {
|
||||
def writeEndLine(): Unit = {
|
||||
val retByte: Byte = '\r'.toByte
|
||||
val delimiter: Byte = '\n'.toByte
|
||||
|
|
@ -243,21 +290,17 @@ case class TestServer(baseDirectory: File)(implicit ec: ExecutionContext) {
|
|||
readContentLength(l)
|
||||
}
|
||||
|
||||
@tailrec
|
||||
final def waitForString(num: Int)(f: String => Boolean): Boolean = {
|
||||
if (num < 0) { throw new Exception("Retries are over.") } else {
|
||||
// readFrame should be called in another Thread in orrder to be able to time limit it's execution
|
||||
val res = Future { readFrame }(ec)
|
||||
|
||||
import scala.concurrent.duration._
|
||||
Try {
|
||||
Await.result(res, 1.second)
|
||||
}.toOption.flatten match {
|
||||
// function f should be called in this Thread in order to be executed exactly once before eventually returning
|
||||
case Some(str) if f(str) => true
|
||||
case _ => waitForString(num - 1)(f)
|
||||
val res = Future {
|
||||
var done = false
|
||||
while (!done) {
|
||||
done = readFrame.fold(false)(f)
|
||||
}
|
||||
}
|
||||
true
|
||||
}(ec)
|
||||
|
||||
import scala.concurrent.duration._
|
||||
Await.result(res, num.seconds)
|
||||
}
|
||||
|
||||
def readLine: Option[String] = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue