mirror of https://github.com/sbt/sbt.git
Upgrade LineReader to JLine3
This commit upgrades sbt to using jline3. The advantage to jline3 is that it has a significantly better tab completion engine that is more similar to what you get from zsh or fish. The diff is bigger than I'd hoped because there are a number of behaviors that are different in jline3 vs jline2 in how the library consumes input streams and implements various features. I also was unable to remove jline2 because we need it for older versions of the scala console to work correctly with the thin client. As a result, the changes are largely additive. A good amount of this commit was in adding more protocol so that the remote client can forward its jline3 terminal information to the server. There were a number of minor changes that I made that either fixed outstanding ui bugs from #5620 or regressions due to differences between jline3 and jline2. The number one thing that caused problems is that the jline3 LineReader insists on using a NonBlockingInputStream. The implementation ofo NonBlockingInputStream seems buggy. Moreover, sbt internally uses a non blocking input stream for system in so jline is adding non blocking to an already non blocking stream, which is frustrating. A long term solution might be to consider insourcing LineReader.java from jline3 and just adapting it to use an sbt terminal rather than fighting with the jline3 api. This would also have the advantage of not conflicting with other versions of jline3. Even if we don't, we may want to shade jline3 if that is possible.
This commit is contained in:
parent
ed4d40d3e2
commit
2ecf5967ee
16
build.sbt
16
build.sbt
|
|
@ -289,6 +289,7 @@ val completeProj = (project in file("internal") / "util-complete")
|
|||
testedBaseSettings,
|
||||
name := "Completion",
|
||||
libraryDependencies += jline,
|
||||
libraryDependencies += jline3,
|
||||
mimaSettings,
|
||||
// Parser is used publicly, so we can't break bincompat.
|
||||
mimaBinaryIssueFilters := Seq(
|
||||
|
|
@ -343,12 +344,20 @@ lazy val utilPosition = (project in file("internal") / "util-position")
|
|||
|
||||
lazy val utilLogging = (project in file("internal") / "util-logging")
|
||||
.enablePlugins(ContrabandPlugin, JsonCodecPlugin)
|
||||
.dependsOn(utilInterface)
|
||||
.dependsOn(utilInterface, collectionProj)
|
||||
.settings(
|
||||
utilCommonSettings,
|
||||
name := "Util Logging",
|
||||
libraryDependencies ++=
|
||||
Seq(jline, log4jApi, log4jCore, disruptor, sjsonNewScalaJson.value, scalaReflect.value),
|
||||
Seq(
|
||||
jline,
|
||||
jline3,
|
||||
log4jApi,
|
||||
log4jCore,
|
||||
disruptor,
|
||||
sjsonNewScalaJson.value,
|
||||
scalaReflect.value
|
||||
),
|
||||
libraryDependencies ++= Seq(scalacheck % "test", scalatest % "test"),
|
||||
libraryDependencies ++= (scalaVersion.value match {
|
||||
case v if v.startsWith("2.12.") => List(compilerPlugin(silencerPlugin))
|
||||
|
|
@ -1047,8 +1056,7 @@ lazy val sbtClientProj = (project in file("client"))
|
|||
crossPaths := false,
|
||||
exportJars := true,
|
||||
libraryDependencies += jansi,
|
||||
libraryDependencies += "net.java.dev.jna" % "jna" % "5.5.0",
|
||||
libraryDependencies += "net.java.dev.jna" % "jna-platform" % "5.5.0",
|
||||
libraryDependencies += jline3Jansi,
|
||||
libraryDependencies += scalatest % "test",
|
||||
/*
|
||||
* On windows, the raw classpath is too large to be a command argument to an
|
||||
|
|
|
|||
|
|
@ -1,7 +1,20 @@
|
|||
{
|
||||
"resources":[
|
||||
{"pattern":"jline/console/completer/CandidateListCompletionHandler.properties"},
|
||||
{"pattern":"library.properties"},
|
||||
{"pattern":"org/jline/utils/ansi.caps"},
|
||||
{"pattern":"org/jline/utils/capabilities.txt"},
|
||||
{"pattern":"org/jline/utils/colors.txt"},
|
||||
{"pattern":"org/jline/utils/dumb-color.caps"},
|
||||
{"pattern":"org/jline/utils/xterm.caps"},
|
||||
{"pattern":"org/jline/utils/xterm-256color.caps"},
|
||||
{"pattern":"org/jline/utils/windows-256color.caps"},
|
||||
{"pattern":"org/jline/utils/screen-256color.caps"},
|
||||
{"pattern":"org/jline/utils/windows.caps"},
|
||||
{"pattern":"org/jline/utils/windows-conemu.caps"},
|
||||
{"pattern":"org/jline/utils/dumb.caps"},
|
||||
{"pattern":"org/jline/utils/windows-vtp.caps"},
|
||||
{"pattern":"org/jline/utils/screen.caps"},
|
||||
{"pattern":"library.properties"},
|
||||
{"pattern":"darwin/x86_64/libsbtipcsocket.dylib"},
|
||||
{"pattern":"linux/x86_64/libsbtipcsocket.so"},
|
||||
{"pattern":"win32/x86_64/sbtipcsocket.dll"}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,28 @@
|
|||
package sbt.internal.util
|
||||
|
||||
import java.io._
|
||||
import java.util.{ List => JList }
|
||||
|
||||
import jline.console.ConsoleReader
|
||||
import jline.console.history.{ FileHistory, MemoryHistory }
|
||||
import org.jline.reader.{
|
||||
Candidate,
|
||||
Completer,
|
||||
EndOfFileException,
|
||||
LineReader => JLineReader,
|
||||
LineReaderBuilder,
|
||||
ParsedLine,
|
||||
UserInterruptException,
|
||||
}
|
||||
import sbt.internal.util.complete.Parser
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
import java.nio.channels.ClosedByInterruptException
|
||||
|
||||
trait LineReader {
|
||||
trait LineReader extends AutoCloseable {
|
||||
def readLine(prompt: String, mask: Option[Char] = None): Option[String]
|
||||
override def close(): Unit = {}
|
||||
}
|
||||
|
||||
object LineReader {
|
||||
|
|
@ -25,7 +37,67 @@ object LineReader {
|
|||
!java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT)
|
||||
val MaxHistorySize = 500
|
||||
|
||||
private def completer(parser: Parser[_]): Completer = new Completer {
|
||||
def complete(lr: JLineReader, pl: ParsedLine, candidates: JList[Candidate]): Unit = {
|
||||
Parser.completions(parser, pl.line(), 10).get.foreach { c =>
|
||||
/*
|
||||
* For commands like `~` that delegate parsing to another parser, the `~` may be
|
||||
* excluded from the completion result. For example,
|
||||
* ~testOnly <TAB>
|
||||
* might return results like
|
||||
* 'testOnly ;'
|
||||
* 'testOnly com.foo.FooSpec'
|
||||
* ...
|
||||
* If we use the raw display, JLine will reject the completions because they are
|
||||
* missing the leading `~`. To workaround this, we append to the result to the
|
||||
* line provided the line does not end with " ". This fixes the missing `~` in
|
||||
* the prefix problem. We also need to split the line on space and take the
|
||||
* last token and append to that otherwise the completion will double print
|
||||
* the prefix, so that `testOnly com<Tab>` might expand to something like:
|
||||
* `testOnly testOnly\ com.foo.FooSpec` instead of `testOnly com.foo.FooSpec`.
|
||||
*/
|
||||
if (c.append.nonEmpty) {
|
||||
if (!pl.line().endsWith(" ")) {
|
||||
candidates.add(new Candidate(pl.line().split(" ").last + c.append))
|
||||
} else {
|
||||
candidates.add(new Candidate(c.append))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def createReader(
|
||||
historyPath: Option[File],
|
||||
parser: Parser[_],
|
||||
terminal: Terminal,
|
||||
prompt: Prompt = Prompt.Running,
|
||||
): LineReader = {
|
||||
val term = JLine3(terminal)
|
||||
// We may want to consider insourcing LineReader.java from jline. We don't otherwise
|
||||
// directly need jline3 for sbt.
|
||||
val reader = LineReaderBuilder.builder().terminal(term).completer(completer(parser)).build()
|
||||
historyPath.foreach(f => reader.setVariable(JLineReader.HISTORY_FILE, f))
|
||||
new LineReader {
|
||||
override def readLine(prompt: String, mask: Option[Char]): Option[String] = {
|
||||
try terminal.withRawSystemIn {
|
||||
Option(mask.map(reader.readLine(prompt, _)).getOrElse(reader.readLine(prompt)))
|
||||
} catch {
|
||||
case e: EndOfFileException =>
|
||||
if (terminal == Terminal.console && System.console == null) None
|
||||
else Some("exit")
|
||||
case _: IOError => Some("exit")
|
||||
case _: UserInterruptException | _: ClosedByInterruptException |
|
||||
_: UncheckedIOException =>
|
||||
throw new InterruptedException
|
||||
} finally {
|
||||
terminal.prompt.reset()
|
||||
term.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def createJLine2Reader(
|
||||
historyPath: Option[File],
|
||||
terminal: Terminal,
|
||||
prompt: Prompt = Prompt.Running,
|
||||
|
|
@ -42,7 +114,6 @@ object LineReader {
|
|||
cr.setHistoryEnabled(true)
|
||||
cr
|
||||
}
|
||||
|
||||
def simple(terminal: Terminal): LineReader = new SimpleReader(None, HandleCONT, terminal)
|
||||
def simple(
|
||||
historyPath: Option[File],
|
||||
|
|
@ -230,7 +301,7 @@ final class FullReader(
|
|||
Terminal.console
|
||||
)
|
||||
protected[this] val reader: ConsoleReader = {
|
||||
val cr = LineReader.createReader(historyPath, terminal)
|
||||
val cr = LineReader.createJLine2Reader(historyPath, terminal)
|
||||
sbt.internal.util.complete.JLineCompletion.installCustomCompletor(cr, complete)
|
||||
cr
|
||||
}
|
||||
|
|
@ -244,7 +315,7 @@ class SimpleReader private[sbt] (
|
|||
def this(historyPath: Option[File], handleCONT: Boolean, injectThreadSleep: Boolean) =
|
||||
this(historyPath, handleCONT, Terminal.console)
|
||||
protected[this] val reader: ConsoleReader =
|
||||
LineReader.createReader(historyPath, terminal)
|
||||
LineReader.createJLine2Reader(historyPath, terminal)
|
||||
}
|
||||
|
||||
object SimpleReader extends SimpleReader(None, LineReader.HandleCONT, false) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ package sbt.internal.util
|
|||
|
||||
import java.io.{ PrintStream, PrintWriter }
|
||||
import java.lang.StringBuilder
|
||||
import java.nio.channels.ClosedChannelException
|
||||
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger }
|
||||
|
||||
import org.apache.logging.log4j.core.appender.AbstractAppender
|
||||
|
|
@ -394,7 +395,8 @@ class ConsoleAppender private[ConsoleAppender] (
|
|||
override def append(event: XLogEvent): Unit = {
|
||||
val level = ConsoleAppender.toLevel(event.getLevel)
|
||||
val message = event.getMessage
|
||||
appendMessage(level, message)
|
||||
try appendMessage(level, message)
|
||||
catch { case _: ClosedChannelException => }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -181,9 +181,10 @@ object EscHelpers {
|
|||
else res(index) = 32
|
||||
case 'm' =>
|
||||
case ';' => state = csi
|
||||
case _ =>
|
||||
case b => state = csi
|
||||
}
|
||||
digit.clear()
|
||||
case b if state == esc => state = 0
|
||||
case b =>
|
||||
res(index) = b
|
||||
index += 1
|
||||
|
|
|
|||
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.internal.util
|
||||
|
||||
import java.io.{ EOFException, InputStream, OutputStream, PrintWriter }
|
||||
import java.nio.charset.Charset
|
||||
import java.util.{ Arrays, EnumSet }
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import org.jline.utils.InfoCmp.Capability
|
||||
import org.jline.utils.{ NonBlocking, OSUtils }
|
||||
import org.jline.terminal.{ Attributes, Size, Terminal => JTerminal }
|
||||
import org.jline.terminal.Terminal.SignalHandler
|
||||
import org.jline.terminal.impl.AbstractTerminal
|
||||
import org.jline.terminal.impl.jansi.JansiSupportImpl
|
||||
import org.jline.terminal.impl.jansi.win.JansiWinSysTerminal
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.util.Try
|
||||
|
||||
private[util] object JLine3 {
|
||||
private val capabilityMap = Capability
|
||||
.values()
|
||||
.map { c =>
|
||||
c.toString -> c
|
||||
}
|
||||
.toMap
|
||||
|
||||
private[util] def system = {
|
||||
/*
|
||||
* For reasons that are unclear to me, TerminalBuilder fails to build
|
||||
* windows terminals. The instructions about the classpath did not work:
|
||||
* https://stackoverflow.com/questions/52851232/jline3-issues-with-windows-terminal
|
||||
* We can deconstruct what TerminalBuilder does and inline it for now.
|
||||
* It is possible that this workaround will break WSL but I haven't checked that.
|
||||
*/
|
||||
if (Util.isNonCygwinWindows) {
|
||||
val support = new JansiSupportImpl
|
||||
val winConsole = support.isWindowsConsole();
|
||||
try {
|
||||
val term = JansiWinSysTerminal.createTerminal(
|
||||
"console",
|
||||
"ansi",
|
||||
OSUtils.IS_CONEMU,
|
||||
Charset.forName("UTF-8"),
|
||||
-1,
|
||||
false,
|
||||
SignalHandler.SIG_DFL,
|
||||
true
|
||||
)
|
||||
term.disableScrolling()
|
||||
term
|
||||
} catch {
|
||||
case _: Exception =>
|
||||
org.jline.terminal.TerminalBuilder
|
||||
.builder()
|
||||
.system(false)
|
||||
.paused(true)
|
||||
.jansi(true)
|
||||
.streams(Terminal.console.inputStream, Terminal.console.outputStream)
|
||||
.build()
|
||||
}
|
||||
} else {
|
||||
org.jline.terminal.TerminalBuilder
|
||||
.builder()
|
||||
.system(System.console != null)
|
||||
.paused(true)
|
||||
.jna(false)
|
||||
.jansi(true)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
private[sbt] def apply(term: Terminal): JTerminal = {
|
||||
new AbstractTerminal(term.name, "ansi", Charset.forName("UTF-8"), SignalHandler.SIG_DFL) {
|
||||
val closed = new AtomicBoolean(false)
|
||||
setOnClose { () =>
|
||||
if (closed.compareAndSet(false, true)) {
|
||||
// This is necessary to shutdown the non blocking input reader
|
||||
// so that it doesn't keep blocking
|
||||
term.inputStream match {
|
||||
case w: Terminal.WriteableInputStream => w.cancel()
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
parseInfoCmp()
|
||||
override val input: InputStream = new InputStream {
|
||||
override def read: Int = {
|
||||
val res = try term.inputStream.read
|
||||
catch { case _: InterruptedException => -2 }
|
||||
if (res == 4 && term.prompt.render().endsWith(term.prompt.mkPrompt()))
|
||||
throw new EOFException
|
||||
res
|
||||
}
|
||||
}
|
||||
override val output: OutputStream = new OutputStream {
|
||||
override def write(b: Int): Unit = write(Array[Byte](b.toByte))
|
||||
override def write(b: Array[Byte]): Unit = if (!closed.get) term.withPrintStream { ps =>
|
||||
term.prompt match {
|
||||
case a: Prompt.AskUser => a.write(b)
|
||||
case _ =>
|
||||
}
|
||||
ps.write(b)
|
||||
}
|
||||
override def write(b: Array[Byte], offset: Int, len: Int) =
|
||||
write(Arrays.copyOfRange(b, offset, offset + len))
|
||||
override def flush(): Unit = term.withPrintStream(_.flush())
|
||||
}
|
||||
|
||||
override val reader =
|
||||
NonBlocking.nonBlocking(term.name, input, Charset.defaultCharset())
|
||||
override val writer: PrintWriter = new PrintWriter(output, true)
|
||||
/*
|
||||
* For now assume that the terminal capabilities for client and server
|
||||
* are the same.
|
||||
*/
|
||||
override def getStringCapability(cap: Capability): String = {
|
||||
term.getStringCapability(cap.toString, jline3 = true)
|
||||
}
|
||||
override def getNumericCapability(cap: Capability): Integer = {
|
||||
term.getNumericCapability(cap.toString, jline3 = true)
|
||||
}
|
||||
override def getBooleanCapability(cap: Capability): Boolean = {
|
||||
term.getBooleanCapability(cap.toString, jline3 = true)
|
||||
}
|
||||
def getAttributes(): Attributes = attributesFromMap(term.getAttributes)
|
||||
def getSize(): Size = new Size(term.getWidth, term.getHeight)
|
||||
def setAttributes(a: Attributes): Unit = term.setAttributes(toMap(a))
|
||||
def setSize(size: Size): Unit = term.setSize(size.getColumns, size.getRows)
|
||||
|
||||
/**
|
||||
* Override enterRawMode because the default implementation modifies System.in
|
||||
* to be non-blocking which means it immediately returns -1 if there is no
|
||||
* data available, which is not desirable for us.
|
||||
*/
|
||||
override def enterRawMode(): Attributes = enterRawModeImpl(this)
|
||||
}
|
||||
}
|
||||
private def enterRawModeImpl(term: JTerminal): Attributes = {
|
||||
val prvAttr = term.getAttributes()
|
||||
val newAttr = new Attributes(prvAttr)
|
||||
newAttr.setLocalFlags(
|
||||
EnumSet
|
||||
.of(Attributes.LocalFlag.ICANON, Attributes.LocalFlag.ECHO, Attributes.LocalFlag.IEXTEN),
|
||||
false
|
||||
)
|
||||
newAttr.setInputFlags(
|
||||
EnumSet
|
||||
.of(Attributes.InputFlag.IXON, Attributes.InputFlag.ICRNL, Attributes.InputFlag.INLCR),
|
||||
false
|
||||
)
|
||||
term.setAttributes(newAttr)
|
||||
prvAttr
|
||||
}
|
||||
private[util] def enterRawMode(term: JTerminal): Map[String, String] =
|
||||
toMap(enterRawModeImpl(term))
|
||||
private[util] def toMap(jattributes: Attributes): Map[String, String] = {
|
||||
val result = new java.util.LinkedHashMap[String, String]
|
||||
result.put(
|
||||
"iflag",
|
||||
jattributes.getInputFlags.iterator.asScala.map(_.name.toLowerCase).mkString(" ")
|
||||
)
|
||||
result.put(
|
||||
"oflag",
|
||||
jattributes.getOutputFlags.iterator.asScala.map(_.name.toLowerCase).mkString(" ")
|
||||
)
|
||||
result.put(
|
||||
"cflag",
|
||||
jattributes.getControlFlags.iterator.asScala.map(_.name.toLowerCase).mkString(" ")
|
||||
)
|
||||
result.put(
|
||||
"lflag",
|
||||
jattributes.getLocalFlags.iterator.asScala.map(_.name.toLowerCase).mkString(" ")
|
||||
)
|
||||
result.put(
|
||||
"cchars",
|
||||
jattributes.getControlChars.entrySet.iterator.asScala
|
||||
.map { e =>
|
||||
s"${e.getKey.name.toLowerCase},${e.getValue}"
|
||||
}
|
||||
.mkString(" ")
|
||||
)
|
||||
result.asScala.toMap
|
||||
}
|
||||
private[this] val iflagMap: Map[String, Attributes.InputFlag] =
|
||||
Attributes.InputFlag.values.map(f => f.name.toLowerCase -> f).toMap
|
||||
private[this] val oflagMap: Map[String, Attributes.OutputFlag] =
|
||||
Attributes.OutputFlag.values.map(f => f.name.toLowerCase -> f).toMap
|
||||
private[this] val cflagMap: Map[String, Attributes.ControlFlag] =
|
||||
Attributes.ControlFlag.values.map(f => f.name.toLowerCase -> f).toMap
|
||||
private[this] val lflagMap: Map[String, Attributes.LocalFlag] =
|
||||
Attributes.LocalFlag.values.map(f => f.name.toLowerCase -> f).toMap
|
||||
private[this] val charMap: Map[String, Attributes.ControlChar] =
|
||||
Attributes.ControlChar.values().map(f => f.name.toLowerCase -> f).toMap
|
||||
private[util] def attributesFromMap(map: Map[String, String]): Attributes = {
|
||||
val attributes = new Attributes
|
||||
map.get("iflag").foreach { flags =>
|
||||
flags.split(" ").foreach(f => iflagMap.get(f).foreach(attributes.setInputFlag(_, true)))
|
||||
}
|
||||
map.get("oflag").foreach { flags =>
|
||||
flags.split(" ").foreach(f => oflagMap.get(f).foreach(attributes.setOutputFlag(_, true)))
|
||||
}
|
||||
map.get("cflag").foreach { flags =>
|
||||
flags.split(" ").foreach(f => cflagMap.get(f).foreach(attributes.setControlFlag(_, true)))
|
||||
}
|
||||
map.get("lflag").foreach { flags =>
|
||||
flags.split(" ").foreach(f => lflagMap.get(f).foreach(attributes.setLocalFlag(_, true)))
|
||||
}
|
||||
map.get("cchars").foreach { chars =>
|
||||
chars.split(" ").foreach { keyValue =>
|
||||
keyValue.split(",") match {
|
||||
case Array(k, v) =>
|
||||
Try(v.toInt).foreach(i => charMap.get(k).foreach(c => attributes.setControlChar(c, i)))
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
attributes
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@ import sbt.internal.util.ConsoleAppender.{
|
|||
ClearScreenAfterCursor,
|
||||
CursorLeft1000,
|
||||
DeleteLine,
|
||||
cursorLeft,
|
||||
cursorUp,
|
||||
}
|
||||
|
||||
|
|
@ -33,6 +32,10 @@ private[sbt] final class ProgressState(
|
|||
blankZone,
|
||||
new AtomicReference(new ArrayBuffer[Byte]),
|
||||
)
|
||||
def currentLine: Option[String] =
|
||||
new String(currentLineBytes.get.toArray, "UTF-8").linesIterator.toSeq.lastOption
|
||||
.map(EscHelpers.stripColorsAndMoves)
|
||||
.filter(_.nonEmpty)
|
||||
def reset(): Unit = {
|
||||
progressLines.set(Nil)
|
||||
padding.set(0)
|
||||
|
|
@ -44,8 +47,9 @@ private[sbt] final class ProgressState(
|
|||
currentLineBytes.set(new ArrayBuffer[Byte])
|
||||
}
|
||||
|
||||
private[util] def addBytes(terminal: Terminal, bytes: ArrayBuffer[Byte]): Unit = {
|
||||
val previous = currentLineBytes.get
|
||||
private[this] val lineSeparatorBytes: Array[Byte] = System.lineSeparator.getBytes("UTF-8")
|
||||
private[util] def addBytes(terminal: Terminal, bytes: Seq[Byte]): Unit = {
|
||||
val previous: ArrayBuffer[Byte] = currentLineBytes.get
|
||||
val padding = this.padding.get
|
||||
val prevLineCount = if (padding > 0) terminal.lineCount(new String(previous.toArray)) else 0
|
||||
previous ++= bytes
|
||||
|
|
@ -54,6 +58,16 @@ private[sbt] final class ProgressState(
|
|||
val diff = newLineCount - prevLineCount
|
||||
this.padding.set(math.max(padding - diff, 0))
|
||||
}
|
||||
val lines = new String(previous.toArray, "UTF-8")
|
||||
if (lines.contains(System.lineSeparator)) {
|
||||
currentLineBytes.set(new ArrayBuffer[Byte])
|
||||
if (!lines.endsWith(System.lineSeparator)) {
|
||||
lines
|
||||
.split(System.lineSeparator)
|
||||
.lastOption
|
||||
.foreach(currentLineBytes.get ++= _.getBytes("UTF-8"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private[util] def printPrompt(terminal: Terminal, printStream: PrintStream): Unit =
|
||||
|
|
@ -62,31 +76,50 @@ private[sbt] final class ProgressState(
|
|||
val pmpt = prefix.getBytes ++ terminal.prompt.render().getBytes
|
||||
pmpt.foreach(b => printStream.write(b & 0xFF))
|
||||
}
|
||||
private[util] def reprint(terminal: Terminal, printStream: PrintStream): Unit = {
|
||||
printPrompt(terminal, printStream)
|
||||
if (progressLines.get.nonEmpty) {
|
||||
val lines = printProgress(terminal, terminal.getLastLine.getOrElse(""))
|
||||
printStream.print(ClearScreenAfterCursor + lines)
|
||||
}
|
||||
private[util] def write(
|
||||
terminal: Terminal,
|
||||
bytes: Array[Byte],
|
||||
printStream: PrintStream,
|
||||
hasProgress: Boolean
|
||||
): Unit = {
|
||||
addBytes(terminal, bytes)
|
||||
if (hasProgress && terminal.prompt != Prompt.Loading) {
|
||||
terminal.prompt match {
|
||||
case a: Prompt.AskUser if a.render.nonEmpty =>
|
||||
printStream.print(System.lineSeparator + ClearScreenAfterCursor + CursorLeft1000)
|
||||
printStream.flush()
|
||||
case _ =>
|
||||
}
|
||||
printStream.write(bytes)
|
||||
printStream.write(ClearScreenAfterCursor.getBytes("UTF-8"))
|
||||
printStream.flush()
|
||||
if (bytes.endsWith(lineSeparatorBytes)) {
|
||||
if (progressLines.get.nonEmpty) {
|
||||
val lastLine = terminal.prompt match {
|
||||
case a: Prompt.AskUser => a.render()
|
||||
case _ => currentLine.getOrElse("")
|
||||
}
|
||||
val lines = printProgress(terminal, lastLine)
|
||||
printStream.print(ClearScreenAfterCursor + lines)
|
||||
}
|
||||
}
|
||||
printPrompt(terminal, printStream)
|
||||
} else printStream.write(bytes)
|
||||
}
|
||||
|
||||
private[util] def printProgress(
|
||||
terminal: Terminal,
|
||||
lastLine: String
|
||||
): String = {
|
||||
private[util] def printProgress(terminal: Terminal, lastLine: String): String = {
|
||||
val previousLines = progressLines.get
|
||||
if (previousLines.nonEmpty) {
|
||||
val currentLength = previousLines.foldLeft(0)(_ + terminal.lineCount(_))
|
||||
val (height, width) = terminal.getLineHeightAndWidth(lastLine)
|
||||
val left = cursorLeft(1000) // resets the position to the left
|
||||
val offset = width > 0
|
||||
val pad = math.max(padding.get - height, 0)
|
||||
val start = (if (offset) "\n" else "")
|
||||
val start = (if (offset) s"\n$CursorLeft1000" else "")
|
||||
val totalSize = currentLength + blankZone + pad
|
||||
val blank = left + s"\n$DeleteLine" * (totalSize - currentLength)
|
||||
val blank = CursorLeft1000 + s"\n$DeleteLine" * (totalSize - currentLength)
|
||||
val lines = previousLines.mkString(DeleteLine, s"\n$DeleteLine", s"\n$DeleteLine")
|
||||
val resetCursorUp = cursorUp(totalSize + (if (offset) 1 else 0))
|
||||
val resetCursor = resetCursorUp + left + lastLine
|
||||
val resetCursor = resetCursorUp + CursorLeft1000 + lastLine
|
||||
start + blank + lines + resetCursor
|
||||
} else {
|
||||
ClearScreenAfterCursor
|
||||
|
|
@ -108,6 +141,7 @@ private[sbt] object ProgressState {
|
|||
terminal: Terminal
|
||||
): Unit = {
|
||||
val state = terminal.progressState
|
||||
val isAskUser = terminal.prompt.isInstanceOf[Prompt.AskUser]
|
||||
val isRunning = terminal.prompt == Prompt.Running
|
||||
val isBatch = terminal.prompt == Prompt.Batch
|
||||
val isWatch = terminal.prompt == Prompt.Watch
|
||||
|
|
@ -115,31 +149,27 @@ private[sbt] object ProgressState {
|
|||
if (terminal.isSupershellEnabled) {
|
||||
if (!pe.skipIfActive.getOrElse(false) || (!isRunning && !isBatch)) {
|
||||
terminal.withPrintStream { ps =>
|
||||
val info =
|
||||
if ((isRunning || isBatch || noPrompt) && pe.channelName
|
||||
.fold(true)(_ == terminal.name)) {
|
||||
pe.items.map { item =>
|
||||
val elapsed = item.elapsedMicros / 1000000L
|
||||
s" | => ${item.name} ${elapsed}s"
|
||||
}
|
||||
} else {
|
||||
pe.command.toSeq.flatMap { cmd =>
|
||||
val tail = if (isWatch) Nil else "enter 'cancel' to stop evaluation" :: Nil
|
||||
s"sbt server is running '$cmd'" :: tail
|
||||
}
|
||||
val commandFromThisTerminal = pe.channelName.fold(true)(_ == terminal.name)
|
||||
val info = if ((isRunning || isBatch || noPrompt) && commandFromThisTerminal) {
|
||||
pe.items.map { item =>
|
||||
val elapsed = item.elapsedMicros / 1000000L
|
||||
s" | => ${item.name} ${elapsed}s"
|
||||
}
|
||||
} else {
|
||||
pe.command.toSeq.flatMap { cmd =>
|
||||
val tail = if (isWatch) Nil else "enter 'cancel' to stop evaluation" :: Nil
|
||||
s"sbt server is running '$cmd'" :: tail
|
||||
}
|
||||
}
|
||||
|
||||
val currentLength = info.foldLeft(0)(_ + terminal.lineCount(_))
|
||||
val previousLines = state.progressLines.getAndSet(info)
|
||||
val prevLength = previousLines.foldLeft(0)(_ + terminal.lineCount(_))
|
||||
val lastLine = terminal.prompt match {
|
||||
case Prompt.Running | Prompt.Batch => terminal.getLastLine.getOrElse("")
|
||||
case a => a.render()
|
||||
}
|
||||
val prevSize = prevLength + state.padding.get
|
||||
|
||||
val newPadding = math.max(0, prevSize - currentLength)
|
||||
state.padding.set(newPadding)
|
||||
val lastLine =
|
||||
if (isAskUser) terminal.prompt.render() else terminal.getLastLine.getOrElse("")
|
||||
state.padding.set(math.max(0, prevSize - currentLength))
|
||||
state.printPrompt(terminal, ps)
|
||||
ps.print(state.printProgress(terminal, lastLine))
|
||||
ps.flush()
|
||||
|
|
|
|||
|
|
@ -7,39 +7,29 @@
|
|||
|
||||
package sbt.internal.util
|
||||
|
||||
import java.io.OutputStream
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
private[sbt] sealed trait Prompt {
|
||||
def mkPrompt: () => String
|
||||
def render(): String
|
||||
def wrappedOutputStream(terminal: Terminal): OutputStream
|
||||
def reset(): Unit
|
||||
}
|
||||
|
||||
private[sbt] object Prompt {
|
||||
private[sbt] case class AskUser(override val mkPrompt: () => String) extends Prompt {
|
||||
private[this] val bytes = new LinkedBlockingQueue[Int]
|
||||
override def wrappedOutputStream(terminal: Terminal): OutputStream = new OutputStream {
|
||||
override def write(b: Int): Unit = {
|
||||
if (b == 10) bytes.clear()
|
||||
else bytes.put(b)
|
||||
terminal.withPrintStream { p =>
|
||||
p.write(b)
|
||||
p.flush()
|
||||
}
|
||||
}
|
||||
override def flush(): Unit = terminal.withPrintStream(_.flush())
|
||||
private[this] val bytes = new LinkedBlockingQueue[Byte]
|
||||
def write(b: Array[Byte]): Unit = b.foreach(bytes.put)
|
||||
override def render(): String = {
|
||||
val res = new String(bytes.asScala.toArray, "UTF-8")
|
||||
if (res.endsWith(System.lineSeparator)) "" else res
|
||||
}
|
||||
|
||||
override def render(): String =
|
||||
EscHelpers.stripMoves(new String(bytes.asScala.toArray.map(_.toByte)))
|
||||
override def reset(): Unit = bytes.clear()
|
||||
}
|
||||
private[sbt] trait NoPrompt extends Prompt {
|
||||
override val mkPrompt: () => String = () => ""
|
||||
override def render(): String = ""
|
||||
override def wrappedOutputStream(terminal: Terminal): OutputStream = terminal.outputStream
|
||||
override def reset(): Unit = {}
|
||||
}
|
||||
private[sbt] case object Running extends NoPrompt
|
||||
private[sbt] case object Batch extends NoPrompt
|
||||
|
|
|
|||
|
|
@ -7,19 +7,17 @@
|
|||
|
||||
package sbt.internal.util
|
||||
|
||||
import java.io.{ InputStream, OutputStream, PrintStream }
|
||||
import java.io.{ InputStream, InterruptedIOException, OutputStream, PrintStream }
|
||||
import java.nio.channels.ClosedChannelException
|
||||
import java.util.Locale
|
||||
import java.util.{ Arrays, Locale }
|
||||
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
|
||||
import java.util.concurrent.{ ConcurrentHashMap, Executors, LinkedBlockingQueue, TimeUnit }
|
||||
import java.util.concurrent.{ ArrayBlockingQueue, Executors, LinkedBlockingQueue, TimeUnit }
|
||||
|
||||
import jline.DefaultTerminal2
|
||||
import jline.console.ConsoleReader
|
||||
import sbt.internal.util.ConsoleAppender.{ ClearScreenAfterCursor, CursorLeft1000 }
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.util.Try
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
trait Terminal extends AutoCloseable {
|
||||
|
||||
|
|
@ -111,9 +109,12 @@ trait Terminal extends AutoCloseable {
|
|||
*/
|
||||
private[sbt] def getLastLine: Option[String]
|
||||
|
||||
private[sbt] def getBooleanCapability(capability: String): Boolean
|
||||
private[sbt] def getNumericCapability(capability: String): Int
|
||||
private[sbt] def getStringCapability(capability: String): String
|
||||
private[sbt] def getBooleanCapability(capability: String, jline3: Boolean): Boolean
|
||||
private[sbt] def getNumericCapability(capability: String, jline3: Boolean): Integer
|
||||
private[sbt] def getStringCapability(capability: String, jline3: Boolean): String
|
||||
private[sbt] def getAttributes: Map[String, String]
|
||||
private[sbt] def setAttributes(attributes: Map[String, String]): Unit
|
||||
private[sbt] def setSize(width: Int, height: Int): Unit
|
||||
|
||||
private[sbt] def name: String
|
||||
private[sbt] def withRawSystemIn[T](f: => T): T = f
|
||||
|
|
@ -142,7 +143,8 @@ trait Terminal extends AutoCloseable {
|
|||
val len = l.length
|
||||
if (width > 0 && len > 0) (len - 1 + width) / width else 0
|
||||
}
|
||||
lines.tail.foldLeft(lines.headOption.fold(0)(count))(_ + count(_))
|
||||
if (lines.nonEmpty) lines.tail.foldLeft(lines.headOption.fold(0)(count))(_ + count(_))
|
||||
else 0
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -188,13 +190,13 @@ object Terminal {
|
|||
override def enableInterruptCharacter(): Unit = {}
|
||||
override def getOutputEncoding: String = null
|
||||
override def getBooleanCapability(capability: String): Boolean = {
|
||||
term.getBooleanCapability(capability)
|
||||
term.getBooleanCapability(capability, jline3 = false)
|
||||
}
|
||||
override def getNumericCapability(capability: String): Integer = {
|
||||
term.getNumericCapability(capability)
|
||||
term.getNumericCapability(capability, jline3 = false)
|
||||
}
|
||||
override def getStringCapability(capability: String): String = {
|
||||
term.getStringCapability(capability)
|
||||
term.getStringCapability(capability, jline3 = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -253,18 +255,45 @@ object Terminal {
|
|||
*/
|
||||
private[sbt] def restore(): Unit = console.toJLine.restore()
|
||||
|
||||
private[this] val hasProgress: AtomicBoolean = new AtomicBoolean(false)
|
||||
|
||||
/**
|
||||
*
|
||||
* @param progress toggles whether or not the console terminal has progress
|
||||
* @param f the thunk to run
|
||||
* @tparam T the result type of the thunk
|
||||
* @return the result of the thunk
|
||||
*/
|
||||
private[sbt] def withStreams[T](f: => T): T =
|
||||
private[sbt] def withStreams[T](isServer: Boolean)(f: => T): T =
|
||||
if (System.getProperty("sbt.io.virtual", "true") == "true") {
|
||||
hasProgress.set(isServer)
|
||||
try withOut(withIn(f))
|
||||
finally {
|
||||
jline.TerminalFactory.reset()
|
||||
console.close()
|
||||
if (isServer) {
|
||||
console match {
|
||||
case c: ConsoleTerminal if !isWindows =>
|
||||
/*
|
||||
* Entering raw mode in this way causes the standard in InputStream
|
||||
* to become non-blocking. After we set it to non-blocking, we spin
|
||||
* up a thread that reads from the inputstream and the resets it
|
||||
* back to blocking mode. We can then close the console. We do
|
||||
* this on a background thread to avoid blocking sbt's exit.
|
||||
*/
|
||||
val prev = c.system.enterRawMode()
|
||||
val runnable: Runnable = () => {
|
||||
c.inputStream.read()
|
||||
c.system.setAttributes(prev)
|
||||
c.close()
|
||||
}
|
||||
val thread = new Thread(runnable, "sbt-console-background-close")
|
||||
thread.setDaemon(true)
|
||||
thread.start()
|
||||
case c => c.close()
|
||||
}
|
||||
} else {
|
||||
console.close()
|
||||
}
|
||||
}
|
||||
} else f
|
||||
|
||||
|
|
@ -281,10 +310,16 @@ object Terminal {
|
|||
override def isEchoEnabled: Boolean = t.isEchoEnabled
|
||||
override def isSuccessEnabled: Boolean = t.isSuccessEnabled
|
||||
override def isSupershellEnabled: Boolean = t.isSupershellEnabled
|
||||
override def getBooleanCapability(capability: String): Boolean =
|
||||
t.getBooleanCapability(capability)
|
||||
override def getNumericCapability(capability: String): Int = t.getNumericCapability(capability)
|
||||
override def getStringCapability(capability: String): String = t.getStringCapability(capability)
|
||||
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean =
|
||||
t.getBooleanCapability(capability, jline3)
|
||||
override def getNumericCapability(capability: String, jline3: Boolean): Integer =
|
||||
t.getNumericCapability(capability, jline3)
|
||||
override def getStringCapability(capability: String, jline3: Boolean): String =
|
||||
t.getStringCapability(capability, jline3)
|
||||
override private[sbt] def getAttributes: Map[String, String] = t.getAttributes
|
||||
override private[sbt] def setAttributes(attributes: Map[String, String]): Unit =
|
||||
t.setAttributes(attributes)
|
||||
override private[sbt] def setSize(width: Int, height: Int): Unit = t.setSize(width, height)
|
||||
override def withRawSystemIn[T](f: => T): T = t.withRawSystemIn(f)
|
||||
override def withCanonicalIn[T](f: => T): T = t.withCanonicalIn(f)
|
||||
override def printStream: PrintStream = t.printStream
|
||||
|
|
@ -322,18 +357,35 @@ object Terminal {
|
|||
}
|
||||
}
|
||||
|
||||
private[this] val originalOut = System.out
|
||||
val sepBytes = System.lineSeparator.getBytes("UTF-8")
|
||||
private class LinePrintStream(outputStream: OutputStream)
|
||||
extends PrintStream(outputStream, true) {
|
||||
override def println(s: String): Unit = synchronized {
|
||||
out.write(s.getBytes("UTF-8") ++ sepBytes)
|
||||
out.flush()
|
||||
}
|
||||
}
|
||||
private[this] val originalOut = new LinePrintStream(System.out)
|
||||
private[this] val originalIn = System.in
|
||||
private[sbt] class WriteableInputStream(in: InputStream, name: String)
|
||||
extends InputStream
|
||||
with AutoCloseable {
|
||||
final def write(bytes: Int*): Unit = bytes.foreach(i => buffer.put(i))
|
||||
final def write(bytes: Int*): Unit = waiting.synchronized {
|
||||
waiting.poll match {
|
||||
case null =>
|
||||
bytes.foreach(b => buffer.put(b))
|
||||
case w =>
|
||||
if (bytes.length > 1) bytes.tail.foreach(b => buffer.put(b))
|
||||
bytes.headOption.foreach(b => w.put(b))
|
||||
}
|
||||
}
|
||||
private[this] val executor =
|
||||
Executors.newSingleThreadExecutor(r => new Thread(r, s"sbt-$name-input-reader"))
|
||||
private[this] val buffer = new LinkedBlockingQueue[Integer]
|
||||
private[this] val closed = new AtomicBoolean(false)
|
||||
private[this] val resultQueue = new LinkedBlockingQueue[LinkedBlockingQueue[Int]]
|
||||
private[this] val waiting = ConcurrentHashMap.newKeySet[LinkedBlockingQueue[Int]]
|
||||
private[this] val readQueue = new LinkedBlockingQueue[Unit]
|
||||
private[this] val waiting = new ArrayBlockingQueue[LinkedBlockingQueue[Integer]](1)
|
||||
private[this] val readThread = new AtomicReference[Thread]
|
||||
/*
|
||||
* Starts a loop that waits for consumers of the InputStream to call read.
|
||||
* When read is called, we enqueue a `LinkedBlockingQueue[Int]` to which
|
||||
|
|
@ -354,11 +406,14 @@ object Terminal {
|
|||
*/
|
||||
private[this] val runnable: Runnable = () => {
|
||||
@tailrec def impl(): Unit = {
|
||||
val result = resultQueue.take
|
||||
val _ = readQueue.take
|
||||
val b = in.read
|
||||
// The downstream consumer may have been interrupted. Buffer the result
|
||||
// when that hapens.
|
||||
if (waiting.contains(result)) result.put(b) else buffer.put(b)
|
||||
waiting.poll match {
|
||||
case null => buffer.put(b)
|
||||
case q => q.put(b)
|
||||
}
|
||||
if (b != -1 && !Thread.interrupted()) impl()
|
||||
else closed.set(true)
|
||||
}
|
||||
|
|
@ -370,21 +425,28 @@ object Terminal {
|
|||
if (closed.get) -1
|
||||
else
|
||||
synchronized {
|
||||
buffer.poll match {
|
||||
readThread.set(Thread.currentThread)
|
||||
try buffer.poll match {
|
||||
case null =>
|
||||
val result = new LinkedBlockingQueue[Int]
|
||||
waiting.add(result)
|
||||
resultQueue.offer(result)
|
||||
try result.take
|
||||
val result = new LinkedBlockingQueue[Integer]
|
||||
waiting.synchronized(waiting.put(result))
|
||||
readQueue.put(())
|
||||
try result.take.toInt
|
||||
catch {
|
||||
case e: InterruptedException =>
|
||||
waiting.remove(result)
|
||||
throw e
|
||||
-1
|
||||
}
|
||||
case b if b == -1 => throw new ClosedChannelException
|
||||
case b => b
|
||||
}
|
||||
case b => b.toInt
|
||||
} finally readThread.set(null)
|
||||
}
|
||||
def cancel(): Unit = waiting.synchronized {
|
||||
Option(readThread.getAndSet(null)).foreach(_.interrupt())
|
||||
waiting.forEach(_.put(-2))
|
||||
waiting.clear()
|
||||
readQueue.clear()
|
||||
}
|
||||
|
||||
override def available(): Int = {
|
||||
buffer.size
|
||||
|
|
@ -524,7 +586,7 @@ object Terminal {
|
|||
}
|
||||
override def flush(): Unit = os.flush()
|
||||
}
|
||||
private[this] val proxyPrintStream = new PrintStream(proxyOutputStream, true) {
|
||||
private[this] val proxyPrintStream = new LinePrintStream(proxyOutputStream) {
|
||||
override def toString: String = s"proxyPrintStream($proxyOutputStream)"
|
||||
}
|
||||
private[this] lazy val isWindows =
|
||||
|
|
@ -592,9 +654,21 @@ object Terminal {
|
|||
case t: jline.Terminal2 => t
|
||||
case _ => new DefaultTerminal2(terminal)
|
||||
}
|
||||
override def init(): Unit = if (alive) terminal.init()
|
||||
override def restore(): Unit = if (alive) terminal.restore()
|
||||
override def reset(): Unit = if (alive) terminal.reset()
|
||||
override def init(): Unit =
|
||||
if (alive)
|
||||
try terminal.init()
|
||||
catch {
|
||||
case _: InterruptedException =>
|
||||
}
|
||||
override def restore(): Unit =
|
||||
if (alive)
|
||||
try terminal.restore()
|
||||
catch {
|
||||
case _: InterruptedException =>
|
||||
}
|
||||
override def reset(): Unit =
|
||||
try terminal.reset()
|
||||
catch { case _: InterruptedException => }
|
||||
override def isSupported: Boolean = terminal.isSupported
|
||||
override def getWidth: Int = props.map(_.width).getOrElse(terminal.getWidth)
|
||||
override def getHeight: Int = props.map(_.height).getOrElse(terminal.getHeight)
|
||||
|
|
@ -650,7 +724,7 @@ object Terminal {
|
|||
fixTerminalProperty()
|
||||
|
||||
private[sbt] def createReader(term: Terminal, prompt: Prompt): ConsoleReader = {
|
||||
new ConsoleReader(term.inputStream, prompt.wrappedOutputStream(term), term.toJLine) {
|
||||
new ConsoleReader(term.inputStream, term.outputStream, term.toJLine) {
|
||||
override def readLine(prompt: String, mask: Character): String =
|
||||
term.withRawSystemIn(super.readLine(prompt, mask))
|
||||
override def readLine(prompt: String): String = term.withRawSystemIn(super.readLine(prompt))
|
||||
|
|
@ -662,6 +736,9 @@ object Terminal {
|
|||
case term => term
|
||||
}
|
||||
|
||||
private val capabilityMap =
|
||||
org.jline.utils.InfoCmp.Capability.values().map(c => c.toString -> c).toMap
|
||||
|
||||
@deprecated("For compatibility only", "1.4.0")
|
||||
private[sbt] def deprecatedTeminal: jline.Terminal = console.toJLine
|
||||
private class ConsoleTerminal(
|
||||
|
|
@ -669,28 +746,36 @@ object Terminal {
|
|||
in: InputStream,
|
||||
out: OutputStream
|
||||
) extends TerminalImpl(in, out, "console0") {
|
||||
private[util] lazy val system = JLine3.system
|
||||
private[this] def isCI = sys.env.contains("BUILD_NUMBER") || sys.env.contains("CI")
|
||||
override def getWidth: Int = term.getWidth
|
||||
override def getHeight: Int = term.getHeight
|
||||
override def getWidth: Int = system.getSize.getColumns
|
||||
override def getHeight: Int = system.getSize.getRows
|
||||
override def isAnsiSupported: Boolean = term.isAnsiSupported && !isCI
|
||||
override def isEchoEnabled: Boolean = term.isEchoEnabled
|
||||
override def isEchoEnabled: Boolean = system.echo()
|
||||
override def isSuccessEnabled: Boolean = true
|
||||
override def getBooleanCapability(capability: String): Boolean =
|
||||
term.getBooleanCapability(capability)
|
||||
override def getNumericCapability(capability: String): Int =
|
||||
term.getNumericCapability(capability)
|
||||
override def getStringCapability(capability: String): String =
|
||||
term.getStringCapability(capability)
|
||||
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean =
|
||||
if (jline3) capabilityMap.get(capability).fold(false)(system.getBooleanCapability)
|
||||
else term.getBooleanCapability(capability)
|
||||
override def getNumericCapability(capability: String, jline3: Boolean): Integer =
|
||||
if (jline3) capabilityMap.get(capability).fold(null: Integer)(system.getNumericCapability)
|
||||
else term.getNumericCapability(capability)
|
||||
override def getStringCapability(capability: String, jline3: Boolean): String =
|
||||
if (jline3) capabilityMap.get(capability).fold(null: String)(system.getStringCapability)
|
||||
else term.getStringCapability(capability)
|
||||
override private[sbt] def restore(): Unit = term.restore()
|
||||
|
||||
override private[sbt] def getAttributes: Map[String, String] =
|
||||
JLine3.toMap(system.getAttributes)
|
||||
override private[sbt] def setAttributes(attributes: Map[String, String]): Unit =
|
||||
system.setAttributes(JLine3.attributesFromMap(attributes))
|
||||
override private[sbt] def setSize(width: Int, height: Int): Unit =
|
||||
system.setSize(new org.jline.terminal.Size(width, height))
|
||||
|
||||
override def withRawSystemIn[T](f: => T): T = term.synchronized {
|
||||
try {
|
||||
term.init()
|
||||
term.setEchoEnabled(false)
|
||||
f
|
||||
} finally {
|
||||
term.restore()
|
||||
term.setEchoEnabled(true)
|
||||
val prev = JLine3.enterRawMode(system)
|
||||
try f
|
||||
catch { case _: InterruptedIOException => throw new InterruptedException } finally {
|
||||
setAttributes(prev)
|
||||
}
|
||||
}
|
||||
override def isColorEnabled: Boolean =
|
||||
|
|
@ -705,29 +790,33 @@ object Terminal {
|
|||
case "true" => true
|
||||
case _ => false
|
||||
})
|
||||
override def close(): Unit = {
|
||||
try system.close()
|
||||
catch { case NonFatal(_) => }
|
||||
super.close()
|
||||
}
|
||||
}
|
||||
private[sbt] abstract class TerminalImpl private[sbt] (
|
||||
val in: InputStream,
|
||||
val out: OutputStream,
|
||||
override private[sbt] val name: String
|
||||
) extends Terminal {
|
||||
private[this] val directWrite = new AtomicBoolean(false)
|
||||
private[this] val currentLine = new AtomicReference(new ArrayBuffer[Byte])
|
||||
private[this] val lineBuffer = new LinkedBlockingQueue[Byte]
|
||||
private[this] val flushQueue = new LinkedBlockingQueue[Seq[Byte]]
|
||||
private[this] val writeLock = new AnyRef
|
||||
private[this] val writeableInputStream = in match {
|
||||
case w: WriteableInputStream => w
|
||||
case _ => new WriteableInputStream(in, name)
|
||||
}
|
||||
def throwIfClosed[R](f: => R): R = if (isStopped.get) throw new ClosedChannelException else f
|
||||
override def getLastLine: Option[String] = progressState.currentLine
|
||||
|
||||
private val combinedOutputStream = new OutputStream {
|
||||
override def write(b: Int): Unit = {
|
||||
Option(bootOutputStreamHolder.get).foreach(_.write(b))
|
||||
out.write(b)
|
||||
}
|
||||
override def write(b: Array[Byte]): Unit = write(b, 0, b.length)
|
||||
override def write(b: Array[Byte]): Unit = {
|
||||
write(b, 0, b.length)
|
||||
}
|
||||
override def write(b: Array[Byte], offset: Int, len: Int): Unit = {
|
||||
Option(bootOutputStreamHolder.get).foreach(_.write(b, offset, len))
|
||||
out.write(b, offset, len)
|
||||
|
|
@ -740,54 +829,19 @@ object Terminal {
|
|||
|
||||
override val outputStream = new OutputStream {
|
||||
override def write(b: Int): Unit = throwIfClosed {
|
||||
writeLock.synchronized {
|
||||
if (b == Int.MinValue) currentLine.set(new ArrayBuffer[Byte])
|
||||
else doWrite(Vector((b & 0xFF).toByte))
|
||||
if (b == 10) combinedOutputStream.flush()
|
||||
}
|
||||
write(Array((b & 0xFF).toByte))
|
||||
}
|
||||
override def write(b: Array[Byte]): Unit = throwIfClosed(write(b, 0, b.length))
|
||||
override def write(b: Array[Byte], off: Int, len: Int): Unit = {
|
||||
throwIfClosed {
|
||||
writeLock.synchronized {
|
||||
val lo = math.max(0, off)
|
||||
val hi = math.min(math.max(off + len, 0), b.length)
|
||||
doWrite(b.slice(off, off + len).toSeq)
|
||||
}
|
||||
}
|
||||
override def write(b: Array[Byte]): Unit = throwIfClosed {
|
||||
writeLock.synchronized(doWrite(b))
|
||||
}
|
||||
override def write(b: Array[Byte], offset: Int, length: Int): Unit = throwIfClosed {
|
||||
write(Arrays.copyOfRange(b, offset, offset + length))
|
||||
}
|
||||
override def flush(): Unit = combinedOutputStream.flush()
|
||||
private[this] val clear = s"$CursorLeft1000$ClearScreenAfterCursor"
|
||||
private def doWrite(bytes: Seq[Byte]): Unit = {
|
||||
def doWrite(b: Byte): Unit = out.write(b & 0xFF)
|
||||
val remaining = bytes.foldLeft(new ArrayBuffer[Byte]) { (buf, i) =>
|
||||
if (i == 10) {
|
||||
progressState.addBytes(TerminalImpl.this, buf)
|
||||
progressState.clearBytes()
|
||||
val cl = currentLine.get
|
||||
if (buf.nonEmpty && isAnsiSupported && cl.isEmpty) clear.getBytes.foreach(doWrite)
|
||||
combinedOutputStream.write(buf.toArray)
|
||||
combinedOutputStream.write(10)
|
||||
currentLine.get match {
|
||||
case s if s.nonEmpty => currentLine.set(new ArrayBuffer[Byte])
|
||||
case _ =>
|
||||
}
|
||||
if (prompt != Prompt.Loading) progressState.reprint(TerminalImpl.this, rawPrintStream)
|
||||
new ArrayBuffer[Byte]
|
||||
} else buf += i
|
||||
}
|
||||
if (remaining.nonEmpty) {
|
||||
val cl = currentLine.get
|
||||
if (isAnsiSupported && cl.isEmpty) {
|
||||
clear.getBytes.foreach(doWrite)
|
||||
}
|
||||
cl ++= remaining
|
||||
combinedOutputStream.write(remaining.toArray)
|
||||
}
|
||||
combinedOutputStream.flush()
|
||||
}
|
||||
}
|
||||
override private[sbt] val printStream: PrintStream = new PrintStream(outputStream, true)
|
||||
private def doWrite(bytes: Array[Byte]): Unit =
|
||||
progressState.write(TerminalImpl.this, bytes, rawPrintStream, hasProgress.get)
|
||||
override private[sbt] val printStream: PrintStream = new LinePrintStream(outputStream)
|
||||
override def inputStream: InputStream = writeableInputStream
|
||||
|
||||
private[sbt] def write(bytes: Int*): Unit = writeableInputStream.write(bytes: _*)
|
||||
|
|
@ -801,17 +855,7 @@ object Terminal {
|
|||
case _ => (0, 0)
|
||||
}
|
||||
|
||||
override def getLastLine: Option[String] = currentLine.get match {
|
||||
case bytes if bytes.isEmpty => None
|
||||
case bytes =>
|
||||
// TODO there are ghost characters when the user deletes prompt characters
|
||||
// when they are given the cancellation option
|
||||
Some(new String(bytes.toArray).replaceAllLiterally(ClearScreenAfterCursor, ""))
|
||||
}
|
||||
|
||||
private[this] val rawPrintStream: PrintStream = new PrintStream(combinedOutputStream, true) {
|
||||
override def close(): Unit = {}
|
||||
}
|
||||
private[this] val rawPrintStream: PrintStream = new LinePrintStream(combinedOutputStream)
|
||||
override def withPrintStream[T](f: PrintStream => T): T =
|
||||
writeLock.synchronized(f(rawPrintStream))
|
||||
|
||||
|
|
@ -821,12 +865,12 @@ object Terminal {
|
|||
}
|
||||
private[sbt] val NullTerminal = new Terminal {
|
||||
override def close(): Unit = {}
|
||||
override def getBooleanCapability(capability: String): Boolean = false
|
||||
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean = false
|
||||
override def getHeight: Int = 0
|
||||
override def getLastLine: Option[String] = None
|
||||
override def getLineHeightAndWidth(line: String): (Int, Int) = (0, 0)
|
||||
override def getNumericCapability(capability: String): Int = -1
|
||||
override def getStringCapability(capability: String): String = null
|
||||
override def getNumericCapability(capability: String, jline3: Boolean): Integer = null
|
||||
override def getStringCapability(capability: String, jline3: Boolean): String = null
|
||||
override def getWidth: Int = 0
|
||||
override def inputStream: java.io.InputStream = () => {
|
||||
try this.synchronized(this.wait)
|
||||
|
|
@ -839,6 +883,9 @@ object Terminal {
|
|||
override def isSuccessEnabled: Boolean = false
|
||||
override def isSupershellEnabled: Boolean = false
|
||||
override def outputStream: java.io.OutputStream = _ => {}
|
||||
override private[sbt] def getAttributes: Map[String, String] = Map.empty
|
||||
override private[sbt] def setAttributes(attributes: Map[String, String]): Unit = {}
|
||||
override private[sbt] def setSize(width: Int, height: Int): Unit = {}
|
||||
override private[sbt] def name: String = "NullTerminal"
|
||||
override private[sbt] val printStream: java.io.PrintStream =
|
||||
new PrintStream(outputStream, false)
|
||||
|
|
|
|||
|
|
@ -63,4 +63,11 @@ class CleanStringSpec extends FlatSpec {
|
|||
val colorArrow = new String(Array[Byte](27, 91, 51, 54, 109, 62))
|
||||
assert(EscHelpers.stripMoves(original) == "foo" + colorArrow + " " + scala.Console.RESET)
|
||||
}
|
||||
it should "remove unusual escape characters" in {
|
||||
val original = new String(
|
||||
Array[Byte](27, 91, 63, 49, 108, 27, 62, 27, 91, 63, 49, 48, 48, 48, 108, 27, 91, 63, 50, 48,
|
||||
48, 52, 108)
|
||||
)
|
||||
assert(EscHelpers.stripColorsAndMoves(original).isEmpty)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ package client
|
|||
import java.io.{ File, IOException, InputStream, PrintStream }
|
||||
import java.lang.ProcessBuilder.Redirect
|
||||
import java.net.Socket
|
||||
import java.nio.channels.ClosedChannelException
|
||||
import java.nio.file.Files
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
|
||||
|
|
@ -43,10 +42,14 @@ import Serialization.{
|
|||
promptChannel,
|
||||
systemIn,
|
||||
systemOut,
|
||||
systemOutFlush,
|
||||
terminalCapabilities,
|
||||
terminalCapabilitiesResponse,
|
||||
terminalPropertiesQuery,
|
||||
terminalPropertiesResponse
|
||||
terminalPropertiesResponse,
|
||||
getTerminalAttributes,
|
||||
setTerminalAttributes,
|
||||
setTerminalSize,
|
||||
}
|
||||
import NetworkClient.Arguments
|
||||
|
||||
|
|
@ -199,7 +202,6 @@ class NetworkClient(
|
|||
case _ => (false, None)
|
||||
}
|
||||
if (rebootCommands.nonEmpty) {
|
||||
if (Terminal.console.getLastLine.isDefined) Terminal.console.printStream.println()
|
||||
rebooting.set(true)
|
||||
attached.set(false)
|
||||
connectionHolder.getAndSet(null) match {
|
||||
|
|
@ -212,7 +214,7 @@ class NetworkClient(
|
|||
rebooting.set(false)
|
||||
rebootCommands match {
|
||||
case Some((execId, cmd)) if execId.nonEmpty =>
|
||||
if (batchMode.get && !pendingResults.contains(execId) && cmd.isEmpty) {
|
||||
if (batchMode.get && !pendingResults.containsKey(execId) && cmd.nonEmpty) {
|
||||
console.appendLog(
|
||||
Level.Error,
|
||||
s"received request to re-run unknown command '$cmd' after reboot"
|
||||
|
|
@ -230,8 +232,6 @@ class NetworkClient(
|
|||
} else {
|
||||
if (!rebooting.get() && running.compareAndSet(true, false) && log) {
|
||||
if (!arguments.commandArguments.contains(Shutdown)) {
|
||||
if (Terminal.console.getLastLine.isDefined)
|
||||
Terminal.console.printStream.println()
|
||||
console.appendLog(Level.Error, "sbt server disconnected")
|
||||
exitClean.set(false)
|
||||
}
|
||||
|
|
@ -306,7 +306,6 @@ class NetworkClient(
|
|||
Some(process)
|
||||
case _ =>
|
||||
if (log) {
|
||||
if (Terminal.console.getLastLine.isDefined) Terminal.console.printStream.println()
|
||||
console.appendLog(Level.Info, "sbt server is booting up")
|
||||
}
|
||||
None
|
||||
|
|
@ -522,17 +521,15 @@ class NetworkClient(
|
|||
}
|
||||
} else Vector()
|
||||
case (`systemOut`, Some(json)) =>
|
||||
Converter.fromJson[Seq[Byte]](json) match {
|
||||
case Success(params) =>
|
||||
if (params.nonEmpty) {
|
||||
if (attached.get) {
|
||||
printStream.write(params.toArray)
|
||||
printStream.flush()
|
||||
}
|
||||
}
|
||||
case Failure(_) =>
|
||||
Converter.fromJson[Array[Byte]](json) match {
|
||||
case Success(bytes) if bytes.nonEmpty && attached.get =>
|
||||
synchronized(printStream.write(bytes))
|
||||
case _ =>
|
||||
}
|
||||
Vector.empty
|
||||
case (`systemOutFlush`, _) =>
|
||||
synchronized(printStream.flush())
|
||||
Vector.empty
|
||||
case (`promptChannel`, _) =>
|
||||
batchMode.set(false)
|
||||
Vector.empty
|
||||
|
|
@ -589,16 +586,23 @@ class NetworkClient(
|
|||
}
|
||||
|
||||
def onRequest(msg: JsonRpcRequestMessage): Unit = {
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
(msg.method, msg.params) match {
|
||||
case (`terminalCapabilities`, Some(json)) =>
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
Converter.fromJson[TerminalCapabilitiesQuery](json) match {
|
||||
case Success(terminalCapabilitiesQuery) =>
|
||||
val jline3 = terminalCapabilitiesQuery.jline3
|
||||
val response = TerminalCapabilitiesResponse(
|
||||
terminalCapabilitiesQuery.boolean.map(Terminal.console.getBooleanCapability),
|
||||
terminalCapabilitiesQuery.numeric.map(Terminal.console.getNumericCapability),
|
||||
terminalCapabilitiesQuery.boolean
|
||||
.map(Terminal.console.getBooleanCapability(_, jline3)),
|
||||
terminalCapabilitiesQuery.numeric
|
||||
.map(
|
||||
c => Option(Terminal.console.getNumericCapability(c, jline3)).fold(-1)(_.toInt)
|
||||
),
|
||||
terminalCapabilitiesQuery.string
|
||||
.map(s => Option(Terminal.console.getStringCapability(s)).getOrElse("null")),
|
||||
.map(
|
||||
s => Option(Terminal.console.getStringCapability(s, jline3)).getOrElse("null")
|
||||
),
|
||||
)
|
||||
sendCommandResponse(
|
||||
terminalCapabilitiesResponse,
|
||||
|
|
@ -617,6 +621,37 @@ class NetworkClient(
|
|||
isEchoEnabled = Terminal.console.isEchoEnabled
|
||||
)
|
||||
sendCommandResponse(terminalPropertiesResponse, response, msg.id)
|
||||
case (`setTerminalAttributes`, Some(json)) =>
|
||||
Converter.fromJson[TerminalSetAttributesCommand](json) match {
|
||||
case Success(attributes) =>
|
||||
val attrs = Map(
|
||||
"iflag" -> attributes.iflag,
|
||||
"oflag" -> attributes.oflag,
|
||||
"cflag" -> attributes.cflag,
|
||||
"lflag" -> attributes.lflag,
|
||||
"cchars" -> attributes.cchars,
|
||||
)
|
||||
Terminal.console.setAttributes(attrs)
|
||||
sendCommandResponse("", TerminalSetAttributesResponse(), msg.id)
|
||||
case Failure(_) =>
|
||||
}
|
||||
case (`getTerminalAttributes`, _) =>
|
||||
val attrs = Terminal.console.getAttributes
|
||||
val response = TerminalAttributesResponse(
|
||||
iflag = attrs.getOrElse("iflag", ""),
|
||||
oflag = attrs.getOrElse("oflag", ""),
|
||||
cflag = attrs.getOrElse("cflag", ""),
|
||||
lflag = attrs.getOrElse("lflag", ""),
|
||||
cchars = attrs.getOrElse("cchars", ""),
|
||||
)
|
||||
sendCommandResponse("", response, msg.id)
|
||||
case (`setTerminalSize`, Some(json)) =>
|
||||
Converter.fromJson[TerminalSetSizeCommand](json) match {
|
||||
case Success(size) =>
|
||||
Terminal.console.setSize(size.width, size.height)
|
||||
sendCommandResponse("", TerminalSetSizeResponse(), msg.id)
|
||||
case Failure(_) =>
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
|
@ -851,7 +886,7 @@ class NetworkClient(
|
|||
}
|
||||
}
|
||||
try Terminal.console.withRawSystemIn(read())
|
||||
catch { case _: InterruptedException | _: ClosedChannelException => stopped.set(true) }
|
||||
catch { case NonFatal(_) => stopped.set(true) }
|
||||
}
|
||||
|
||||
def drain(): Unit = inLock.synchronized {
|
||||
|
|
@ -897,20 +932,18 @@ object NetworkClient {
|
|||
override def success(msg: String): Unit = appender.success(msg)
|
||||
}
|
||||
}
|
||||
private def simpleConsoleInterface(printStream: PrintStream): ConsoleInterface =
|
||||
private def simpleConsoleInterface(doPrintln: String => Unit): ConsoleInterface =
|
||||
new ConsoleInterface {
|
||||
import scala.Console.{ GREEN, RED, RESET, YELLOW }
|
||||
override def appendLog(level: Level.Value, message: => String): Unit = {
|
||||
override def appendLog(level: Level.Value, message: => String): Unit = synchronized {
|
||||
val prefix = level match {
|
||||
case Level.Error => s"[$RED$level$RESET]"
|
||||
case Level.Warn => s"[$YELLOW$level$RESET]"
|
||||
case _ => s"[$RESET$level$RESET]"
|
||||
}
|
||||
message.split("\n").foreach { line =>
|
||||
if (!line.trim.isEmpty) printStream.println(s"$prefix $line")
|
||||
}
|
||||
message.linesIterator.foreach(line => doPrintln(s"$prefix $line"))
|
||||
}
|
||||
override def success(msg: String): Unit = printStream.println(s"[${GREEN}success$RESET] $msg")
|
||||
override def success(msg: String): Unit = doPrintln(s"[${GREEN}success$RESET] $msg")
|
||||
}
|
||||
private[client] class Arguments(
|
||||
val baseDirectory: File,
|
||||
|
|
@ -961,8 +994,29 @@ object NetworkClient {
|
|||
baseDirectory: File,
|
||||
args: Array[String],
|
||||
inputStream: InputStream,
|
||||
errorStream: PrintStream,
|
||||
printStream: PrintStream,
|
||||
errorStream: PrintStream,
|
||||
useJNI: Boolean
|
||||
): Int = {
|
||||
val client =
|
||||
simpleClient(
|
||||
NetworkClient.parseArgs(args).withBaseDirectory(baseDirectory),
|
||||
inputStream,
|
||||
printStream,
|
||||
errorStream,
|
||||
useJNI,
|
||||
)
|
||||
try {
|
||||
if (client.connect(log = true, promptCompleteUsers = false)) client.run()
|
||||
else 1
|
||||
} catch { case _: Exception => 1 } finally client.close()
|
||||
}
|
||||
def client(
|
||||
baseDirectory: File,
|
||||
args: Array[String],
|
||||
inputStream: InputStream,
|
||||
errorStream: PrintStream,
|
||||
terminal: Terminal,
|
||||
useJNI: Boolean
|
||||
): Int = {
|
||||
val client =
|
||||
|
|
@ -970,8 +1024,8 @@ object NetworkClient {
|
|||
NetworkClient.parseArgs(args).withBaseDirectory(baseDirectory),
|
||||
inputStream,
|
||||
errorStream,
|
||||
printStream,
|
||||
useJNI,
|
||||
terminal
|
||||
)
|
||||
try {
|
||||
if (client.connect(log = true, promptCompleteUsers = false)) client.run()
|
||||
|
|
@ -982,17 +1036,27 @@ object NetworkClient {
|
|||
arguments: Arguments,
|
||||
inputStream: InputStream,
|
||||
errorStream: PrintStream,
|
||||
printStream: PrintStream,
|
||||
useJNI: Boolean,
|
||||
): NetworkClient =
|
||||
new NetworkClient(
|
||||
arguments,
|
||||
NetworkClient.simpleConsoleInterface(printStream),
|
||||
inputStream,
|
||||
errorStream,
|
||||
printStream,
|
||||
useJNI,
|
||||
)
|
||||
terminal: Terminal
|
||||
): NetworkClient = {
|
||||
val doPrint: String => Unit = line => {
|
||||
if (terminal.getLastLine.isDefined) terminal.printStream.println()
|
||||
terminal.printStream.println(line)
|
||||
}
|
||||
val interface = NetworkClient.simpleConsoleInterface(doPrint)
|
||||
val printStream = terminal.printStream
|
||||
new NetworkClient(arguments, interface, inputStream, errorStream, printStream, useJNI)
|
||||
}
|
||||
private def simpleClient(
|
||||
arguments: Arguments,
|
||||
inputStream: InputStream,
|
||||
printStream: PrintStream,
|
||||
errorStream: PrintStream,
|
||||
useJNI: Boolean,
|
||||
): NetworkClient = {
|
||||
val interface = NetworkClient.simpleConsoleInterface(printStream.println)
|
||||
new NetworkClient(arguments, interface, inputStream, errorStream, printStream, useJNI)
|
||||
}
|
||||
def main(args: Array[String]): Unit = {
|
||||
val (jnaArg, restOfArgs) = args.partition(_ == "--jna")
|
||||
val useJNI = jnaArg.isEmpty
|
||||
|
|
@ -1005,8 +1069,9 @@ object NetworkClient {
|
|||
System.out.flush()
|
||||
})
|
||||
Runtime.getRuntime.addShutdownHook(hook)
|
||||
System.exit(Terminal.withStreams {
|
||||
try client(base, restOfArgs, System.in, System.err, System.out, useJNI)
|
||||
System.exit(Terminal.withStreams(false) {
|
||||
val term = Terminal.console
|
||||
try client(base, restOfArgs, term.inputStream, System.err, term, useJNI)
|
||||
finally {
|
||||
Runtime.getRuntime.removeShutdownHook(hook)
|
||||
hook.run()
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@ import java.io.File
|
|||
import java.nio.channels.ClosedChannelException
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
import jline.console.history.PersistentHistory
|
||||
//import jline.console.history.PersistentHistory
|
||||
import sbt.BasicCommandStrings.{ Cancel, TerminateAction, Shutdown }
|
||||
import sbt.BasicKeys.{ historyPath, terminalShellPrompt }
|
||||
import sbt.State
|
||||
import sbt.internal.CommandChannel
|
||||
import sbt.internal.util.ConsoleAppender.{ ClearPromptLine, ClearScreenAfterCursor, DeleteLine }
|
||||
import sbt.internal.util._
|
||||
import sbt.internal.util.complete.{ JLineCompletion, Parser }
|
||||
import sbt.internal.util.complete.{ Parser }
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
|
@ -47,44 +47,31 @@ private[sbt] object UITask {
|
|||
def terminalReader(parser: Parser[_])(
|
||||
terminal: Terminal,
|
||||
state: State
|
||||
): Reader = {
|
||||
val lineReader = LineReader.createReader(history(state), terminal, terminal.prompt)
|
||||
JLineCompletion.installCustomCompletor(lineReader, parser)
|
||||
() => {
|
||||
): Reader = { () =>
|
||||
try {
|
||||
val clear = terminal.ansi(ClearPromptLine, "")
|
||||
try {
|
||||
@tailrec def impl(): Either[String, String] = {
|
||||
lineReader.readLine(clear + terminal.prompt.mkPrompt()) match {
|
||||
case null if terminal == Terminal.console && System.console == null =>
|
||||
// No stdin is attached to the process so just ignore the result and
|
||||
// block until the thread is interrupted.
|
||||
this.synchronized(this.wait())
|
||||
Right("") // should be unreachable
|
||||
// JLine returns null on ctrl+d when there is no other input. This interprets
|
||||
// ctrl+d with no imput as an exit
|
||||
case null => Left(TerminateAction)
|
||||
case s: String =>
|
||||
lineReader.getHistory match {
|
||||
case p: PersistentHistory =>
|
||||
p.add(s)
|
||||
p.flush()
|
||||
case _ =>
|
||||
}
|
||||
s match {
|
||||
case "" => impl()
|
||||
case cmd @ (`Shutdown` | `TerminateAction` | `Cancel`) => Left(cmd)
|
||||
case cmd =>
|
||||
if (terminal.prompt != Prompt.Batch) terminal.setPrompt(Prompt.Running)
|
||||
terminal.printStream.write(Int.MinValue)
|
||||
Right(cmd)
|
||||
}
|
||||
}
|
||||
@tailrec def impl(): Either[String, String] = {
|
||||
val reader = LineReader.createReader(history(state), parser, terminal, terminal.prompt)
|
||||
(try reader.readLine(clear + terminal.prompt.mkPrompt())
|
||||
finally reader.close) match {
|
||||
case None if terminal == Terminal.console && System.console == null =>
|
||||
// No stdin is attached to the process so just ignore the result and
|
||||
// block until the thread is interrupted.
|
||||
this.synchronized(this.wait())
|
||||
Right("") // should be unreachable
|
||||
// JLine returns null on ctrl+d when there is no other input. This interprets
|
||||
// ctrl+d with no imput as an exit
|
||||
case None => Left(TerminateAction)
|
||||
case Some(s: String) =>
|
||||
s.trim() match {
|
||||
case "" => impl()
|
||||
case cmd @ (`Shutdown` | `TerminateAction` | `Cancel`) => Left(cmd)
|
||||
case cmd => Right(cmd)
|
||||
}
|
||||
}
|
||||
impl()
|
||||
} catch {
|
||||
case _: InterruptedException => Right("")
|
||||
} finally lineReader.close()
|
||||
}
|
||||
}
|
||||
impl()
|
||||
} catch { case e: InterruptedException => Right("") }
|
||||
}
|
||||
}
|
||||
private[this] def history(s: State): Option[File] =
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public final class MetaBuildLoader extends URLClassLoader {
|
|||
*/
|
||||
public static MetaBuildLoader makeLoader(final AppProvider appProvider) throws IOException {
|
||||
final Pattern pattern =
|
||||
Pattern.compile("(test-interface-[0-9.]+|jline-[0-9.]+-sbt-.*|jansi-[0-9.]+)\\.jar");
|
||||
Pattern.compile("^(test-interface-[0-9.]+|jline-[0-9.]+-sbt-.*|jansi-[0-9.]+)\\.jar");
|
||||
final File[] cp = appProvider.mainClasspath();
|
||||
final URL[] interfaceURLs = new URL[3];
|
||||
final File[] extra =
|
||||
|
|
|
|||
|
|
@ -1504,13 +1504,13 @@ object Defaults extends BuildCommon {
|
|||
def print(st: String) = { scala.Console.out.print(st); scala.Console.out.flush() }
|
||||
print(s)
|
||||
Terminal.get.withRawSystemIn {
|
||||
Terminal.get.inputStream.read match {
|
||||
case -1 => None
|
||||
try Terminal.get.inputStream.read match {
|
||||
case -1 | -2 => None
|
||||
case b =>
|
||||
val res = b.toChar.toString
|
||||
println(res)
|
||||
Some(res)
|
||||
}
|
||||
} catch { case e: InterruptedException => None }
|
||||
}
|
||||
}),
|
||||
classes
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ private[sbt] object xMain {
|
|||
BspClient.run(dealiasBaseDirectory(configuration))
|
||||
} else {
|
||||
bootServerSocket.foreach(l => Terminal.setBootStreams(l.inputStream, l.outputStream))
|
||||
Terminal.withStreams {
|
||||
Terminal.withStreams(true) {
|
||||
if (clientModByEnv || userCommands.exists(isClient)) {
|
||||
val args = userCommands.toList.filterNot(isClient)
|
||||
NetworkClient.run(dealiasBaseDirectory(configuration), args)
|
||||
|
|
|
|||
|
|
@ -133,10 +133,17 @@ private[sbt] final class CommandExchange {
|
|||
}
|
||||
}
|
||||
// Do not manually run GC until the user has been idling for at least the min gc interval.
|
||||
impl(interval match {
|
||||
val exec = impl(interval match {
|
||||
case d: FiniteDuration => Some(d.fromNow)
|
||||
case _ => None
|
||||
}, idleDeadline)
|
||||
exec.source.foreach { s =>
|
||||
channelForName(s.channelName).foreach {
|
||||
case c if c.terminal.prompt != Prompt.Batch => c.terminal.setPrompt(Prompt.Running)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
exec
|
||||
}
|
||||
|
||||
private def addConsoleChannel(): Unit =
|
||||
|
|
@ -412,6 +419,10 @@ private[sbt] final class CommandExchange {
|
|||
case _ =>
|
||||
}
|
||||
case _ =>
|
||||
channels.foreach {
|
||||
case nc: NetworkChannel => nc.shutdown(true, Some(("", "")))
|
||||
case c => c.shutdown(false)
|
||||
}
|
||||
}
|
||||
|
||||
private[sbt] def shutdown(name: String): Unit = {
|
||||
|
|
@ -448,7 +459,9 @@ private[sbt] final class CommandExchange {
|
|||
case mt: FastTrackTask =>
|
||||
mt.task match {
|
||||
case `attach` => mt.channel.prompt(ConsolePromptEvent(lastState.get))
|
||||
case `Cancel` => Option(currentExecRef.get).foreach(cancel)
|
||||
case `Cancel` =>
|
||||
Option(currentExecRef.get).foreach(cancel)
|
||||
mt.channel.prompt(ConsolePromptEvent(lastState.get))
|
||||
case t if t.startsWith(ContinuousCommands.stopWatch) =>
|
||||
ContinuousCommands.stopWatchImpl(mt.channel.name)
|
||||
mt.channel match {
|
||||
|
|
@ -458,6 +471,10 @@ private[sbt] final class CommandExchange {
|
|||
commandQueue.add(Exec(t, None, None))
|
||||
case `TerminateAction` => exit(mt)
|
||||
case `Shutdown` =>
|
||||
val console = Terminal.console
|
||||
val needNewLine = console.prompt.isInstanceOf[Prompt.AskUser]
|
||||
console.setPrompt(Prompt.Batch)
|
||||
if (needNewLine) console.printStream.println()
|
||||
channels.find(_.name == mt.channel.name) match {
|
||||
case Some(c: NetworkChannel) => c.shutdown(false)
|
||||
case _ =>
|
||||
|
|
|
|||
|
|
@ -1217,7 +1217,6 @@ private[sbt] object ContinuousCommands {
|
|||
) extends Thread(s"sbt-${channel.name}-watch-ui-thread")
|
||||
with UITask {
|
||||
override private[sbt] def reader: UITask.Reader = () => {
|
||||
channel.terminal.printStream.write(Int.MinValue)
|
||||
def stop = Right(s"${ContinuousCommands.stopWatch} ${channel.name}")
|
||||
val exitAction: Watch.Action = {
|
||||
Watch.apply(
|
||||
|
|
|
|||
|
|
@ -53,11 +53,12 @@ private[sbt] class TaskProgress private ()
|
|||
if (firstTime.compareAndSet(true, activeExceedingThreshold.isEmpty)) threshold
|
||||
else sleepDuration
|
||||
val limit = duration.fromNow
|
||||
while (Deadline.now < limit) {
|
||||
while (Deadline.now < limit && !isClosed.get && active.nonEmpty) {
|
||||
var task = tasks.poll((limit - Deadline.now).toMillis, TimeUnit.MILLISECONDS)
|
||||
while (task != null) {
|
||||
if (containsSkipTasks(Vector(task)) || lastTaskCount.get == 0) doReport()
|
||||
task = tasks.poll
|
||||
tasks.clear()
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -99,7 +99,6 @@ final class NetworkChannel(
|
|||
addFastTrackTask(attach)
|
||||
}
|
||||
private[sbt] def prompt(): Unit = {
|
||||
terminal.setPrompt(Prompt.Running)
|
||||
interactive.set(true)
|
||||
jsonRpcNotify(promptChannel, "")
|
||||
}
|
||||
|
|
@ -641,7 +640,7 @@ final class NetworkChannel(
|
|||
case -1 => throw new ClosedChannelException()
|
||||
case b => b
|
||||
}
|
||||
} catch { case _: IOException => -1 }
|
||||
} catch { case e: IOException => -1 }
|
||||
}
|
||||
override def available(): Int = inputBuffer.size
|
||||
}
|
||||
|
|
@ -774,25 +773,82 @@ final class NetworkChannel(
|
|||
Some(result(queue.take))
|
||||
}
|
||||
}
|
||||
override def getBooleanCapability(capability: String): Boolean =
|
||||
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean =
|
||||
getCapability(
|
||||
TerminalCapabilitiesQuery(boolean = Some(capability), numeric = None, string = None),
|
||||
TerminalCapabilitiesQuery(
|
||||
boolean = Some(capability),
|
||||
numeric = None,
|
||||
string = None,
|
||||
jline3
|
||||
),
|
||||
_.boolean.getOrElse(false)
|
||||
).getOrElse(false)
|
||||
override def getNumericCapability(capability: String): Int =
|
||||
override def getNumericCapability(capability: String, jline3: Boolean): Integer =
|
||||
getCapability(
|
||||
TerminalCapabilitiesQuery(boolean = None, numeric = Some(capability), string = None),
|
||||
_.numeric.getOrElse(-1)
|
||||
).getOrElse(-1)
|
||||
override def getStringCapability(capability: String): String =
|
||||
TerminalCapabilitiesQuery(
|
||||
boolean = None,
|
||||
numeric = Some(capability),
|
||||
string = None,
|
||||
jline3
|
||||
),
|
||||
(_: TerminalCapabilitiesResponse).numeric.map(Integer.valueOf).getOrElse(-1: Integer)
|
||||
).getOrElse(-1: Integer)
|
||||
override def getStringCapability(capability: String, jline3: Boolean): String =
|
||||
getCapability(
|
||||
TerminalCapabilitiesQuery(boolean = None, numeric = None, string = Some(capability)),
|
||||
TerminalCapabilitiesQuery(
|
||||
boolean = None,
|
||||
numeric = None,
|
||||
string = Some(capability),
|
||||
jline3
|
||||
),
|
||||
_.string.flatMap {
|
||||
case "null" => None
|
||||
case s => Some(s)
|
||||
}.orNull
|
||||
).getOrElse("")
|
||||
|
||||
override private[sbt] def getAttributes: Map[String, String] =
|
||||
if (closed.get) Map.empty
|
||||
else {
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
val queue = VirtualTerminal.sendTerminalAttributesQuery(
|
||||
name,
|
||||
jsonRpcRequest
|
||||
)
|
||||
try {
|
||||
val a = queue.take
|
||||
Map(
|
||||
"iflag" -> a.iflag,
|
||||
"oflag" -> a.oflag,
|
||||
"cflag" -> a.cflag,
|
||||
"lflag" -> a.lflag,
|
||||
"cchars" -> a.cchars
|
||||
)
|
||||
} catch { case _: InterruptedException => Map.empty }
|
||||
}
|
||||
override private[sbt] def setAttributes(attributes: Map[String, String]): Unit =
|
||||
if (!closed.get) {
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
val attrs = TerminalSetAttributesCommand(
|
||||
iflag = attributes.getOrElse("iflag", ""),
|
||||
oflag = attributes.getOrElse("oflag", ""),
|
||||
cflag = attributes.getOrElse("cflag", ""),
|
||||
lflag = attributes.getOrElse("lflag", ""),
|
||||
cchars = attributes.getOrElse("cchars", ""),
|
||||
)
|
||||
val queue = VirtualTerminal.setTerminalAttributes(name, jsonRpcRequest, attrs)
|
||||
try queue.take
|
||||
catch { case _: InterruptedException => }
|
||||
}
|
||||
override def setSize(width: Int, height: Int): Unit =
|
||||
if (!closed.get) {
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
val size = TerminalSetSizeCommand(width, height)
|
||||
val queue = VirtualTerminal.setTerminalSize(name, jsonRpcRequest, size)
|
||||
try queue.take
|
||||
catch { case _: InterruptedException => }
|
||||
}
|
||||
|
||||
override def toString: String = s"NetworkTerminal($name)"
|
||||
override def close(): Unit = if (closed.compareAndSet(false, true)) {
|
||||
val threads = blockedThreads.synchronized {
|
||||
|
|
|
|||
|
|
@ -25,16 +25,27 @@ import sbt.protocol.Serialization.{
|
|||
import sjsonnew.support.scalajson.unsafe.Converter
|
||||
import sbt.protocol.{
|
||||
Attach,
|
||||
TerminalAttributesQuery,
|
||||
TerminalAttributesResponse,
|
||||
TerminalCapabilitiesQuery,
|
||||
TerminalCapabilitiesResponse,
|
||||
TerminalPropertiesResponse
|
||||
TerminalPropertiesResponse,
|
||||
TerminalSetAttributesCommand,
|
||||
TerminalSetSizeCommand,
|
||||
}
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
|
||||
object VirtualTerminal {
|
||||
private[this] val pendingTerminalProperties =
|
||||
new ConcurrentHashMap[(String, String), ArrayBlockingQueue[TerminalPropertiesResponse]]()
|
||||
private[this] val pendingTerminalCapabilities =
|
||||
new ConcurrentHashMap[(String, String), ArrayBlockingQueue[TerminalCapabilitiesResponse]]
|
||||
private[this] val pendingTerminalAttributes =
|
||||
new ConcurrentHashMap[(String, String), ArrayBlockingQueue[TerminalAttributesResponse]]
|
||||
private[this] val pendingTerminalSetAttributes =
|
||||
new ConcurrentHashMap[(String, String), ArrayBlockingQueue[Unit]]
|
||||
private[this] val pendingTerminalSetSize =
|
||||
new ConcurrentHashMap[(String, String), ArrayBlockingQueue[Unit]]
|
||||
private[sbt] def sendTerminalPropertiesQuery(
|
||||
channelName: String,
|
||||
jsonRpcRequest: (String, String, String) => Unit
|
||||
|
|
@ -70,6 +81,39 @@ object VirtualTerminal {
|
|||
case _ =>
|
||||
}
|
||||
}
|
||||
private[sbt] def sendTerminalAttributesQuery(
|
||||
channelName: String,
|
||||
jsonRpcRequest: (String, String, TerminalAttributesQuery) => Unit,
|
||||
): ArrayBlockingQueue[TerminalAttributesResponse] = {
|
||||
val id = UUID.randomUUID.toString
|
||||
val queue = new ArrayBlockingQueue[TerminalAttributesResponse](1)
|
||||
pendingTerminalAttributes.put((channelName, id), queue)
|
||||
jsonRpcRequest(id, terminalCapabilities, TerminalAttributesQuery())
|
||||
queue
|
||||
}
|
||||
private[sbt] def setTerminalAttributes(
|
||||
channelName: String,
|
||||
jsonRpcRequest: (String, String, TerminalSetAttributesCommand) => Unit,
|
||||
query: TerminalSetAttributesCommand
|
||||
): ArrayBlockingQueue[Unit] = {
|
||||
val id = UUID.randomUUID.toString
|
||||
val queue = new ArrayBlockingQueue[Unit](1)
|
||||
pendingTerminalSetAttributes.put((channelName, id), queue)
|
||||
jsonRpcRequest(id, terminalCapabilities, query)
|
||||
queue
|
||||
}
|
||||
|
||||
private[sbt] def setTerminalSize(
|
||||
channelName: String,
|
||||
jsonRpcRequest: (String, String, TerminalSetSizeCommand) => Unit,
|
||||
query: TerminalSetSizeCommand
|
||||
): ArrayBlockingQueue[Unit] = {
|
||||
val id = UUID.randomUUID.toString
|
||||
val queue = new ArrayBlockingQueue[Unit](1)
|
||||
pendingTerminalSetSize.put((channelName, id), queue)
|
||||
jsonRpcRequest(id, terminalCapabilities, query)
|
||||
queue
|
||||
}
|
||||
val handler = ServerHandler { cb =>
|
||||
ServerIntent(requestHandler(cb), responseHandler(cb), notificationHandler(cb))
|
||||
}
|
||||
|
|
@ -77,7 +121,6 @@ object VirtualTerminal {
|
|||
private val requestHandler: Handler[JsonRpcRequestMessage] =
|
||||
callback => {
|
||||
case r if r.method == attach =>
|
||||
import sbt.protocol.codec.JsonProtocol.AttachFormat
|
||||
val isInteractive = r.params
|
||||
.flatMap(Converter.fromJson[Attach](_).toOption.map(_.interactive))
|
||||
.exists(identity)
|
||||
|
|
@ -89,7 +132,6 @@ object VirtualTerminal {
|
|||
private val responseHandler: Handler[JsonRpcResponseMessage] =
|
||||
callback => {
|
||||
case r if pendingTerminalProperties.get((callback.name, r.id)) != null =>
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
val response =
|
||||
r.result.flatMap(Converter.fromJson[TerminalPropertiesResponse](_).toOption)
|
||||
pendingTerminalProperties.remove((callback.name, r.id)) match {
|
||||
|
|
@ -97,7 +139,6 @@ object VirtualTerminal {
|
|||
case buffer => response.foreach(buffer.put)
|
||||
}
|
||||
case r if pendingTerminalCapabilities.get((callback.name, r.id)) != null =>
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
val response =
|
||||
r.result.flatMap(
|
||||
Converter.fromJson[TerminalCapabilitiesResponse](_).toOption
|
||||
|
|
@ -107,6 +148,24 @@ object VirtualTerminal {
|
|||
case buffer =>
|
||||
buffer.put(response.getOrElse(TerminalCapabilitiesResponse(None, None, None)))
|
||||
}
|
||||
case r if pendingTerminalAttributes.get((callback.name, r.id)) != null =>
|
||||
val response =
|
||||
r.result.flatMap(Converter.fromJson[TerminalAttributesResponse](_).toOption)
|
||||
pendingTerminalAttributes.remove((callback.name, r.id)) match {
|
||||
case null =>
|
||||
case buffer =>
|
||||
buffer.put(response.getOrElse(TerminalAttributesResponse("", "", "", "", "")))
|
||||
}
|
||||
case r if pendingTerminalSetAttributes.get((callback.name, r.id)) != null =>
|
||||
pendingTerminalSetAttributes.remove((callback.name, r.id)) match {
|
||||
case null =>
|
||||
case buffer => buffer.put(())
|
||||
}
|
||||
case r if pendingTerminalSetSize.get((callback.name, r.id)) != null =>
|
||||
pendingTerminalSetSize.remove((callback.name, r.id)) match {
|
||||
case null =>
|
||||
case buffer => buffer.put(())
|
||||
}
|
||||
}
|
||||
private val notificationHandler: Handler[JsonRpcNotificationMessage] =
|
||||
callback => {
|
||||
|
|
|
|||
|
|
@ -84,7 +84,9 @@ object Dependencies {
|
|||
val sjsonNewMurmurhash = sjsonNew("sjson-new-murmurhash")
|
||||
|
||||
val jline = "org.scala-sbt.jline" % "jline" % "2.14.7-sbt-5e51b9d4f9631ebfa29753ce4accc57808e7fd6b"
|
||||
val jansi = "org.fusesource.jansi" % "jansi" % "1.12"
|
||||
val jline3 = "org.jline" % "jline" % "3.15.0"
|
||||
val jline3Jansi = "org.jline" % "jline-terminal-jansi" % "3.15.0"
|
||||
val jansi = "org.fusesource.jansi" % "jansi" % "1.18"
|
||||
val scalatest = "org.scalatest" %% "scalatest" % "3.0.8"
|
||||
val scalacheck = "org.scalacheck" %% "scalacheck" % "1.14.0"
|
||||
val specs2 = "org.specs2" %% "specs2-junit" % "4.0.1"
|
||||
|
|
|
|||
29
protocol/src/main/contraband-scala/sbt/protocol/TerminalAttributesQuery.scala
generated
Normal file
29
protocol/src/main/contraband-scala/sbt/protocol/TerminalAttributesQuery.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.protocol
|
||||
final class TerminalAttributesQuery private () extends sbt.protocol.CommandMessage() with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case _: TerminalAttributesQuery => true
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (17 + "sbt.protocol.TerminalAttributesQuery".##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TerminalAttributesQuery()"
|
||||
}
|
||||
private[this] def copy(): TerminalAttributesQuery = {
|
||||
new TerminalAttributesQuery()
|
||||
}
|
||||
|
||||
}
|
||||
object TerminalAttributesQuery {
|
||||
|
||||
def apply(): TerminalAttributesQuery = new TerminalAttributesQuery()
|
||||
}
|
||||
48
protocol/src/main/contraband-scala/sbt/protocol/TerminalAttributesResponse.scala
generated
Normal file
48
protocol/src/main/contraband-scala/sbt/protocol/TerminalAttributesResponse.scala
generated
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol
|
||||
final class TerminalAttributesResponse private (
|
||||
val iflag: String,
|
||||
val oflag: String,
|
||||
val cflag: String,
|
||||
val lflag: String,
|
||||
val cchars: String) extends sbt.protocol.EventMessage() with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: TerminalAttributesResponse => (this.iflag == x.iflag) && (this.oflag == x.oflag) && (this.cflag == x.cflag) && (this.lflag == x.lflag) && (this.cchars == x.cchars)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.protocol.TerminalAttributesResponse".##) + iflag.##) + oflag.##) + cflag.##) + lflag.##) + cchars.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TerminalAttributesResponse(" + iflag + ", " + oflag + ", " + cflag + ", " + lflag + ", " + cchars + ")"
|
||||
}
|
||||
private[this] def copy(iflag: String = iflag, oflag: String = oflag, cflag: String = cflag, lflag: String = lflag, cchars: String = cchars): TerminalAttributesResponse = {
|
||||
new TerminalAttributesResponse(iflag, oflag, cflag, lflag, cchars)
|
||||
}
|
||||
def withIflag(iflag: String): TerminalAttributesResponse = {
|
||||
copy(iflag = iflag)
|
||||
}
|
||||
def withOflag(oflag: String): TerminalAttributesResponse = {
|
||||
copy(oflag = oflag)
|
||||
}
|
||||
def withCflag(cflag: String): TerminalAttributesResponse = {
|
||||
copy(cflag = cflag)
|
||||
}
|
||||
def withLflag(lflag: String): TerminalAttributesResponse = {
|
||||
copy(lflag = lflag)
|
||||
}
|
||||
def withCchars(cchars: String): TerminalAttributesResponse = {
|
||||
copy(cchars = cchars)
|
||||
}
|
||||
}
|
||||
object TerminalAttributesResponse {
|
||||
|
||||
def apply(iflag: String, oflag: String, cflag: String, lflag: String, cchars: String): TerminalAttributesResponse = new TerminalAttributesResponse(iflag, oflag, cflag, lflag, cchars)
|
||||
}
|
||||
|
|
@ -7,22 +7,23 @@ package sbt.protocol
|
|||
final class TerminalCapabilitiesQuery private (
|
||||
val boolean: Option[String],
|
||||
val numeric: Option[String],
|
||||
val string: Option[String]) extends sbt.protocol.CommandMessage() with Serializable {
|
||||
val string: Option[String],
|
||||
val jline3: Boolean) extends sbt.protocol.CommandMessage() with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: TerminalCapabilitiesQuery => (this.boolean == x.boolean) && (this.numeric == x.numeric) && (this.string == x.string)
|
||||
case x: TerminalCapabilitiesQuery => (this.boolean == x.boolean) && (this.numeric == x.numeric) && (this.string == x.string) && (this.jline3 == x.jline3)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (37 * (17 + "sbt.protocol.TerminalCapabilitiesQuery".##) + boolean.##) + numeric.##) + string.##)
|
||||
37 * (37 * (37 * (37 * (37 * (17 + "sbt.protocol.TerminalCapabilitiesQuery".##) + boolean.##) + numeric.##) + string.##) + jline3.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TerminalCapabilitiesQuery(" + boolean + ", " + numeric + ", " + string + ")"
|
||||
"TerminalCapabilitiesQuery(" + boolean + ", " + numeric + ", " + string + ", " + jline3 + ")"
|
||||
}
|
||||
private[this] def copy(boolean: Option[String] = boolean, numeric: Option[String] = numeric, string: Option[String] = string): TerminalCapabilitiesQuery = {
|
||||
new TerminalCapabilitiesQuery(boolean, numeric, string)
|
||||
private[this] def copy(boolean: Option[String] = boolean, numeric: Option[String] = numeric, string: Option[String] = string, jline3: Boolean = jline3): TerminalCapabilitiesQuery = {
|
||||
new TerminalCapabilitiesQuery(boolean, numeric, string, jline3)
|
||||
}
|
||||
def withBoolean(boolean: Option[String]): TerminalCapabilitiesQuery = {
|
||||
copy(boolean = boolean)
|
||||
|
|
@ -42,9 +43,12 @@ final class TerminalCapabilitiesQuery private (
|
|||
def withString(string: String): TerminalCapabilitiesQuery = {
|
||||
copy(string = Option(string))
|
||||
}
|
||||
def withJline3(jline3: Boolean): TerminalCapabilitiesQuery = {
|
||||
copy(jline3 = jline3)
|
||||
}
|
||||
}
|
||||
object TerminalCapabilitiesQuery {
|
||||
|
||||
def apply(boolean: Option[String], numeric: Option[String], string: Option[String]): TerminalCapabilitiesQuery = new TerminalCapabilitiesQuery(boolean, numeric, string)
|
||||
def apply(boolean: String, numeric: String, string: String): TerminalCapabilitiesQuery = new TerminalCapabilitiesQuery(Option(boolean), Option(numeric), Option(string))
|
||||
def apply(boolean: Option[String], numeric: Option[String], string: Option[String], jline3: Boolean): TerminalCapabilitiesQuery = new TerminalCapabilitiesQuery(boolean, numeric, string, jline3)
|
||||
def apply(boolean: String, numeric: String, string: String, jline3: Boolean): TerminalCapabilitiesQuery = new TerminalCapabilitiesQuery(Option(boolean), Option(numeric), Option(string), jline3)
|
||||
}
|
||||
|
|
|
|||
48
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetAttributesCommand.scala
generated
Normal file
48
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetAttributesCommand.scala
generated
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol
|
||||
final class TerminalSetAttributesCommand private (
|
||||
val iflag: String,
|
||||
val oflag: String,
|
||||
val cflag: String,
|
||||
val lflag: String,
|
||||
val cchars: String) extends sbt.protocol.CommandMessage() with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: TerminalSetAttributesCommand => (this.iflag == x.iflag) && (this.oflag == x.oflag) && (this.cflag == x.cflag) && (this.lflag == x.lflag) && (this.cchars == x.cchars)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.protocol.TerminalSetAttributesCommand".##) + iflag.##) + oflag.##) + cflag.##) + lflag.##) + cchars.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TerminalSetAttributesCommand(" + iflag + ", " + oflag + ", " + cflag + ", " + lflag + ", " + cchars + ")"
|
||||
}
|
||||
private[this] def copy(iflag: String = iflag, oflag: String = oflag, cflag: String = cflag, lflag: String = lflag, cchars: String = cchars): TerminalSetAttributesCommand = {
|
||||
new TerminalSetAttributesCommand(iflag, oflag, cflag, lflag, cchars)
|
||||
}
|
||||
def withIflag(iflag: String): TerminalSetAttributesCommand = {
|
||||
copy(iflag = iflag)
|
||||
}
|
||||
def withOflag(oflag: String): TerminalSetAttributesCommand = {
|
||||
copy(oflag = oflag)
|
||||
}
|
||||
def withCflag(cflag: String): TerminalSetAttributesCommand = {
|
||||
copy(cflag = cflag)
|
||||
}
|
||||
def withLflag(lflag: String): TerminalSetAttributesCommand = {
|
||||
copy(lflag = lflag)
|
||||
}
|
||||
def withCchars(cchars: String): TerminalSetAttributesCommand = {
|
||||
copy(cchars = cchars)
|
||||
}
|
||||
}
|
||||
object TerminalSetAttributesCommand {
|
||||
|
||||
def apply(iflag: String, oflag: String, cflag: String, lflag: String, cchars: String): TerminalSetAttributesCommand = new TerminalSetAttributesCommand(iflag, oflag, cflag, lflag, cchars)
|
||||
}
|
||||
29
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetAttributesResponse.scala
generated
Normal file
29
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetAttributesResponse.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.protocol
|
||||
final class TerminalSetAttributesResponse private () extends sbt.protocol.EventMessage() with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case _: TerminalSetAttributesResponse => true
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (17 + "sbt.protocol.TerminalSetAttributesResponse".##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TerminalSetAttributesResponse()"
|
||||
}
|
||||
private[this] def copy(): TerminalSetAttributesResponse = {
|
||||
new TerminalSetAttributesResponse()
|
||||
}
|
||||
|
||||
}
|
||||
object TerminalSetAttributesResponse {
|
||||
|
||||
def apply(): TerminalSetAttributesResponse = new TerminalSetAttributesResponse()
|
||||
}
|
||||
36
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetSizeCommand.scala
generated
Normal file
36
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetSizeCommand.scala
generated
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol
|
||||
final class TerminalSetSizeCommand private (
|
||||
val width: Int,
|
||||
val height: Int) extends sbt.protocol.CommandMessage() with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: TerminalSetSizeCommand => (this.width == x.width) && (this.height == x.height)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (17 + "sbt.protocol.TerminalSetSizeCommand".##) + width.##) + height.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TerminalSetSizeCommand(" + width + ", " + height + ")"
|
||||
}
|
||||
private[this] def copy(width: Int = width, height: Int = height): TerminalSetSizeCommand = {
|
||||
new TerminalSetSizeCommand(width, height)
|
||||
}
|
||||
def withWidth(width: Int): TerminalSetSizeCommand = {
|
||||
copy(width = width)
|
||||
}
|
||||
def withHeight(height: Int): TerminalSetSizeCommand = {
|
||||
copy(height = height)
|
||||
}
|
||||
}
|
||||
object TerminalSetSizeCommand {
|
||||
|
||||
def apply(width: Int, height: Int): TerminalSetSizeCommand = new TerminalSetSizeCommand(width, height)
|
||||
}
|
||||
29
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetSizeResponse.scala
generated
Normal file
29
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetSizeResponse.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.protocol
|
||||
final class TerminalSetSizeResponse private () extends sbt.protocol.EventMessage() with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case _: TerminalSetSizeResponse => true
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (17 + "sbt.protocol.TerminalSetSizeResponse".##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TerminalSetSizeResponse()"
|
||||
}
|
||||
private[this] def copy(): TerminalSetSizeResponse = {
|
||||
new TerminalSetSizeResponse()
|
||||
}
|
||||
|
||||
}
|
||||
object TerminalSetSizeResponse {
|
||||
|
||||
def apply(): TerminalSetSizeResponse = new TerminalSetSizeResponse()
|
||||
}
|
||||
|
|
@ -6,6 +6,6 @@
|
|||
package sbt.protocol.codec
|
||||
|
||||
import _root_.sjsonnew.JsonFormat
|
||||
trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.InitCommandFormats with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.SettingQueryFormats with sbt.protocol.codec.AttachFormats with sbt.protocol.codec.TerminalCapabilitiesQueryFormats =>
|
||||
implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat5[sbt.protocol.CommandMessage, sbt.protocol.InitCommand, sbt.protocol.ExecCommand, sbt.protocol.SettingQuery, sbt.protocol.Attach, sbt.protocol.TerminalCapabilitiesQuery]("type")
|
||||
trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.InitCommandFormats with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.SettingQueryFormats with sbt.protocol.codec.AttachFormats with sbt.protocol.codec.TerminalCapabilitiesQueryFormats with sbt.protocol.codec.TerminalSetAttributesCommandFormats with sbt.protocol.codec.TerminalAttributesQueryFormats with sbt.protocol.codec.TerminalSetSizeCommandFormats =>
|
||||
implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat8[sbt.protocol.CommandMessage, sbt.protocol.InitCommand, sbt.protocol.ExecCommand, sbt.protocol.SettingQuery, sbt.protocol.Attach, sbt.protocol.TerminalCapabilitiesQuery, sbt.protocol.TerminalSetAttributesCommand, sbt.protocol.TerminalAttributesQuery, sbt.protocol.TerminalSetSizeCommand]("type")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,6 @@
|
|||
package sbt.protocol.codec
|
||||
|
||||
import _root_.sjsonnew.JsonFormat
|
||||
trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.internal.util.codec.JValueFormats with sbt.protocol.codec.SettingQuerySuccessFormats with sbt.protocol.codec.SettingQueryFailureFormats with sbt.protocol.codec.TerminalPropertiesResponseFormats with sbt.protocol.codec.TerminalCapabilitiesResponseFormats =>
|
||||
implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat7[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent, sbt.protocol.SettingQuerySuccess, sbt.protocol.SettingQueryFailure, sbt.protocol.TerminalPropertiesResponse, sbt.protocol.TerminalCapabilitiesResponse]("type")
|
||||
trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.internal.util.codec.JValueFormats with sbt.protocol.codec.SettingQuerySuccessFormats with sbt.protocol.codec.SettingQueryFailureFormats with sbt.protocol.codec.TerminalPropertiesResponseFormats with sbt.protocol.codec.TerminalCapabilitiesResponseFormats with sbt.protocol.codec.TerminalSetAttributesResponseFormats with sbt.protocol.codec.TerminalAttributesResponseFormats with sbt.protocol.codec.TerminalSetSizeResponseFormats =>
|
||||
implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat10[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent, sbt.protocol.SettingQuerySuccess, sbt.protocol.SettingQueryFailure, sbt.protocol.TerminalPropertiesResponse, sbt.protocol.TerminalCapabilitiesResponse, sbt.protocol.TerminalSetAttributesResponse, sbt.protocol.TerminalAttributesResponse, sbt.protocol.TerminalSetSizeResponse]("type")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
|||
with sbt.protocol.codec.SettingQueryFormats
|
||||
with sbt.protocol.codec.AttachFormats
|
||||
with sbt.protocol.codec.TerminalCapabilitiesQueryFormats
|
||||
with sbt.protocol.codec.TerminalSetAttributesCommandFormats
|
||||
with sbt.protocol.codec.TerminalAttributesQueryFormats
|
||||
with sbt.protocol.codec.TerminalSetSizeCommandFormats
|
||||
with sbt.protocol.codec.CommandMessageFormats
|
||||
with sbt.protocol.codec.CompletionParamsFormats
|
||||
with sbt.protocol.codec.ChannelAcceptedEventFormats
|
||||
|
|
@ -20,6 +23,9 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
|||
with sbt.protocol.codec.SettingQueryFailureFormats
|
||||
with sbt.protocol.codec.TerminalPropertiesResponseFormats
|
||||
with sbt.protocol.codec.TerminalCapabilitiesResponseFormats
|
||||
with sbt.protocol.codec.TerminalSetAttributesResponseFormats
|
||||
with sbt.protocol.codec.TerminalAttributesResponseFormats
|
||||
with sbt.protocol.codec.TerminalSetSizeResponseFormats
|
||||
with sbt.protocol.codec.EventMessageFormats
|
||||
with sbt.protocol.codec.SettingQueryResponseFormats
|
||||
with sbt.protocol.codec.CompletionResponseFormats
|
||||
|
|
|
|||
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalAttributesQueryFormats.scala
generated
Normal file
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalAttributesQueryFormats.scala
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait TerminalAttributesQueryFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val TerminalAttributesQueryFormat: JsonFormat[sbt.protocol.TerminalAttributesQuery] = new JsonFormat[sbt.protocol.TerminalAttributesQuery] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalAttributesQuery = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.TerminalAttributesQuery()
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.protocol.TerminalAttributesQuery, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
35
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalAttributesResponseFormats.scala
generated
Normal file
35
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalAttributesResponseFormats.scala
generated
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait TerminalAttributesResponseFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val TerminalAttributesResponseFormat: JsonFormat[sbt.protocol.TerminalAttributesResponse] = new JsonFormat[sbt.protocol.TerminalAttributesResponse] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalAttributesResponse = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
val iflag = unbuilder.readField[String]("iflag")
|
||||
val oflag = unbuilder.readField[String]("oflag")
|
||||
val cflag = unbuilder.readField[String]("cflag")
|
||||
val lflag = unbuilder.readField[String]("lflag")
|
||||
val cchars = unbuilder.readField[String]("cchars")
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.TerminalAttributesResponse(iflag, oflag, cflag, lflag, cchars)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.protocol.TerminalAttributesResponse, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("iflag", obj.iflag)
|
||||
builder.addField("oflag", obj.oflag)
|
||||
builder.addField("cflag", obj.cflag)
|
||||
builder.addField("lflag", obj.lflag)
|
||||
builder.addField("cchars", obj.cchars)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,8 +14,9 @@ implicit lazy val TerminalCapabilitiesQueryFormat: JsonFormat[sbt.protocol.Termi
|
|||
val boolean = unbuilder.readField[Option[String]]("boolean")
|
||||
val numeric = unbuilder.readField[Option[String]]("numeric")
|
||||
val string = unbuilder.readField[Option[String]]("string")
|
||||
val jline3 = unbuilder.readField[Boolean]("jline3")
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.TerminalCapabilitiesQuery(boolean, numeric, string)
|
||||
sbt.protocol.TerminalCapabilitiesQuery(boolean, numeric, string, jline3)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
|
|
@ -25,6 +26,7 @@ implicit lazy val TerminalCapabilitiesQueryFormat: JsonFormat[sbt.protocol.Termi
|
|||
builder.addField("boolean", obj.boolean)
|
||||
builder.addField("numeric", obj.numeric)
|
||||
builder.addField("string", obj.string)
|
||||
builder.addField("jline3", obj.jline3)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
35
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetAttributesCommandFormats.scala
generated
Normal file
35
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetAttributesCommandFormats.scala
generated
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait TerminalSetAttributesCommandFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val TerminalSetAttributesCommandFormat: JsonFormat[sbt.protocol.TerminalSetAttributesCommand] = new JsonFormat[sbt.protocol.TerminalSetAttributesCommand] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetAttributesCommand = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
val iflag = unbuilder.readField[String]("iflag")
|
||||
val oflag = unbuilder.readField[String]("oflag")
|
||||
val cflag = unbuilder.readField[String]("cflag")
|
||||
val lflag = unbuilder.readField[String]("lflag")
|
||||
val cchars = unbuilder.readField[String]("cchars")
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.TerminalSetAttributesCommand(iflag, oflag, cflag, lflag, cchars)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.protocol.TerminalSetAttributesCommand, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("iflag", obj.iflag)
|
||||
builder.addField("oflag", obj.oflag)
|
||||
builder.addField("cflag", obj.cflag)
|
||||
builder.addField("lflag", obj.lflag)
|
||||
builder.addField("cchars", obj.cchars)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetAttributesResponseFormats.scala
generated
Normal file
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetAttributesResponseFormats.scala
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait TerminalSetAttributesResponseFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val TerminalSetAttributesResponseFormat: JsonFormat[sbt.protocol.TerminalSetAttributesResponse] = new JsonFormat[sbt.protocol.TerminalSetAttributesResponse] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetAttributesResponse = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.TerminalSetAttributesResponse()
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.protocol.TerminalSetAttributesResponse, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
29
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetSizeCommandFormats.scala
generated
Normal file
29
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetSizeCommandFormats.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.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait TerminalSetSizeCommandFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val TerminalSetSizeCommandFormat: JsonFormat[sbt.protocol.TerminalSetSizeCommand] = new JsonFormat[sbt.protocol.TerminalSetSizeCommand] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetSizeCommand = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
val width = unbuilder.readField[Int]("width")
|
||||
val height = unbuilder.readField[Int]("height")
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.TerminalSetSizeCommand(width, height)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.protocol.TerminalSetSizeCommand, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("width", obj.width)
|
||||
builder.addField("height", obj.height)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetSizeResponseFormats.scala
generated
Normal file
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetSizeResponseFormats.scala
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait TerminalSetSizeResponseFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val TerminalSetSizeResponseFormat: JsonFormat[sbt.protocol.TerminalSetSizeResponse] = new JsonFormat[sbt.protocol.TerminalSetSizeResponse] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetSizeResponse = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.TerminalSetSizeResponse()
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.protocol.TerminalSetSizeResponse, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -85,22 +85,50 @@ type ExecutionEvent {
|
|||
}
|
||||
|
||||
type TerminalPropertiesResponse implements EventMessage {
|
||||
width: Int!
|
||||
height: Int!
|
||||
isAnsiSupported: Boolean!
|
||||
isColorEnabled: Boolean!
|
||||
isSupershellEnabled: Boolean!
|
||||
isEchoEnabled: Boolean!
|
||||
width: Int!
|
||||
height: Int!
|
||||
isAnsiSupported: Boolean!
|
||||
isColorEnabled: Boolean!
|
||||
isSupershellEnabled: Boolean!
|
||||
isEchoEnabled: Boolean!
|
||||
}
|
||||
|
||||
type TerminalCapabilitiesQuery implements CommandMessage {
|
||||
boolean: String
|
||||
numeric: String
|
||||
string: String
|
||||
boolean: String
|
||||
numeric: String
|
||||
string: String
|
||||
jline3: Boolean!
|
||||
}
|
||||
|
||||
type TerminalCapabilitiesResponse implements EventMessage {
|
||||
boolean: Boolean
|
||||
numeric: Int
|
||||
string: String
|
||||
boolean: Boolean
|
||||
numeric: Int
|
||||
string: String
|
||||
}
|
||||
|
||||
type TerminalSetAttributesCommand implements CommandMessage {
|
||||
iflag: String!,
|
||||
oflag: String!,
|
||||
cflag: String!,
|
||||
lflag: String!,
|
||||
cchars: String!,
|
||||
}
|
||||
|
||||
type TerminalSetAttributesResponse implements EventMessage {}
|
||||
|
||||
type TerminalAttributesQuery implements CommandMessage {}
|
||||
|
||||
type TerminalAttributesResponse implements EventMessage {
|
||||
iflag: String!,
|
||||
oflag: String!,
|
||||
cflag: String!,
|
||||
lflag: String!,
|
||||
cchars: String!,
|
||||
}
|
||||
|
||||
type TerminalSetSizeCommand implements CommandMessage {
|
||||
width: Int!
|
||||
height: Int!
|
||||
}
|
||||
|
||||
type TerminalSetSizeResponse implements EventMessage {}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ object Serialization {
|
|||
private[sbt] val VsCode = "application/vscode-jsonrpc; charset=utf-8"
|
||||
val systemIn = "sbt/systemIn"
|
||||
val systemOut = "sbt/systemOut"
|
||||
val systemOutFlush = "sbt/systemOutFlush"
|
||||
val terminalPropertiesQuery = "sbt/terminalPropertiesQuery"
|
||||
val terminalPropertiesResponse = "sbt/terminalPropertiesResponse"
|
||||
val terminalCapabilities = "sbt/terminalCapabilities"
|
||||
|
|
@ -34,6 +35,9 @@ object Serialization {
|
|||
val attachResponse = "sbt/attachResponse"
|
||||
val cancelRequest = "sbt/cancelRequest"
|
||||
val promptChannel = "sbt/promptChannel"
|
||||
val setTerminalAttributes = "sbt/setTerminalAttributes"
|
||||
val getTerminalAttributes = "sbt/getTerminalAttributes"
|
||||
val setTerminalSize = "sbt/setTerminalSize"
|
||||
val CancelAll = "__CancelAll"
|
||||
|
||||
@deprecated("unused", since = "1.4.0")
|
||||
|
|
|
|||
Loading…
Reference in New Issue