From 5ac2104e688409e24785cfe62cdc7e8ef0bc6428 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 20 Mar 2013 17:39:32 +0100 Subject: Pretty-printing improvements. --- src/dotty/tools/dotc/config/ScalaSettings.scala | 1 + src/dotty/tools/dotc/config/Settings.scala | 2 +- src/dotty/tools/dotc/core/Constants.scala | 2 +- src/dotty/tools/dotc/core/Contexts.scala | 3 +- src/dotty/tools/dotc/core/Decorators.scala | 6 +- src/dotty/tools/dotc/core/Denotations.scala | 3 +- src/dotty/tools/dotc/core/Names.scala | 3 +- src/dotty/tools/dotc/core/Printers.scala | 368 +++++++++++---------- src/dotty/tools/dotc/core/Scopes.scala | 3 +- src/dotty/tools/dotc/core/Showable.scala | 9 +- src/dotty/tools/dotc/core/SymDenotations.scala | 3 +- src/dotty/tools/dotc/core/Symbols.scala | 15 +- src/dotty/tools/dotc/core/Trees.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 4 +- src/dotty/tools/dotc/core/pickling/UnPickler.scala | 8 +- src/dotty/tools/dotc/util/Text.scala | 108 +++--- src/test/showClass.scala | 28 +- 17 files changed, 315 insertions(+), 253 deletions(-) (limited to 'src') diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 483e163bf..1eeae8453 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -33,6 +33,7 @@ class ScalaSettings extends Settings.SettingGroup { val usejavacp = BooleanSetting("-usejavacp", "Utilize the java.class.path in classpath resolution.") val verbose = BooleanSetting("-verbose", "Output messages about what the compiler is doing.") val version = BooleanSetting("-version", "Print product version and exit.") + val pageWidth = IntSetting("-pagewidth", "Set page width", 80) val jvmargs = PrefixSetting("-J", "-J", "Pass directly to the runtime system.") val defines = PrefixSetting("-Dproperty=value", "-D", "Pass -Dproperty=value directly to the runtime system.") diff --git a/src/dotty/tools/dotc/config/Settings.scala b/src/dotty/tools/dotc/config/Settings.scala index adac2ecfd..96b047ede 100644 --- a/src/dotty/tools/dotc/config/Settings.scala +++ b/src/dotty/tools/dotc/config/Settings.scala @@ -230,7 +230,7 @@ object Settings { def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String): Setting[String] = publish(Setting(name, descr, default, helpArg, choices)) - def IntSetting(name: String, descr: String, default: Int, range: Seq[Int]): Setting[Int] = + def IntSetting(name: String, descr: String, default: Int, range: Seq[Int] = Nil): Setting[Int] = publish(Setting(name, descr, default, choices = range)) def MultiStringSetting(name: String, helpArg: String, descr: String): Setting[List[String]] = diff --git a/src/dotty/tools/dotc/core/Constants.scala b/src/dotty/tools/dotc/core/Constants.scala index 9dd5a2627..0b4f51cf2 100644 --- a/src/dotty/tools/dotc/core/Constants.scala +++ b/src/dotty/tools/dotc/core/Constants.scala @@ -189,7 +189,7 @@ object Constants { def stringValue: String = value.toString - def show(implicit ctx: Context) = ctx.show(this) + def toText(implicit ctx: Context) = ctx.toText(this) def typeValue: Type = value.asInstanceOf[Type] def symbolValue: Symbol = value.asInstanceOf[Symbol] diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 6874cd74c..2759d1079 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -256,6 +256,7 @@ object Contexts { .withSetting(settings.debug, true) .withSetting(settings.Ylogcp, true) .withSetting(settings.printtypes, true) + .withSetting(settings.pageWidth, 120) /** The symbol loaders */ val loaders = new SymbolLoaders @@ -334,7 +335,7 @@ object Contexts { // Printers state /** Number of recursive invocations of a show method on cuyrrent stack */ - private[core] var showRecursions = 0 + private[core] var toTextRecursions = 0 // Reporters state private[dotc] var indent = 0 diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala index dd93b216c..2d6526c26 100644 --- a/src/dotty/tools/dotc/core/Decorators.scala +++ b/src/dotty/tools/dotc/core/Decorators.scala @@ -3,7 +3,7 @@ package core import annotation.tailrec import Symbols._ -import Contexts._, Names._, Phases._ +import Contexts._, Names._, Phases._, util.Texts._ /** This object provides useful implicit decorators for types defined elsewhere */ object Decorators { @@ -71,6 +71,10 @@ object Decorators { } } + implicit class TextToString(val text: Text) extends AnyVal { + def show(implicit ctx: Context) = text.mkString(ctx.settings.pageWidth.value) + } + /** Implements a test whether a list of strings representing phases contains * a given phase. The test returns true if the given phase starts with * one of the names in the list of strings. diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index a3b48926e..24008ab63 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -9,6 +9,7 @@ import Names.TypeName import Symbols.NoSymbol import Symbols._ import Types._, Periods._, Flags._, Transformers._ +import util.Texts._ import io.AbstractFile import Decorators.SymbolIteratorDecorator @@ -276,7 +277,7 @@ object Denotations { } } - def show(implicit ctx: Context): String = ctx.show(this) + def toText(implicit ctx: Context): Text = ctx.toText(this) } /** An overloaded denotation consisting of the alternatives of both given denotations. diff --git a/src/dotty/tools/dotc/core/Names.scala b/src/dotty/tools/dotc/core/Names.scala index 8a345fcc8..b61f5ee06 100644 --- a/src/dotty/tools/dotc/core/Names.scala +++ b/src/dotty/tools/dotc/core/Names.scala @@ -33,7 +33,6 @@ object Names { */ abstract class Name extends DotClass with PreName - with Showable with Seq[Char] with IndexedSeqOptimized[Char, Name] { @@ -82,7 +81,7 @@ object Names { override def toString = new String(chrs, start, length) - def show(implicit ctx: Context): String = ctx.show(this) + def show(implicit ctx: Context): String = ctx.nameString(this) /** Write to UTF8 representation of this name to given character array. * Start copying to index `to`. Return index of next free byte in array. diff --git a/src/dotty/tools/dotc/core/Printers.scala b/src/dotty/tools/dotc/core/Printers.scala index a8059e6c4..e6b2288ab 100644 --- a/src/dotty/tools/dotc/core/Printers.scala +++ b/src/dotty/tools/dotc/core/Printers.scala @@ -3,6 +3,7 @@ package core import Types._, Symbols._, Contexts._, Scopes._, Names._, NameOps._, Flags._ import Constants._, Annotations._, StdNames._, Denotations._, Trees._ +import util.Texts._ import java.lang.Integer.toOctalString import scala.annotation.switch @@ -17,8 +18,8 @@ trait Printers { this: Context => object Printers { class Precedence(val value: Int) extends AnyVal { - def parenthesize(nested: Precedence)(str: String) = - if (nested.value < value) "(" + str + ")" else str + def parenthesize(nested: Precedence)(text: Text) = + if (nested.value < value) "(" ~ text ~ ")" else text } val DotPrec = new Precedence(4) @@ -30,71 +31,73 @@ object Printers { abstract class Printer { - /** Show name with namespace suffix: /L for local names, - * /V for other term names, /T for type names + /** The name, possibley with with namespace suffix if debugNames is set: + * /L for local names, /V for other term names, /T for type names */ - def show(name: Name): String + def nameString(name: Name): String - /** Show type in context with given precedence */ - def show(tp: Type, precedence: Precedence): String - - /** Show name of symbol. - * If !settings.debug shows original name and - * translates expansions of operators back to operator symbol. + /** The name of the given symbol. + * If !settings.debug, the original name where + * expansions of operators are translated back to operator symbol. * E.g. $eq => =. * If settings.uniqid, adds id. */ - def showName(sym: Symbol): String + def nameString(sym: Symbol): String - /** Show fully qualified name of symbol */ - def showFullName(sym: Symbol): String + /** The fully qualified name of the symbol */ + def fullNameString(sym: Symbol): String - /** Show kind of symbol */ - def showKind(sym: Symbol): String + /** The kind of the symbol */ + def kindString(sym: Symbol): String - /** String representation, including symbol's kind e.g., "class Foo", "method Bar". + /** Textual representation, including symbol's kind e.g., "class Foo", "method Bar". * If hasMeaninglessName is true, uses the owner's name to disambiguate identity. */ - def show(sym: Symbol): String + def toText(sym: Symbol): Text + + /** Textual representation of symbol's declaration */ + def dclText(sym: Symbol): Text - /** Show symbol's declaration */ - def showDcl(sym: Symbol): String + /** If symbol's owner is a printable class C, the text "in C", otherwise "" */ + def locationText(sym: Symbol): Text - /** If symbol's owner is a printable class C, the string "in C", otherwise "" */ - def showLocation(sym: Symbol): String + /** Textual representation of symbol and its location */ + def locatedText(sym: Symbol): Text - /** Show symbol and its location */ - def showLocated(sym: Symbol): String + /** Textual representation of denotation */ + def toText(denot: Denotation): Text - /** Show denotation */ - def show(denot: Denotation): String + /** Textual representation of constant */ + def toText(const: Constant): Text - /** Show constant */ - def show(const: Constant): String + /** Textual representation of annotation */ + def toText(annot: Annotation): Text - /** Show annotation */ - def show(annot: Annotation): String + /** Textual representation of type in context with given precedence */ + def toText(tp: Type, precedence: Precedence): Text - /** Show all symbols in given list separated by `sep`, using `showDcl` for each */ - def showDcls(syms: List[Symbol], sep: String): String + /** Textual representation of all symbols in given list, + * using `dclText` for displaying each. + */ + def dclsText(syms: List[Symbol], sep: String = "\n"): Text - /** Show all definitions in a scope using `showDcl` for each */ - def show(sc: Scope): String + /** Textual representation of all definitions in a scope using `dclText` for each */ + def toText(sc: Scope): Text - /** Show tree */ - def show[T](tree: Tree[T]): String + /** Textual representation of tree */ + def toText[T](tree: Tree[T]): Text } class PlainPrinter(_ctx: Context) extends Printer { protected[this] implicit val ctx = _ctx - def controlled(op: => String): String = - if (ctx.showRecursions < maxShowRecursions) + def controlled(op: => Text): Text = + if (ctx.toTextRecursions < maxToTextRecursions) try { - ctx.showRecursions += 1 + ctx.toTextRecursions += 1 op } finally { - ctx.showRecursions -= 1 + ctx.toTextRecursions -= 1 } else { recursionLimitExceeeded() @@ -106,9 +109,6 @@ object Printers { (new Throwable).printStackTrace } - /** Concatenate strings separated by spaces */ - protected def compose(ss: String*) = ss.filter(_.nonEmpty).mkString(" ") - /** If the name of the symbol's owner should be used when you care about * seeing an interesting name: in such cases this symbol is e.g. a method * parameter with a synthetic name, a constructor named "this", an object @@ -121,7 +121,7 @@ object Printers { || (sym.name == nme.PACKAGE) // package ) - def show(name: Name): String = name.toString + { + def nameString(name: Name): String = name.toString + { if (ctx.settings.debugNames.value) if (name.isLocalName) "/L" else if (name.isTypeName) "/T" @@ -132,11 +132,11 @@ object Printers { /** String representation of a name used in a refinement * In refined printing this undoes type parameter expansion */ - protected def showRefinementName(tp: RefinedType) = show(tp.refinedName) + protected def refinementNameString(tp: RefinedType) = nameString(tp.refinedName) /** String representation of a refinement */ - protected def showRefinement(rt: RefinedType) = - showRefinementName(rt) + showRHS(rt.refinedInfo) + protected def toTextRefinement(rt: RefinedType) = + (refinementNameString(rt) ~ toTextRHS(rt.refinedInfo)).close /** The longest sequence of refinement types, starting at given type * and following parents. @@ -147,28 +147,29 @@ object Printers { case _ => Nil }) - def show(tp: Type, prec: Precedence): String = controlled { + def toText(tp: Type, prec: Precedence): Text = controlled { tp match { case tp: TypeType => - showRHS(tp) + toTextRHS(tp) case tp: SingletonType => - val str = showPrefix(tp) - if (str.endsWith(".")) str + "type" - else showFullName(tp.typeSymbol.skipPackageObject) + ".type" + val pre = toTextPrefix(tp) + if (pre.lastLine.endsWith(".")) pre ~ "type" + else fullNameString(tp.typeSymbol.skipPackageObject) ~ ".type" case TypeRef(pre, name) => - showPrefix(pre) + showName(tp.typeSymbol) + toTextPrefix(pre) ~ nameString(tp.typeSymbol) case tp: RefinedType => // return tp.toString // !!! DEBUG - val parent :: refined = refinementChain(tp).reverse - showLocal(parent) + - refined.asInstanceOf[List[RefinedType]].map(showRefinement).mkString("{", "; ", "}") + val parent :: (refined: List[RefinedType]) = + refinementChain(tp).reverse + toTextLocal(parent) ~ "{" ~ + Text(refined.map(toTextRefinement), "; ").close ~ "}" case AndType(tp1, tp2) => (prec parenthesize AndPrec) { - show(tp1, AndPrec) + "&" + show(tp2, AndPrec) + toText(tp1, AndPrec) ~ "&" ~ toText(tp2, AndPrec) } case OrType(tp1, tp2) => (prec parenthesize OrPrec) { - show(tp1, OrPrec) + "|" + show(tp2, OrPrec) + toText(tp1, OrPrec) ~ "|" ~ toText(tp2, OrPrec) } case ErrorType => "" @@ -180,76 +181,81 @@ object Printers { "" case tp: MethodType => (prec parenthesize GlobalPrec) { - val openStr = if (tp.isImplicit) "(implicit " else "(" + (if (tp.isImplicit) "(implicit " else "(") ~ + Text( (tp.paramNames, tp.paramTypes).zipped - .map((name, tp) => show(name) + ": " + showGlobal(tp)) - .mkString(openStr, ", ", ")" + showGlobal(tp.resultType)) + .map((name, tp) => nameString(name) ~ ": " ~ toTextGlobal(tp)), + ", ") ~ + ")" ~ toTextGlobal(tp.resultType) } case tp: ExprType => (prec parenthesize GlobalPrec) { - "=> " + showGlobal(tp.resultType) + "=> " ~ toTextGlobal(tp.resultType) } case tp: PolyType => (prec parenthesize GlobalPrec) { - (tp.paramNames, tp.paramBounds).zipped - .map((name, bounds) => show(name) + showGlobal(bounds)) - .mkString("[", ", ", "]" + showGlobal(tp.resultType)) + "[" ~ + Text( + (tp.paramNames, tp.paramBounds).zipped + .map((name, bounds) => nameString(name) ~ toTextGlobal(bounds)), + ", ") ~ + "]" ~ toTextGlobal(tp.resultType) } case PolyParam(pt, n) => - show(pt.paramNames(n)) + nameString(pt.paramNames(n)) case AnnotatedType(annot, tpe) => - showLocal(tpe) + " " + show(annot) + toTextLocal(tpe) ~ " " ~ toText(annot) } - } + }.close - /** Show type within highest precedence */ - protected def showLocal(tp: Type) = show(tp, DotPrec) + /** Render type within highest precedence */ + protected def toTextLocal(tp: Type) = toText(tp, DotPrec) - /** Show type within lowest precedence */ - protected def showGlobal(tp: Type) = show(tp, GlobalPrec) + /** Render type within lowest precedence */ + protected def toTextGlobal(tp: Type) = toText(tp, GlobalPrec) - /** Show name of symbol without unique id. Under refined printing, - * shows decoded original name. + /** The name of the symbol without a unique id. Under refined printing, + * the decoded original name. */ - protected def showSimpleName(sym: Symbol) = show(sym.name) + protected def simpleNameString(sym: Symbol): String = nameString(sym.name) - /** Show unique id of symbol, after a # */ - protected def showId(sym: Symbol) = + /** The unique id of symbol, after a # */ + protected def idString(sym: Symbol): String = if (ctx.settings.uniqid.value) "#" + sym.id else "" - def showName(sym: Symbol): String = showSimpleName(sym) + showId(sym) + def nameString(sym: Symbol): String = simpleNameString(sym) + idString(sym) - def showFullName(sym: Symbol): String = + def fullNameString(sym: Symbol): String = if (sym.isRoot || sym == NoSymbol || sym.owner.isEffectiveRoot) - showName(sym) + nameString(sym) else - showFullName(sym.effectiveOwner.enclosingClass) + "." + showName(sym) + fullNameString(sym.effectiveOwner.enclosingClass) + "." + nameString(sym) protected def objectPrefix = "object " protected def packagePrefix = "package " - protected def trimPrefix(str: String) = - str.stripPrefix(objectPrefix).stripPrefix(packagePrefix) + protected def trimPrefix(text: Text) = + text.stripPrefix(objectPrefix).stripPrefix(packagePrefix) /** The string representation of this type used as a prefix */ - protected def showPrefix(tp: Type): String = controlled { + protected def toTextPrefix(tp: Type): Text = controlled { tp match { case tp @ TermRef(pre, name) => - showPrefix(pre) + showName(tp.symbol) + "." + toTextPrefix(pre) ~ nameString(tp.symbol) ~ "." case ThisType(cls) => - showName(cls) + ".this." + nameString(cls) + ".this." case SuperType(thistpe, _) => - showPrefix(thistpe).replaceAll("""\bthis\.$""", "super.") + toTextPrefix(thistpe).map(_.replaceAll("""\bthis\.$""", "super.")) case tp @ ConstantType(value) => - showLocal(tp.underlying) + "(" + show(value) + ")." + toTextLocal(tp.underlying) ~ "(" ~ toText(value) ~ ")." case MethodParam(mt, idx) => - show(mt.paramNames(idx)) + "." + nameString(mt.paramNames(idx)) + "." case RefinedThis(_) => "this." case NoPrefix => "" case _ => - trimPrefix(showLocal(tp)) + "#" + trimPrefix(toTextLocal(tp)) ~ "#" } } @@ -260,31 +266,31 @@ object Printers { sym.isEffectiveRoot || sym.isAnonymousClass || sym.name.isReplWrapperName /** String representation of a definition's type following its name */ - protected def showRHS(tp: Type): String = controlled { + protected def toTextRHS(tp: Type): Text = controlled { tp match { case TypeBounds(lo, hi) => if (lo eq hi) - " = " + lo.show + " = " ~ lo.toText else - (if (lo == defn.NothingType) "" else " >: " + lo.show) + - (if (hi == defn.AnyType) "" else " <: " + hi.show) + (if (lo == defn.NothingType) Text() else " >: " ~ lo.toText) ~ + (if (hi == defn.AnyType) Text() else " <: " ~ hi.toText) case ClassInfo(pre, cdenot, cparents, decls, optSelfType) => - val preStr = showLocal(pre) - val selfStr = - if (optSelfType.exists) s"this: ${show(optSelfType, LeftArrowPrec)} =>" - else "" - val parentsStr = cparents.map(show(_, WithPrec)).mkString(" with ") - val declsStr = - if (decls.isEmpty) "" - else "\n " + showDcls(decls.toList, "\n ") - s""" extends $parentsStr { $selfStr$declsStr - |} at $preStr""".stripMargin - case _ => ": " + showGlobal(tp) + val preText = toTextLocal(pre) + val selfText = + if (optSelfType.exists) + "this: " ~ toText(optSelfType, LeftArrowPrec) ~ " =>" + else Text() + val parentsText = Text(cparents.map(toText(_, WithPrec)), " with ") + val declsText = if (decls.isEmpty) Text() else dclsText(decls.toList) + "extends " ~ parentsText ~ "{" ~ selfText ~ declsText ~ + "} at " ~ preText + case _ => + ": " ~ toTextGlobal(tp) } } - /** Show kind of symbol */ - def showKind(sym: Symbol) = + /** String representation of symbol's kind. */ + def kindString(sym: Symbol): String = if (sym is PackageClass) "package class" else if (sym is PackageVal) "package" else if (sym.isPackageObject) @@ -308,7 +314,7 @@ object Printers { else "" /** String representation of symbol's definition key word */ - protected def showKey(sym: Symbol): String = + protected def keyString(sym: Symbol): String = if (sym is JavaInterface) "interface" else if (sym is (Trait, butNot = ImplClass)) "trait" else if (sym.isClass) "class" @@ -321,35 +327,35 @@ object Printers { else "" /** String representation of symbol's flags */ - protected def showFlags(sym: Symbol) = sym.flags.toString + protected def toTextFlags(sym: Symbol): Text = + Text(sym.flags.flagStrings map stringToText, " ") /** String representation of symbol's variance or "" if not applicable */ - protected def showVariance(sym: Symbol) = sym.variance match { + protected def varianceString(sym: Symbol): String = sym.variance match { case -1 => "-" case 1 => "+" case _ => "" } - def showDcl(sym: Symbol): String = compose( - showFlags(sym), - showKey(sym), - showVariance(sym) + showName(sym) + showRHS(sym.info)) + def dclText(sym: Symbol): Text = + (toTextFlags(sym) ~~ keyString(sym) ~~ + (varianceString(sym) ~ nameString(sym)) ~ toTextRHS(sym.info)).close - def show(sym: Symbol): String = compose( - showKind(sym), - if (hasMeaninglessName(sym)) showSimpleName(sym.owner) + showId(sym) - else showName(sym) - ) + def toText(sym: Symbol): Text = + (kindString(sym) ~~ { + if (hasMeaninglessName(sym)) simpleNameString(sym.owner) + idString(sym) + else nameString(sym) + }).close - def showLocation(sym: Symbol): String = { + def locationText(sym: Symbol): Text = { val owns = sym.effectiveOwner - if (owns.isClass && !isEmptyPrefix(owns)) "in "+show(owns) else "" + if (owns.isClass && !isEmptyPrefix(owns)) "in " ~ toText(owns) else Text() } - def showLocated(sym: Symbol): String = - show(sym) + showLocation(sym) + def locatedText(sym: Symbol): Text = + (toText(sym) ~ locationText(sym)).close - def show(denot: Denotation): String = show(denot.symbol) + "/D" + def toText(denot: Denotation): Text = toText(denot.symbol) ~ "/D" @switch private def escapedChar(ch: Char): String = ch match { case '\b' => "\\b" @@ -363,82 +369,87 @@ object Printers { case _ => if (ch.isControl) "\\0" + toOctalString(ch) else String.valueOf(ch) } - def show(const: Constant) = const.tag match { + def toText(const: Constant): Text = const.tag match { case StringTag => "\"" + (const.value.toString flatMap escapedChar) + "\"" - case ClazzTag => s"classOf[${const.tpe.show}]" + case ClazzTag => "classOf[" ~ const.tpe.toText ~ "]" case CharTag => s"'${escapedChar(const.charValue)}'" case LongTag => const.longValue.toString + "L" case EnumTag => const.symbolValue.name.toString case _ => String.valueOf(const.value) } - def show(annot: Annotation): String = s"@${annot.symbol.name}" // for now + def toText(annot: Annotation): Text = s"@${annot.symbol.name}" // for now - def showDcls(syms: List[Symbol], sep: String): String = - syms map (_.showDcl) mkString sep + def dclsText(syms: List[Symbol], sep: String): Text = + if (sep == "\n") Text.lines(syms map dclText) + else Text(syms map dclText, sep) - def show(sc: Scope): String = - "Scope{\n" + showDcls(sc.toList, ";\n ") + "\n}" + def toText(sc: Scope): Text = + ("Scope{" ~ dclsText(sc.toList) ~ "}").close - def show[T](tree: Tree[T]): String = tree match { - case node: Product => - def showElem(elem: Any) = elem match { - case elem: Showable => elem.show - case elem => elem.toString - } - val nodeName = node.productPrefix - val elems = node.productIterator.map(showElem).mkString(", ") - val tpSuffix = - if (ctx.settings.printtypes.value && tree.hasType) - s" | ${tree.tpe.asInstanceOf[Type].show}" - else - "" - - s"$nodeName($elems$tpSuffix)" - case _ => - tree.toString - } // todo: override in refined printer + def toText[T](tree: Tree[T]): Text = { + tree match { + case node: Product => + def toTextElem(elem: Any): Text = elem match { + case elem: Showable => elem.toText + case elem => elem.toString + } + val nodeName = node.productPrefix + val elems = + Text(node.productIterator.map(toTextElem).toList, ", ") + val tpSuffix = + if (ctx.settings.printtypes.value && tree.hasType) + " | " ~ tree.tpe.asInstanceOf[Type].toText + else + Text() + + nodeName ~ "(" ~ elems ~ tpSuffix ~ ")" + case _ => + tree.toString: Text + } + }.close // todo: override in refined printer } class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { override protected def recursionLimitExceeeded() = {} - override def show(name: Name): String = name.toString + override def nameString(name: Name): String = name.toString - override protected def showSimpleName(sym: Symbol) = sym.originalName.decode + override protected def simpleNameString(sym: Symbol) = sym.originalName.decode - override def showPrefix(tp: Type): String = controlled { + override def toTextPrefix(tp: Type): Text = controlled { tp match { case ThisType(cls) => if (cls.isAnonymousClass) return "this." if (isOmittablePrefix(cls)) return "" - if (cls is ModuleClass) return showFullName(cls) + "." + if (cls is ModuleClass) return fullNameString(cls) + "." case tp @ TermRef(pre, name) => val sym = tp.symbol - if (sym.isPackageObject) return showPrefix(pre) + if (sym.isPackageObject) return toTextPrefix(pre) if (isOmittablePrefix(sym)) return "" case _ => } - super.showPrefix(tp) + super.toTextPrefix(tp) } - override protected def showRefinementName(tp: RefinedType): String = { + override protected def refinementNameString(tp: RefinedType): String = { val tsym = tp.member(tp.refinedName).symbol val name = tsym.originalName - show(if (tsym is ExpandedTypeParam) name.asTypeName.unexpandedName() else name) + nameString(if (tsym is ExpandedTypeParam) name.asTypeName.unexpandedName() else name) } - override def show(tp: Type, prec: Precedence): String = controlled { - def showFunction(args: List[Type]): String = + override def toText(tp: Type, prec: Precedence): Text = controlled { + def toTextFunction(args: List[Type]): Text = (prec parenthesize GlobalPrec) { - val argStr = + val argStr: Text = if (args.length == 2 && - !(defn.TupleClasses contains args.head.typeSymbol)) show(args.head, LeftArrowPrec) - else args.init.map(showGlobal(_)).mkString("(", ", ", ")") - argStr + " => " + showGlobal(args.last) + !(defn.TupleClasses contains args.head.typeSymbol)) toText(args.head, LeftArrowPrec) + else + "(" ~ Text(args.init.map(toTextGlobal(_)), ", ") ~ ")" + argStr ~ " => " ~ toTextGlobal(args.last) } - def showTuple(args: List[Type]): String = - args.map(showGlobal(_)).mkString("(", ", ", ")") + def toTextTuple(args: List[Type]): Text = + "(" ~ Text(args.map(toTextGlobal(_)), ", ") ~ ")" try { tp match { case tp: RefinedType => @@ -447,36 +458,37 @@ object Printers { val tycon = tp.unrefine val cls = tycon.typeSymbol if (cls.typeParams.length == args.length) { - if (cls == defn.RepeatedParamClass) return showLocal(args.head) + "*" - if (cls == defn.ByNameParamClass) return "=> " + showGlobal(args.head) - if (defn.FunctionClasses contains cls) return showFunction(args) - if (defn.TupleClasses contains cls) return showTuple(args) + if (cls == defn.RepeatedParamClass) return toTextLocal(args.head) ~ "*" + if (cls == defn.ByNameParamClass) return "=> " ~ toTextGlobal(args.head) + if (defn.FunctionClasses contains cls) return toTextFunction(args) + if (defn.TupleClasses contains cls) return toTextTuple(args) } - return showLocal(tycon) + args.map(showGlobal(_)).mkString("[", ", ", "]") + return toTextLocal(tycon) ~ "[" ~ + Text(args.map(toTextGlobal(_)), ", ") ~ "]" } case _ => } } catch { case ex: CyclicReference => - "" + super.show(tp, prec) + "" ~ super.toText(tp, prec) } - super.show(tp, prec) + super.toText(tp, prec) } - override def showKind(sym: Symbol) = + override def kindString(sym: Symbol) = if (sym is Package) "package" else if (sym.isPackageObject) "package object" else if (sym is Module) "object" else if (sym is ImplClass) "class" else if (sym.isClassConstructor) "constructor" - else super.showKind(sym) + else super.kindString(sym) - override def showFlags(sym: Symbol) = - sym.flags.flagStrings.filterNot(_.startsWith("<")).mkString(" ") + override def toTextFlags(sym: Symbol) = + Text(sym.flags.flagStrings.filterNot(_.startsWith("<")) map stringToText, " ") - override def show(denot: Denotation): String = show(denot.symbol) + override def toText(denot: Denotation): Text = toText(denot.symbol) } - final val maxShowRecursions = 100 + final val maxToTextRecursions = 100 } diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala index cddf2e990..2d9df1962 100644 --- a/src/dotty/tools/dotc/core/Scopes.scala +++ b/src/dotty/tools/dotc/core/Scopes.scala @@ -12,6 +12,7 @@ import Periods._ import Decorators._ import Contexts._ import Denotations._ +import util.Texts._ import SymDenotations.NoDenotation object Scopes { @@ -110,7 +111,7 @@ object Scopes { /** Cast this scope to a mutable scope */ final def openForMutations: MutableScope = this.asInstanceOf[MutableScope] - final def show(implicit ctx: Context): String = ctx.show(this) + final def toText(implicit ctx: Context): Text = ctx.toText(this) } /** A subclass of Scope that defines methods for entering and diff --git a/src/dotty/tools/dotc/core/Showable.scala b/src/dotty/tools/dotc/core/Showable.scala index 770b3c818..54a25506a 100644 --- a/src/dotty/tools/dotc/core/Showable.scala +++ b/src/dotty/tools/dotc/core/Showable.scala @@ -1,9 +1,12 @@ -package dotty.tools.dotc.core +package dotty.tools.dotc +package core -import Contexts._ +import Contexts._, util.Texts._, Decorators._ trait Showable { - def show(implicit ctx: Context): String + def toText(implicit ctx: Context): Text + + def show(implicit ctx: Context): String = toText.show } \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 3f37c41f5..d5ccfb74a 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -991,7 +991,8 @@ object SymDenotations { if (file != null) (s" in $file", file.toString) else ("", "the signature") cctx.error( - s"""|bad symbolic reference. A signature$location refers to ${cctx.fresh.withDebug.show(denot.name)} + s"""|bad symbolic reference. A signature$location refers to + |${cctx.fresh.withSetting(cctx.settings.debugNames, true).nameString(denot.name)} |in ${denot.owner.showKind} ${denot.owner.showFullName} which is not available. |It may be completely missing from the current classpath, or the version on |the classpath might be incompatible with the version used when compiling $src.""".stripMargin) diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 089d41be7..d39dabe1d 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -10,7 +10,7 @@ import java.lang.AssertionError import Decorators._ import Symbols._ import Contexts._ -import SymDenotations._ +import SymDenotations._, util.Texts._ import Types._, Annotations._, Positions._, StdNames._, Trees._ import Denotations.{ Denotation, SingleDenotation, MultiDenotation } import collection.mutable @@ -376,12 +376,13 @@ object Symbols { if (lastDenot == null) s"Naked$prefixString#$id" else lastDenot.toString - def show(implicit ctx: Context): String = ctx.show(this) - def showLocated(implicit ctx: Context): String = ctx.showLocated(this) - def showDcl(implicit ctx: Context): String = ctx.showDcl(this) - def showKind(implicit ctx: Context): String = ctx.showKind(this) - def showName(implicit ctx: Context): String = ctx.showName(this) - def showFullName(implicit ctx: Context): String = ctx.showFullName(this) + def toText(implicit ctx: Context): Text = ctx.toText(this) + + def showLocated(implicit ctx: Context): String = ctx.locatedText(this).show + def showDcl(implicit ctx: Context): Text = ctx.dclText(this).show + def showKind(implicit ctx: Context): String = ctx.kindString(this) + def showName(implicit ctx: Context): String = ctx.nameString(this) + def showFullName(implicit ctx: Context): String = ctx.fullNameString(this) } type TermSymbol = Symbol { type ThisName = TermName } diff --git a/src/dotty/tools/dotc/core/Trees.scala b/src/dotty/tools/dotc/core/Trees.scala index bc3d83aa1..5fcab4de7 100644 --- a/src/dotty/tools/dotc/core/Trees.scala +++ b/src/dotty/tools/dotc/core/Trees.scala @@ -100,7 +100,7 @@ object Trees { /** Is this tree either the empty tree or the empty ValDef? */ def isEmpty: Boolean = false - override def show(implicit ctx: Context) = ctx.show(this) + override def toText(implicit ctx: Context) = ctx.toText(this) override def hashCode(): Int = System.identityHashCode(this) override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 297f58cd5..252901b17 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -15,7 +15,7 @@ import SymDenotations._ import Decorators._ import Denotations._ import Periods._ -import TypedTrees.tpd._, TypedTrees.TreeMapper +import TypedTrees.tpd._, TypedTrees.TreeMapper, util.Texts._ import transform.Erasure import scala.util.hashing.{ MurmurHash3 => hashing } import collection.mutable @@ -663,7 +663,7 @@ object Types { */ def signature(implicit ctx: Context): Signature = NotAMethod - def show(implicit ctx: Context): String = ctx.show(this, Printers.GlobalPrec) + def toText(implicit ctx: Context): Text = ctx.toText(this, Printers.GlobalPrec) // ----- hashing ------------------------------------------------------ diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index f7ee0d05c..2f7bcc94d 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -10,8 +10,10 @@ import java.lang.Double.longBitsToDouble import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._ import StdNames._, Denotations._, NameOps._, Flags._, Constants._, Annotations._ import Positions._, TypedTrees.tpd._, TypedTrees.TreeOps +import util.Texts._ import io.AbstractFile import scala.reflect.internal.pickling.PickleFormat._ +import Decorators._ import scala.collection.{ mutable, immutable } import scala.collection.mutable.ListBuffer import scala.annotation.switch @@ -22,8 +24,8 @@ object UnPickler { class BadSignature(msg: String) extends RuntimeException(msg) case class TempPolyType(tparams: List[Symbol], tpe: Type) extends UncachedGroundType { - override def show(implicit ctx: Context): String = - s"[${ctx.showDcls(tparams, ", ")}]${tpe.show}" + override def toText(implicit ctx: Context): Text = + "[" ~ ctx.dclsText(tparams, ", ") ~ "]" ~ tpe.show } /** Temporary type for classinfos, will be decomposed on completion of the class */ @@ -529,7 +531,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: if (tp1 existsPart isBound) { val tp2 = tp1.subst(boundSyms, boundSyms map (_ => defn.AnyType)) cctx.warning(s"""failure to eliminate existential - |original type : $tp forSome {${cctx.showDcls(boundSyms, "; ")}} + |original type : $tp forSome {${cctx.dclsText(boundSyms, "; ").show} |reduces to : $tp1 |type used instead: $tp2 |proceed at own risk.""".stripMargin) diff --git a/src/dotty/tools/dotc/util/Text.scala b/src/dotty/tools/dotc/util/Text.scala index a63f36b83..251274371 100644 --- a/src/dotty/tools/dotc/util/Text.scala +++ b/src/dotty/tools/dotc/util/Text.scala @@ -4,33 +4,40 @@ import language.implicitConversions object Texts { - class Text { + abstract class Text { protected def indentMargin = 2 - protected def pageWidth = 80 def relems: List[Text] def isEmpty: Boolean = this match { case Str(s) => s.isEmpty case Fluid(relems) => relems forall (_.isEmpty) - case Rigid(relems) => relems.isEmpty + case Vertical(relems) => relems.isEmpty } - def isRigid = isInstanceOf[Rigid] + def isVertical = isInstanceOf[Vertical] + def isClosed = isVertical || isInstanceOf[Closed] + def isFluid = isInstanceOf[Fluid] + def isSplittable = isFluid && !isClosed - def fitsIn(width: Int): Boolean = this match { + def close = new Closed(relems) + + def remaining(width: Int): Int = this match { case Str(s) => - s.length <= width - case Fluid(Str(s) :: prevs) => - s.length < width && Fluid(prevs).fitsIn(width - s.length) - case Rigid(_) => - false + width - s.length + case Fluid(Nil) => + width + case Fluid(last :: prevs) => + val r = last remaining width + if (r < 0) r else Fluid(prevs) remaining r + case Vertical(_) => + -1 } - def lastLineWidth: Int = this match { - case Str(s) => s.length - case _ => relems.head.lastLineWidth + def lastLine: String = this match { + case Str(s) => s + case _ => relems.head.lastLine } def appendToLastLine(that: Text): Text = that match { @@ -44,80 +51,103 @@ object Texts { } private def appendIndented(that: Text)(width: Int): Text = - Rigid(that.layout(width - indentMargin).indented :: this.relems) + Vertical(that.layout(width - indentMargin).indented :: this.relems) - private def append(width: Int)(that: Text): Text = + private def append(width: Int)(that: Text): Text = { if (this.isEmpty) that.layout(width) else if (that.isEmpty) this - else if (that.isRigid) appendIndented(that)(width) - else if (this.isRigid) Fluid(that.layout(width) :: this.relems) - else if (that.fitsIn(width - this.lastLineWidth)) appendToLastLine(that) + else if (that.isVertical) appendIndented(that)(width) + else if (this.isVertical) Fluid(that.layout(width) :: this.relems) + else if (that.remaining(width - lastLine.length) >= 0) appendToLastLine(that) + else if (that.isSplittable) (this /: that.relems.reverse)(_.append(width)(_)) else appendIndented(that)(width) + } def layout(width: Int): Text = this match { case Str(_) => this case Fluid(relems) => ((Str(""): Text) /: relems.reverse)(_.append(width)(_)) - case Rigid(relems) => - Rigid(relems map (_ layout width)) + case Vertical(relems) => + Vertical(relems map (_ layout width)) + } + + def map(f: String => String): Text = this match { + case Str(s) => Str(f(s)) + case Fluid(relems) => Fluid(relems map (_ map f)) + case Vertical(relems) => Vertical(relems map (_ map f)) + } + + def stripPrefix(pre: String): Text = this match { + case Str(s) => + if (s.startsWith(pre)) s drop pre.length else s + case Fluid(relems) => + val elems = relems.reverse + val head = elems.head.stripPrefix(pre) + if (head eq elems.head) this else Fluid((head :: elems.tail).reverse) + case Vertical(relems) => + val elems = relems.reverse + val head = elems.head.stripPrefix(pre) + if (head eq elems.head) this else Vertical((head :: elems.tail).reverse) } private def indented: Text = this match { case Str(s) => Str((" " * indentMargin) + s) case Fluid(relems) => Fluid(relems map (_.indented)) - case Rigid(relems) => Rigid(relems map (_.indented)) + case Vertical(relems) => Vertical(relems map (_.indented)) } def print(sb: StringBuilder): Unit = this match { case Str(s) => sb.append(s) - case txt: CompoundText => + case _ => var follow = false - for (elem <- txt.relems.reverse) { + for (elem <- relems.reverse) { if (follow) sb.append("\n") elem.print(sb) follow = true } } - def show: String = - layout(pageWidth).showRaw - - def showRaw: String = { + def mkString(width: Int): String = { val sb = new StringBuilder - print(sb) + layout(width).print(sb) sb.toString } - def +++ (that: Text) = + def ~ (that: Text) = if (this.isEmpty) that else if (that.isEmpty) this else Fluid(that :: this :: Nil) + def ~~ (that: Text) = + if (this.isEmpty) that + else if (that.isEmpty) this + else Fluid(that :: Str(" ") :: this :: Nil) + def over (that: Text) = - if (this.isRigid) Rigid(that :: this.relems) - else Rigid(that :: this :: Nil) + if (this.isVertical) Vertical(that :: this.relems) + else Vertical(that :: this :: Nil) } object Text { - def apply(xs: Traversable[Text], sep: String): Text = { + def apply(): Text = Str("") + def apply(xs: Traversable[Text], sep: String = " "): Text = { val ys = xs filterNot (_.isEmpty) if (ys.isEmpty) Str("") - else ys reduce (_ +++ sep +++ _) + else ys reduce (_ ~ sep ~ _) } - } - - abstract class CompoundText extends Text { - def relems: List[Text] + def lines(xs: Traversable[Text]) = Vertical(xs.toList.reverse) } case class Str(s: String) extends Text { override def relems: List[Text] = List(this) } - case class Rigid(relems: List[Text]) extends CompoundText - case class Fluid(relems: List[Text]) extends CompoundText + case class Vertical(relems: List[Text]) extends Text + case class Fluid(relems: List[Text]) extends Text + + class Closed(relems: List[Text]) extends Fluid(relems) implicit def stringToText(s: String): Text = Str(s) } \ No newline at end of file diff --git a/src/test/showClass.scala b/src/test/showClass.scala index e7e01ae84..a7896c733 100644 --- a/src/test/showClass.scala +++ b/src/test/showClass.scala @@ -11,15 +11,20 @@ object showClass { println(s"showing $path") val cls = ctx.requiredClass(path.toTypeName) println(s"showing $path -> ${cls.denot}") + println((cls.toText ~ cls.info.toText).show) +/* val info = cls.info info match { case ClassInfo(pre, c, cps, decls, optSelfType) => println(s"prefix = ${pre.show}") println(s"self = ${c.show}") println(s"parents = ${cps.map(_.show).mkString(",")}") + println(s"showClass $path") // !!! DEBUG println(s"decls = ${decls.show}") println(s"selftype = ${optSelfType.show}") + println(s"type-params = ${info.typeParams}") } +*/ } def main(args: Array[String]) = { @@ -30,17 +35,18 @@ object showClass { for (arg <- args) showClass(arg) - showClass("java.lang.Class") - showClass("scala.Boolean") - showClass("scala.Array") - showClass("scala.math.Ordering") - showClass("scala.collection.Traversable") - showClass("scala.collection.LinearSeqLike") - showClass("scala.collection.immutable.List") - showClass("scala.collection.TraversableLike") - showClass("scala.collection.immutable.Seq") - showClass("scala.collection.MapLike") - //showClass("dotty.tools.dotc.core.Types") +// showClass("java.lang.Class") +// showClass("scala.Boolean") +// showClass("scala.Array") +// showClass("scala.math.Ordering") +// showClass("scala.collection.Traversable") +// showClass("scala.collection.LinearSeqLike") +// showClass("scala.collection.immutable.List") + showClass("scala.collection.TraversableLike") +// showClass("scala.collection.immutable.Seq") +// showClass("scala.collection.MapLike") +// showClass("scala.Function1") +// showClass("dotty.tools.dotc.core.Types") println("done") } } \ No newline at end of file -- cgit v1.2.3