mirror of https://github.com/sbt/sbt.git
Switch from JLine 1.0 to 2.10.
This commit is contained in:
parent
1870edfd27
commit
92e99cfef0
|
|
@ -3,7 +3,7 @@
|
||||||
*/
|
*/
|
||||||
package xsbt.boot
|
package xsbt.boot
|
||||||
|
|
||||||
import jline.ConsoleReader
|
import jline.console.ConsoleReader
|
||||||
abstract class JLine
|
abstract class JLine
|
||||||
{
|
{
|
||||||
protected[this] val reader: ConsoleReader
|
protected[this] val reader: ConsoleReader
|
||||||
|
|
@ -17,12 +17,12 @@ abstract class JLine
|
||||||
}
|
}
|
||||||
private object JLine
|
private object JLine
|
||||||
{
|
{
|
||||||
def terminal = jline.Terminal.getTerminal
|
def terminal = jline.TerminalFactory.get
|
||||||
def createReader() =
|
def createReader() =
|
||||||
terminal.synchronized
|
terminal.synchronized
|
||||||
{
|
{
|
||||||
val cr = new ConsoleReader
|
val cr = new ConsoleReader
|
||||||
terminal.enableEcho()
|
terminal.setEchoEnabled(true)
|
||||||
cr.setBellEnabled(false)
|
cr.setBellEnabled(false)
|
||||||
cr
|
cr
|
||||||
}
|
}
|
||||||
|
|
@ -31,9 +31,9 @@ private object JLine
|
||||||
val t = terminal
|
val t = terminal
|
||||||
t.synchronized
|
t.synchronized
|
||||||
{
|
{
|
||||||
t.disableEcho()
|
t.setEchoEnabled(false)
|
||||||
try { action }
|
try { action }
|
||||||
finally { t.enableEcho() }
|
finally { t.setEchoEnabled(true) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ object Graph
|
||||||
// [info] |
|
// [info] |
|
||||||
// [info] +-quux
|
// [info] +-quux
|
||||||
def toAscii[A](top: A, children: A => Seq[A], display: A => String): String = {
|
def toAscii[A](top: A, children: A => Seq[A], display: A => String): String = {
|
||||||
val maxColumn = JLine.usingTerminal(_.getTerminalWidth) - 8
|
val maxColumn = JLine.usingTerminal(_.getWidth) - 8
|
||||||
val twoSpaces = " " + " " // prevent accidentally being converted into a tab
|
val twoSpaces = " " + " " // prevent accidentally being converted into a tab
|
||||||
def limitLine(s: String): String =
|
def limitLine(s: String): String =
|
||||||
if (s.length > maxColumn) s.slice(0, maxColumn - 2) + ".."
|
if (s.length > maxColumn) s.slice(0, maxColumn - 2) + ".."
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ object %s {
|
||||||
object Common
|
object Common
|
||||||
{
|
{
|
||||||
def lib(m: ModuleID) = libraryDependencies += m
|
def lib(m: ModuleID) = libraryDependencies += m
|
||||||
lazy val jlineDep = "jline" % "jline" % "1.0" intransitive()
|
lazy val jlineDep = "jline" % "jline" % "2.10" intransitive()
|
||||||
lazy val jline = lib(jlineDep)
|
lazy val jline = lib(jlineDep)
|
||||||
lazy val ivy = lib("org.apache.ivy" % "ivy" % "2.3.0-rc1")
|
lazy val ivy = lib("org.apache.ivy" % "ivy" % "2.3.0-rc1")
|
||||||
lazy val httpclient = lib("commons-httpclient" % "commons-httpclient" % "3.1")
|
lazy val httpclient = lib("commons-httpclient" % "commons-httpclient" % "3.1")
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
*/
|
*/
|
||||||
package sbt
|
package sbt
|
||||||
|
|
||||||
import jline.{ConsoleReader, History}
|
import jline.console.ConsoleReader
|
||||||
|
import jline.console.history.{FileHistory, MemoryHistory}
|
||||||
import java.io.{File, InputStream, PrintWriter}
|
import java.io.{File, InputStream, PrintWriter}
|
||||||
import complete.Parser
|
import complete.Parser
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
@ -12,9 +13,6 @@ abstract class JLine extends LineReader
|
||||||
{
|
{
|
||||||
protected[this] val handleCONT: Boolean
|
protected[this] val handleCONT: Boolean
|
||||||
protected[this] val reader: ConsoleReader
|
protected[this] val reader: ConsoleReader
|
||||||
/** Is the input stream at EOF? Compensates for absent EOF detection in JLine's UnsupportedTerminal. */
|
|
||||||
protected[this] val inputEof = new AtomicBoolean(false)
|
|
||||||
protected[this] val historyPath: Option[File]
|
|
||||||
|
|
||||||
def readLine(prompt: String, mask: Option[Char] = None) = JLine.withJLine { unsynchronizedReadLine(prompt, mask) }
|
def readLine(prompt: String, mask: Option[Char] = None) = JLine.withJLine { unsynchronizedReadLine(prompt, mask) }
|
||||||
|
|
||||||
|
|
@ -26,14 +24,12 @@ abstract class JLine extends LineReader
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def readLineWithHistory(prompt: String, mask: Option[Char]): String =
|
private[this] def readLineWithHistory(prompt: String, mask: Option[Char]): String =
|
||||||
historyPath match
|
reader.getHistory match
|
||||||
{
|
{
|
||||||
case None => readLineDirect(prompt, mask)
|
case fh: FileHistory =>
|
||||||
case Some(file) =>
|
|
||||||
val h = reader.getHistory
|
|
||||||
JLine.loadHistory(h, file)
|
|
||||||
try { readLineDirect(prompt, mask) }
|
try { readLineDirect(prompt, mask) }
|
||||||
finally { JLine.saveHistory(h, file) }
|
finally { fh.flush() }
|
||||||
|
case _ => readLineDirect(prompt, mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def readLineDirect(prompt: String, mask: Option[Char]): String =
|
private[this] def readLineDirect(prompt: String, mask: Option[Char]): String =
|
||||||
|
|
@ -44,34 +40,33 @@ abstract class JLine extends LineReader
|
||||||
private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): String =
|
private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): String =
|
||||||
{
|
{
|
||||||
val newprompt = handleMultilinePrompt(prompt)
|
val newprompt = handleMultilinePrompt(prompt)
|
||||||
val line = mask match {
|
mask match {
|
||||||
case Some(m) => reader.readLine(newprompt, m)
|
case Some(m) => reader.readLine(newprompt, m)
|
||||||
case None => reader.readLine(newprompt)
|
case None => reader.readLine(newprompt)
|
||||||
}
|
}
|
||||||
if (inputEof.get) null else line
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def handleMultilinePrompt(prompt: String): String = {
|
private[this] def handleMultilinePrompt(prompt: String): String = {
|
||||||
val lines = """\r?\n""".r.split(prompt)
|
val lines = """\r?\n""".r.split(prompt)
|
||||||
lines.size match {
|
lines.size match {
|
||||||
case 0 | 1 => prompt
|
case 0 | 1 => prompt
|
||||||
case _ => reader.printString(lines.init.mkString("\n") + "\n"); lines.last;
|
case _ => reader.print(lines.init.mkString("\n") + "\n"); lines.last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def resume()
|
private[this] def resume()
|
||||||
{
|
{
|
||||||
jline.Terminal.resetTerminal
|
jline.TerminalFactory.reset
|
||||||
JLine.terminal.disableEcho()
|
JLine.terminal.setEchoEnabled(false)
|
||||||
reader.drawLine()
|
reader.drawLine()
|
||||||
reader.flushConsole()
|
reader.flush()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private object JLine
|
private object JLine
|
||||||
{
|
{
|
||||||
// When calling this, ensure that enableEcho has been or will be called.
|
// When calling this, ensure that enableEcho has been or will be called.
|
||||||
// getTerminal will initialize the terminal to disable echo.
|
// TerminalFactory.get will initialize the terminal to disable echo.
|
||||||
private def terminal = jline.Terminal.getTerminal
|
private def terminal = jline.TerminalFactory.get
|
||||||
private def withTerminal[T](f: jline.Terminal => T): T =
|
private def withTerminal[T](f: jline.Terminal => T): T =
|
||||||
synchronized
|
synchronized
|
||||||
{
|
{
|
||||||
|
|
@ -82,33 +77,26 @@ private object JLine
|
||||||
* This ensures synchronized access as well as re-enabling echo after getting the Terminal. */
|
* This ensures synchronized access as well as re-enabling echo after getting the Terminal. */
|
||||||
def usingTerminal[T](f: jline.Terminal => T): T =
|
def usingTerminal[T](f: jline.Terminal => T): T =
|
||||||
withTerminal { t =>
|
withTerminal { t =>
|
||||||
t.enableEcho()
|
t.setEchoEnabled(true)
|
||||||
f(t)
|
f(t)
|
||||||
}
|
}
|
||||||
def createReader() =
|
def createReader(historyPath: Option[File]) =
|
||||||
usingTerminal { t =>
|
usingTerminal { t =>
|
||||||
val cr = new ConsoleReader
|
val cr = new ConsoleReader
|
||||||
cr.setBellEnabled(false)
|
cr.setBellEnabled(false)
|
||||||
|
val h = historyPath match {
|
||||||
|
case None => new MemoryHistory
|
||||||
|
case Some(file) => new FileHistory(file)
|
||||||
|
}
|
||||||
|
h.setMaxSize(MaxHistorySize)
|
||||||
|
cr.setHistory(h)
|
||||||
cr
|
cr
|
||||||
}
|
}
|
||||||
def withJLine[T](action: => T): T =
|
def withJLine[T](action: => T): T =
|
||||||
withTerminal { t =>
|
withTerminal { t =>
|
||||||
t.disableEcho()
|
t.setEchoEnabled(false)
|
||||||
try { action }
|
try { action }
|
||||||
finally { t.enableEcho() }
|
finally { t.setEchoEnabled(true) }
|
||||||
}
|
|
||||||
private[sbt] def loadHistory(h: History, file: File)
|
|
||||||
{
|
|
||||||
h.setMaxSize(MaxHistorySize)
|
|
||||||
if(file.isFile) IO.reader(file)( h.load )
|
|
||||||
}
|
|
||||||
private[sbt] def saveHistory(h: History, file: File): Unit =
|
|
||||||
Using.fileWriter()(file) { writer =>
|
|
||||||
val out = new PrintWriter(writer, false)
|
|
||||||
h.setOutput(out)
|
|
||||||
h.flushBuffer()
|
|
||||||
out.close()
|
|
||||||
h.setOutput(null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def simple(historyPath: Option[File], handleCONT: Boolean = HandleCONT): SimpleReader = new SimpleReader(historyPath, handleCONT)
|
def simple(historyPath: Option[File], handleCONT: Boolean = HandleCONT): SimpleReader = new SimpleReader(historyPath, handleCONT)
|
||||||
|
|
@ -120,30 +108,19 @@ trait LineReader
|
||||||
{
|
{
|
||||||
def readLine(prompt: String, mask: Option[Char] = None): Option[String]
|
def readLine(prompt: String, mask: Option[Char] = None): Option[String]
|
||||||
}
|
}
|
||||||
final class FullReader(val historyPath: Option[File], complete: Parser[_], val handleCONT: Boolean = JLine.HandleCONT) extends JLine
|
final class FullReader(historyPath: Option[File], complete: Parser[_], val handleCONT: Boolean = JLine.HandleCONT) extends JLine
|
||||||
{
|
{
|
||||||
protected[this] val reader =
|
protected[this] val reader =
|
||||||
{
|
{
|
||||||
val cr = new ConsoleReader
|
val cr = JLine.createReader(historyPath)
|
||||||
if (!cr.getTerminal.isSupported) {
|
|
||||||
val input = cr.getInput
|
|
||||||
cr.setInput(new InputStream {
|
|
||||||
def read(): Int = {
|
|
||||||
val c = input.read()
|
|
||||||
if (c == -1) inputEof.set(true)
|
|
||||||
c
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
cr.setBellEnabled(false)
|
|
||||||
sbt.complete.JLineCompletion.installCustomCompletor(cr, complete)
|
sbt.complete.JLineCompletion.installCustomCompletor(cr, complete)
|
||||||
cr
|
cr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimpleReader private[sbt] (val historyPath: Option[File], val handleCONT: Boolean) extends JLine
|
class SimpleReader private[sbt] (historyPath: Option[File], val handleCONT: Boolean) extends JLine
|
||||||
{
|
{
|
||||||
protected[this] val reader = JLine.createReader()
|
protected[this] val reader = JLine.createReader(historyPath)
|
||||||
}
|
}
|
||||||
object SimpleReader extends SimpleReader(None, JLine.HandleCONT)
|
object SimpleReader extends SimpleReader(None, JLine.HandleCONT)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
*/
|
*/
|
||||||
package sbt.complete
|
package sbt.complete
|
||||||
|
|
||||||
import jline.{CandidateListCompletionHandler,Completor,CompletionHandler,ConsoleReader}
|
import jline.console.ConsoleReader
|
||||||
|
import jline.console.completer.{CandidateListCompletionHandler,Completer,CompletionHandler}
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import collection.JavaConversions
|
import collection.JavaConversions
|
||||||
|
|
||||||
|
|
@ -15,8 +16,8 @@ object JLineCompletion
|
||||||
installCustomCompletor(customCompletor(complete), reader)
|
installCustomCompletor(customCompletor(complete), reader)
|
||||||
def installCustomCompletor(complete: (ConsoleReader, Int) => Boolean, reader: ConsoleReader): Unit =
|
def installCustomCompletor(complete: (ConsoleReader, Int) => Boolean, reader: ConsoleReader): Unit =
|
||||||
{
|
{
|
||||||
reader.removeCompletor(DummyCompletor)
|
reader.removeCompleter(DummyCompletor)
|
||||||
reader.addCompletor(DummyCompletor)
|
reader.addCompleter(DummyCompletor)
|
||||||
reader.setCompletionHandler(new CustomHandler(complete))
|
reader.setCompletionHandler(new CustomHandler(complete))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,13 +25,13 @@ object JLineCompletion
|
||||||
{
|
{
|
||||||
private[this] var previous: Option[(String,Int)] = None
|
private[this] var previous: Option[(String,Int)] = None
|
||||||
private[this] var level: Int = 1
|
private[this] var level: Int = 1
|
||||||
override def complete(reader: ConsoleReader, candidates: java.util.List[_], position: Int) = {
|
override def complete(reader: ConsoleReader, candidates: java.util.List[CharSequence], position: Int) = {
|
||||||
val current = Some(bufferSnapshot(reader))
|
val current = Some(bufferSnapshot(reader))
|
||||||
level = if(current == previous) level + 1 else 1
|
level = if(current == previous) level + 1 else 1
|
||||||
previous = current
|
previous = current
|
||||||
try completeImpl(reader, level)
|
try completeImpl(reader, level)
|
||||||
catch { case e: Exception =>
|
catch { case e: Exception =>
|
||||||
reader.printString("\nException occurred while determining completions.")
|
reader.print("\nException occurred while determining completions.")
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
@ -40,9 +41,9 @@ object JLineCompletion
|
||||||
// always provides dummy completions so that the custom completion handler gets called
|
// always provides dummy completions so that the custom completion handler gets called
|
||||||
// (ConsoleReader doesn't call the handler if there aren't any completions)
|
// (ConsoleReader doesn't call the handler if there aren't any completions)
|
||||||
// the custom handler will then throw away the candidates and call the custom function
|
// the custom handler will then throw away the candidates and call the custom function
|
||||||
private[this] final object DummyCompletor extends Completor
|
private[this] final object DummyCompletor extends Completer
|
||||||
{
|
{
|
||||||
override def complete(buffer: String, cursor: Int, candidates: java.util.List[_]): Int =
|
override def complete(buffer: String, cursor: Int, candidates: java.util.List[CharSequence]): Int =
|
||||||
{
|
{
|
||||||
candidates.asInstanceOf[java.util.List[String]] add "dummy"
|
candidates.asInstanceOf[java.util.List[String]] add "dummy"
|
||||||
0
|
0
|
||||||
|
|
@ -73,19 +74,19 @@ object JLineCompletion
|
||||||
def customCompletor(f: (String, Int) => (Seq[String], Seq[String])): (ConsoleReader, Int) => Boolean =
|
def customCompletor(f: (String, Int) => (Seq[String], Seq[String])): (ConsoleReader, Int) => Boolean =
|
||||||
(reader, level) => {
|
(reader, level) => {
|
||||||
val success = complete(beforeCursor(reader), reader => f(reader, level), reader)
|
val success = complete(beforeCursor(reader), reader => f(reader, level), reader)
|
||||||
reader.flushConsole()
|
reader.flush()
|
||||||
success
|
success
|
||||||
}
|
}
|
||||||
|
|
||||||
def bufferSnapshot(reader: ConsoleReader): (String, Int) =
|
def bufferSnapshot(reader: ConsoleReader): (String, Int) =
|
||||||
{
|
{
|
||||||
val b = reader.getCursorBuffer
|
val b = reader.getCursorBuffer
|
||||||
(b.getBuffer.toString, b.cursor)
|
(b.buffer.toString, b.cursor)
|
||||||
}
|
}
|
||||||
def beforeCursor(reader: ConsoleReader): String =
|
def beforeCursor(reader: ConsoleReader): String =
|
||||||
{
|
{
|
||||||
val b = reader.getCursorBuffer
|
val b = reader.getCursorBuffer
|
||||||
b.getBuffer.substring(0, b.cursor)
|
b.buffer.substring(0, b.cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns false if there was nothing to insert and nothing to display
|
// returns false if there was nothing to insert and nothing to display
|
||||||
|
|
@ -120,16 +121,16 @@ object JLineCompletion
|
||||||
def printCompletions(cs: Seq[String], reader: ConsoleReader)
|
def printCompletions(cs: Seq[String], reader: ConsoleReader)
|
||||||
{
|
{
|
||||||
val print = shouldPrint(cs, reader)
|
val print = shouldPrint(cs, reader)
|
||||||
reader.printNewline()
|
reader.println()
|
||||||
if(print) printLinesAndColumns(cs, reader)
|
if(print) printLinesAndColumns(cs, reader)
|
||||||
}
|
}
|
||||||
def printLinesAndColumns(cs: Seq[String], reader: ConsoleReader)
|
def printLinesAndColumns(cs: Seq[String], reader: ConsoleReader)
|
||||||
{
|
{
|
||||||
val (lines, columns) = cs partition hasNewline
|
val (lines, columns) = cs partition hasNewline
|
||||||
for(line <- lines) {
|
for(line <- lines) {
|
||||||
reader.printString(line)
|
reader.print(line)
|
||||||
if(line.charAt(line.length - 1) != '\n')
|
if(line.charAt(line.length - 1) != '\n')
|
||||||
reader.printNewline()
|
reader.println()
|
||||||
}
|
}
|
||||||
reader.printColumns(JavaConversions.seqAsJavaList(columns.map(_.trim)))
|
reader.printColumns(JavaConversions.seqAsJavaList(columns.map(_.trim)))
|
||||||
}
|
}
|
||||||
|
|
@ -137,15 +138,15 @@ object JLineCompletion
|
||||||
def shouldPrint(cs: Seq[String], reader: ConsoleReader): Boolean =
|
def shouldPrint(cs: Seq[String], reader: ConsoleReader): Boolean =
|
||||||
{
|
{
|
||||||
val size = cs.size
|
val size = cs.size
|
||||||
(size <= reader.getAutoprintThreshhold) ||
|
(size <= reader.getAutoprintThreshold) ||
|
||||||
confirm("Display all %d possibilities? (y or n) ".format(size), 'y', 'n', reader)
|
confirm("Display all %d possibilities? (y or n) ".format(size), 'y', 'n', reader)
|
||||||
}
|
}
|
||||||
def confirm(prompt: String, trueC: Char, falseC: Char, reader: ConsoleReader): Boolean =
|
def confirm(prompt: String, trueC: Char, falseC: Char, reader: ConsoleReader): Boolean =
|
||||||
{
|
{
|
||||||
reader.printNewline()
|
reader.println()
|
||||||
reader.printString(prompt)
|
reader.print(prompt)
|
||||||
reader.flushConsole()
|
reader.flush()
|
||||||
reader.readCharacter( Array(trueC, falseC) ) == trueC
|
reader.readCharacter(trueC, falseC) == trueC
|
||||||
}
|
}
|
||||||
|
|
||||||
def commonPrefix(s: Seq[String]): String = if(s.isEmpty) "" else s reduceLeft commonPrefix
|
def commonPrefix(s: Seq[String]): String = if(s.isEmpty) "" else s reduceLeft commonPrefix
|
||||||
|
|
|
||||||
|
|
@ -81,9 +81,9 @@ object ConsoleLogger
|
||||||
|
|
||||||
private[this] def ansiSupported =
|
private[this] def ansiSupported =
|
||||||
try {
|
try {
|
||||||
val terminal = jline.Terminal.getTerminal
|
val terminal = jline.TerminalFactory.get
|
||||||
terminal.enableEcho() // #460
|
terminal.setEchoEnabled(true) // #460
|
||||||
terminal.isANSISupported
|
terminal.isAnsiSupported
|
||||||
} catch {
|
} catch {
|
||||||
case e: Exception => !isWindows
|
case e: Exception => !isWindows
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue