Skip to content

Commit

Permalink
Improve REPL display - Fixes scala/bug#10024 (#8319)
Browse files Browse the repository at this point in the history
Improve REPL display - Fixes scala/bug#10024
  • Loading branch information
lrytz committed Sep 26, 2019
2 parents 22f060b + 11a932b commit 59dec61
Show file tree
Hide file tree
Showing 105 changed files with 673 additions and 634 deletions.
29 changes: 29 additions & 0 deletions src/repl/scala/tools/nsc/interpreter/IMain.scala
Expand Up @@ -37,6 +37,7 @@ import scala.tools.nsc.util.{stackTraceString, stringFromWriter}
import scala.tools.nsc.interpreter.Results.{Error, Incomplete, Result, Success}
import scala.tools.nsc.util.Exceptional.rootCause
import scala.util.control.NonFatal
import scala.annotation.tailrec


/** An interpreter for Scala code.
Expand Down Expand Up @@ -710,6 +711,30 @@ class IMain(val settings: Settings, parentClassLoaderOverride: Option[ClassLoade
}
}

@inline private final def tyParens[T](ts: Iterable[T]): String = ts.mkString("[", ", ", "]")
@inline private final def implicitParens[T](ts: Iterable[T]): String = ts.mkString("(implicit ", ", ", ")")
@inline private final def parens[T](ts: Iterable[T]): String = ts.mkString("(", ", ", ")")

private def methodTypeAsDef(tp: Type): String = {

def withoutImplicit(sym: Symbol): Symbol = sym.cloneSymbol(sym.owner, sym.flags & ~Flag.IMPLICIT)

def formatParams(params: List[Symbol]): String = {
if (params.headOption.exists(_.isImplicit)) implicitParens(params.map(withoutImplicit(_).defString))
else parens(params.map(_.defString))
}

@tailrec
def loop(tpe: Type, acc: StringBuilder): StringBuilder = tpe match {
case NullaryMethodType(resultType) => acc ++= s": $resultType"
case PolyType(tyParams, resultType) => loop(resultType, acc ++= tyParens(tyParams.map(_.defString)))
case MethodType(params, resultType) => loop(resultType, acc ++= formatParams(params))
case other => acc ++= s": $other"
}

loop(tp, new StringBuilder).toString
}

/** One line of code submitted by the user for interpretation */
class Request(val line: String, origTrees: List[Tree], firstXmlPos: Position = NoPosition, generousImports: Boolean = false, synthetic: Boolean = false) extends ReplRequest {
def defines = defHandlers flatMap (_.definedSymbols)
Expand Down Expand Up @@ -933,6 +958,10 @@ class IMain(val settings: Settings, parentClassLoaderOverride: Option[ClassLoade
lazy val compilerTypeOf = typeMap[Type](x => x) withDefaultValue NoType
/** String representations of same. */
lazy val typeOf = typeMap[String](tp => exitingTyper(tp.toString))
/** String representations as if a method type. */
private[this] lazy val defTypeOfMap = typeMap[String](tp => exitingTyper(methodTypeAsDef(tp)))

def defTypeOf(name: Name)(implicit show: Name => String): String = show(name) + defTypeOfMap(name)

lazy val definedSymbols = (
termNames.map(x => x -> applyToResultMember(x, x => x)) ++
Expand Down
43 changes: 25 additions & 18 deletions src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala
Expand Up @@ -26,7 +26,7 @@ trait MemberHandlers {
import global._
import naming._

import ReplStrings.{string2codeQuoted, string2code, any2stringOf}
import ReplStrings.{string2codeQuoted, string2code, any2stringOf, quotedString}

private def codegenln(leadingPlus: Boolean, xs: String*): String = codegen(leadingPlus, (xs ++ Array("\n")): _*)
private def codegenln(xs: String*): String = codegenln(true, xs: _*)
Expand Down Expand Up @@ -133,23 +133,32 @@ trait MemberHandlers {
else {
// if this is a lazy val we avoid evaluating it here
val resultString =
if (mods.isLazy) codegenln(false, "<lazy>")
else any2stringOf(path, maxStringElements)
if (mods.isLazy) quotedString(" // unevaluated")
else quotedString(" = ") + " + " + any2stringOf(path, maxStringElements)

val varOrValOrLzy =
if (mods.isMutable) "var"
else if (mods.isLazy) "lazy val"
else "val"

val nameString = {
string2code(prettyName) + (
if (showObjIds) s"""" + f"@$${System.identityHashCode($path)}%8x" + """"
else ""
)
}

val typeString = string2code(req.typeOf(name))

val nameString = string2code(prettyName) + (if (showObjIds) s"""" + f"@$${System.identityHashCode($path)}%8x" + """" else "")
val typeString = string2code(req typeOf name)
s""" + "$nameString: $typeString = " + $resultString"""
s""" + "$varOrValOrLzy $nameString: $typeString" + $resultString"""
}
}
}

class DefHandler(member: DefDef) extends MemberDefHandler(member) {
override def definesValue = flattensToEmpty(member.vparamss) // true if 0-arity
override def resultExtractionCode(req: Request) = {
val nameString = string2code(name)
val typeString = string2code(req typeOf name)
if (mods.isPublic) s""" + "$nameString: $typeString\\n"""" else ""
}
override def resultExtractionCode(req: Request) =
if (mods.isPublic) codegenln(s"def ${req.defTypeOf(name)}") else ""
}

abstract class MacroHandler(member: DefDef) extends MemberDefHandler(member) {
Expand All @@ -162,36 +171,34 @@ trait MemberHandlers {
}

class TermMacroHandler(member: DefDef) extends MacroHandler(member) {
def notification(req: Request) = s"defined term macro $name: ${req.typeOf(name)}"
def notification(req: Request) = s"def ${req.defTypeOf(name)}"
}

class AssignHandler(member: Assign) extends MemberHandler(member) {
override def resultExtractionCode(req: Request) =
codegenln(s"mutated ${member.lhs}")
codegenln(s"// mutated ${member.lhs}")
}

class ModuleHandler(module: ModuleDef) extends MemberDefHandler(module) {
override def definesTerm = Some(name.toTermName)
override def definesValue = true

override def resultExtractionCode(req: Request) = codegenln("defined object ", name)
override def resultExtractionCode(req: Request) = codegenln(s"object $name")
}

class ClassHandler(member: ClassDef) extends MemberDefHandler(member) {
override def definedSymbols = List(symbol, symbol.companionSymbol) filterNot (_ == NoSymbol)
override def definesType = Some(name.toTypeName)
override def definesTerm = Some(name.toTermName) filter (_ => mods.isCase)

override def resultExtractionCode(req: Request) =
codegenln("defined %s %s".format(keyword, name))
override def resultExtractionCode(req: Request) = codegenln(s"$keyword $name")
}

class TypeAliasHandler(member: TypeDef) extends MemberDefHandler(member) {
private def isAlias = mods.isPublic && treeInfo.isAliasTypeDef(member)
override def definesType = Some(name.toTypeName) filter (_ => isAlias)

override def resultExtractionCode(req: Request) =
codegenln("defined type alias ", name) + "\n"
override def resultExtractionCode(req: Request) = codegenln(s"type $name")
}

class ImportHandler(imp: Import) extends MemberHandler(imp) {
Expand Down
7 changes: 5 additions & 2 deletions src/repl/scala/tools/nsc/interpreter/ReplStrings.scala
Expand Up @@ -37,11 +37,14 @@ object ReplStrings {
res.toString
}

@inline final def quotedString(str: String) =
"\"" + str + "\""

def string2codeQuoted(str: String) =
"\"" + string2code(str) + "\""
quotedString(string2code(str))

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

// no escaped or nested quotes
private[this] val inquotes = """(['"])(.*?)\1""".r
Expand Down

0 comments on commit 59dec61

Please sign in to comment.