Skip to content

Introduce ReplPrinter as a hook for alternatives to replStringOf #5222

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions src/repl/scala/tools/nsc/interpreter/IMain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -909,32 +909,63 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
private[interpreter] lazy val ObjectSourceCode: Wrapper =
if (isClassBased) new ClassBasedWrapper else new ObjectBasedWrapper

private object ResultObjectSourceCode extends IMain.CodeAssembler[MemberHandler] {
private lazy val ResultObjectSourceCode: ResultObjectSourceCode =
if (isClassBased) ClassBasedResultObjectSourceCode else ObjectBasedResultObjectSourceCode

private object ObjectBasedResultObjectSourceCode extends ResultObjectSourceCode {
def prewrap = importsPreamble
def postwrap = s"""
| %s
| lazy val %s: _root_.java.lang.String = %s {
| %s
| }
""".stripMargin.format(
importsTrailer, lineRep.printName, executionWrapper,
lineRep.evalPath + accessPath + "." + lineRep.printName
)
}

private object ClassBasedResultObjectSourceCode extends ResultObjectSourceCode {
def prewrap = ""
def postwrap = ""
}

private abstract class ResultObjectSourceCode extends IMain.CodeAssembler[MemberHandler] {
/** We only want to generate this code when the result
* is a value which can be referred to as-is.
*/
val evalResult = Request.this.value match {
case NoSymbol => ""
case sym => "lazy val %s = %s".format(lineRep.resultName, originalPath(sym))
}

/** Like preamble for an import wrapper. */
def prewrap: String

/** Like postamble for an import wrapper. */
def postwrap: String

// first line evaluates object to make sure constructor is run
// initial "" so later code can uniformly be: + etc
val preamble = """
|object %s {
| %s
| %s
| lazy val %s: _root_.java.lang.String = %s {
| %s
| (""
""".stripMargin.format(
lineRep.evalName, evalResult, lineRep.printName,
lineRep.evalName, evalResult, prewrap, lineRep.printName,
executionWrapper, fullAccessPath
)

val postamble = """
| )
| }
| %s
|}
""".stripMargin
""".stripMargin.format(postwrap)

val generate = (m: MemberHandler) => m resultExtractionCode Request.this
}

Expand Down
14 changes: 14 additions & 0 deletions src/repl/scala/tools/nsc/interpreter/ReplPrinter.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package scala.tools.nsc
package interpreter

import scala.runtime.ScalaRunTime

trait ReplPrinter[-A] extends Any {
def print(x: A, maxElements: Int): String
}

object ReplPrinter {
implicit def default[A]: ReplPrinter[A] = new ReplPrinter[A] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have one instance, like so:

 implicit object defaultReplPrinter extends ReplPrinter[Any] {
  def print(x: Any, maxElements: Int) = ScalaRunTime.replStringOf(x, maxElements)
}

?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, good eye. Done.

def print(x: A, maxElements: Int) = ScalaRunTime.replStringOf(x, maxElements)
}
}
4 changes: 3 additions & 1 deletion src/repl/scala/tools/nsc/interpreter/ReplStrings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ trait ReplStrings {
"\"" + string2code(str) + "\""

def any2stringOf(x: Any, maxlen: Int) =
"_root_.scala.runtime.ScalaRunTime.replStringOf(%s, %s)".format(x, maxlen)
"_root_.scala.tools.nsc.interpreter.replStringOf(%s, %s)".format(x, maxlen)

def replStringOf[A](x: A, maxElements: Int)(implicit p: ReplPrinter[A]): String = p.print(x, maxElements)

// no escaped or nested quotes
private[this] val inquotes = """(['"])(.*?)\1""".r
Expand Down
8 changes: 8 additions & 0 deletions test/files/run/repl-out-dir.check
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ repl-out-dir-run.obj
$eval$.class
$eval.class
$line2
$eval$$iw$$iw$.class
$eval$$iw$.class
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this mean every repl line generates two additional classfiles? is there a drawback to this? (performance?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it does. I'm not sure what drawbacks there might be, but I avoided it for Spark-reasons.

$eval$.class
$eval.class
$read$$iw$$iw$.class
$read$$iw$.class
$read$.class
$read.class
$line3
$eval$$iw$$iw$.class
$eval$$iw$.class
$eval$.class
$eval.class
$read$$iw$$iw$.class
Expand All @@ -27,13 +31,17 @@ repl-out-dir-run.obj
$read$.class
$read.class
$line4
$eval$$iw$$iw$.class
$eval$$iw$.class
$eval$.class
$eval.class
$read$$iw$$iw$.class
$read$$iw$.class
$read$.class
$read.class
$line5
$eval$$iw$$iw$.class
$eval$$iw$.class
$eval$.class
$eval.class
$read$$iw$$iw$.class
Expand Down
41 changes: 41 additions & 0 deletions test/files/run/repl-printer-classbased.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

scala> :paste < EOF
// Entering paste mode (EOF to finish)

trait Show[-A] extends Any { def show(x: A): String }
object Show {
implicit def shouty[Any]: Show[Any] =
new Show[Any] { def show(x: Any) = if (x == null) "" else x.toString + "!" }
}
EOF

// Exiting paste mode, now interpreting.

defined trait Show
defined object Show

scala>

scala> implicitly[Show[Int]] show 23
res0: String = 23!

scala>

scala> import scala.tools.nsc.interpreter.ReplPrinter
import scala.tools.nsc.interpreter.ReplPrinter

scala> implicit def showReplPrinter[A](implicit z: Show[A]): ReplPrinter[A] = new ReplPrinter[A] {
def print(x: A, maxElements: Int): String = {
val s = z show x
val nl = if (s contains "\n") "\n" else ""
nl + s + "\n"
}
}
showReplPrinter: [A](implicit z: Show[A])scala.tools.nsc.interpreter.ReplPrinter[A]

scala>

scala> 23
res1: Int = 23
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was expecting a "!" at the end here -- how come there's not?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this is the class-based one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps add a comment to the test case that says we don't enable custom string of under this mode

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everyone expects it to end with a bang.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure how to leave the comment, so I've opted for in the transcript.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment didn't fold because that line it's on didn't change, so, for clarity, I've added a commit that adds the comment.


scala> :quit
29 changes: 29 additions & 0 deletions test/files/run/repl-printer-classbased.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
object Test extends scala.tools.partest.ReplTest {
override def transformSettings(settings: scala.tools.nsc.Settings) = {
settings.Yreplclassbased.value = true
settings
}

def code = """
:paste < EOF
trait Show[-A] extends Any { def show(x: A): String }
object Show {
implicit def shouty[Any]: Show[Any] =
new Show[Any] { def show(x: Any) = if (x == null) "" else x.toString + "!" }
}
EOF

implicitly[Show[Int]] show 23

import scala.tools.nsc.interpreter.ReplPrinter
implicit def showReplPrinter[A](implicit z: Show[A]): ReplPrinter[A] = new ReplPrinter[A] {
def print(x: A, maxElements: Int): String = {
val s = z show x
val nl = if (s contains "\n") "\n" else ""
nl + s + "\n"
}
}

23
"""
}
41 changes: 41 additions & 0 deletions test/files/run/repl-printer.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

scala> :paste < EOF
// Entering paste mode (EOF to finish)

trait Show[-A] extends Any { def show(x: A): String }
object Show {
implicit def shouty[Any]: Show[Any] =
new Show[Any] { def show(x: Any) = if (x == null) "" else x.toString + "!" }
}
EOF

// Exiting paste mode, now interpreting.

defined trait Show
defined object Show

scala>

scala> implicitly[Show[Int]] show 23
res0: String = 23!

scala>

scala> import scala.tools.nsc.interpreter.ReplPrinter
import scala.tools.nsc.interpreter.ReplPrinter

scala> implicit def showReplPrinter[A](implicit z: Show[A]): ReplPrinter[A] = new ReplPrinter[A] {
def print(x: A, maxElements: Int): String = {
val s = z show x
val nl = if (s contains "\n") "\n" else ""
nl + s + "\n"
}
}
showReplPrinter: [A](implicit z: Show[A])scala.tools.nsc.interpreter.ReplPrinter[A]

scala>

scala> 23
res1: Int = 23!

scala> :quit
24 changes: 24 additions & 0 deletions test/files/run/repl-printer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
object Test extends scala.tools.partest.ReplTest {
def code = """
:paste < EOF
trait Show[-A] extends Any { def show(x: A): String }
object Show {
implicit def shouty[Any]: Show[Any] =
new Show[Any] { def show(x: Any) = if (x == null) "" else x.toString + "!" }
}
EOF

implicitly[Show[Int]] show 23

import scala.tools.nsc.interpreter.ReplPrinter
implicit def showReplPrinter[A](implicit z: Show[A]): ReplPrinter[A] = new ReplPrinter[A] {
def print(x: A, maxElements: Int): String = {
val s = z show x
val nl = if (s contains "\n") "\n" else ""
nl + s + "\n"
}
}

23
"""
}