summaryrefslogtreecommitdiff
path: root/src/repl
diff options
context:
space:
mode:
Diffstat (limited to 'src/repl')
-rw-r--r--src/repl/scala/tools/nsc/interpreter/Formatting.scala27
-rw-r--r--src/repl/scala/tools/nsc/interpreter/ILoop.scala110
-rw-r--r--src/repl/scala/tools/nsc/interpreter/IMain.scala19
-rw-r--r--src/repl/scala/tools/nsc/interpreter/Pasted.scala22
-rw-r--r--src/repl/scala/tools/nsc/interpreter/ReplProps.scala11
5 files changed, 97 insertions, 92 deletions
diff --git a/src/repl/scala/tools/nsc/interpreter/Formatting.scala b/src/repl/scala/tools/nsc/interpreter/Formatting.scala
index 43e653edfd..844997429c 100644
--- a/src/repl/scala/tools/nsc/interpreter/Formatting.scala
+++ b/src/repl/scala/tools/nsc/interpreter/Formatting.scala
@@ -8,28 +8,25 @@ package interpreter
import util.stringFromWriter
-trait Formatting {
- def prompt: String
+class Formatting(indent: Int) {
- def spaces(code: String): String = {
+ private val indentation = " " * indent
+
+ private def indenting(code: String): Boolean = {
/** Heuristic to avoid indenting and thereby corrupting """-strings and XML literals. */
val tokens = List("\"\"\"", "</", "/>")
val noIndent = (code contains "\n") && (tokens exists code.contains)
- if (noIndent) ""
- else prompt drop 1 map (_ => ' ')
+ !noIndent
}
/** Indent some code by the width of the scala> prompt.
* This way, compiler error messages read better.
*/
- def indentCode(code: String) = {
- val indent = spaces(code)
- stringFromWriter(str =>
- for (line <- code.lines) {
- str print indent
- str print (line + "\n")
- str.flush()
- }
- )
- }
+ def indentCode(code: String) = stringFromWriter(str =>
+ for (line <- code.lines) {
+ if (indenting(code)) str print indentation
+ str println line
+ str.flush()
+ }
+ )
}
diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala
index a3047ccc8e..525609171e 100644
--- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala
@@ -111,11 +111,10 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
}
class ILoopInterpreter extends IMain(settings, out) {
- outer =>
-
- override lazy val formatting = new Formatting {
- def prompt = ILoop.this.prompt
- }
+ // the expanded prompt but without color escapes and without leading newline, for purposes of indenting
+ override lazy val formatting: Formatting = new Formatting(
+ (replProps.promptString format Properties.versionNumberString).lines.toList.last.length
+ )
override protected def parentClassLoader =
settings.explicitParentLoader.getOrElse( classOf[ILoop].getClassLoader )
}
@@ -199,10 +198,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
echo("%d %s".format(index + offset, line))
}
- private val currentPrompt = Properties.shellPromptString
-
/** Prompt to print when awaiting input */
- def prompt = currentPrompt
+ def prompt = replProps.prompt
import LoopCommand.{ cmd, nullary }
@@ -412,14 +409,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
}
private def readOneLine() = {
- import scala.io.AnsiColor.{ MAGENTA, RESET }
out.flush()
- in readLine (
- if (replProps.colorOk)
- MAGENTA + prompt + RESET
- else
- prompt
- )
+ in readLine prompt
}
/** The main read-eval-print loop for the repl. It calls
@@ -770,8 +761,13 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
}
private object paste extends Pasted {
+ import scala.util.matching.Regex.quote
val ContinueString = " | "
- val PromptString = "scala> "
+ val PromptString = prompt.lines.toList.last
+ val anyPrompt = s"""\\s*(?:${quote(PromptString.trim)}|${quote(AltPromptString.trim)})\\s*""".r
+
+ def isPrompted(line: String) = matchesPrompt(line)
+ def isPromptOnly(line: String) = line match { case anyPrompt() => true ; case _ => false }
def interpret(line: String): Unit = {
echo(line.trim)
@@ -781,10 +777,17 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
def transcript(start: String) = {
echo("\n// Detected repl transcript paste: ctrl-D to finish.\n")
- apply(Iterator(start) ++ readWhile(_.trim != PromptString.trim))
+ apply(Iterator(start) ++ readWhile(!isPromptOnly(_)))
}
+
+ def unapply(line: String): Boolean = isPrompted(line)
+ }
+
+ private object invocation {
+ def unapply(line: String): Boolean = Completion.looksLikeInvocation(line)
}
- import paste.{ ContinueString, PromptString }
+
+ private val lineComment = """\s*//.*""".r // all comment
/** Interpret expressions starting with the first line.
* Read lines until a complete compilation unit is available
@@ -796,53 +799,42 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
// signal completion non-completion input has been received
in.completion.resetVerbosity()
- def reallyInterpret = {
- val reallyResult = intp.interpret(code)
- (reallyResult, reallyResult match {
- case IR.Error => None
- case IR.Success => Some(code)
- case IR.Incomplete =>
- if (in.interactive && code.endsWith("\n\n")) {
- echo("You typed two blank lines. Starting a new command.")
+ def reallyInterpret = intp.interpret(code) match {
+ case IR.Error => None
+ case IR.Success => Some(code)
+ case IR.Incomplete if in.interactive && code.endsWith("\n\n") =>
+ echo("You typed two blank lines. Starting a new command.")
+ None
+ case IR.Incomplete =>
+ in.readLine(paste.ContinueString) match {
+ case null =>
+ // we know compilation is going to fail since we're at EOF and the
+ // parser thinks the input is still incomplete, but since this is
+ // a file being read non-interactively we want to fail. So we send
+ // it straight to the compiler for the nice error message.
+ intp.compileString(code)
None
- }
- else in.readLine(ContinueString) match {
- case null =>
- // we know compilation is going to fail since we're at EOF and the
- // parser thinks the input is still incomplete, but since this is
- // a file being read non-interactively we want to fail. So we send
- // it straight to the compiler for the nice error message.
- intp.compileString(code)
- None
-
- case line => interpretStartingWith(code + "\n" + line)
- }
- })
+
+ case line => interpretStartingWith(code + "\n" + line)
+ }
}
- /** Here we place ourselves between the user and the interpreter and examine
- * the input they are ostensibly submitting. We intervene in several cases:
+ /* Here we place ourselves between the user and the interpreter and examine
+ * the input they are ostensibly submitting. We intervene in several cases:
*
- * 1) If the line starts with "scala> " it is assumed to be an interpreter paste.
- * 2) If the line starts with "." (but not ".." or "./") it is treated as an invocation
- * on the previous result.
- * 3) If the Completion object's execute returns Some(_), we inject that value
- * and avoid the interpreter, as it's likely not valid scala code.
+ * 1) If the line starts with "scala> " it is assumed to be an interpreter paste.
+ * 2) If the line starts with "." (but not ".." or "./") it is treated as an invocation
+ * on the previous result.
+ * 3) If the Completion object's execute returns Some(_), we inject that value
+ * and avoid the interpreter, as it's likely not valid scala code.
*/
- if (code == "") None
- else if (!paste.running && code.trim.startsWith(PromptString)) {
- paste.transcript(code)
- None
- }
- else if (Completion.looksLikeInvocation(code) && intp.mostRecentVar != "") {
- interpretStartingWith(intp.mostRecentVar + code)
+ code match {
+ case "" => None
+ case lineComment() => None // line comment, do nothing
+ case paste() if !paste.running => paste.transcript(code) ; None
+ case invocation() if intp.mostRecentVar != "" => interpretStartingWith(intp.mostRecentVar + code)
+ case _ => reallyInterpret
}
- else if (code.trim startsWith "//") {
- // line comment, do nothing
- None
- }
- else
- reallyInterpret._2
}
// runs :load `file` on any files passed via -i
diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala
index e355d9f864..2550a5dc57 100644
--- a/src/repl/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala
@@ -112,12 +112,13 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
def this(factory: ScriptEngineFactory) = this(factory, new Settings())
def this() = this(new Settings())
- lazy val formatting: Formatting = new Formatting {
- val prompt = Properties.shellPromptString
- }
+ // the expanded prompt but without color escapes and without leading newline, for purposes of indenting
+ lazy val formatting: Formatting = new Formatting(
+ (replProps.promptString format Properties.versionNumberString).lines.toList.last.length
+ )
lazy val reporter: ReplReporter = new ReplReporter(this)
- import formatting._
+ import formatting.indentCode
import reporter.{ printMessage, printUntruncatedMessage }
// This exists mostly because using the reporter too early leads to deadlock.
@@ -468,7 +469,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
}
private def requestFromLine(line: String, synthetic: Boolean): Either[IR.Result, Request] = {
- val content = indentCode(line)
+ val content = line //indentCode(line)
val trees = parse(content) match {
case parse.Incomplete => return Left(IR.Incomplete)
case parse.Error => return Left(IR.Error)
@@ -909,10 +910,10 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
else List("def %s = %s".format("$line", tquoted(originalLine)), "def %s = Nil".format("$trees"))
}
def preamble = s"""
- |$preambleHeader
- |%s%s%s
- """.stripMargin.format(lineRep.readName, envLines.map(" " + _ + ";\n").mkString,
- importsPreamble, indentCode(toCompute))
+ |${preambleHeader format lineRep.readName}
+ |${envLines mkString (" ", ";\n ", ";\n")}
+ |$importsPreamble
+ |${indentCode(toCompute)}""".stripMargin
val generate = (m: MemberHandler) => m extraCodeToEvaluate Request.this
diff --git a/src/repl/scala/tools/nsc/interpreter/Pasted.scala b/src/repl/scala/tools/nsc/interpreter/Pasted.scala
index f5db3d9e3a..5f388eb15b 100644
--- a/src/repl/scala/tools/nsc/interpreter/Pasted.scala
+++ b/src/repl/scala/tools/nsc/interpreter/Pasted.scala
@@ -16,17 +16,21 @@ package interpreter
* the same result.
*/
abstract class Pasted {
+ def interpret(line: String): Unit
def ContinueString: String
def PromptString: String
- def interpret(line: String): Unit
+ def AltPromptString: String = "scala> "
+
+ private val testBoth = PromptString != AltPromptString
+ private val spacey = " \t".toSet
- def matchesPrompt(line: String) = matchesString(line, PromptString)
+ def matchesPrompt(line: String) = matchesString(line, PromptString) || testBoth && matchesString(line, AltPromptString)
def matchesContinue(line: String) = matchesString(line, ContinueString)
def running = isRunning
private def matchesString(line: String, target: String): Boolean = (
(line startsWith target) ||
- (line.nonEmpty && " \t".toSet(line.head) && matchesString(line.tail, target))
+ (line.nonEmpty && spacey(line.head) && matchesString(line.tail, target))
)
private def stripString(line: String, target: String) = line indexOf target match {
case -1 => line
@@ -39,7 +43,9 @@ abstract class Pasted {
private class PasteAnalyzer(val lines: List[String]) {
val referenced = lines flatMap (resReference findAllIn _.trim.stripPrefix("res")) toSet
- val cmds = lines reduceLeft append split PromptString filterNot (_.trim == "") toList
+ val ActualPromptString = lines find matchesPrompt map (s =>
+ if (matchesString(s, PromptString)) PromptString else AltPromptString) getOrElse PromptString
+ val cmds = lines reduceLeft append split ActualPromptString filterNot (_.trim == "") toList
/** If it's a prompt or continuation line, strip the formatting bits and
* assemble the code. Otherwise ship it off to be analyzed for res references
@@ -67,10 +73,10 @@ abstract class Pasted {
*/
def fixResRefs(code: String, line: String) = line match {
case resCreation(resName) if referenced(resName) =>
- code.lastIndexOf(PromptString) match {
+ code.lastIndexOf(ActualPromptString) match {
case -1 => code
case idx =>
- val (str1, str2) = code splitAt (idx + PromptString.length)
+ val (str1, str2) = code splitAt (idx + ActualPromptString.length)
str2 match {
case resAssign(`resName`) => code
case _ => "%sval %s = { %s }".format(str1, resName, str2)
@@ -79,10 +85,10 @@ abstract class Pasted {
case _ => code
}
- def run() {
+ def run(): Unit = {
println("// Replaying %d commands from transcript.\n" format cmds.size)
cmds foreach { cmd =>
- print(PromptString)
+ print(ActualPromptString)
interpret(cmd)
}
}
diff --git a/src/repl/scala/tools/nsc/interpreter/ReplProps.scala b/src/repl/scala/tools/nsc/interpreter/ReplProps.scala
index 8c4faf7278..df65e9974d 100644
--- a/src/repl/scala/tools/nsc/interpreter/ReplProps.scala
+++ b/src/repl/scala/tools/nsc/interpreter/ReplProps.scala
@@ -6,12 +6,13 @@
package scala.tools.nsc
package interpreter
+import Properties.shellPromptString
import scala.sys._
import Prop._
class ReplProps {
private def bool(name: String) = BooleanProp.keyExists(name)
- private def int(name: String) = IntProp(name)
+ private def int(name: String) = Prop[Int](name)
// This property is used in TypeDebugging. Let's recycle it.
val colorOk = bool("scala.color")
@@ -21,6 +22,14 @@ class ReplProps {
val trace = bool("scala.repl.trace")
val power = bool("scala.repl.power")
+ // Handy system prop for shell prompt, or else pick it up from compiler.properties
+ val promptString = Prop[String]("scala.repl.prompt").option getOrElse (if (info) "%nscala %s> " else shellPromptString)
+ val prompt = {
+ import scala.io.AnsiColor.{ MAGENTA, RESET }
+ val p = promptString format Properties.versionNumberString
+ if (colorOk) s"$MAGENTA$p$RESET" else p
+ }
+
/** CSV of paged,across to enable pagination or `-x` style
* columns, "across" instead of down the column. Since
* pagination turns off columnar output, these flags are