diff --git a/launch/src/main/scala/xsbt/boot/SimpleReader.scala b/launch/src/main/scala/xsbt/boot/SimpleReader.scala index 7f1f96c51..e76bbb2bc 100644 --- a/launch/src/main/scala/xsbt/boot/SimpleReader.scala +++ b/launch/src/main/scala/xsbt/boot/SimpleReader.scala @@ -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) } } } } diff --git a/main/src/main/scala/sbt/SettingGraph.scala b/main/src/main/scala/sbt/SettingGraph.scala index aa8650cfd..b9d7e2ffd 100644 --- a/main/src/main/scala/sbt/SettingGraph.scala +++ b/main/src/main/scala/sbt/SettingGraph.scala @@ -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) + ".." diff --git a/project/Util.scala b/project/Util.scala index 7a0c4dcdf..3909ce1ca 100644 --- a/project/Util.scala +++ b/project/Util.scala @@ -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") diff --git a/util/complete/src/main/scala/sbt/LineReader.scala b/util/complete/src/main/scala/sbt/LineReader.scala index b30d657ec..896ab7a8b 100644 --- a/util/complete/src/main/scala/sbt/LineReader.scala +++ b/util/complete/src/main/scala/sbt/LineReader.scala @@ -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) diff --git a/util/complete/src/main/scala/sbt/complete/JLineCompletion.scala b/util/complete/src/main/scala/sbt/complete/JLineCompletion.scala index d41f47b85..1aae8e826 100644 --- a/util/complete/src/main/scala/sbt/complete/JLineCompletion.scala +++ b/util/complete/src/main/scala/sbt/complete/JLineCompletion.scala @@ -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 diff --git a/util/log/src/main/scala/sbt/ConsoleLogger.scala b/util/log/src/main/scala/sbt/ConsoleLogger.scala index 124c61e90..0bc73c2fe 100644 --- a/util/log/src/main/scala/sbt/ConsoleLogger.scala +++ b/util/log/src/main/scala/sbt/ConsoleLogger.scala @@ -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 }