aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-04-27 12:00:29 +0200
committerFelix Mulder <felix.mulder@gmail.com>2016-04-28 11:00:39 +0200
commitd04984596c6abfa27b217b12a42caca26f0c269f (patch)
tree5657a2a1da905954fbab71f49a9cb477f7a690e7 /src
parent96cedcdcd82148f091989836eb4959b2c3ec3382 (diff)
downloaddotty-d04984596c6abfa27b217b12a42caca26f0c269f.tar.gz
dotty-d04984596c6abfa27b217b12a42caca26f0c269f.tar.bz2
dotty-d04984596c6abfa27b217b12a42caca26f0c269f.zip
Add multiline support using ammonite multilineFilter
Diffstat (limited to 'src')
-rw-r--r--src/dotty/tools/dotc/repl/AmmoniteReader.scala22
-rw-r--r--src/dotty/tools/dotc/repl/InteractiveReader.scala6
-rw-r--r--src/dotty/tools/dotc/repl/InterpreterLoop.scala24
-rw-r--r--src/dotty/tools/dotc/repl/JLineReader.scala2
-rw-r--r--src/dotty/tools/dotc/repl/REPL.scala4
-rw-r--r--src/dotty/tools/dotc/repl/SimpleReader.scala2
-rw-r--r--src/dotty/tools/dotc/repl/SyntaxHighlighter.scala116
7 files changed, 82 insertions, 94 deletions
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("<console>", 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
}