diff options
Diffstat (limited to 'src')
12 files changed, 171 insertions, 50 deletions
diff --git a/src/compiler/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala b/src/compiler/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala new file mode 100644 index 0000000000..d0d3349038 --- /dev/null +++ b/src/compiler/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala @@ -0,0 +1,66 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package interpreter + +import scala.tools.jline.console.{ ConsoleReader, CursorBuffer } +import scala.tools.jline.console.completer.CompletionHandler +import Completion._ + +trait ConsoleReaderHelper extends ConsoleReader { + def currentLine = "" + getCursorBuffer.buffer + def currentPos = getCursorBuffer.cursor + def terminal = getTerminal() + def width = terminal.getWidth() + def height = terminal.getHeight() + def paginate = isPaginationEnabled() + def paginate_=(value: Boolean) = setPaginationEnabled(value) + + def goBack(num: Int): Unit + def readOneKey(prompt: String): Int + def eraseLine(): Unit + + private val marginSize = 3 + private def morePrompt = "--More--" + private def emulateMore(): Int = { + val key = readOneKey(morePrompt) + try key match { + case '\r' | '\n' => 1 + case 'q' => -1 + case _ => height - 1 + } + finally { + eraseLine() + // TODO: still not quite managing to erase --More-- and get + // back to a scala prompt without another keypress. + if (key == 'q') { + putString(getPrompt()) + redrawLine() + flush() + } + } + } + + override def printColumns(items: JCollection[_ <: CharSequence]): Unit = + printColumns(items: List[String]) + + def printColumns(items: List[String]): Unit = { + val longest = items map (_.length) max + var linesLeft = if (isPaginationEnabled()) height - 1 else Int.MaxValue + val columnSize = longest + marginSize + val padded = items map ("%-" + columnSize + "s" format _) + + padded grouped (width / columnSize) foreach { xs => + println(xs.mkString) + linesLeft -= 1 + if (linesLeft <= 0) { + linesLeft = emulateMore() + if (linesLeft < 0) + return + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index 90346c480f..38084af016 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -249,13 +249,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter) import global.Symbol def p(x: Any) = intp.reporter.printMessage("" + x) - def toDefString(sym: Symbol) = { - TypeStrings.quieter( - intp.afterTyper(sym.defString), - sym.owner.name + ".this.", - sym.owner.fullName + "." - ) - } // If an argument is given, only show a source with that // in its name somewhere. @@ -300,7 +293,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter) p(" /* " + members.size + ownerMessage + owner.fullName + " */") memberGroups foreach { group => - group foreach (s => p(" " + toDefString(s))) + group foreach (s => p(" " + intp.symbolDefString(s))) p("") } } @@ -392,6 +385,12 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter) else powerCommands ) + val replayQuestionMessage = + """|The repl compiler has crashed spectacularly. Shall I replay your + |session? I can re-run all lines except the last one. + |[y/n] + """.trim.stripMargin + private val crashRecovery: PartialFunction[Throwable, Unit] = { case ex: Throwable => if (settings.YrichExes.value) { @@ -406,9 +405,19 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter) else { out.println(util.stackTraceString(ex)) } - out.println("Attempting session recovery...") - - replay() + ex match { + case _: NoSuchMethodError | _: NoClassDefFoundError => + out.println("Unrecoverable error.") + throw ex + case _ => + out.print(replayQuestionMessage) + out.flush() + if (in.readAssumingNo("")) { + out.println("\nAttempting session recovery with replay.") + replay() + } + else out.println("\nAbandoning crashed session.") + } } /** The main read-eval-print loop for the repl. It calls diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index c3cc0f9e03..33dcfa59e6 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -83,6 +83,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { /** reporter */ lazy val reporter: ConsoleReporter = new IMain.ReplReporter(this) import reporter.{ printMessage, withoutTruncating } + // not sure if we have some motivation to print directly to console private def echo(msg: String) { Console println msg } @@ -1088,6 +1089,14 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { finally isettings.unwrapStrings = saved } + def symbolDefString(sym: Symbol) = { + TypeStrings.quieter( + afterTyper(sym.defString), + sym.owner.name + ".this.", + sym.owner.fullName + "." + ) + } + def showCodeIfDebugging(code: String) { /** Secret bookcase entrance for repl debuggers: end the line * with "// show" and see what's going on. @@ -1110,6 +1119,14 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { /** Utility methods for the Interpreter. */ object IMain { + // The two name forms this is catching are the two sides of this assignment: + // + // $line3.$read.$iw.$iw.Bippy = + // $line3.$read$$iw$$iw$Bippy@4a6a00ca + private def removeLineWrapper(s: String) = s.replaceAll("""\$line\d+[./]\$(read|eval|print)[$.]""", "") + private def removeIWPackages(s: String) = s.replaceAll("""\$(iw|read|eval|print)[$.]""", "") + def stripString(s: String) = removeIWPackages(removeLineWrapper(s)) + trait CodeAssembler[T] { def preamble: String def generate: T => String @@ -1152,7 +1169,7 @@ object IMain { def isTruncating = reporter.truncationOK def stripImpl(str: String): String = { - val cleaned = removeIWPackages(removeLineWrapper(str)) + val cleaned = stripString(str) var ctrlChars = 0 cleaned map { ch => if (ch.isControl && !ch.isWhitespace) { @@ -1163,13 +1180,6 @@ object IMain { else ch } } - - // The two name forms this is catching are the two sides of this assignment: - // - // $line3.$read.$iw.$iw.Bippy = - // $line3.$read$$iw$$iw$Bippy@4a6a00ca - private def removeLineWrapper(s: String) = s.replaceAll("""\$line\d+[./]\$(read|eval|print)[$.]""", "") - private def removeIWPackages(s: String) = s.replaceAll("""\$(iw|read|eval|print)[$.]""", "") } class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, null, new ReplStrippingWriter(intp)) { diff --git a/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala b/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala index 782fd52ab2..1430cbe55c 100644 --- a/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala @@ -17,16 +17,25 @@ import Properties.isMac trait InteractiveReader { val interactive: Boolean + def init(): Unit + def reset(): Unit + def history: History def completion: Completion def keyBindings: List[KeyBinding] + def eraseLine(): Unit + def redrawLine(): Unit + def currentLine: String - def init(): Unit - def reset(): Unit + def readYesOrNo(prompt: String) = readOneKey(prompt) match { + case 'y' => true + case 'n' => false + } + def readAssumingNo(prompt: String) = try readYesOrNo(prompt) catch { case _: MatchError => false } + def readAssumingYes(prompt: String) = try readYesOrNo(prompt) catch { case _: MatchError => true } protected def readOneLine(prompt: String): String - def redrawLine(): Unit - def currentLine: String + protected def readOneKey(prompt: String): Int def readLine(prompt: String): String = // hack necessary for OSX jvm suspension because read calls are not restarted after SIGTSTP diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala index 9ca10b9e6b..d9260427b3 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala @@ -35,33 +35,41 @@ class JLineReader(val completion: Completion) extends InteractiveReader { } } - def argCompletor: ArgumentCompleter = { - val c = new ArgumentCompleter(new JLineDelimiter, scalaToJline(completion.completer())) - c setStrict false - c - } + class JLineConsoleReader extends ConsoleReader 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() } - val consoleReader = { - val r = new ConsoleReader() - r setBellEnabled false + this setBellEnabled false if (history ne NoHistory) - r setHistory history + this setHistory history if (completion ne NoCompletion) { - r addCompleter argCompletor - r setAutoprintThreshold 400 // max completion candidates without warning - } + val argCompletor: ArgumentCompleter = + new ArgumentCompleter(new JLineDelimiter, scalaToJline(completion.completer())) + argCompletor setStrict false - r + this addCompleter argCompletor + this setAutoprintThreshold 400 // max completion candidates without warning + } } + val consoleReader: JLineConsoleReader = new JLineConsoleReader() + def currentLine: String = consoleReader.getCursorBuffer.buffer.toString - def redrawLine() = { - consoleReader.flush() - consoleReader.drawLine() - consoleReader.flush() + def redrawLine() = consoleReader.redrawLineAndFlush() + def eraseLine() = { + while (consoleReader.delete()) { } + // consoleReader.eraseLine() } def readOneLine(prompt: String) = consoleReader readLine prompt + def readOneKey(prompt: String) = consoleReader readOneKey prompt } object JLineReader { diff --git a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala b/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala index d8181e1793..5249f89dfb 100644 --- a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala @@ -22,6 +22,7 @@ extends InteractiveReader def init() = () def reset() = () + def eraseLine() = () def redrawLine() = () def currentLine = "" def readOneLine(prompt: String): String = { @@ -31,6 +32,7 @@ extends InteractiveReader } in.readLine() } + def readOneKey(prompt: String) = sys.error("No char-based input in SimpleReader") } object SimpleReader { diff --git a/src/compiler/scala/tools/nsc/interpreter/package.scala b/src/compiler/scala/tools/nsc/interpreter/package.scala index 42826e9c6d..e2e6cb6651 100644 --- a/src/compiler/scala/tools/nsc/interpreter/package.scala +++ b/src/compiler/scala/tools/nsc/interpreter/package.scala @@ -23,7 +23,9 @@ package scala.tools.nsc * IMain contains { global: Global } */ package object interpreter { - type JClass = java.lang.Class[_] + type JClass = java.lang.Class[_] + type JList[T] = java.util.List[T] + type JCollection[T] = java.util.Collection[T] private[nsc] val DebugProperty = "scala.repl.debug" private[nsc] val TraceProperty = "scala.repl.trace" @@ -31,6 +33,10 @@ package object interpreter { private[nsc] var isReplDebug = sys.props contains DebugProperty // Also set by -Yrepl-debug private[nsc] implicit def enrichClass[T](clazz: Class[T]) = new RichClass[T](clazz) + private[interpreter] implicit def javaCharSeqCollectionToScala(xs: JCollection[_ <: CharSequence]): List[String] = { + import collection.JavaConverters._ + xs.asScala.toList map ("" + _) + } /** Debug output */ private[nsc] def repldbg(msg: String) = if (isReplDebug) Console println msg @@ -43,6 +49,16 @@ package object interpreter { x } + // Longest common prefix + def longestCommonPrefix(xs: List[String]): String = { + if (xs.isEmpty || xs.contains("")) "" + else xs.head.head match { + case ch => + if (xs.tail forall (_.head == ch)) "" + ch + longestCommonPrefix(xs map (_.tail)) + else "" + } + } + private[nsc] def words(s: String) = s.trim split "\\s+" toList private[nsc] def isQuoted(s: String) = (s.length >= 2) && (s.head == s.last) && ("\"'" contains s.head) diff --git a/src/compiler/scala/tools/nsc/interpreter/session/package.scala b/src/compiler/scala/tools/nsc/interpreter/session/package.scala index e6ff2c3e03..8fbba2f05e 100644 --- a/src/compiler/scala/tools/nsc/interpreter/session/package.scala +++ b/src/compiler/scala/tools/nsc/interpreter/session/package.scala @@ -12,7 +12,6 @@ package interpreter package object session { type JIterator[T] = java.util.Iterator[T] type JListIterator[T] = java.util.ListIterator[T] - type JList[T] = java.util.List[T] type JEntry = scala.tools.jline.console.history.History.Entry type JHistory = scala.tools.jline.console.history.History diff --git a/src/jline/project/build.properties b/src/jline/project/build.properties index c6143fe8df..3ecffcd808 100644 --- a/src/jline/project/build.properties +++ b/src/jline/project/build.properties @@ -3,6 +3,6 @@ project.organization=org.improving project.name=jline sbt.version=0.7.5.RC0 project.version=0.98 -#build.scala.versions=2.8.1 -build.scala.versions=2.9.0-SNAPSHOT +build.scala.versions=2.8.1 +/*build.scala.versions=2.9.0-SNAPSHOT*/ project.initialize=false diff --git a/src/jline/project/plugins/project/build.properties b/src/jline/project/plugins/project/build.properties index f39984bd73..0b7014c531 100644 --- a/src/jline/project/plugins/project/build.properties +++ b/src/jline/project/plugins/project/build.properties @@ -1,3 +1,3 @@ #Project properties -#Fri Jan 21 08:49:59 PST 2011 +#Thu Feb 10 14:58:03 PST 2011 plugin.uptodate=true diff --git a/src/jline/src/main/java/scala/tools/jline/WindowsTerminal.java b/src/jline/src/main/java/scala/tools/jline/WindowsTerminal.java index 66d2c69f8d..4c70155f59 100644 --- a/src/jline/src/main/java/scala/tools/jline/WindowsTerminal.java +++ b/src/jline/src/main/java/scala/tools/jline/WindowsTerminal.java @@ -196,6 +196,8 @@ public class WindowsTerminal if (indicator == SPECIAL_KEY_INDICATOR.code || indicator == NUMPAD_KEY_INDICATOR.code) { int c = readCharacter(in); WindowsKey key = WindowsKey.valueOf(c); + if (key == null) + return 0; switch (key) { case UP_ARROW_KEY: diff --git a/src/jline/src/main/java/scala/tools/jline/console/ConsoleReader.java b/src/jline/src/main/java/scala/tools/jline/console/ConsoleReader.java index bff0a10648..861c2d58bd 100644 --- a/src/jline/src/main/java/scala/tools/jline/console/ConsoleReader.java +++ b/src/jline/src/main/java/scala/tools/jline/console/ConsoleReader.java @@ -219,7 +219,7 @@ public class ConsoleReader * * @return false if we failed (e.g., the buffer was empty) */ - final boolean resetLine() throws IOException { + protected final boolean resetLine() throws IOException { if (buf.cursor == 0) { return false; } @@ -378,7 +378,7 @@ public class ConsoleReader * @param str * @return */ - final String expandEvents(String str) throws IOException { + protected String expandEvents(String str) throws IOException { StringBuilder sb = new StringBuilder(); for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); @@ -650,7 +650,7 @@ public class ConsoleReader /** * Move the visual cursor backwards without modifying the buffer cursor. */ - private void back(final int num) throws IOException { + protected void back(final int num) throws IOException { if (num == 0) return; if (terminal.isAnsiSupported()) { int width = getTerminal().getWidth(); @@ -736,7 +736,7 @@ public class ConsoleReader return backspace(1) == 1; } - private boolean moveToEnd() throws IOException { + protected boolean moveToEnd() throws IOException { return moveCursor(buf.length() - buf.cursor) > 0; } @@ -1502,7 +1502,7 @@ public class ConsoleReader * * @return true if successful */ - private boolean complete() throws IOException { + protected boolean complete() throws IOException { // debug ("tab for (" + buf + ")"); if (completers.size() == 0) { return false; |