Skip to content

Commit 64ba4df

Browse files
committed
Merge pull request #1251 from felixmulder/topic/fix-stdoutredirect-repl
Fix stdout redirect for REPL's println
2 parents 5838fda + f594b17 commit 64ba4df

File tree

6 files changed

+62
-10
lines changed

6 files changed

+62
-10
lines changed

src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class ScalaSettings extends Settings.SettingGroup {
2828
val help = BooleanSetting("-help", "Print a synopsis of standard options")
2929
val nowarn = BooleanSetting("-nowarn", "Generate no warnings.")
3030
val print = BooleanSetting("-print", "Print program with Scala-specific features removed.")
31+
val color = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/)
3132
val target = ChoiceSetting("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.",
3233
List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "jvm-1.8", "msil"),
3334
"jvm-1.8")

src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,10 @@ object Contexts {
377377
/** Is the verbose option set? */
378378
def verbose: Boolean = base.settings.verbose.value
379379

380+
/** Should use colors when printing? */
381+
def useColors: Boolean =
382+
base.settings.color.value == "always"
383+
380384
/** A condensed context containing essential information of this but
381385
* no outer contexts except the initial context.
382386
private var _condensed: CondensedContext = null

src/dotty/tools/dotc/repl/AmmoniteReader.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ class AmmoniteReader(val interpreter: Interpreter)(implicit ctx: Context) extend
5757
writer,
5858
allFilters,
5959
displayTransform = (buffer, cursor) => {
60-
val ansiBuffer = Ansi.Str.parse(SyntaxHighlighting(buffer))
60+
val coloredBuffer =
61+
if (ctx.useColors) SyntaxHighlighting(buffer)
62+
else buffer
63+
64+
val ansiBuffer = Ansi.Str.parse(coloredBuffer)
6165
val (newBuffer, cursorOffset) = SelectionFilter.mangleBuffer(
6266
selectionFilter, ansiBuffer, cursor, Ansi.Reversed.On
6367
)

src/dotty/tools/dotc/repl/CompilingInterpreter.scala

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package dotty.tools
22
package dotc
33
package repl
44

5-
import java.io.{File, PrintWriter, StringWriter, Writer}
5+
import java.io.{
6+
File, PrintWriter, PrintStream, StringWriter, Writer, OutputStream,
7+
ByteArrayOutputStream => ByteOutputStream
8+
}
69
import java.lang.{Class, ClassLoader}
710
import java.net.{URL, URLClassLoader}
811

@@ -24,6 +27,7 @@ import dotty.tools.backend.jvm.GenBCode
2427
import Symbols._, Types._, Contexts._, StdNames._, Names._, NameOps._
2528
import Decorators._
2629
import scala.util.control.NonFatal
30+
import printing.SyntaxHighlighting
2731

2832
/** An interpreter for Scala code which is based on the `dotc` compiler.
2933
*
@@ -210,11 +214,11 @@ class CompilingInterpreter(out: PrintWriter, ictx: Context) extends Compiler wit
210214
if (!req.compile())
211215
Interpreter.Error // an error happened during compilation, e.g. a type error
212216
else {
213-
val (interpreterResultString, succeeded) = req.loadAndRun()
217+
val (resultStrings, succeeded) = req.loadAndRun()
214218
if (delayOutput)
215-
previousOutput = clean(interpreterResultString) :: previousOutput
219+
previousOutput = resultStrings.map(clean) ::: previousOutput
216220
else if (printResults || !succeeded)
217-
out.print(clean(interpreterResultString))
221+
resultStrings.map(x => out.print(clean(x)))
218222
if (succeeded) {
219223
prevRequests += req
220224
Interpreter.Success
@@ -383,24 +387,53 @@ class CompilingInterpreter(out: PrintWriter, ictx: Context) extends Compiler wit
383387
names1 ++ names2
384388
}
385389

390+
/** Sets both System.{out,err} and Console.{out,err} to supplied
391+
* `os: OutputStream`
392+
*/
393+
private def withOutput[T](os: ByteOutputStream)(op: ByteOutputStream => T) = {
394+
val ps = new PrintStream(os)
395+
val oldOut = System.out
396+
val oldErr = System.err
397+
System.setOut(ps)
398+
System.setErr(ps)
399+
400+
try {
401+
Console.withOut(os)(Console.withErr(os)(op(os)))
402+
} finally {
403+
System.setOut(oldOut)
404+
System.setErr(oldErr)
405+
}
406+
}
407+
386408
/** load and run the code using reflection.
387-
* @return A pair consisting of the run's result as a string, and
409+
* @return A pair consisting of the run's result as a `List[String]`, and
388410
* a boolean indicating whether the run succeeded without throwing
389411
* an exception.
390412
*/
391-
def loadAndRun(): (String, Boolean) = {
413+
def loadAndRun(): (List[String], Boolean) = {
392414
val interpreterResultObject: Class[_] =
393415
Class.forName(resultObjectName, true, classLoader)
394-
val resultValMethod: java.lang.reflect.Method =
416+
val valMethodRes: java.lang.reflect.Method =
395417
interpreterResultObject.getMethod("result")
396418
try {
397-
(resultValMethod.invoke(interpreterResultObject).toString, true)
419+
withOutput(new ByteOutputStream) { ps =>
420+
val rawRes = valMethodRes.invoke(interpreterResultObject).toString
421+
val res =
422+
if (ictx.useColors) new String(SyntaxHighlighting(rawRes).toArray)
423+
else rawRes
424+
val prints = ps.toString("utf-8")
425+
val printList = if (prints != "") prints :: Nil else Nil
426+
427+
if (!delayOutput) out.print(prints)
428+
429+
(printList :+ res, true)
430+
}
398431
} catch {
399432
case NonFatal(ex) =>
400433
def cause(ex: Throwable): Throwable =
401434
if (ex.getCause eq null) ex else cause(ex.getCause)
402435
val orig = cause(ex)
403-
(stringFrom(str => orig.printStackTrace(str)), false)
436+
(stringFrom(str => orig.printStackTrace(str)) :: Nil, false)
404437
}
405438
}
406439

src/dotty/tools/dotc/repl/REPL.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ class REPL extends Driver {
2525

2626
lazy val config = new REPL.Config
2727

28+
override def setup(args: Array[String], rootCtx: Context): (List[String], Context) = {
29+
val (strs, ctx) = super.setup(args, rootCtx)
30+
(strs, config.context(ctx))
31+
}
32+
2833
override def newCompiler(implicit ctx: Context): Compiler =
2934
new repl.CompilingInterpreter(config.output, ctx)
3035

@@ -45,6 +50,8 @@ object REPL {
4550
val continuationPrompt = " | "
4651
val version = ".next (pre-alpha)"
4752

53+
def context(ctx: Context): Context = ctx
54+
4855
/** The default input reader */
4956
def input(in: Interpreter)(implicit ctx: Context): InteractiveReader = {
5057
val emacsShell = System.getProperty("env.emacs", "") != ""

test/test/TestREPL.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ class TestREPL(script: String) extends REPL {
2020
override lazy val config = new REPL.Config {
2121
override val output = new NewLinePrintWriter(out)
2222

23+
override def context(ctx: Context) =
24+
ctx.fresh.setSetting(ctx.settings.color, "never")
25+
2326
override def input(in: Interpreter)(implicit ctx: Context) = new InteractiveReader {
2427
val lines = script.lines
2528
def readLine(prompt: String): String = {

0 commit comments

Comments
 (0)