Switch from JLine 1.0 to 2.10.

This commit is contained in:
Alex Dupre 2013-02-25 16:06:45 +01:00 committed by Mark Harrah
parent 1870edfd27
commit 92e99cfef0
6 changed files with 56 additions and 78 deletions

View File

@ -3,7 +3,7 @@
*/
package xsbt.boot
import jline.ConsoleReader
import jline.console.ConsoleReader
abstract class JLine
{
protected[this] val reader: ConsoleReader
@ -17,12 +17,12 @@ abstract class JLine
}
private object JLine
{
def terminal = jline.Terminal.getTerminal
def terminal = jline.TerminalFactory.get
def createReader() =
terminal.synchronized
{
val cr = new ConsoleReader
terminal.enableEcho()
terminal.setEchoEnabled(true)
cr.setBellEnabled(false)
cr
}
@ -31,9 +31,9 @@ private object JLine
val t = terminal
t.synchronized
{
t.disableEcho()
t.setEchoEnabled(false)
try { action }
finally { t.enableEcho() }
finally { t.setEchoEnabled(true) }
}
}
}

View File

@ -56,7 +56,7 @@ object Graph
// [info] |
// [info] +-quux
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
def limitLine(s: String): String =
if (s.length > maxColumn) s.slice(0, maxColumn - 2) + ".."

View File

@ -155,7 +155,7 @@ object %s {
object Common
{
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 ivy = lib("org.apache.ivy" % "ivy" % "2.3.0-rc1")
lazy val httpclient = lib("commons-httpclient" % "commons-httpclient" % "3.1")

View File

@ -3,7 +3,8 @@
*/
package sbt
import jline.{ConsoleReader, History}
import jline.console.ConsoleReader
import jline.console.history.{FileHistory, MemoryHistory}
import java.io.{File, InputStream, PrintWriter}
import complete.Parser
import java.util.concurrent.atomic.AtomicBoolean
@ -12,9 +13,6 @@ abstract class JLine extends LineReader
{
protected[this] val handleCONT: Boolean
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) }
@ -26,14 +24,12 @@ abstract class JLine extends LineReader
}
private[this] def readLineWithHistory(prompt: String, mask: Option[Char]): String =
historyPath match
reader.getHistory match
{
case None => readLineDirect(prompt, mask)
case Some(file) =>
val h = reader.getHistory
JLine.loadHistory(h, file)
case fh: FileHistory =>
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 =
@ -44,34 +40,33 @@ abstract class JLine extends LineReader
private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): String =
{
val newprompt = handleMultilinePrompt(prompt)
val line = mask match {
mask match {
case Some(m) => reader.readLine(newprompt, m)
case None => reader.readLine(newprompt)
}
if (inputEof.get) null else line
}
private[this] def handleMultilinePrompt(prompt: String): String = {
val lines = """\r?\n""".r.split(prompt)
lines.size match {
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()
{
jline.Terminal.resetTerminal
JLine.terminal.disableEcho()
jline.TerminalFactory.reset
JLine.terminal.setEchoEnabled(false)
reader.drawLine()
reader.flushConsole()
reader.flush()
}
}
private object JLine
{
// When calling this, ensure that enableEcho has been or will be called.
// getTerminal will initialize the terminal to disable echo.
private def terminal = jline.Terminal.getTerminal
// TerminalFactory.get will initialize the terminal to disable echo.
private def terminal = jline.TerminalFactory.get
private def withTerminal[T](f: jline.Terminal => T): T =
synchronized
{
@ -82,33 +77,26 @@ private object JLine
* This ensures synchronized access as well as re-enabling echo after getting the Terminal. */
def usingTerminal[T](f: jline.Terminal => T): T =
withTerminal { t =>
t.enableEcho()
t.setEchoEnabled(true)
f(t)
}
def createReader() =
def createReader(historyPath: Option[File]) =
usingTerminal { t =>
val cr = new ConsoleReader
cr.setBellEnabled(false)
val h = historyPath match {
case None => new MemoryHistory
case Some(file) => new FileHistory(file)
}
h.setMaxSize(MaxHistorySize)
cr.setHistory(h)
cr
}
def withJLine[T](action: => T): T =
withTerminal { t =>
t.disableEcho()
t.setEchoEnabled(false)
try { action }
finally { t.enableEcho() }
}
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)
finally { t.setEchoEnabled(true) }
}
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]
}
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 =
{
val cr = new ConsoleReader
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)
val cr = JLine.createReader(historyPath)
sbt.complete.JLineCompletion.installCustomCompletor(cr, complete)
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)

