mirror of https://github.com/sbt/sbt.git
commit
d96ecb2c21
|
|
@ -261,6 +261,13 @@ private[sbt] object JLine3 {
|
|||
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[sbt] def setMode(term: Terminal, canonical: Boolean, echo: Boolean): Unit = {
|
||||
val prev = attributesFromMap(term.getAttributes)
|
||||
val newAttrs = new Attributes(prev)
|
||||
newAttrs.setLocalFlag(LocalFlag.ICANON, canonical)
|
||||
newAttrs.setLocalFlag(LocalFlag.ECHO, echo)
|
||||
term.setAttributes(toMap(newAttrs))
|
||||
}
|
||||
private[util] def attributesFromMap(map: Map[String, String]): Attributes = {
|
||||
val attributes = new Attributes
|
||||
map.get("iflag").foreach { flags =>
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@
|
|||
|
||||
package sbt
|
||||
|
||||
import sbt.internal.util.{ SimpleReader, Terminal }
|
||||
import sbt.internal.util.{ SimpleReader, Terminal => ITerminal }
|
||||
|
||||
trait CommandLineUIService extends InteractionService {
|
||||
override def readLine(prompt: String, mask: Boolean): Option[String] = {
|
||||
val maskChar = if (mask) Some('*') else None
|
||||
SimpleReader(Terminal.get).readLine(prompt, maskChar)
|
||||
SimpleReader(ITerminal.get).readLine(prompt, maskChar)
|
||||
}
|
||||
// TODO - Implement this better.
|
||||
override def confirm(msg: String): Boolean = {
|
||||
|
|
@ -21,15 +21,15 @@ trait CommandLineUIService extends InteractionService {
|
|||
(in == "y" || in == "yes")
|
||||
}
|
||||
}
|
||||
SimpleReader(Terminal.get).readLine(msg + " (yes/no): ", None) match {
|
||||
SimpleReader(ITerminal.get).readLine(msg + " (yes/no): ", None) match {
|
||||
case Some(Assent()) => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
override def terminalWidth: Int = Terminal.get.getWidth
|
||||
override def terminalWidth: Int = ITerminal.get.getWidth
|
||||
|
||||
override def terminalHeight: Int = Terminal.get.getHeight
|
||||
override def terminalHeight: Int = ITerminal.get.getHeight
|
||||
}
|
||||
|
||||
object CommandLineUIService extends CommandLineUIService
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ import sbt.internal.server.{
|
|||
import sbt.internal.testing.TestLogger
|
||||
import sbt.internal.util.Attributed.data
|
||||
import sbt.internal.util.Types._
|
||||
import sbt.internal.util._
|
||||
import sbt.internal.util.{ Terminal => ITerminal, _ }
|
||||
import sbt.internal.util.complete._
|
||||
import sbt.io.Path._
|
||||
import sbt.io._
|
||||
|
|
@ -333,7 +333,7 @@ object Defaults extends BuildCommon {
|
|||
if (!useScalaReplJLine.value) classOf[org.jline.terminal.Terminal].getClassLoader
|
||||
else appConfiguration.value.provider.scalaProvider.launcher.topLoader.getParent
|
||||
},
|
||||
useSuperShell := { if (insideCI.value) false else Terminal.console.isSupershellEnabled },
|
||||
useSuperShell := { if (insideCI.value) false else ITerminal.console.isSupershellEnabled },
|
||||
superShellThreshold :== SysProp.supershellThreshold,
|
||||
superShellMaxTasks :== SysProp.supershellMaxTasks,
|
||||
superShellSleep :== SysProp.supershellSleep.millis,
|
||||
|
|
@ -388,6 +388,7 @@ object Defaults extends BuildCommon {
|
|||
pollInterval :== Watch.defaultPollInterval,
|
||||
canonicalInput :== true,
|
||||
echoInput :== true,
|
||||
terminal := state.value.get(terminalKey).getOrElse(Terminal(ITerminal.get)),
|
||||
) ++ LintUnused.lintSettings
|
||||
++ DefaultBackgroundJobService.backgroundJobServiceSettings
|
||||
++ RemoteCache.globalSettings
|
||||
|
|
@ -1663,13 +1664,13 @@ object Defaults extends BuildCommon {
|
|||
|
||||
def askForMainClass(classes: Seq[String]): Option[String] =
|
||||
sbt.SelectMainClass(
|
||||
if (classes.length >= 10) Some(SimpleReader(Terminal.get).readLine(_))
|
||||
if (classes.length >= 10) Some(SimpleReader(ITerminal.get).readLine(_))
|
||||
else
|
||||
Some(s => {
|
||||
def print(st: String) = { scala.Console.out.print(st); scala.Console.out.flush() }
|
||||
print(s)
|
||||
Terminal.get.withRawInput {
|
||||
try Terminal.get.inputStream.read match {
|
||||
ITerminal.get.withRawInput {
|
||||
try ITerminal.get.inputStream.read match {
|
||||
case -1 | -2 => None
|
||||
case b =>
|
||||
val res = b.toChar.toString
|
||||
|
|
@ -1705,7 +1706,7 @@ object Defaults extends BuildCommon {
|
|||
private[this] def termWrapper(canonical: Boolean, echo: Boolean): (() => Unit) => (() => Unit) =
|
||||
(f: () => Unit) =>
|
||||
() => {
|
||||
val term = Terminal.get
|
||||
val term = ITerminal.get
|
||||
if (!canonical) {
|
||||
term.enterRawMode()
|
||||
if (echo) term.setEchoEnabled(echo)
|
||||
|
|
@ -3952,7 +3953,7 @@ object Classpaths {
|
|||
}
|
||||
}
|
||||
|
||||
def shellPromptFromState: State => String = shellPromptFromState(Terminal.console.isColorEnabled)
|
||||
def shellPromptFromState: State => String = shellPromptFromState(ITerminal.console.isColorEnabled)
|
||||
def shellPromptFromState(isColorEnabled: Boolean): State => String = { s: State =>
|
||||
val extracted = Project.extract(s)
|
||||
(name in extracted.currentRef).get(extracted.structure.data) match {
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ object Keys {
|
|||
val serverLog = taskKey[Unit]("A dummy task to set server log level using Global / serverLog / logLevel.").withRank(CTask)
|
||||
val canonicalInput = settingKey[Boolean]("Toggles whether a task should use canonical input (line buffered with echo) or raw input").withRank(DSetting)
|
||||
val echoInput = settingKey[Boolean]("Toggles whether a task should echo user input").withRank(DSetting)
|
||||
val terminal = taskKey[Terminal]("The Terminal associated with a task").withRank(DTask)
|
||||
private[sbt] val terminalKey = AttributeKey[Terminal]("terminal-key", Invisible)
|
||||
|
||||
// Project keys
|
||||
val autoGeneratedProject = settingKey[Boolean]("If it exists, represents that the project (and name) were automatically created, rather than user specified.").withRank(DSetting)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import sbt.internal.inc.ScalaInstance
|
|||
import sbt.internal.nio.{ CheckBuildSources, FileTreeRepository }
|
||||
import sbt.internal.server.{ BuildServerProtocol, NetworkChannel }
|
||||
import sbt.internal.util.Types.{ const, idFun }
|
||||
import sbt.internal.util._
|
||||
import sbt.internal.util.{ Terminal => ITerminal, _ }
|
||||
import sbt.internal.util.complete.{ Parser, SizeParser }
|
||||
import sbt.io._
|
||||
import sbt.io.syntax._
|
||||
|
|
@ -78,8 +78,8 @@ private[sbt] object xMain {
|
|||
if (userCommands.exists(isBsp)) {
|
||||
BspClient.run(dealiasBaseDirectory(configuration))
|
||||
} else {
|
||||
bootServerSocket.foreach(l => Terminal.setBootStreams(l.inputStream, l.outputStream))
|
||||
Terminal.withStreams(true) {
|
||||
bootServerSocket.foreach(l => ITerminal.setBootStreams(l.inputStream, l.outputStream))
|
||||
ITerminal.withStreams(true) {
|
||||
if (clientModByEnv || userCommands.exists(isClient)) {
|
||||
val args = userCommands.toList.filterNot(isClient)
|
||||
NetworkClient.run(dealiasBaseDirectory(configuration), args)
|
||||
|
|
@ -105,7 +105,7 @@ private[sbt] object xMain {
|
|||
} finally {
|
||||
// Clear any stray progress lines
|
||||
ShutdownHooks.close()
|
||||
if (Terminal.formatEnabledInEnv) {
|
||||
if (ITerminal.formatEnabledInEnv) {
|
||||
System.out.print(ConsoleAppender.ClearScreenAfterCursor)
|
||||
System.out.flush()
|
||||
}
|
||||
|
|
@ -118,9 +118,9 @@ private[sbt] object xMain {
|
|||
try (Some(new BootServerSocket(configuration)) -> None)
|
||||
catch {
|
||||
case _: ServerAlreadyBootingException
|
||||
if System.console != null && !Terminal.startedByRemoteClient =>
|
||||
if System.console != null && !ITerminal.startedByRemoteClient =>
|
||||
println("sbt server is already booting. Create a new server? y/n (default y)")
|
||||
val exit = Terminal.get.withRawInput(System.in.read) match {
|
||||
val exit = ITerminal.get.withRawInput(System.in.read) match {
|
||||
case 110 => Some(Exit(1))
|
||||
case _ => None
|
||||
}
|
||||
|
|
@ -841,7 +841,7 @@ object BuiltinCommands {
|
|||
@tailrec
|
||||
private[this] def doLoadFailed(s: State, loadArg: String): State = {
|
||||
s.log.warn("Project loading failed: (r)etry, (q)uit, (l)ast, or (i)gnore? (default: r)")
|
||||
val result = try Terminal.get.withRawInput(System.in.read) match {
|
||||
val result = try ITerminal.get.withRawInput(System.in.read) match {
|
||||
case -1 => 'q'.toInt
|
||||
case b => b
|
||||
} catch { case _: ClosedChannelException => 'q' }
|
||||
|
|
@ -957,7 +957,7 @@ object BuiltinCommands {
|
|||
val threshold =
|
||||
extracted.getOpt(Keys.superShellThreshold).getOrElse(SysProp.supershellThreshold)
|
||||
val maxItems = extracted.getOpt(Keys.superShellMaxTasks).getOrElse(SysProp.supershellMaxTasks)
|
||||
Terminal.setConsoleProgressState(new ProgressState(1, maxItems))
|
||||
ITerminal.setConsoleProgressState(new ProgressState(1, maxItems))
|
||||
s.put(Keys.superShellSleep.key, sleep)
|
||||
.put(Keys.superShellThreshold.key, threshold)
|
||||
.put(Keys.superShellMaxTasks.key, maxItems)
|
||||
|
|
@ -1032,10 +1032,10 @@ object BuiltinCommands {
|
|||
* by a remote client and only one is able to start a server. This seems to
|
||||
* happen primarily on windows.
|
||||
*/
|
||||
if (Terminal.startedByRemoteClient && !exchange.hasServer) {
|
||||
if (ITerminal.startedByRemoteClient && !exchange.hasServer) {
|
||||
Exec(Shutdown, None) +: s1
|
||||
} else {
|
||||
if (Terminal.console.prompt == Prompt.Batch) Terminal.console.setPrompt(Prompt.Pending)
|
||||
if (ITerminal.console.prompt == Prompt.Batch) ITerminal.console.setPrompt(Prompt.Pending)
|
||||
exchange prompt ConsolePromptEvent(s0)
|
||||
val minGCInterval = Project
|
||||
.extract(s1)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import sbt.internal.ShutdownHooks
|
|||
import sbt.internal.langserver.ErrorCodes
|
||||
import sbt.internal.protocol.JsonRpcResponseError
|
||||
import sbt.internal.nio.CheckBuildSources.CheckBuildSourcesKey
|
||||
import sbt.internal.util.{ ErrorHandling, GlobalLogBacking, Prompt, Terminal }
|
||||
import sbt.internal.util.{ ErrorHandling, GlobalLogBacking, Prompt, Terminal => ITerminal }
|
||||
import sbt.internal.{ ShutdownHooks, TaskProgress }
|
||||
import sbt.io.{ IO, Using }
|
||||
import sbt.protocol._
|
||||
|
|
@ -35,7 +35,7 @@ object MainLoop {
|
|||
// We've disabled jline shutdown hooks to prevent classloader leaks, and have been careful to always restore
|
||||
// the jline terminal in finally blocks, but hitting ctrl+c prevents finally blocks from being executed, in that
|
||||
// case the only way to restore the terminal is in a shutdown hook.
|
||||
val shutdownHook = ShutdownHooks.add(Terminal.restore)
|
||||
val shutdownHook = ShutdownHooks.add(ITerminal.restore)
|
||||
|
||||
try {
|
||||
runLoggedLoop(state, state.globalLogging.backing)
|
||||
|
|
@ -219,19 +219,19 @@ object MainLoop {
|
|||
}
|
||||
exchange.setState(progressState)
|
||||
exchange.setExec(Some(exec))
|
||||
val restoreTerminal = channelName.flatMap(exchange.channelForName) match {
|
||||
val (restoreTerminal, termState) = channelName.flatMap(exchange.channelForName) match {
|
||||
case Some(c) =>
|
||||
val prevTerminal = Terminal.set(c.terminal)
|
||||
val prevTerminal = ITerminal.set(c.terminal)
|
||||
val prevPrompt = c.terminal.prompt
|
||||
// temporarily set the prompt to running during task evaluation
|
||||
c.terminal.setPrompt(Prompt.Running)
|
||||
() => {
|
||||
(() => {
|
||||
c.terminal.setPrompt(prevPrompt)
|
||||
Terminal.set(prevTerminal)
|
||||
ITerminal.set(prevTerminal)
|
||||
c.terminal.setPrompt(prevPrompt)
|
||||
c.terminal.flush()
|
||||
}
|
||||
case _ => () => ()
|
||||
}) -> progressState.put(Keys.terminalKey, Terminal(c.terminal))
|
||||
case _ => (() => ()) -> progressState.put(Keys.terminalKey, Terminal(ITerminal.get))
|
||||
}
|
||||
/*
|
||||
* FastTrackCommands.evaluate can be significantly faster than Command.process because
|
||||
|
|
@ -241,8 +241,8 @@ object MainLoop {
|
|||
*/
|
||||
val newState = try {
|
||||
FastTrackCommands
|
||||
.evaluate(progressState, exec.commandLine)
|
||||
.getOrElse(Command.process(exec.commandLine, progressState))
|
||||
.evaluate(termState, exec.commandLine)
|
||||
.getOrElse(Command.process(exec.commandLine, termState))
|
||||
} finally {
|
||||
// Flush the terminal output after command evaluation to ensure that all output
|
||||
// is displayed in the thin client before we report the command status. Also
|
||||
|
|
@ -262,7 +262,7 @@ object MainLoop {
|
|||
}
|
||||
exchange.setExec(None)
|
||||
newState.get(sbt.Keys.currentTaskProgress).foreach(_.progress.stop())
|
||||
newState.remove(sbt.Keys.currentTaskProgress)
|
||||
newState.remove(sbt.Keys.currentTaskProgress).remove(Keys.terminalKey)
|
||||
}
|
||||
state.get(CheckBuildSourcesKey) match {
|
||||
case Some(cbs) =>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt
|
||||
|
||||
import java.io.{ InputStream, PrintStream }
|
||||
import sbt.internal.util.{ JLine3, Terminal => ITerminal }
|
||||
|
||||
/**
|
||||
* A Terminal represents a ui connection to sbt. It may control the embedded console
|
||||
* for an sbt server or it may control a remote client connected through sbtn. The
|
||||
* Terminal is particularly useful whenever an sbt task needs to receive input from
|
||||
* the user.
|
||||
*
|
||||
*/
|
||||
trait Terminal {
|
||||
|
||||
/**
|
||||
* Returns the width of the terminal.
|
||||
*
|
||||
* @return the width ot the terminal
|
||||
*/
|
||||
def getWidth: Int
|
||||
|
||||
/**
|
||||
* Returns the height of the terminal
|
||||
*
|
||||
* @return the height of the terminal
|
||||
*/
|
||||
def getHeight: Int
|
||||
|
||||
/**
|
||||
* An input stream associated with the terminal. Bytes inputted by the
|
||||
* user may be read from this input stream.
|
||||
*
|
||||
* @return the terminal's input stream
|
||||
*/
|
||||
def inputStream: InputStream
|
||||
|
||||
/**
|
||||
* A print stream associated with the terminal. Writing to this output
|
||||
* stream should display text on the terminal.
|
||||
*
|
||||
* @return the terminal's input stream
|
||||
*/
|
||||
def printStream: PrintStream
|
||||
|
||||
/**
|
||||
* Sets the mode of the terminal. By default,the terminal will be in canonical mode
|
||||
* with echo enabled. This means that the terminal's inputStream will not return any
|
||||
* bytes until a newline is received and that all of the characters inputed by the
|
||||
* user will be echoed to the terminal's output stream.
|
||||
*
|
||||
* @param canonical toggles whether or not the terminal input stream is line buffered
|
||||
* @param echo toggles whether or not to echo the characters received from the terminal input stream
|
||||
*/
|
||||
def setMode(canonical: Boolean, echo: Boolean): Unit
|
||||
|
||||
}
|
||||
private[sbt] object Terminal {
|
||||
private[sbt] def apply(term: ITerminal): Terminal = new Terminal {
|
||||
override def getHeight: Int = term.getHeight
|
||||
override def getWidth: Int = term.getWidth
|
||||
override def inputStream: InputStream = term.inputStream
|
||||
override def printStream: PrintStream = term.printStream
|
||||
override def setMode(canonical: Boolean, echo: Boolean): Unit =
|
||||
JLine3.setMode(term, canonical, echo)
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ import sbt.internal.util.{
|
|||
GlobalLogging,
|
||||
MainAppender,
|
||||
Settings,
|
||||
Terminal
|
||||
Terminal => ITerminal,
|
||||
}
|
||||
|
||||
object PluginCommandTestPlugin0 extends AutoPlugin { override def requires = empty }
|
||||
|
|
@ -77,7 +77,7 @@ object FakeState {
|
|||
val logFile = File.createTempFile("sbt", ".log")
|
||||
try {
|
||||
val state = FakeState(logFile, enabledPlugins: _*)
|
||||
Terminal.withOut(new PrintStream(outBuffer, true)) {
|
||||
ITerminal.withOut(new PrintStream(outBuffer, true)) {
|
||||
MainLoop.processCommand(Exec(input, None), state)
|
||||
}
|
||||
new String(outBuffer.toByteArray)
|
||||
|
|
|
|||
Loading…
Reference in New Issue