From d04984596c6abfa27b217b12a42caca26f0c269f Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Wed, 27 Apr 2016 12:00:29 +0200 Subject: Add multiline support using ammonite multilineFilter --- src/dotty/tools/dotc/repl/AmmoniteReader.scala | 22 ++-- src/dotty/tools/dotc/repl/InteractiveReader.scala | 6 +- src/dotty/tools/dotc/repl/InterpreterLoop.scala | 24 ++--- src/dotty/tools/dotc/repl/JLineReader.scala | 2 +- src/dotty/tools/dotc/repl/REPL.scala | 4 +- src/dotty/tools/dotc/repl/SimpleReader.scala | 2 +- src/dotty/tools/dotc/repl/SyntaxHighlighter.scala | 116 ++++++++++------------ 7 files changed, 82 insertions(+), 94 deletions(-) (limited to 'src/dotty/tools') diff --git a/src/dotty/tools/dotc/repl/AmmoniteReader.scala b/src/dotty/tools/dotc/repl/AmmoniteReader.scala index 0c4dd80ee..a3b2a1c56 100644 --- a/src/dotty/tools/dotc/repl/AmmoniteReader.scala +++ b/src/dotty/tools/dotc/repl/AmmoniteReader.scala @@ -11,9 +11,15 @@ import BasicFilters._ import GUILikeFilters._ import util.SourceFile -class AmmoniteReader extends InteractiveReader { +class AmmoniteReader(val interpreter: Interpreter)(implicit ctx: Context) extends InteractiveReader { val interactive = true + def incompleteInput(str: String): Boolean = + interpreter.beQuietDuring(interpreter.interpret(str)) match { + case Interpreter.Incomplete => true + case _ => false // TODO: should perhaps save output here? + } + val reader = new java.io.InputStreamReader(System.in) val writer = new java.io.OutputStreamWriter(System.out) val cutPasteFilter = ReadlineFilters.CutPasteFilter() @@ -21,11 +27,11 @@ class AmmoniteReader extends InteractiveReader { val selectionFilter = GUILikeFilters.SelectionFilter(indent = 2) val multilineFilter: Filter = Filter("multilineFilter") { case TermState(lb ~: rest, b, c, _) - if (lb == 10 || lb == 13) => // Enter - + if (lb == 10 || lb == 13) && incompleteInput(b.mkString) => BasicFilters.injectNewLine(b, c, rest) } - def readLine(prompt: String)(implicit ctx: Context): String = { + + def readLine(prompt: String): String = { val historyFilter = new HistoryFilter( () => history.toVector, Console.BLUE, @@ -39,9 +45,8 @@ class AmmoniteReader extends InteractiveReader { GUILikeFilters.altFilter, GUILikeFilters.fnFilter, ReadlineFilters.navFilter, - //autocompleteFilter, cutPasteFilter, - //multilineFilter, + multilineFilter, BasicFilters.all ) @@ -52,7 +57,6 @@ class AmmoniteReader extends InteractiveReader { allFilters, displayTransform = (buffer, cursor) => { val ansiBuffer = Ansi.Str.parse(SyntaxHighlighting(buffer)) - //val ansiBuffer = Ansi.Str.parse(SyntaxHighlighting(new SourceFile("", buffer))) val (newBuffer, cursorOffset) = SelectionFilter.mangleBuffer( selectionFilter, ansiBuffer, cursor, Ansi.Reversed.On ) @@ -64,7 +68,9 @@ class AmmoniteReader extends InteractiveReader { (newNewBuffer, cursorOffset) } ) match { - case Some(s) => history = s :: history; s + case Some(res) => + history = res :: history; + res case None => ":q" } } diff --git a/src/dotty/tools/dotc/repl/InteractiveReader.scala b/src/dotty/tools/dotc/repl/InteractiveReader.scala index 6bab0a0c6..6ec6a0463 100644 --- a/src/dotty/tools/dotc/repl/InteractiveReader.scala +++ b/src/dotty/tools/dotc/repl/InteractiveReader.scala @@ -6,7 +6,7 @@ import dotc.core.Contexts.Context /** Reads lines from an input stream */ trait InteractiveReader { - def readLine(prompt: String)(implicit ctx: Context): String + def readLine(prompt: String): String val interactive: Boolean } @@ -16,9 +16,9 @@ object InteractiveReader { /** Create an interactive reader. Uses JLine if the * library is available, but otherwise uses a * SimpleReader. */ - def createDefault(): InteractiveReader = { + def createDefault(in: Interpreter)(implicit ctx: Context): InteractiveReader = { try { - new AmmoniteReader() + new AmmoniteReader(in) } catch { case e => //out.println("jline is not available: " + e) //debug e.printStackTrace() diff --git a/src/dotty/tools/dotc/repl/InterpreterLoop.scala b/src/dotty/tools/dotc/repl/InterpreterLoop.scala index 4ac9602e7..64414fec3 100644 --- a/src/dotty/tools/dotc/repl/InterpreterLoop.scala +++ b/src/dotty/tools/dotc/repl/InterpreterLoop.scala @@ -24,10 +24,10 @@ import scala.concurrent.ExecutionContext.Implicits.global class InterpreterLoop(compiler: Compiler, config: REPL.Config)(implicit ctx: Context) { import config._ - private var in = input - val interpreter = compiler.asInstanceOf[Interpreter] + private var in = input(interpreter) + /** The context class loader at the time this object was created */ protected val originalClassLoader = Thread.currentThread.getContextClassLoader @@ -74,10 +74,10 @@ class InterpreterLoop(compiler: Compiler, config: REPL.Config)(implicit ctx: Con * line of input to be entered. */ def firstLine(): String = { - val futLine = Future(in.readLine(prompt)) + val line = in.readLine(prompt) interpreter.beQuietDuring( interpreter.interpret("val theAnswerToLifeInTheUniverseAndEverything = 21 * 2")) - Await.result(futLine, Duration.Inf) + line } /** The main read-eval-print loop for the interpreter. It calls @@ -178,23 +178,11 @@ class InterpreterLoop(compiler: Compiler, config: REPL.Config)(implicit ctx: Con * read, go ahead and interpret it. Return the full string * to be recorded for replay, if any. */ - def interpretStartingWith(code: String): Option[String] = { + def interpretStartingWith(code: String): Option[String] = interpreter.interpret(code) match { case Interpreter.Success => Some(code) - case Interpreter.Error => None - case Interpreter.Incomplete => - if (in.interactive && code.endsWith("\n\n")) { - output.println("You typed two blank lines. Starting a new command.") - None - } else { - val nextLine = in.readLine(continuationPrompt) - if (nextLine == null) - None // end of file - else - interpretStartingWith(code + "\n" + nextLine) - } + case _ => None } - } /* def loadFiles(settings: Settings) { settings match { diff --git a/src/dotty/tools/dotc/repl/JLineReader.scala b/src/dotty/tools/dotc/repl/JLineReader.scala index c7bdebb54..73463cd7c 100644 --- a/src/dotty/tools/dotc/repl/JLineReader.scala +++ b/src/dotty/tools/dotc/repl/JLineReader.scala @@ -12,5 +12,5 @@ class JLineReader extends InteractiveReader { val interactive = true - def readLine(prompt: String)(implicit ctx: Context) = reader.readLine(prompt) + def readLine(prompt: String) = reader.readLine(prompt) } diff --git a/src/dotty/tools/dotc/repl/REPL.scala b/src/dotty/tools/dotc/repl/REPL.scala index e5ff2d3af..1f5e3347b 100644 --- a/src/dotty/tools/dotc/repl/REPL.scala +++ b/src/dotty/tools/dotc/repl/REPL.scala @@ -46,11 +46,11 @@ object REPL { val version = ".next (pre-alpha)" /** The default input reader */ - def input(implicit ctx: Context): InteractiveReader = { + def input(in: Interpreter)(implicit ctx: Context): InteractiveReader = { val emacsShell = System.getProperty("env.emacs", "") != "" //println("emacsShell="+emacsShell) //debug if (ctx.settings.Xnojline.value || emacsShell) new SimpleReader() - else InteractiveReader.createDefault() + else InteractiveReader.createDefault(in) } /** The default output writer */ diff --git a/src/dotty/tools/dotc/repl/SimpleReader.scala b/src/dotty/tools/dotc/repl/SimpleReader.scala index b69fcdd2a..5fab47bbe 100644 --- a/src/dotty/tools/dotc/repl/SimpleReader.scala +++ b/src/dotty/tools/dotc/repl/SimpleReader.scala @@ -14,7 +14,7 @@ class SimpleReader( extends InteractiveReader { def this() = this(Console.in, new PrintWriter(Console.out), true) - def readLine(prompt: String)(implicit ctx: Context) = { + def readLine(prompt: String) = { if (interactive) { out.print(prompt) out.flush() diff --git a/src/dotty/tools/dotc/repl/SyntaxHighlighter.scala b/src/dotty/tools/dotc/repl/SyntaxHighlighter.scala index 1988ea7ad..edb0ba040 100644 --- a/src/dotty/tools/dotc/repl/SyntaxHighlighter.scala +++ b/src/dotty/tools/dotc/repl/SyntaxHighlighter.scala @@ -40,9 +40,9 @@ object SyntaxHighlighting { private val typeEnders = '{' :: '}' :: ')' :: '(' :: '=' :: ' ' :: ',' :: '.' :: Nil - def apply(buffer: Iterable[Char]): Vector[Char] = { + def apply(chars: Iterable[Char]): Vector[Char] = { var prev: Char = 0 - var iter = buffer.toIterator + var remaining = chars.toStream val newBuf = new StringBuilder @inline def keywordStart = @@ -51,18 +51,24 @@ object SyntaxHighlighting { @inline def numberStart(c: Char) = c.isDigit && (!prev.isLetter || prev == '.' || prev == ' ' || prev == '(' || prev == '\u0000') - while (iter.hasNext) { - val n = iter.next + def takeChar(): Char = takeChars(1).head + def takeChars(x: Int): Seq[Char] = { + val taken = remaining.take(x) + remaining = remaining.drop(x) + taken + } + + while (remaining.nonEmpty) { + val n = takeChar() if (interpolationPrefixes.contains(n)) { // Interpolation prefixes are a superset of the keyword start chars - val next = iter.take(3).mkString + val next = remaining.take(3).mkString if (next.startsWith("\"")) { newBuf += n prev = n - appendLiteral('"', next.toIterator.drop(1), next == "\"\"\"") + if (remaining.nonEmpty) takeChar() // drop 1 for appendLiteral + appendLiteral('"', next == "\"\"\"") } else { - val (dup, _ ) = iter.duplicate - iter = next.toIterator ++ dup if (n.isUpper && keywordStart) { appendWhile(n, !typeEnders.contains(_), typeDef) } else if (keywordStart) { @@ -75,15 +81,11 @@ object SyntaxHighlighting { } else { (n: @switch) match { case '/' => - if (iter.hasNext) { - iter.next match { + if (remaining.nonEmpty) { + takeChar() match { case '/' => eolComment() case '*' => blockComment() - case x => { - newBuf += '/' - val (dup, _) = iter.duplicate - iter = List(x).toIterator ++ dup - } + case x => newBuf += '/'; remaining = x #:: remaining } } else newBuf += '/' case '=' => @@ -92,19 +94,17 @@ object SyntaxHighlighting { append('<', { x => x == "<-" || x == "<:" || x == "<%" }, keyword) case '>' => append('>', { x => x == ">:" }, keyword) - case '#' => + case '#' if prev != ' ' && prev != '.' => newBuf append keyword("#") prev = '#' case '@' => appendWhile('@', _ != ' ', annotation) - case '\"' => iter.take(2).mkString match { - case "\"\"" => appendLiteral('\"', Iterator.empty, multiline = true) - case lit => appendLiteral('\"', lit.toIterator) - } + case '\"' => + appendLiteral('\"', multiline = remaining.take(2).mkString == "\"\"") case '\'' => - appendLiteral('\'', Iterator.empty) + appendLiteral('\'') case '`' => - appendUntil('`', _ == '`', none) + appendTo('`', _ == '`', none) case c if c.isUpper && keywordStart => appendWhile(c, !typeEnders.contains(_), typeDef) case c if numberStart(c) => @@ -118,8 +118,8 @@ object SyntaxHighlighting { def eolComment() = { newBuf append (CommentColor + "//") var curr = '/' - while (curr != '\n' && iter.hasNext) { - curr = iter.next + while (curr != '\n' && remaining.nonEmpty) { + curr = takeChar() newBuf += curr } prev = curr @@ -130,16 +130,16 @@ object SyntaxHighlighting { newBuf append (CommentColor + "/*") var curr = '*' var open = 1 - while (open > 0 && iter.hasNext) { - curr = iter.next + while (open > 0 && remaining.nonEmpty) { + curr = takeChar() newBuf += curr - if (curr == '*' && iter.hasNext) { - curr = iter.next + if (curr == '*' && remaining.nonEmpty) { + curr = takeChar() newBuf += curr if (curr == '/') open -= 1 - } else if (curr == '/' && iter.hasNext) { - curr = iter.next + } else if (curr == '/' && remaining.nonEmpty) { + curr = takeChar() newBuf += curr if (curr == '*') open += 1 } @@ -148,7 +148,7 @@ object SyntaxHighlighting { newBuf append NoColor } - def appendLiteral(delim: Char, succ: Iterator[Char], multiline: Boolean = false) = { + def appendLiteral(delim: Char, multiline: Boolean = false) = { var curr: Char = 0 var continue = true var closing = 0 @@ -156,32 +156,31 @@ object SyntaxHighlighting { newBuf append (LiteralColor + delim) def shouldInterpolate = - inInterpolation && curr == '$' && prev != '$' && (iter.hasNext || succ.hasNext) + inInterpolation && curr == '$' && prev != '$' && remaining.nonEmpty def interpolate() = { - val next: Char = if (succ.hasNext) succ.next else iter.next + val next = takeChar() if (next == '$') { newBuf += curr newBuf += next prev = '$' } else if (next == '{') { + var open = 1 // keep track of open blocks newBuf append (KeywordColor + curr) newBuf += next - if (iter.hasNext) { - var c = iter.next - while (iter.hasNext && c != '}') { - newBuf += c - c = iter.next - } + while (remaining.nonEmpty && open > 0) { + var c = takeChar() newBuf += c - newBuf append LiteralColor + if (c == '}') open -= 1 + else if (c == '{') open += 1 } + newBuf append LiteralColor } else { newBuf append (KeywordColor + curr) newBuf += next var c: Char = 'a' - while (c.isLetterOrDigit && (iter.hasNext || succ.hasNext)) { - c = if (succ.hasNext) succ.next else iter.next + while (c.isLetterOrDigit && remaining.nonEmpty) { + c = takeChar() if (c != '"') newBuf += c } newBuf append LiteralColor @@ -193,13 +192,13 @@ object SyntaxHighlighting { closing = 0 } - while (continue && (iter.hasNext || succ.hasNext)) { - curr = if(succ.hasNext) succ.next else iter.next - if (curr == '\\' && (iter.hasNext || succ.hasNext)) { - val next = if (succ.hasNext) succ.next else iter.next + while (continue && remaining.nonEmpty) { + curr = takeChar() + if (curr == '\\' && remaining.nonEmpty) { + val next = takeChar() newBuf append (KeywordColor + curr) if (next == 'u') { - val code = "u" + iter.take(4).mkString + val code = "u" + takeChars(4).mkString newBuf append code } else newBuf += next newBuf append LiteralColor @@ -220,18 +219,13 @@ object SyntaxHighlighting { } newBuf append NoColor prev = curr - - if (succ.hasNext) { - val (dup, _) = iter.duplicate - iter = succ ++ dup - } } - def append(c: Char, shouldHL: String => Boolean, highlight: String => String, pre: Iterator[Char] = Iterator.empty) = { + def append(c: Char, shouldHL: String => Boolean, highlight: String => String) = { var curr: Char = 0 val sb = new StringBuilder(s"$c") - while ((pre.hasNext || iter.hasNext) && curr != ' ' && curr != '(') { - curr = if (pre.hasNext) pre.next else iter.next + while (remaining.nonEmpty && curr != ' ' && curr != '(') { + curr = takeChar() if (curr != ' ') sb += curr } @@ -245,8 +239,8 @@ object SyntaxHighlighting { def appendWhile(c: Char, pred: Char => Boolean, highlight: String => String) = { var curr: Char = 0 val sb = new StringBuilder(s"$c") - while (iter.hasNext && pred(curr)) { - curr = iter.next + while (remaining.nonEmpty && pred(curr)) { + curr = takeChar() if (pred(curr)) sb += curr } @@ -256,15 +250,15 @@ object SyntaxHighlighting { prev = curr } - def appendUntil(c: Char, pred: Char => Boolean, highlight: String => String) = { + def appendTo(c: Char, pred: Char => Boolean, highlight: String => String) = { var curr: Char = 0 val sb = new StringBuilder(s"$c") - while (iter.hasNext && !pred(curr)) { - curr = iter.next + while (remaining.nonEmpty && !pred(curr)) { + curr = takeChar() sb += curr } - newBuf append (highlight(sb.toString)) + newBuf append highlight(sb.toString) prev = curr } -- cgit v1.2.3