View File

@ -3,7 +3,8 @@
*/
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 collection.JavaConversions
@ -15,8 +16,8 @@ object JLineCompletion
installCustomCompletor(customCompletor(complete), reader)
def installCustomCompletor(complete: (ConsoleReader, Int) => Boolean, reader: ConsoleReader): Unit =
{
reader.removeCompletor(DummyCompletor)
reader.addCompletor(DummyCompletor)
reader.removeCompleter(DummyCompletor)
reader.addCompleter(DummyCompletor)
reader.setCompletionHandler(new CustomHandler(complete))
}
@ -24,13 +25,13 @@ object JLineCompletion
{
private[this] var previous: Option[(String,Int)] = None
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))
level = if(current == previous) level + 1 else 1
previous = current
try completeImpl(reader, level)
catch { case e: Exception =>
reader.printString("\nException occurred while determining completions.")
reader.print("\nException occurred while determining completions.")
e.printStackTrace()
false
}
@ -40,9 +41,9 @@ object JLineCompletion
// always provides dummy completions so that the custom completion handler gets called
// (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
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"
0
@ -73,19 +74,19 @@ object JLineCompletion
def customCompletor(f: (String, Int) => (Seq[String], Seq[String])): (ConsoleReader, Int) => Boolean =
(reader, level) => {
val success = complete(beforeCursor(reader), reader => f(reader, level), reader)
reader.flushConsole()
reader.flush()
success
}
def bufferSnapshot(reader: ConsoleReader): (String, Int) =
{
val b = reader.getCursorBuffer
(b.getBuffer.toString, b.cursor)
(b.buffer.toString, b.cursor)
}
def beforeCursor(reader: ConsoleReader): String =
{
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
@ -120,16 +121,16 @@ object JLineCompletion
def printCompletions(cs: Seq[String], reader: ConsoleReader)
{
val print = shouldPrint(cs, reader)
reader.printNewline()
reader.println()
if(print) printLinesAndColumns(cs, reader)
}
def printLinesAndColumns(cs: Seq[String], reader: ConsoleReader)
{
val (lines, columns) = cs partition hasNewline
for(line <- lines) {
reader.printString(line)
reader.print(line)
if(line.charAt(line.length - 1) != '\n')
reader.printNewline()
reader.println()
}
reader.printColumns(JavaConversions.seqAsJavaList(columns.map(_.trim)))
}
@ -137,15 +138,15 @@ object JLineCompletion
def shouldPrint(cs: Seq[String], reader: ConsoleReader): Boolean =
{
val size = cs.size
(size <= reader.getAutoprintThreshhold) ||
(size <= reader.getAutoprintThreshold) ||
confirm("Display all %d possibilities? (y or n) ".format(size), 'y', 'n', reader)
}
def confirm(prompt: String, trueC: Char, falseC: Char, reader: ConsoleReader): Boolean =
{
reader.printNewline()
reader.printString(prompt)
reader.flushConsole()
reader.readCharacter( Array(trueC, falseC) ) == trueC
reader.println()
reader.print(prompt)
reader.flush()
reader.readCharacter(trueC, falseC) == trueC
}
def commonPrefix(s: Seq[String]): String = if(s.isEmpty) "" else s reduceLeft commonPrefix

View File

@ -81,9 +81,9 @@ object ConsoleLogger
private[this] def ansiSupported =
try {
val terminal = jline.Terminal.getTerminal
terminal.enableEcho() // #460
terminal.isANSISupported
val terminal = jline.TerminalFactory.get
terminal.setEchoEnabled(true) // #460
terminal.isAnsiSupported
} catch {
case e: Exception => !isWindows
}