diff options
author | Felix Mulder <felix.mulder@gmail.com> | 2016-06-01 19:11:11 +0200 |
---|---|---|
committer | Felix Mulder <felix.mulder@gmail.com> | 2016-06-01 19:11:11 +0200 |
commit | 64ba4dfca1bfed077e220a8bfa5ea860150c8fb1 (patch) | |
tree | 1ea8360517d7ddef717757a0543e17da793eecd4 | |
parent | 5838fda0234b224203b1a304a11992e8c7c004c3 (diff) | |
parent | f594b17ac5b7a05cfca3f7b4b1113c063b974327 (diff) | |
download | dotty-64ba4dfca1bfed077e220a8bfa5ea860150c8fb1.tar.gz dotty-64ba4dfca1bfed077e220a8bfa5ea860150c8fb1.tar.bz2 dotty-64ba4dfca1bfed077e220a8bfa5ea860150c8fb1.zip |
Merge pull request #1251 from felixmulder/topic/fix-stdoutredirect-repl
Fix stdout redirect for REPL's println
-rw-r--r-- | src/dotty/tools/dotc/config/ScalaSettings.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Contexts.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/repl/AmmoniteReader.scala | 6 | ||||
-rw-r--r-- | src/dotty/tools/dotc/repl/CompilingInterpreter.scala | 51 | ||||
-rw-r--r-- | src/dotty/tools/dotc/repl/REPL.scala | 7 | ||||
-rw-r--r-- | test/test/TestREPL.scala | 3 |
6 files changed, 62 insertions, 10 deletions
diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 583778db2..74ff76444 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -28,6 +28,7 @@ class ScalaSettings extends Settings.SettingGroup { val help = BooleanSetting("-help", "Print a synopsis of standard options") val nowarn = BooleanSetting("-nowarn", "Generate no warnings.") val print = BooleanSetting("-print", "Print program with Scala-specific features removed.") + val color = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/) val target = ChoiceSetting("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.", List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "jvm-1.8", "msil"), "jvm-1.8") diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 0c916eb4f..262443314 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -377,6 +377,10 @@ object Contexts { /** Is the verbose option set? */ def verbose: Boolean = base.settings.verbose.value + /** Should use colors when printing? */ + def useColors: Boolean = + base.settings.color.value == "always" + /** A condensed context containing essential information of this but * no outer contexts except the initial context. private var _condensed: CondensedContext = null diff --git a/src/dotty/tools/dotc/repl/AmmoniteReader.scala b/src/dotty/tools/dotc/repl/AmmoniteReader.scala index e399105b3..614654a28 100644 --- a/src/dotty/tools/dotc/repl/AmmoniteReader.scala +++ b/src/dotty/tools/dotc/repl/AmmoniteReader.scala @@ -57,7 +57,11 @@ class AmmoniteReader(val interpreter: Interpreter)(implicit ctx: Context) extend writer, allFilters, displayTransform = (buffer, cursor) => { - val ansiBuffer = Ansi.Str.parse(SyntaxHighlighting(buffer)) + val coloredBuffer = + if (ctx.useColors) SyntaxHighlighting(buffer) + else buffer + + val ansiBuffer = Ansi.Str.parse(coloredBuffer) val (newBuffer, cursorOffset) = SelectionFilter.mangleBuffer( selectionFilter, ansiBuffer, cursor, Ansi.Reversed.On ) diff --git a/src/dotty/tools/dotc/repl/CompilingInterpreter.scala b/src/dotty/tools/dotc/repl/CompilingInterpreter.scala index b3b7ab13c..7b8ba698a 100644 --- a/src/dotty/tools/dotc/repl/CompilingInterpreter.scala +++ b/src/dotty/tools/dotc/repl/CompilingInterpreter.scala @@ -2,7 +2,10 @@ package dotty.tools package dotc package repl -import java.io.{File, PrintWriter, StringWriter, Writer} +import java.io.{ + File, PrintWriter, PrintStream, StringWriter, Writer, OutputStream, + ByteArrayOutputStream => ByteOutputStream +} import java.lang.{Class, ClassLoader} import java.net.{URL, URLClassLoader} @@ -24,6 +27,7 @@ import dotty.tools.backend.jvm.GenBCode import Symbols._, Types._, Contexts._, StdNames._, Names._, NameOps._ import Decorators._ import scala.util.control.NonFatal +import printing.SyntaxHighlighting /** An interpreter for Scala code which is based on the `dotc` compiler. * @@ -210,11 +214,11 @@ class CompilingInterpreter(out: PrintWriter, ictx: Context) extends Compiler wit if (!req.compile()) Interpreter.Error // an error happened during compilation, e.g. a type error else { - val (interpreterResultString, succeeded) = req.loadAndRun() + val (resultStrings, succeeded) = req.loadAndRun() if (delayOutput) - previousOutput = clean(interpreterResultString) :: previousOutput + previousOutput = resultStrings.map(clean) ::: previousOutput else if (printResults || !succeeded) - out.print(clean(interpreterResultString)) + resultStrings.map(x => out.print(clean(x))) if (succeeded) { prevRequests += req Interpreter.Success @@ -383,24 +387,53 @@ class CompilingInterpreter(out: PrintWriter, ictx: Context) extends Compiler wit names1 ++ names2 } + /** Sets both System.{out,err} and Console.{out,err} to supplied + * `os: OutputStream` + */ + private def withOutput[T](os: ByteOutputStream)(op: ByteOutputStream => T) = { + val ps = new PrintStream(os) + val oldOut = System.out + val oldErr = System.err + System.setOut(ps) + System.setErr(ps) + + try { + Console.withOut(os)(Console.withErr(os)(op(os))) + } finally { + System.setOut(oldOut) + System.setErr(oldErr) + } + } + /** load and run the code using reflection. - * @return A pair consisting of the run's result as a string, and + * @return A pair consisting of the run's result as a `List[String]`, and * a boolean indicating whether the run succeeded without throwing * an exception. */ - def loadAndRun(): (String, Boolean) = { + def loadAndRun(): (List[String], Boolean) = { val interpreterResultObject: Class[_] = Class.forName(resultObjectName, true, classLoader) - val resultValMethod: java.lang.reflect.Method = + val valMethodRes: java.lang.reflect.Method = interpreterResultObject.getMethod("result") try { - (resultValMethod.invoke(interpreterResultObject).toString, true) + withOutput(new ByteOutputStream) { ps => + val rawRes = valMethodRes.invoke(interpreterResultObject).toString + val res = + if (ictx.useColors) new String(SyntaxHighlighting(rawRes).toArray) + else rawRes + val prints = ps.toString("utf-8") + val printList = if (prints != "") prints :: Nil else Nil + + if (!delayOutput) out.print(prints) + + (printList :+ res, true) + } } catch { case NonFatal(ex) => def cause(ex: Throwable): Throwable = if (ex.getCause eq null) ex else cause(ex.getCause) val orig = cause(ex) - (stringFrom(str => orig.printStackTrace(str)), false) + (stringFrom(str => orig.printStackTrace(str)) :: Nil, false) } } diff --git a/src/dotty/tools/dotc/repl/REPL.scala b/src/dotty/tools/dotc/repl/REPL.scala index 1f5e3347b..977f67719 100644 --- a/src/dotty/tools/dotc/repl/REPL.scala +++ b/src/dotty/tools/dotc/repl/REPL.scala @@ -25,6 +25,11 @@ class REPL extends Driver { lazy val config = new REPL.Config + override def setup(args: Array[String], rootCtx: Context): (List[String], Context) = { + val (strs, ctx) = super.setup(args, rootCtx) + (strs, config.context(ctx)) + } + override def newCompiler(implicit ctx: Context): Compiler = new repl.CompilingInterpreter(config.output, ctx) @@ -45,6 +50,8 @@ object REPL { val continuationPrompt = " | " val version = ".next (pre-alpha)" + def context(ctx: Context): Context = ctx + /** The default input reader */ def input(in: Interpreter)(implicit ctx: Context): InteractiveReader = { val emacsShell = System.getProperty("env.emacs", "") != "" diff --git a/test/test/TestREPL.scala b/test/test/TestREPL.scala index 0fe05794f..4899607ce 100644 --- a/test/test/TestREPL.scala +++ b/test/test/TestREPL.scala @@ -20,6 +20,9 @@ class TestREPL(script: String) extends REPL { override lazy val config = new REPL.Config { override val output = new NewLinePrintWriter(out) + override def context(ctx: Context) = + ctx.fresh.setSetting(ctx.settings.color, "never") + override def input(in: Interpreter)(implicit ctx: Context) = new InteractiveReader { val lines = script.lines def readLine(prompt: String): String = { |