diff --git a/bundledevtool/pom.xml b/bundledevtool/pom.xml index 4262a649b..4a6b09ce3 100644 --- a/bundledevtool/pom.xml +++ b/bundledevtool/pom.xml @@ -36,11 +36,6 @@ org.apache.clerezza.scala script-engine - - jline - jline - 0.9.94 - org.scala-lang scala-compiler diff --git a/parent/pom.xml b/parent/pom.xml index 776e3bef3..613a3f7c1 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -25,7 +25,6 @@ org.apache apache 10 - ../parent org.apache.clerezza clerezza @@ -35,7 +34,7 @@ 0.3-incubating-SNAPSHOT The direct or indirect parent of all Clerezza Artifacts - 2.8.1 + 2.9.2 multimodule-source-release diff --git a/platform.launcher.storageless.parent/pom.xml b/platform.launcher.storageless.parent/pom.xml index 40f66d808..de5cae4a8 100644 --- a/platform.launcher.storageless.parent/pom.xml +++ b/platform.launcher.storageless.parent/pom.xml @@ -30,7 +30,7 @@ 0.9-incubating-SNAPSHOT pom Clerezza - Platform Launcher Storageless Parent POM - A parent for Platform launchers, containing the platform bundles as runtime dependencies. + A parent for Platform launchers, containing the platform bundlessca as runtime dependencies. platform.launcher.storageless @@ -83,19 +83,13 @@ org.apache.servicemix.bundles org.apache.servicemix.bundles.scala-library - 2.8.1_1 + 2.9.2_1 runtime org.apache.servicemix.bundles org.apache.servicemix.bundles.scala-compiler - 2.8.1_1 - runtime - - - org.apache.servicemix.bundles - org.apache.servicemix.bundles.jline - 0.9.94_1 + 2.9.2_1 runtime diff --git a/scala-scripting/pom.xml b/scala-scripting/pom.xml index 8fa8dcc33..463dbc5ab 100644 --- a/scala-scripting/pom.xml +++ b/scala-scripting/pom.xml @@ -43,12 +43,12 @@ org.scala-lang scala-library - 2.8.1 + 2.9.2 org.scala-lang scala-compiler - 2.8.1 + 2.9.2 org.apache.felix diff --git a/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/BundleContextScalaInterpreter.scala b/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/BundleContextScalaInterpreter.scala index 0fd01167f..f713e3071 100644 --- a/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/BundleContextScalaInterpreter.scala +++ b/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/BundleContextScalaInterpreter.scala @@ -29,7 +29,7 @@ import scala.tools.nsc.reporters.Reporter class BundleContextScalaInterpreter(bundleContext : BundleContext, out: PrintWriter) - extends Interpreter(new Settings, out) { + extends IMain(new Settings, out) { def this(bundleContext : BundleContext) = { this(bundleContext, new PrintWriter(System.out)) diff --git a/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/InterpreterFactory.scala b/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/InterpreterFactory.scala index 8ca491fec..dda2595ae 100644 --- a/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/InterpreterFactory.scala +++ b/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/InterpreterFactory.scala @@ -42,7 +42,7 @@ class InterpreterFactory() { bundleContext = null } - def createInterpreter(out: PrintWriter) : Interpreter = { + def createInterpreter(out: PrintWriter) : IMain = { val i = new BundleContextScalaInterpreter(bundleContext, out) i } diff --git a/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/ScriptEngineFactory.scala b/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/ScriptEngineFactory.scala index 85b8238d5..00d72773c 100644 --- a/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/ScriptEngineFactory.scala +++ b/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/ScriptEngineFactory.scala @@ -55,7 +55,7 @@ class ScriptEngineFactory() extends JavaxEngineFactory with BundleListener { private var factory: InterpreterFactory = null private var compilerService: CompilerService = null - var _interpreter : Interpreter = null; + var _interpreter : IMain = null; private var bundleContext: BundleContext = null def interpreter = { if (_interpreter == null) { @@ -157,10 +157,7 @@ class ScriptEngineFactory() extends JavaxEngineFactory with BundleListener { interpreter.bind(entry._1, getAccessibleClass(entry._2.getClass).getName, entry._2) } - val result = interpreter.eval[Object](script) match { - case Some(x) => x - case None => null - } + val result = interpreter.interpret(script) if (interpreter.reporter.hasErrors) { throw new ScriptException("some error","script-file",1) } diff --git a/scala-scripting/tests/pom.xml b/scala-scripting/tests/pom.xml index 010afa775..ce6364892 100644 --- a/scala-scripting/tests/pom.xml +++ b/scala-scripting/tests/pom.xml @@ -76,12 +76,6 @@ 2.8.1_1 provided - - org.apache.servicemix.bundles - org.apache.servicemix.bundles.jline - 0.9.94_1 - provided - org.apache.clerezza.scala script-engine diff --git a/shell/pom.xml b/shell/pom.xml index d7b8e08b8..f9f591661 100644 --- a/shell/pom.xml +++ b/shell/pom.xml @@ -24,7 +24,7 @@ org.apache.clerezza clerezza - 0.2-incubating + 0.3-incubating-SNAPSHOT shell 0.2-incubating-SNAPSHOT @@ -39,11 +39,6 @@ org.apache.clerezza osgi.services - - jline - jline - 0.9.94 - org.scala-lang scala-compiler @@ -52,6 +47,11 @@ org.scala-lang scala-library + + org.scala-lang + jline + 2.9.0-1 + org.apache.felix org.apache.felix.scr.annotations @@ -79,9 +79,14 @@ OSGI-INF/serviceComponents.xml - org.apache.clerezza.shell + org.apache.clerezza.shell, + scala.tools.jline, + scala.tools.jline.console, + scala.tools.jline.console.history, + scala.tools.jline.console.completer org.apache.clerezza.shell org.apache.clerezza.platform.security;resolution:=optional, * + jline diff --git a/shell/src/main/scala/org/apache/clerezza/shell/ConsoleShell.scala b/shell/src/main/scala/org/apache/clerezza/shell/ConsoleShell.scala index fa1f0e70f..835d9e112 100644 --- a/shell/src/main/scala/org/apache/clerezza/shell/ConsoleShell.scala +++ b/shell/src/main/scala/org/apache/clerezza/shell/ConsoleShell.scala @@ -56,7 +56,7 @@ class ConsoleShell() { } //this call sets the console terminal to the right settings //and it must not be invoked when there is no console input, or the system will stop - val terminalOption = Some(jline.Terminal.setupTerminal()) + val terminalOption = Some(scala.tools.jline.TerminalFactory.create()) val in = Channels.newInputStream( (new FileInputStream(FileDescriptor.in)).getChannel()); interruptibleIn = new InterruptibleInputStream(in) diff --git a/shell/src/main/scala/org/apache/clerezza/shell/Shell.scala b/shell/src/main/scala/org/apache/clerezza/shell/Shell.scala index bd7c02c5b..041f83506 100644 --- a/shell/src/main/scala/org/apache/clerezza/shell/Shell.scala +++ b/shell/src/main/scala/org/apache/clerezza/shell/Shell.scala @@ -18,99 +18,113 @@ */ package org.apache.clerezza.shell; - - -import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Component import org.osgi.framework.BundleContext import org.osgi.framework.BundleEvent import org.osgi.framework.BundleListener -import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.ComponentContext import org.osgi.framework.Bundle -import java.io.{File, PrintWriter, Reader, StringWriter, BufferedReader, InputStreamReader, InputStream, Writer, OutputStream} +import java.io.{ File, PrintWriter, Reader, StringWriter, BufferedReader, InputStreamReader, InputStream, Writer, OutputStream } import java.lang.reflect.InvocationTargetException import java.net._ import java.security.PrivilegedActionException import java.security.AccessController import java.security.PrivilegedAction - import javax.script.ScriptContext -import javax.script.{ScriptEngineFactory => JavaxEngineFactory, Compilable, - CompiledScript, ScriptEngine, AbstractScriptEngine, Bindings, - SimpleBindings, ScriptException} -import jline.CandidateListCompletionHandler -import jline.{CompletionHandler, Completor, Terminal, ConsoleReader, ArgumentCompletor, History => JHistory} -import java.util.{ArrayList, Arrays} - -//import scala.collection.immutable.Map +import javax.script.{ + ScriptEngineFactory => JavaxEngineFactory, + Compilable, + CompiledScript, + ScriptEngine, + AbstractScriptEngine, + Bindings, + SimpleBindings, + ScriptException +} +import java.util.{ ArrayList, Arrays } import scala.actors.DaemonActor import scala.collection.immutable -import scala.tools.nsc._; -import scala.tools.nsc.interpreter._; -import scala.tools.nsc.io.{AbstractFile, PlainFile, VirtualDirectory} +import scala.tools.nsc._ +import scala.tools.nsc.interpreter._ +import scala.tools.nsc.io.{ AbstractFile, PlainFile, VirtualDirectory } import scala.tools.nsc.util._ import scala.tools.nsc.symtab.SymbolLoaders import scala.tools.nsc.reporters.ConsoleReporter import scala.tools.nsc.reporters.Reporter import scala.tools.util.PathResolver -import scala.tools.nsc.util.{ClassPath, JavaClassPath} +import scala.tools.nsc.util.{ ClassPath, JavaClassPath } import scala.actors.Actor import scala.actors.Actor._ import org.apache.clerezza.scala.scripting._ import java.io.File import org.slf4j.scala.Logging - -class Shell(factory: InterpreterFactory, val inStream: InputStream, - out: OutputStream, shellCommands: immutable.Set[ShellCommand], terminalOption: Option[Terminal] = None ) extends Logging { - - - private var bundleContext: BundleContext = null - - private var bindings = Set[(String, String, Any)]() - private var imports = Set[String]() - private var terminationListeners = Set[Shell.TerminationListener](); - - val terminal = terminalOption match { - case Some(x) => x - case None => new jline.UnixTerminal - } - - val interpreterLoop = new InterpreterLoop(new BufferedReader(new InputStreamReader(inStream)), new PrintWriter(out, true)) { - override def createInterpreter() { - interpreter = factory.createInterpreter(out) - interpreter.beQuietDuring { - for (binding <- bindings) { - interpreter.bind(binding._1, binding._2, binding._3) - } - for (v <- imports) { - interpreter.interpret("import "+v) - } - } - } - - override val prompt = "zz>" - - override val standardCommands: List[Command] = { - import CommandImplicits._ - (for (shellCommand <- shellCommands) yield { - LineArg(shellCommand.command, shellCommand.description, (line: String)=> { - val (continue, linesToRecord) = shellCommand.execute(line, Shell.this.out) - Result(continue, linesToRecord) - }) - }).toList ::: - List( - NoArgs("help", "print this help message", printHelp), - VarArgs("history", "show the history (optional arg: lines to show)", printHistory), - LineArg("h?", "search the history", searchHistory), - OneArg("load", "load and interpret a Scala file", load), - NoArgs("power", "enable power user mode", power), - NoArgs("quit", "terminate the console shell (use shutdown to shut down clerezza)", () => Result(false, None)), - NoArgs("replay", "reset execution and replay all previous commands", replay), - LineArg("sh", "fork a shell and run a command", runShellCmd), - NoArgs("silent", "disable/enable automatic printing of results", verbosity) - ) - } - - override def printHelp() = { +import scala.tools.nsc.interpreter.JLineReader +import scala.tools.jline.Terminal +import scala.tools.jline.console.completer.CompletionHandler +import scala.tools.jline.console.ConsoleReader +import scala.tools.jline.console.completer.CandidateListCompletionHandler +import java.lang.CharSequence +import scala.tools.jline.UnixTerminal +import scala.tools.jline.console.history.History + +class Shell(factory: InterpreterFactory, val inStream: InputStream, + outStream: OutputStream, shellCommands: immutable.Set[ShellCommand], terminalOption: Option[Terminal] = None) extends Logging { + + private var bundleContext: BundleContext = null + + private var bindings = Set[(String, String, Any)]() + private var imports = Set[String]() + private var terminationListeners = Set[Shell.TerminationListener](); + + val terminal = terminalOption match { + case Some(x) => x + case None => new UnixTerminal + } + + val interpreterLoop = new ILoop(new BufferedReader(new InputStreamReader(inStream)), new PrintWriter(outStream, true)) { + override def createInterpreter() { + intp = factory.createInterpreter(out) + intp.beQuietDuring { + for (binding <- bindings) { + intp.bind(binding._1, binding._2, binding._3) + } + for (v <- imports) { + intp.interpret("import " + v) + } + } + } + + override val prompt = "zz>" + override def isAsync = false + + override lazy val standardCommands: List[LoopCommand] = { + import LoopCommand._ + (for (shellCommand <- shellCommands) yield { + new LineCmd(shellCommand.command, "", shellCommand.description, (line: String) => { + val (continue, linesToRecord) = shellCommand.execute(line, Shell.this.outStream) + Result(continue, linesToRecord) + }) + }).toList ::: List( + cmd("help", "[command]", "print this summary or command-specific help", helpCommand), + historyCommand, + cmd("h?", "", "search the history", searchHistory), + cmd("load", "", "load and interpret a Scala file", loadCommand), + nullary("paste", "enter paste mode: all input up to ctrl-D compiled together", pasteCommand), + nullary("power", "enable power user mode", powerCmd), + nullary("quit", "terminate the console shell (use shutdown to shut down clerezza)", () => Result(false, None)), + nullary("replay", "reset execution and replay all previous commands", replay), + shCommand, + nullary("silent", "disable/enable automatic printing of results", verbosity)) + } + + /** print a friendly help message */ + override def helpCommand(line: String): Result = { + if (line == "") printAdditinalHelp(); + + super.helpCommand(line); + } + + def printAdditinalHelp() = { out println "This is a scala based console, it supports any Scala expression, as well as the command described below." out println "To access an OSGi service use $[interface]." out println "" @@ -124,158 +138,107 @@ class Shell(factory: InterpreterFactory, val inStream: InputStream, out println ("import "+v) } out println "" - super.printHelp() - } - - - override def main(settings: Settings) { - this.settings = settings - createInterpreter() - - // sets in to some kind of reader depending on environmental cues - in = new InteractiveReader() { - - override lazy val history = Some(History(consoleReader)) - override lazy val completion = Option(interpreter) map (x => new Completion(x)) - - val consoleReader = { - - val r = new jline.ConsoleReader(inStream, out, null, terminal) - r setHistory (History().jhistory) - r setBellEnabled false - completion foreach { c => - r addCompletor c.jline - r setAutoprintThreshhold 250 - } - import java.util.List - r setCompletionHandler new CompletionHandler { - def complete(reader: ConsoleReader, candidates: List[_], pos: Int) = { - val buffer = reader.getCursorBuffer() - if (candidates.size == 1) { - CandidateListCompletionHandler.setBuffer(reader, candidates.get(0).toString, pos) - } else { - import collection.JavaConversions._ - def commonHead(a: String,b: String):String = { - if (a.isEmpty || b.isEmpty) { - "" - } else { - if (a(0) == b(0)) { - a(0)+commonHead(a.tail, b.tail) - } else "" - } - } - val canStrings = candidates.map(_.toString) - val longestCommonPrefix = canStrings.tail.foldRight(canStrings.head)((a,b) => commonHead(a,b)) - CandidateListCompletionHandler.setBuffer(reader, longestCommonPrefix, pos) - out.println() - out.println(candidates.mkString("\t")) - out.print(prompt) - out.print(reader.getCursorBuffer()) - } - true - } - } - /* a second completor is ignored, would have to wrapp the above - r addCompletor new Completor { - def complete(p1: String, p2: Int, candidates: java.util.List[_]) = { - logger.warn("JLINE: candidates : "+candidates) - val canStrings = candidates.asInstanceOf[List[String]] - canStrings.add("Clerezza") - canStrings.add("Apache") - try { - throw new RuntimeException - } catch { - case e => logger.warn("stack ", e) - } - 0 - } - }*/ - - r - } - - def readOneLine(prompt: String) = consoleReader readLine prompt - val interactive = false - } - //in = new SimpleReader(inStream, out, true) - - loadFiles(settings) - try { - // it is broken on startup; go ahead and exit - if (interpreter.reporter.hasErrors) return - - printWelcome() - - // this is about the illusion of snappiness. We call initialize() - // which spins off a separate thread, then print the prompt and try - // our best to look ready. Ideally the user will spend a - // couple seconds saying "wow, it starts so fast!" and by the time - // they type a command the compiler is ready to roll. - interpreter.initialize() - repl() - } - finally closeInterpreter() } - override def printWelcome() { - import Properties._ - val welcomeMsg = - """|Welcome to the Apache Clerezza Console + override def process(settings: Settings): Boolean = { + this.settings = settings + createInterpreter() + + // sets in to some kind of reader depending on environmental cues + //ignore settings.noCompletion.value) + { + val myIn = new StreamJLineReader(new JLineCompletion(intp), inStream, outStream, terminal) + in = myIn + //are we postinit already? + addThunk(myIn.consoleReader.postInit) + } + loadFiles(settings) + // it is broken on startup; go ahead and exit + if (intp.reporter.hasErrors) + return false + + // This is about the illusion of snappiness. We call initialize() + // which spins off a separate thread, then print the prompt and try + // our best to look ready. The interlocking lazy vals tend to + // inter-deadlock, so we break the cycle with a single asynchronous + // message to an actor. + if (isAsync) { + intp initialize initializedCallback() + createAsyncListener() // listens for signal to run postInitialization + } + else { + intp.initializeSynchronous() + postInitialization() + } + printWelcome() + + try loop() + catch AbstractOrMissingHandler() + finally closeInterpreter() + + true + } + + override def printWelcome() { + import Properties._ + val welcomeMsg = + """|Welcome to the Apache Clerezza Console |Console is based on Scala %s (%s, Java %s). |Type in expressions to have them evaluated. - |Type :help for more information.""" . - stripMargin.format(versionString, javaVmName, javaVersion) - - plushln(welcomeMsg) - } - } - val console: Actor = new DaemonActor { - def act() { - try { - interpreterLoop.main(Array[String]()) - } finally { - for (l <- terminationListeners) { - l.terminated - } - println("console terminated") - } - } - } - - def start() { - console.start - } - - def stop() { - interpreterLoop.command(":q") - interpreterLoop.closeInterpreter() - } - - def bind(name: String, boundType: String, value: Any) { - bindings += ((name, boundType, value)) - } - - def addImport(importValue: String) { - imports += importValue - } - - def addTerminationListener(l: Shell.TerminationListener) { - terminationListeners += l - } - - def removeTerminationListener(l: Shell.TerminationListener) { - terminationListeners -= l - } + |Type :help for more information.""". + stripMargin.format(versionString, javaVmName, javaVersion) + + echo(welcomeMsg) + } + } + val console: Actor = new DaemonActor { + def act() { + try { + interpreterLoop.process(Array[String]()) + } finally { + for (l <- terminationListeners) { + l.terminated + } + println("console terminated") + } + } + } + + def start() { + + console.start + } + + def stop() { + interpreterLoop.command(":q") + interpreterLoop.closeInterpreter() + } + + def bind(name: String, boundType: String, value: Any) { + bindings += ((name, boundType, value)) + } + + def addImport(importValue: String) { + imports += importValue + } + + def addTerminationListener(l: Shell.TerminationListener) { + terminationListeners += l + } + + def removeTerminationListener(l: Shell.TerminationListener) { + terminationListeners -= l + } } object Shell { - trait TerminationListener { - def terminated: Unit - } - - trait Environment { - val componentContext: ComponentContext; - val in: InputStream; - val out: OutputStream; - } + trait TerminationListener { + def terminated: Unit + } + + trait Environment { + val componentContext: ComponentContext; + val in: InputStream; + val out: OutputStream; + } } \ No newline at end of file diff --git a/shell/src/main/scala/org/apache/clerezza/shell/ShellFactory.scala b/shell/src/main/scala/org/apache/clerezza/shell/ShellFactory.scala index 2c84ff39e..ecff56c72 100644 --- a/shell/src/main/scala/org/apache/clerezza/shell/ShellFactory.scala +++ b/shell/src/main/scala/org/apache/clerezza/shell/ShellFactory.scala @@ -27,7 +27,7 @@ import java.io.OutputStream import java.security.AccessController import java.security.PrivilegedAction import org.apache.clerezza.scala.scripting.InterpreterFactory -import jline.Terminal +import scala.tools.jline.Terminal class ShellFactory() { diff --git a/shell/src/main/scala/org/apache/clerezza/shell/StreamJLineReader.scala b/shell/src/main/scala/org/apache/clerezza/shell/StreamJLineReader.scala new file mode 100644 index 000000000..dcbc34a1f --- /dev/null +++ b/shell/src/main/scala/org/apache/clerezza/shell/StreamJLineReader.scala @@ -0,0 +1,91 @@ +package org.apache.clerezza.shell + +import scala.tools.jline.Terminal +import scala.tools.jline.TerminalFactory +import scala.tools.jline.console.completer.CandidateListCompletionHandler +import scala.tools.jline.console.completer.Completer +import scala.tools.jline.console.completer.CompletionHandler +import scala.tools.jline.console.history.History +import scala.tools.jline.console.history.MemoryHistory +import scala.tools.jline.internal.Configuration +import scala.tools.jline.internal.Log +import org.fusesource.jansi.AnsiOutputStream +import java.awt.Toolkit +import java.awt.datatransfer.Clipboard +import java.awt.datatransfer.DataFlavor +import java.awt.datatransfer.Transferable +import java.awt.datatransfer.UnsupportedFlavorException +import java.awt.event.ActionListener +import scala.tools.jline.console.ConsoleReader +import scala.tools.jline.console.completer._ +import scala.tools.nsc.interpreter.session._ +import scala.collection.JavaConverters._ +import scala.tools.nsc.interpreter.Completion._ +import scala.tools.nsc.io.Streamable.slurp +import scala.tools.nsc.interpreter._ +import java.io.OutputStream + +//a modified version of scala.tools.nsc.interpreter.JLineReader +/** + * Reads from the console using JLine. + */ +class StreamJLineReader(_completion: => Completion, in: InputStream, out: OutputStream, terminal: Terminal) extends InteractiveReader { + val interactive = false + val consoleReader = new StreamJLineConsoleReader() + + lazy val completion = _completion + lazy val history: JLineHistory = JLineHistory() + lazy val keyBindings = + try KeyBinding parse slurp(term.getDefaultBindings) + catch { case _: Exception => Nil } + + private def term = consoleReader.getTerminal() + def reset() = term.reset() + def init() = term.init() + + def scalaToJline(tc: ScalaCompleter): Completer = new Completer { + def complete(_buf: String, cursor: Int, candidates: JList[CharSequence]): Int = { + val buf = if (_buf == null) "" else _buf + val Candidates(newCursor, newCandidates) = tc.complete(buf, cursor) + newCandidates foreach (candidates add _) + newCursor + } + } + + class StreamJLineConsoleReader extends ConsoleReader(in, out, null, terminal) with ConsoleReaderHelper { + // working around protected/trait/java insufficiencies. + def goBack(num: Int): Unit = back(num) + def readOneKey(prompt: String) = { + this.print(prompt) + this.flush() + this.readVirtualKey() + } + def eraseLine() = consoleReader.resetPromptLine("", "", 0) + def redrawLineAndFlush(): Unit = { flush() ; drawLine() ; flush() } + // override def readLine(prompt: String): String + + // A hook for running code after the repl is done initializing. + lazy val postInit: Unit = { + this setBellEnabled false + if (history ne NoHistory) + this setHistory history + + if (completion ne NoCompletion) { + val argCompletor: ArgumentCompleter = + new ArgumentCompleter(new JLineDelimiter, scalaToJline(completion.completer())) + argCompletor setStrict false + + this addCompleter argCompletor + this setAutoprintThreshold 400 // max completion candidates without warning + } + } + } + + def currentLine = consoleReader.getCursorBuffer.buffer.toString + def redrawLine() = consoleReader.redrawLineAndFlush() + def eraseLine() = consoleReader.eraseLine() + // Alternate implementation, not sure if/when I need this. + // def eraseLine() = while (consoleReader.delete()) { } + def readOneLine(prompt: String) = consoleReader readLine prompt + def readOneKey(prompt: String) = consoleReader readOneKey prompt +}