Fix switching between raw and canonical input

There were a number of issues with swithcing between raw and canonical
issues that affected both the server and the thin client. These were
reported in #5863 and #5856. In both cases, there were issues with
reading input or having the input be displayed. Debugging those issues
revealed a number of issues with how we were using the jline 3 system
terminal and the hybrid interaction with the jline 2 terminal. This
commit eliminates all of our internal jline 2 usage. The only remaining
jline 2 usage is that we create and override the global terminal for the
scala console for scala versions < 2.13. By moving away from jline 2, I
was also able to fix #5828, which reported that the home, end and delete
keys were not working.

One of the big issues that this commit addresses is that the
NetworkClient was always performing blocking reads on System.in. This
was problematic because it turns out that you can't switch between raw
and canonical modes when there is a read present. To fix this, the
server now sends a message to the client when it wants to read bytes and
only then does the client create a background thread to read a single
byte.

I also figured out how to set the terminal type properly for the thin
client on windows where we had been manually setting the capabilities to
ansi, which only worked for some keys. This fix required switching to
the WindowsInputStream that I introduced in a prior commit. Before we
were using the jline 2 wrapped input stream which was converting some
system events, like home and end, to the wrong escape sequence mappings.

The remainder of the commit is mostly just converting from jline 2 apis
to jline 3 apis.

I verified that tab completions, the scala console, the ammonite console
and a run task that read from System.in all work with both the server
and the thin client on mac, linux and windows after these changes.

