Skip to content
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

Improve REPL display of method types #8319

Merged
merged 1 commit into from Sep 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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"
bishabosha marked this conversation as resolved.
Show resolved Hide resolved

val nameString = {
string2code(prettyName) + (
if (showObjIds) s"""" + f"@$${System.identityHashCode($path)}%8x" + """"
bishabosha marked this conversation as resolved.
Show resolved Hide resolved
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