Move JLine apis into LineReader

It is better that sbt not expose the implementation detail that
LineReader is implemented by JLine. Other terminal related apis should
be handled by sbt.internal.util.Terminal.
This commit is contained in:
Ethan Atkins 2019-12-12 18:50:53 -08:00
parent 9218d3c087
commit a449b1ff2d
4 changed files with 55 additions and 18 deletions

View File

@ -16,6 +16,36 @@ import sbt.internal.util.complete.Parser
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.concurrent.duration._ import scala.concurrent.duration._
trait LineReader {
def readLine(prompt: String, mask: Option[Char] = None): Option[String]
def redraw(): Unit = ()
}
object LineReader {
val HandleCONT =
!java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT)
val MaxHistorySize = 500
def createReader(historyPath: Option[File], in: InputStream): ConsoleReader = {
val cr = Terminal.createReader(in)
cr.setExpandEvents(false) // https://issues.scala-lang.org/browse/SI-7650
cr.setBellEnabled(false)
val h = historyPath match {
case None => new MemoryHistory
case Some(file) => new FileHistory(file): MemoryHistory
}
h.setMaxSize(MaxHistorySize)
cr.setHistory(h)
cr
}
def simple(
historyPath: Option[File],
handleCONT: Boolean = HandleCONT,
injectThreadSleep: Boolean = false
): LineReader = new SimpleReader(historyPath, handleCONT, injectThreadSleep)
}
abstract class JLine extends LineReader { abstract class JLine extends LineReader {
protected[this] def handleCONT: Boolean protected[this] def handleCONT: Boolean
protected[this] def reader: ConsoleReader protected[this] def reader: ConsoleReader
@ -92,6 +122,7 @@ abstract class JLine extends LineReader {
} }
} }
@deprecated("Use LineReader apis", "1.4.0")
private[sbt] object JLine { private[sbt] object JLine {
@deprecated("For binary compatibility only", "1.4.0") @deprecated("For binary compatibility only", "1.4.0")
protected[this] val originalIn = new FileInputStream(FileDescriptor.in) protected[this] val originalIn = new FileInputStream(FileDescriptor.in)
@ -117,8 +148,10 @@ private[sbt] object JLine {
def usingTerminal[T](f: jline.Terminal => T): T = def usingTerminal[T](f: jline.Terminal => T): T =
Terminal.withCanonicalIn(f(Terminal.deprecatedTeminal)) Terminal.withCanonicalIn(f(Terminal.deprecatedTeminal))
@deprecated("unused", "1.4.0")
def createReader(): ConsoleReader = createReader(None, Terminal.wrappedSystemIn) def createReader(): ConsoleReader = createReader(None, Terminal.wrappedSystemIn)
@deprecated("Use LineReader.createReader", "1.4.0")
def createReader(historyPath: Option[File], in: InputStream): ConsoleReader = { def createReader(historyPath: Option[File], in: InputStream): ConsoleReader = {
val cr = Terminal.createReader(in) val cr = Terminal.createReader(in)
cr.setExpandEvents(false) // https://issues.scala-lang.org/browse/SI-7650 cr.setExpandEvents(false) // https://issues.scala-lang.org/browse/SI-7650
@ -135,16 +168,18 @@ private[sbt] object JLine {
@deprecated("Avoid referencing JLine directly. Use Terminal.withRawSystemIn instead.", "1.4.0") @deprecated("Avoid referencing JLine directly. Use Terminal.withRawSystemIn instead.", "1.4.0")
def withJLine[T](action: => T): T = Terminal.withRawSystemIn(action) def withJLine[T](action: => T): T = Terminal.withRawSystemIn(action)
@deprecated("Use LineReader.simple instead", "1.4.0")
def simple( def simple(
historyPath: Option[File], historyPath: Option[File],
handleCONT: Boolean = HandleCONT, handleCONT: Boolean = LineReader.HandleCONT,
injectThreadSleep: Boolean = false injectThreadSleep: Boolean = false
): SimpleReader = new SimpleReader(historyPath, handleCONT, injectThreadSleep) ): SimpleReader = new SimpleReader(historyPath, handleCONT, injectThreadSleep)
val MaxHistorySize = 500 @deprecated("Use LineReader.MaxHistorySize", "1.4.0")
val MaxHistorySize = LineReader.MaxHistorySize
val HandleCONT = @deprecated("Use LineReader.HandleCONT", "1.4.0")
!java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT) val HandleCONT = LineReader.HandleCONT
} }
@deprecated("For binary compatibility only", "1.4.0") @deprecated("For binary compatibility only", "1.4.0")
@ -172,11 +207,6 @@ private[sbt] class InputStreamWrapper(is: InputStream, val poll: Duration)
} }
} }
trait LineReader {
def readLine(prompt: String, mask: Option[Char] = None): Option[String]
def redraw(): Unit = ()
}
final class FullReader( final class FullReader(
historyPath: Option[File], historyPath: Option[File],
complete: Parser[_], complete: Parser[_],
@ -187,11 +217,11 @@ final class FullReader(
def this( def this(
historyPath: Option[File], historyPath: Option[File],
complete: Parser[_], complete: Parser[_],
handleCONT: Boolean = JLine.HandleCONT, handleCONT: Boolean = LineReader.HandleCONT,
injectThreadSleep: Boolean = false injectThreadSleep: Boolean = false
) = this(historyPath, complete, handleCONT, JLine.makeInputStream(injectThreadSleep)) ) = this(historyPath, complete, handleCONT, JLine.makeInputStream(injectThreadSleep))
protected[this] val reader: ConsoleReader = { protected[this] val reader: ConsoleReader = {
val cr = JLine.createReader(historyPath, inputStream) val cr = LineReader.createReader(historyPath, inputStream)
sbt.internal.util.complete.JLineCompletion.installCustomCompletor(cr, complete) sbt.internal.util.complete.JLineCompletion.installCustomCompletor(cr, complete)
cr cr
} }
@ -205,7 +235,7 @@ class SimpleReader private[sbt] (
def this(historyPath: Option[File], handleCONT: Boolean, injectThreadSleep: Boolean) = def this(historyPath: Option[File], handleCONT: Boolean, injectThreadSleep: Boolean) =
this(historyPath, handleCONT, Terminal.wrappedSystemIn) this(historyPath, handleCONT, Terminal.wrappedSystemIn)
protected[this] val reader: ConsoleReader = protected[this] val reader: ConsoleReader =
JLine.createReader(historyPath, inputStream) LineReader.createReader(historyPath, inputStream)
} }
object SimpleReader extends SimpleReader(None, JLine.HandleCONT, false) object SimpleReader extends SimpleReader(None, LineReader.HandleCONT, false)

View File

@ -9,7 +9,7 @@ package sbt
import java.nio.file.Paths import java.nio.file.Paths
import sbt.util.Level import sbt.util.Level
import sbt.internal.util.{ AttributeKey, FullReader, JLine, Terminal } import sbt.internal.util.{ AttributeKey, FullReader, LineReader, Terminal }
import sbt.internal.util.complete.{ import sbt.internal.util.complete.{
Completion, Completion,
Completions, Completions,
@ -34,7 +34,9 @@ import BasicKeys._
import java.io.File import java.io.File
import sbt.io.IO import sbt.io.IO
import sbt.util.Level
import scala.Function.tupled
import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
import scala.util.control.NonFatal import scala.util.control.NonFatal
@ -374,7 +376,7 @@ object BasicCommands {
val history = (s get historyPath) getOrElse (new File(s.baseDir, ".history")).some val history = (s get historyPath) getOrElse (new File(s.baseDir, ".history")).some
val prompt = (s get shellPrompt) match { case Some(pf) => pf(s); case None => "> " } val prompt = (s get shellPrompt) match { case Some(pf) => pf(s); case None => "> " }
val reader = val reader =
new FullReader(history, s.combinedParser, JLine.HandleCONT, Terminal.wrappedSystemIn) new FullReader(history, s.combinedParser, LineReader.HandleCONT, Terminal.wrappedSystemIn)
val line = reader.readLine(prompt) val line = reader.readLine(prompt)
line match { line match {
case Some(line) => case Some(line) =>

View File

@ -29,7 +29,12 @@ private[sbt] final class ConsoleChannel(val name: String) extends CommandChannel
private val history = s.get(historyPath).getOrElse(Some(new File(s.baseDir, ".history"))) private val history = s.get(historyPath).getOrElse(Some(new File(s.baseDir, ".history")))
private val prompt = getPrompt(s) private val prompt = getPrompt(s)
private val reader = private val reader =
new FullReader(history, s.combinedParser, JLine.HandleCONT, Terminal.throwOnClosedSystemIn) new FullReader(
history,
s.combinedParser,
LineReader.HandleCONT,
Terminal.throwOnClosedSystemIn
)
setDaemon(true) setDaemon(true)
start() start()
override def run(): Unit = override def run(): Unit =

View File

@ -15,7 +15,7 @@ import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
import sbt.internal.langserver.{ LogMessageParams, MessageType, PublishDiagnosticsParams } import sbt.internal.langserver.{ LogMessageParams, MessageType, PublishDiagnosticsParams }
import sbt.internal.protocol._ import sbt.internal.protocol._
import sbt.internal.util.{ ConsoleAppender, JLine } import sbt.internal.util.{ ConsoleAppender, LineReader }
import sbt.io.IO import sbt.io.IO
import sbt.io.syntax._ import sbt.io.syntax._
import sbt.protocol._ import sbt.protocol._
@ -216,7 +216,7 @@ class NetworkClient(configuration: xsbti.AppConfiguration, arguments: List[Strin
} }
def shell(): Unit = { def shell(): Unit = {
val reader = JLine.simple(None, JLine.HandleCONT, injectThreadSleep = true) val reader = LineReader.simple(None, LineReader.HandleCONT, injectThreadSleep = true)
while (running.get) { while (running.get) {
reader.readLine("> ", None) match { reader.readLine("> ", None) match {
case Some("shutdown") => case Some("shutdown") =>