Fixes #5828, #5863, #5856
This commit is contained in:
Ethan Atkins
2020-09-21 13:42:03 -07:00
parent 410a8dd4b1
commit bb8b9a1c99
25 changed files with 609 additions and 241 deletions
@@ -7,23 +7,22 @@ package sbt.protocol
final class TerminalCapabilitiesQuery private (
val boolean: Option[String],
val numeric: Option[String],
val string: Option[String],
val jline3: Boolean) extends sbt.protocol.CommandMessage() with Serializable {
val string: Option[String]) 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) && (this.jline3 == x.jline3)
case x: TerminalCapabilitiesQuery => (this.boolean == x.boolean) && (this.numeric == x.numeric) && (this.string == x.string)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (37 * (37 * (17 + "sbt.protocol.TerminalCapabilitiesQuery".##) + boolean.##) + numeric.##) + string.##) + jline3.##)
37 * (37 * (37 * (37 * (17 + "sbt.protocol.TerminalCapabilitiesQuery".##) + boolean.##) + numeric.##) + string.##)
}
override def toString: String = {
"TerminalCapabilitiesQuery(" + boolean + ", " + numeric + ", " + string + ", " + jline3 + ")"
"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)
private[this] def copy(boolean: Option[String] = boolean, numeric: Option[String] = numeric, string: Option[String] = string): TerminalCapabilitiesQuery = {
new TerminalCapabilitiesQuery(boolean, numeric, string)
}
def withBoolean(boolean: Option[String]): TerminalCapabilitiesQuery = {
copy(boolean = boolean)
@@ -43,12 +42,9 @@ 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], 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)
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))
}
@@ -0,0 +1,32 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
final class TerminalSetEchoCommand private (
val toggle: Boolean) extends sbt.protocol.CommandMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: TerminalSetEchoCommand => (this.toggle == x.toggle)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + "sbt.protocol.TerminalSetEchoCommand".##) + toggle.##)
}
override def toString: String = {
"TerminalSetEchoCommand(" + toggle + ")"
}
private[this] def copy(toggle: Boolean = toggle): TerminalSetEchoCommand = {
new TerminalSetEchoCommand(toggle)
}
def withToggle(toggle: Boolean): TerminalSetEchoCommand = {
copy(toggle = toggle)
}
}
object TerminalSetEchoCommand {
def apply(toggle: Boolean): TerminalSetEchoCommand = new TerminalSetEchoCommand(toggle)
}
@@ -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 TerminalSetEchoResponse private () extends sbt.protocol.EventMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case _: TerminalSetEchoResponse => true
case _ => false
}
override def hashCode: Int = {
37 * (17 + "sbt.protocol.TerminalSetEchoResponse".##)
}
override def toString: String = {
"TerminalSetEchoResponse()"
}
private[this] def copy(): TerminalSetEchoResponse = {
new TerminalSetEchoResponse()
}
}
object TerminalSetEchoResponse {
def apply(): TerminalSetEchoResponse = new TerminalSetEchoResponse()
}
@@ -0,0 +1,32 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
final class TerminalSetRawModeCommand private (
val toggle: Boolean) extends sbt.protocol.CommandMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: TerminalSetRawModeCommand => (this.toggle == x.toggle)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + "sbt.protocol.TerminalSetRawModeCommand".##) + toggle.##)
}
override def toString: String = {
"TerminalSetRawModeCommand(" + toggle + ")"
}
private[this] def copy(toggle: Boolean = toggle): TerminalSetRawModeCommand = {
new TerminalSetRawModeCommand(toggle)
}
def withToggle(toggle: Boolean): TerminalSetRawModeCommand = {
copy(toggle = toggle)
}
}
object TerminalSetRawModeCommand {
def apply(toggle: Boolean): TerminalSetRawModeCommand = new TerminalSetRawModeCommand(toggle)
}
@@ -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 TerminalSetRawModeReponse private () extends sbt.protocol.EventMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case _: TerminalSetRawModeReponse => true
case _ => false
}
override def hashCode: Int = {
37 * (17 + "sbt.protocol.TerminalSetRawModeReponse".##)
}
override def toString: String = {
"TerminalSetRawModeReponse()"
}
private[this] def copy(): TerminalSetRawModeReponse = {
new TerminalSetRawModeReponse()
}
}
object TerminalSetRawModeReponse {
def apply(): TerminalSetRawModeReponse = new TerminalSetRawModeReponse()
}
@@ -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 TerminalSetRawModeResponse private () extends sbt.protocol.EventMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case _: TerminalSetRawModeResponse => true
case _ => false
}
override def hashCode: Int = {
37 * (17 + "sbt.protocol.TerminalSetRawModeResponse".##)
}
override def toString: String = {
"TerminalSetRawModeResponse()"
}
private[this] def copy(): TerminalSetRawModeResponse = {
new TerminalSetRawModeResponse()
}
}
object TerminalSetRawModeResponse {
def apply(): TerminalSetRawModeResponse = new TerminalSetRawModeResponse()
}
@@ -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 with sbt.protocol.codec.TerminalSetAttributesCommandFormats with sbt.protocol.codec.TerminalAttributesQueryFormats with sbt.protocol.codec.TerminalGetSizeQueryFormats with sbt.protocol.codec.TerminalSetSizeCommandFormats =>
implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat9[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.TerminalGetSizeQuery, sbt.protocol.TerminalSetSizeCommand]("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.TerminalGetSizeQueryFormats with sbt.protocol.codec.TerminalSetSizeCommandFormats with sbt.protocol.codec.TerminalSetEchoCommandFormats with sbt.protocol.codec.TerminalSetRawModeCommandFormats =>
implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat11[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.TerminalGetSizeQuery, sbt.protocol.TerminalSetSizeCommand, sbt.protocol.TerminalSetEchoCommand, sbt.protocol.TerminalSetRawModeCommand]("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 with sbt.protocol.codec.TerminalSetAttributesResponseFormats with sbt.protocol.codec.TerminalAttributesResponseFormats with sbt.protocol.codec.TerminalGetSizeResponseFormats with sbt.protocol.codec.TerminalSetSizeResponseFormats =>
implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat11[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.TerminalGetSizeResponse, sbt.protocol.TerminalSetSizeResponse]("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.TerminalGetSizeResponseFormats with sbt.protocol.codec.TerminalSetSizeResponseFormats with sbt.protocol.codec.TerminalSetEchoResponseFormats with sbt.protocol.codec.TerminalSetRawModeResponseFormats =>
implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat13[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.TerminalGetSizeResponse, sbt.protocol.TerminalSetSizeResponse, sbt.protocol.TerminalSetEchoResponse, sbt.protocol.TerminalSetRawModeResponse]("type")
}
@@ -14,6 +14,8 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.protocol.codec.TerminalAttributesQueryFormats
with sbt.protocol.codec.TerminalGetSizeQueryFormats
with sbt.protocol.codec.TerminalSetSizeCommandFormats
with sbt.protocol.codec.TerminalSetEchoCommandFormats
with sbt.protocol.codec.TerminalSetRawModeCommandFormats
with sbt.protocol.codec.CommandMessageFormats
with sbt.protocol.codec.CompletionParamsFormats
with sbt.protocol.codec.ChannelAcceptedEventFormats
@@ -28,6 +30,8 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.protocol.codec.TerminalAttributesResponseFormats
with sbt.protocol.codec.TerminalGetSizeResponseFormats
with sbt.protocol.codec.TerminalSetSizeResponseFormats
with sbt.protocol.codec.TerminalSetEchoResponseFormats
with sbt.protocol.codec.TerminalSetRawModeResponseFormats
with sbt.protocol.codec.EventMessageFormats
with sbt.protocol.codec.SettingQueryResponseFormats
with sbt.protocol.codec.CompletionResponseFormats
@@ -14,9 +14,8 @@ 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, jline3)
sbt.protocol.TerminalCapabilitiesQuery(boolean, numeric, string)
case None =>
deserializationError("Expected JsObject but found None")
}
@@ -26,7 +25,6 @@ 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()
}
}
@@ -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 TerminalSetEchoCommandFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val TerminalSetEchoCommandFormat: JsonFormat[sbt.protocol.TerminalSetEchoCommand] = new JsonFormat[sbt.protocol.TerminalSetEchoCommand] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetEchoCommand = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val toggle = unbuilder.readField[Boolean]("toggle")
unbuilder.endObject()
sbt.protocol.TerminalSetEchoCommand(toggle)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.TerminalSetEchoCommand, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("toggle", obj.toggle)
builder.endObject()
}
}
}
@@ -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 TerminalSetEchoResponseFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val TerminalSetEchoResponseFormat: JsonFormat[sbt.protocol.TerminalSetEchoResponse] = new JsonFormat[sbt.protocol.TerminalSetEchoResponse] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetEchoResponse = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
unbuilder.endObject()
sbt.protocol.TerminalSetEchoResponse()
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.TerminalSetEchoResponse, builder: Builder[J]): Unit = {
builder.beginObject()
builder.endObject()
}
}
}
@@ -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 TerminalSetRawModeCommandFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val TerminalSetRawModeCommandFormat: JsonFormat[sbt.protocol.TerminalSetRawModeCommand] = new JsonFormat[sbt.protocol.TerminalSetRawModeCommand] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetRawModeCommand = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val toggle = unbuilder.readField[Boolean]("toggle")
unbuilder.endObject()
sbt.protocol.TerminalSetRawModeCommand(toggle)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.TerminalSetRawModeCommand, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("toggle", obj.toggle)
builder.endObject()
}
}
}
@@ -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 TerminalSetRawModeReponseFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val TerminalSetRawModeReponseFormat: JsonFormat[sbt.protocol.TerminalSetRawModeReponse] = new JsonFormat[sbt.protocol.TerminalSetRawModeReponse] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetRawModeReponse = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
unbuilder.endObject()
sbt.protocol.TerminalSetRawModeReponse()
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.TerminalSetRawModeReponse, builder: Builder[J]): Unit = {
builder.beginObject()
builder.endObject()
}
}
}
@@ -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 TerminalSetRawModeResponseFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val TerminalSetRawModeResponseFormat: JsonFormat[sbt.protocol.TerminalSetRawModeResponse] = new JsonFormat[sbt.protocol.TerminalSetRawModeResponse] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetRawModeResponse = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
unbuilder.endObject()
sbt.protocol.TerminalSetRawModeResponse()
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.TerminalSetRawModeResponse, builder: Builder[J]): Unit = {
builder.beginObject()
builder.endObject()
}
}
}
+12 -1
View File
@@ -97,7 +97,6 @@ type TerminalCapabilitiesQuery implements CommandMessage {
boolean: String
numeric: String
string: String
jline3: Boolean!
}
type TerminalCapabilitiesResponse implements EventMessage {
@@ -138,3 +137,15 @@ type TerminalSetSizeCommand implements CommandMessage {
}
type TerminalSetSizeResponse implements EventMessage {}
type TerminalSetEchoCommand implements CommandMessage {
toggle: Boolean!
}
type TerminalSetEchoResponse implements EventMessage {}
type TerminalSetRawModeCommand implements CommandMessage {
toggle: Boolean!
}
type TerminalSetRawModeResponse implements EventMessage {}
@@ -24,6 +24,8 @@ import sbt.internal.protocol.{
object Serialization {
private[sbt] val VsCode = "application/vscode-jsonrpc; charset=utf-8"
val readSystemIn = "sbt/readSystemIn"
val cancelReadSystemIn = "sbt/cancelReadSystemIn"
val systemIn = "sbt/systemIn"
val systemOut = "sbt/systemOut"
val systemErr = "sbt/systemErr"
@@ -41,6 +43,8 @@ object Serialization {
val getTerminalAttributes = "sbt/getTerminalAttributes"
val terminalGetSize = "sbt/terminalGetSize"
val terminalSetSize = "sbt/terminalSetSize"
val terminalSetEcho = "sbt/terminalSetEcho"
val terminalSetRawMode = "sbt/terminalSetRawMode"
val CancelAll = "__CancelAll"
@deprecated("unused", since = "1.4.0")