summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorVlad Ureche <vlad.ureche@gmail.com>2012-07-12 14:24:54 +0200
committerVlad Ureche <vlad.ureche@gmail.com>2012-07-16 23:41:44 +0200
commita119ad1ae58723bd2e757ed331a536ff4ae49bdf (patch)
tree45692e87e13f0870f8b6f928b33ff6668cd46389 /src/compiler
parent891769fae541513d68ce7a8e84b7213472c333c9 (diff)
downloadscala-a119ad1ae58723bd2e757ed331a536ff4ae49bdf.tar.gz
scala-a119ad1ae58723bd2e757ed331a536ff4ae49bdf.tar.bz2
scala-a119ad1ae58723bd2e757ed331a536ff4ae49bdf.zip
SI-4360 Adds prefixes to scaladoc
This was a long-standing issue in scaladoc: It was unable to disambiguate between entries with the same name. One example is: immutable.Seq: trait Seq[+A] extends Iterable[A] with Seq[A] ... What's that? Seq extends Seq? No, immutable.Seq extends collection.Seq, but scaladoc was unable to show that. Now it does, depending on the template you're in. Prefixes are relative and can go back: -scala.collection.Seq has subclasses *immutable.Seq* and *mutable.Seq* -scala.immutable.Seq extends *collection.Seq* Unfortunately the price we pay for this is high, a 20% slowdown in scaladoc. This is why there is a new flag called -no-prefixes that disables the prefixes in front of types. Btw, it also fixes the notorious "booleanValue: This member is added by an implicit conversion from Boolean to Boolean ...". That's now java.lang.Boolean, so it becomes clear. Conflicts: src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/ant/Scaladoc.scala9
-rw-r--r--src/compiler/scala/tools/nsc/doc/DocFactory.scala1
-rw-r--r--src/compiler/scala/tools/nsc/doc/Settings.scala8
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala8
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala9
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/Entity.scala9
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala196
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala7
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala215
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala8
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala33
11 files changed, 303 insertions, 200 deletions
diff --git a/src/compiler/scala/tools/ant/Scaladoc.scala b/src/compiler/scala/tools/ant/Scaladoc.scala
index 2cada92c1e..9aa2f6f921 100644
--- a/src/compiler/scala/tools/ant/Scaladoc.scala
+++ b/src/compiler/scala/tools/ant/Scaladoc.scala
@@ -153,6 +153,8 @@ class Scaladoc extends ScalaMatchingTask {
/** Instruct the scaladoc to produce textual ouput from html pages, for easy diff-ing */
private var docRawOutput: Boolean = false
+ /** Instruct the scaladoc not to generate prefixes */
+ private var docNoPrefixes: Boolean = false
/*============================================================================*\
** Properties setters **
@@ -427,6 +429,12 @@ class Scaladoc extends ScalaMatchingTask {
def setRawOutput(input: String) =
docRawOutput = Flag.getBooleanValue(input, "rawOutput")
+ /** Set the `noPrefixes` bit to prevent Scaladoc from generating prefixes in
+ * front of types -- may lead to confusion, but significantly speeds up the generation.
+ * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */
+ def setNoPrefixes(input: String) =
+ docNoPrefixes = Flag.getBooleanValue(input, "noPrefixes")
+
/*============================================================================*\
** Properties getters **
\*============================================================================*/
@@ -625,6 +633,7 @@ class Scaladoc extends ScalaMatchingTask {
docSettings.docDiagrams.value = docDiagrams
docSettings.docDiagramsDebug.value = docDiagramsDebug
docSettings.docRawOutput.value = docRawOutput
+ docSettings.docNoPrefixes.value = docNoPrefixes
if(!docDiagramsDotPath.isEmpty) docSettings.docDiagramsDotPath.value = docDiagramsDotPath.get
if (!docgenerator.isEmpty) docSettings.docgenerator.value = docgenerator.get
diff --git a/src/compiler/scala/tools/nsc/doc/DocFactory.scala b/src/compiler/scala/tools/nsc/doc/DocFactory.scala
index 3c92c3b4b6..964227a6a5 100644
--- a/src/compiler/scala/tools/nsc/doc/DocFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/DocFactory.scala
@@ -81,6 +81,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor
new { override val global: compiler.type = compiler }
with model.ModelFactory(compiler, settings)
with model.ModelFactoryImplicitSupport
+ with model.ModelFactoryTypeSupport
with model.diagram.DiagramFactory
with model.comment.CommentFactory
with model.TreeFactory {
diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala
index 31e49131f6..c7bdf74ebd 100644
--- a/src/compiler/scala/tools/nsc/doc/Settings.scala
+++ b/src/compiler/scala/tools/nsc/doc/Settings.scala
@@ -166,6 +166,11 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_))
"For each html file, create another .html.raw file containing only the text. (can be used for quickly diffing two scaladoc outputs)"
)
+ val docNoPrefixes = BooleanSetting (
+ "-no-prefixes",
+ "Prevents generating prefixes in types, possibly creating ambiguous references, but significantly speeding up scaladoc."
+ )
+
// Somewhere slightly before r18708 scaladoc stopped building unless the
// self-type check was suppressed. I hijacked the slotted-for-removal-anyway
// suppress-vt-warnings option and renamed it for this purpose.
@@ -177,7 +182,8 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_))
docDiagrams, docDiagramsDebug, docDiagramsDotPath,
docDiagramsDotTimeout, docDiagramsDotRestart,
docImplicits, docImplicitsDebug, docImplicitsShowAll,
- docDiagramsMaxNormalClasses, docDiagramsMaxImplicitClasses
+ docDiagramsMaxNormalClasses, docDiagramsMaxImplicitClasses,
+ docNoPrefixes
)
val isScaladocSpecific: String => Boolean = scaladocSpecific map (_.name)
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala
index 59560befc9..f3454f71b8 100644
--- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala
@@ -86,22 +86,22 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
// them by on node with a corresponding tooltip
superClasses = if (_superClasses.length > settings.docDiagramsMaxNormalClasses.value) {
val superClassesTooltip = Some(limitSize(_superClasses.map(_.tpe.name).mkString(", ")))
- List(NormalNode(textTypeEntity(_superClasses.length + MultiSuffix), None, superClassesTooltip))
+ List(NormalNode(textTypeEntity(_superClasses.length + MultiSuffix), None)(superClassesTooltip))
} else _superClasses
subClasses = if (_subClasses.length > settings.docDiagramsMaxNormalClasses.value) {
val subClassesTooltip = Some(limitSize(_subClasses.map(_.tpe.name).mkString(", ")))
- List(NormalNode(textTypeEntity(_subClasses.length + MultiSuffix), None, subClassesTooltip))
+ List(NormalNode(textTypeEntity(_subClasses.length + MultiSuffix), None)(subClassesTooltip))
} else _subClasses
incomingImplicits = if (_incomingImplicits.length > settings.docDiagramsMaxImplicitClasses.value) {
val incomingImplicitsTooltip = Some(limitSize(_incomingImplicits.map(_.tpe.name).mkString(", ")))
- List(ImplicitNode(textTypeEntity(_incomingImplicits.length + MultiSuffix), None, incomingImplicitsTooltip))
+ List(ImplicitNode(textTypeEntity(_incomingImplicits.length + MultiSuffix), None)(incomingImplicitsTooltip))
} else _incomingImplicits
outgoingImplicits = if (_outgoingImplicits.length > settings.docDiagramsMaxImplicitClasses.value) {
val outgoingImplicitsTooltip = Some(limitSize(_outgoingImplicits.map(_.tpe.name).mkString(", ")))
- List(ImplicitNode(textTypeEntity(_outgoingImplicits.length + MultiSuffix), None, outgoingImplicitsTooltip))
+ List(ImplicitNode(textTypeEntity(_outgoingImplicits.length + MultiSuffix), None)(outgoingImplicitsTooltip))
} else _outgoingImplicits
thisNode = _thisNode
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala
index 37600fa908..adfeb3b012 100644
--- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala
@@ -28,7 +28,7 @@ class DotRunner(settings: doc.Settings) {
if (dotProcess == null) {
if (dotRestarts < settings.docDiagramsDotRestart.value) {
if (dotRestarts != 0)
- settings.printMsg("A new graphviz dot process will be created...\n")
+ settings.printMsg("Graphviz will be restarted...\n")
dotRestarts += 1
dotProcess = new DotProcess(settings)
} else
@@ -145,9 +145,10 @@ class DotProcess(settings: doc.Settings) {
settings.printMsg("**********************************************************************")
} else {
// we shouldn't just sit there for 50s not reporting anything, no?
- settings.printMsg("Graphviz dot encountered an error when generating the diagram for")
- settings.printMsg(templateName + ". Use the " + settings.docDiagramsDebug.name + " flag")
- settings.printMsg("for more information.")
+ settings.printMsg("Graphviz dot encountered an error when generating the diagram for:")
+ settings.printMsg(templateName)
+ settings.printMsg("These are usually spurious errors, but if you notice a persistant error on")
+ settings.printMsg("a diagram, please use the " + settings.docDiagramsDebug.name + " flag and report a bug with the output.")
}
}
}
diff --git a/src/compiler/scala/tools/nsc/doc/model/Entity.scala b/src/compiler/scala/tools/nsc/doc/model/Entity.scala
index 4ab77b356b..41ba95e072 100644
--- a/src/compiler/scala/tools/nsc/doc/model/Entity.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/Entity.scala
@@ -96,9 +96,6 @@ trait TemplateEntity extends Entity {
/** The self-type of this template, if it differs from the template type. */
def selfType : Option[TypeEntity]
-
- /** The type of this entity, with type members */
- def ownType: TypeEntity
}
@@ -206,7 +203,8 @@ trait HigherKinded {
/** A template (class, trait, object or package) which is referenced in the universe, but for which no further
* documentation is available. Only templates for which a source file is given are documented by Scaladoc. */
trait NoDocTemplate extends TemplateEntity {
- def kind = "<no doc>"
+ def kind = ""
+ //def kind = "(not documented) template"
}
/** An inherited template that was not documented in its original owner - example:
@@ -214,7 +212,8 @@ trait NoDocTemplate extends TemplateEntity {
* in the source: trait U extends T -- C appears in U as a NoDocTemplateMemberImpl
* -- that is, U has a member for it but C doesn't get its own page */
trait NoDocTemplateMemberEntity extends TemplateEntity with MemberEntity {
- def kind = "<no doc, mbr>"
+ def kind = ""
+ //def kind = "(not documented) member template"
}
/** A template (class, trait, object or package) for which documentation is available. Only templates for which
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
index 25b4a174ec..b7c4eed87d 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
@@ -19,7 +19,12 @@ import model.{ RootPackage => RootPackageEntity }
/** This trait extracts all required information for documentation from compilation units */
class ModelFactory(val global: Global, val settings: doc.Settings) {
- thisFactory: ModelFactory with ModelFactoryImplicitSupport with DiagramFactory with CommentFactory with TreeFactory =>
+ thisFactory: ModelFactory
+ with ModelFactoryImplicitSupport
+ with ModelFactoryTypeSupport
+ with DiagramFactory
+ with CommentFactory
+ with TreeFactory =>
import global._
import definitions.{ ObjectClass, NothingClass, AnyClass, AnyValClass, AnyRefClass }
@@ -32,7 +37,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
private var universe: Universe = null
private def dbg(msg: String) = if (sys.props contains "scala.scaladoc.debug") println(msg)
- private def closestPackage(sym: Symbol) = {
+ protected def closestPackage(sym: Symbol) = {
if (sym.isPackage || sym.isPackageClass) sym
else sym.enclosingPackage
}
@@ -63,7 +68,10 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
private val droppedPackages = mutable.Set[PackageImpl]()
protected val docTemplatesCache = new mutable.LinkedHashMap[Symbol, DocTemplateImpl]
protected val noDocTemplatesCache = new mutable.LinkedHashMap[Symbol, NoDocTemplateImpl]
- protected var typeCache = new mutable.LinkedHashMap[Type, TypeEntity]
+ def packageDropped(tpl: DocTemplateImpl) = tpl match {
+ case p: PackageImpl => droppedPackages(p)
+ case _ => false
+ }
def optimize(str: String): String =
if (str.length < 16) str.intern else str
@@ -95,7 +103,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
def isObject = sym.isModule && !sym.isPackage
def isCaseClass = sym.isCaseClass
def isRootPackage = false
- def ownType = makeType(sym.tpe, this)
def selfType = if (sym.thisSym eq sym) None else Some(makeType(sym.thisSym.typeOfThis, this))
}
@@ -338,14 +345,14 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
def directSubClasses = allSubClasses.filter(_.parentTypes.map(_._1).contains(this))
/* Implcitly convertible class cache */
- private var implicitlyConvertibleClassesCache: mutable.ListBuffer[(DocTemplateEntity, ImplicitConversionImpl)] = null
- def registerImplicitlyConvertibleClass(dtpl: DocTemplateEntity, conv: ImplicitConversionImpl): Unit = {
+ private var implicitlyConvertibleClassesCache: mutable.ListBuffer[(DocTemplateImpl, ImplicitConversionImpl)] = null
+ def registerImplicitlyConvertibleClass(dtpl: DocTemplateImpl, conv: ImplicitConversionImpl): Unit = {
if (implicitlyConvertibleClassesCache == null)
- implicitlyConvertibleClassesCache = mutable.ListBuffer[(DocTemplateEntity, ImplicitConversionImpl)]()
+ implicitlyConvertibleClassesCache = mutable.ListBuffer[(DocTemplateImpl, ImplicitConversionImpl)]()
implicitlyConvertibleClassesCache += ((dtpl, conv))
}
- def incomingImplicitlyConvertedClasses: List[(DocTemplateEntity, ImplicitConversionImpl)] =
+ def incomingImplicitlyConvertedClasses: List[(DocTemplateImpl, ImplicitConversionImpl)] =
if (implicitlyConvertibleClassesCache == null)
List()
else
@@ -408,7 +415,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
conv.targetTypeComponents map {
case pair@(template, tpe) =>
template match {
- case d: DocTemplateImpl => d.registerImplicitlyConvertibleClass(this, conv)
+ case d: DocTemplateImpl if (d != this) => d.registerImplicitlyConvertibleClass(this, conv)
case _ => // nothing
}
(pair._1, pair._2, conv)
@@ -508,6 +515,8 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
normalizeTemplate(aSym.owner)
case _ if aSym.isModuleClass =>
normalizeTemplate(aSym.sourceModule)
+ // case t: ThisType =>
+ // t.
case _ =>
aSym
}
@@ -737,12 +746,12 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
def findTemplate(query: String): Option[DocTemplateImpl] = {
assert(modelFinished)
- docTemplatesCache.values find { (tpl: TemplateImpl) => tpl.qualifiedName == query && !tpl.isObject }
+ docTemplatesCache.values find { (tpl: DocTemplateImpl) => tpl.qualifiedName == query && !packageDropped(tpl) && !tpl.isObject }
}
def findTemplateMaybe(aSym: Symbol): Option[DocTemplateImpl] = {
assert(modelFinished)
- docTemplatesCache.get(normalizeTemplate(aSym))
+ docTemplatesCache.get(normalizeTemplate(aSym)).filterNot(packageDropped(_))
}
def makeTemplate(aSym: Symbol): TemplateImpl = makeTemplate(aSym, None)
@@ -890,158 +899,25 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
List((makeTemplate(aType.typeSymbol), makeType(aType, inTpl)))
}
- /** */
- def makeType(aType: Type, inTpl: TemplateImpl): TypeEntity = {
- def templatePackage = closestPackage(inTpl.sym)
-
- def createTypeEntity = new TypeEntity {
- private val nameBuffer = new StringBuilder
- private var refBuffer = new immutable.TreeMap[Int, (LinkTo, Int)]
- private def appendTypes0(types: List[Type], sep: String): Unit = types match {
- case Nil =>
- case tp :: Nil =>
- appendType0(tp)
- case tp :: tps =>
- appendType0(tp)
- nameBuffer append sep
- appendTypes0(tps, sep)
- }
-
- private def appendType0(tpe: Type): Unit = tpe match {
- /* Type refs */
- case tp: TypeRef if definitions.isFunctionType(tp) =>
- val args = tp.normalize.typeArgs
- nameBuffer append '('
- appendTypes0(args.init, ", ")
- nameBuffer append ") ⇒ "
- appendType0(args.last)
- case tp: TypeRef if definitions.isScalaRepeatedParamType(tp) =>
- appendType0(tp.args.head)
- nameBuffer append '*'
- case tp: TypeRef if definitions.isByNameParamType(tp) =>
- nameBuffer append "⇒ "
- appendType0(tp.args.head)
- case tp: TypeRef if definitions.isTupleType(tp) =>
- val args = tp.normalize.typeArgs
- nameBuffer append '('
- appendTypes0(args, ", ")
- nameBuffer append ')'
- case TypeRef(pre, aSym, targs) =>
- val preSym = pre.widen.typeSymbol
- // There's a work in progress here trying to deal with the
- // places where undesirable prefixes are printed.
- // ...
- // If the prefix is something worthy of printing, see if the prefix type
- // is in the same package as the enclosing template. If so, print it
- // unqualified and they'll figure it out.
- //
- // val stripPrefixes = List(templatePackage.fullName + ".", "package.", "java.lang.")
- // if (!preSym.printWithoutPrefix) {
- // nameBuffer append stripPrefixes.foldLeft(pre.prefixString)(_ stripPrefix _)
- // }
-
- // SI-3314/SI-4888: Classes, Traits and Types can be inherited from a template to another:
- // class Enum { abstract class Value }
- // class Day extends Enum { object Mon extends Value /*...*/ }
- // ===> in such cases we have two options:
- // (0) if there's no inheritance taking place (Enum#Value) we can link to the template directly
- // (1) if we generate the doc template for Day, we can link to the correct member
- // (2) if we don't generate the doc template, we should at least indicate the correct prefix in the tooltip
- val bSym = normalizeTemplate(aSym)
- val owner =
- if ((preSym != NoSymbol) && /* it needs a prefix */
- (preSym != bSym.owner) && /* prefix is different from owner */
- // ((preSym != inTpl.sym) && /* prevent prefixes from being shown inside the defining class */
- // (preSym != inTpl.sym.moduleClass)) && /* or object */
- (aSym == bSym)) /* normalization doesn't play tricks on us */
- preSym
- else
- bSym.owner
-
- val bTpl = findTemplateMaybe(bSym)
- val link =
- if (owner == bSym.owner && bTpl.isDefined)
- // (0) the owner's class is linked AND has a template - lovely
- LinkToTpl(bTpl.get)
- else {
- val oTpl = findTemplateMaybe(owner)
- val bMbr = oTpl.map(findMember(bSym, _))
- if (oTpl.isDefined && bMbr.isDefined && bMbr.get.isDefined)
- // (1) the owner's class
- LinkToMember(bMbr.get.get, oTpl.get) //ugh
- else
- // (2) if we still couldn't find the owner, make a noDocTemplate for everything (in the correct owner!)
- LinkToTpl(makeTemplate(bSym, Some(makeTemplate(owner))))
- }
-
- // TODO: The name might include a prefix, take care of that!
- val name = bSym.nameString
- val pos0 = nameBuffer.length
- refBuffer += pos0 -> ((link, name.length))
- nameBuffer append name
-
- // if (bSym.isNonClassType && bSym != AnyRefClass) {
- // nameBuffer append bSym.decodedName
- // } else {
- // val tpl = makeTemplate(bSym)
- // val pos0 = nameBuffer.length
- // refBuffer += pos0 -> ((LinkToTpl(tpl), tpl.name.length))
- // nameBuffer append tpl.name
- // }
-
- if (!targs.isEmpty) {
- nameBuffer append '['
- appendTypes0(targs, ", ")
- nameBuffer append ']'
- }
- /* Refined types */
- case RefinedType(parents, defs) =>
- val ignoreParents = Set[Symbol](AnyClass, ObjectClass)
- val filtParents = parents filterNot (x => ignoreParents(x.typeSymbol)) match {
- case Nil => parents
- case ps => ps
- }
- appendTypes0(filtParents, " with ")
- // XXX Still todo: properly printing refinements.
- // Since I didn't know how to go about displaying a multi-line type, I went with
- // printing single method refinements (which should be the most common) and printing
- // the number of members if there are more.
- defs.toList match {
- case Nil => ()
- case x :: Nil => nameBuffer append (" { " + x.defString + " }")
- case xs => nameBuffer append (" { ... /* %d definitions in type refinement */ }" format xs.size)
- }
- /* Eval-by-name types */
- case NullaryMethodType(result) =>
- nameBuffer append '⇒'
- appendType0(result)
- /* Polymorphic types */
- case PolyType(tparams, result) => assert(tparams.nonEmpty)
-// throw new Error("Polymorphic type '" + tpe + "' cannot be printed as a type")
- def typeParamsToString(tps: List[Symbol]): String = if (tps.isEmpty) "" else
- tps.map{tparam =>
- tparam.varianceString + tparam.name + typeParamsToString(tparam.typeParams)
- }.mkString("[", ", ", "]")
- nameBuffer append typeParamsToString(tparams)
- appendType0(result)
- case tpen =>
- nameBuffer append tpen.toString
+ def makeQualifiedName(sym: Symbol, relativeTo: Option[Symbol] = None): String = {
+ val stop = if (relativeTo.isDefined) relativeTo.get.ownerChain.toSet else Set[Symbol]()
+ var sym1 = sym
+ var path = new StringBuilder()
+ // var path = List[Symbol]()
+
+ while ((sym1 != NoSymbol) && (path.isEmpty || !stop(sym1))) {
+ val sym1Norm = normalizeTemplate(sym1)
+ if (!sym1.sourceModule.isPackageObject && sym1Norm != RootPackage) {
+ if (path.length != 0)
+ path.insert(0, ".")
+ path.insert(0, sym1Norm.nameString)
+ // path::= sym1Norm
}
- appendType0(aType)
- val refEntity = refBuffer
- val name = optimize(nameBuffer.toString)
+ sym1 = sym1.owner
}
- if (aType.isTrivial)
- typeCache.get(aType) match {
- case Some(typeEntity) => typeEntity
- case None =>
- val typeEntity = createTypeEntity
- typeCache += aType -> typeEntity
- typeEntity
- }
- else
- createTypeEntity
+ optimize(path.toString)
+ //path.mkString(".")
}
def normalizeOwner(aSym: Symbol): Symbol =
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
index 493ad3d831..ddcdf1cf5c 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
@@ -53,7 +53,7 @@ import model.{ RootPackage => RootPackageEntity }
* TODO: Give an overview here
*/
trait ModelFactoryImplicitSupport {
- thisFactory: ModelFactory with CommentFactory with TreeFactory =>
+ thisFactory: ModelFactory with ModelFactoryTypeSupport with CommentFactory with TreeFactory =>
import global._
import global.analyzer._
@@ -329,11 +329,6 @@ trait ModelFactoryImplicitSupport {
}
}
- def makeQualifiedName(sym: Symbol): String = {
- val remove = Set[Symbol](RootPackage, RootClass, EmptyPackage, EmptyPackageClass)
- sym.ownerChain.filterNot(remove.contains(_)).reverse.map(_.nameString).mkString(".")
- }
-
/* ============== IMPLEMENTATION PROVIDING ENTITY TYPES ============== */
class ImplicitConversionImpl(
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala
new file mode 100644
index 0000000000..2bc9f070b1
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala
@@ -0,0 +1,215 @@
+/* NSC -- new Scala compiler -- Copyright 2007-2011 LAMP/EPFL */
+
+package scala.tools.nsc
+package doc
+package model
+
+import comment._
+
+import diagram._
+
+import scala.collection._
+import scala.util.matching.Regex
+
+import symtab.Flags
+
+import io._
+
+import model.{ RootPackage => RootPackageEntity }
+
+/** This trait extracts all required information for documentation from compilation units */
+trait ModelFactoryTypeSupport {
+ thisFactory: ModelFactory
+ with ModelFactoryImplicitSupport
+ with ModelFactoryTypeSupport
+ with DiagramFactory
+ with CommentFactory
+ with TreeFactory =>
+
+ import global._
+ import definitions.{ ObjectClass, NothingClass, AnyClass, AnyValClass, AnyRefClass }
+ import rootMirror.{ RootPackage, RootClass, EmptyPackage }
+
+ protected var typeCache = new mutable.LinkedHashMap[(Type, TemplateImpl), TypeEntity]
+ protected var typeCacheNoPrefix = new mutable.LinkedHashMap[Type, TypeEntity]
+
+ /** */
+ def makeType(aType: Type, inTpl: TemplateImpl): TypeEntity = {
+ def templatePackage = closestPackage(inTpl.sym)
+
+ def createTypeEntity = new TypeEntity {
+ private var nameBuffer = new StringBuilder
+ private var refBuffer = new immutable.TreeMap[Int, (LinkTo, Int)]
+ private def appendTypes0(types: List[Type], sep: String): Unit = types match {
+ case Nil =>
+ case tp :: Nil =>
+ appendType0(tp)
+ case tp :: tps =>
+ appendType0(tp)
+ nameBuffer append sep
+ appendTypes0(tps, sep)
+ }
+
+ private def appendType0(tpe: Type): Unit = tpe match {
+ /* Type refs */
+ case tp: TypeRef if definitions.isFunctionType(tp) =>
+ val args = tp.normalize.typeArgs
+ nameBuffer append '('
+ appendTypes0(args.init, ", ")
+ nameBuffer append ") ⇒ "
+ appendType0(args.last)
+ case tp: TypeRef if definitions.isScalaRepeatedParamType(tp) =>
+ appendType0(tp.args.head)
+ nameBuffer append '*'
+ case tp: TypeRef if definitions.isByNameParamType(tp) =>
+ nameBuffer append "⇒ "
+ appendType0(tp.args.head)
+ case tp: TypeRef if definitions.isTupleType(tp) =>
+ val args = tp.normalize.typeArgs
+ nameBuffer append '('
+ appendTypes0(args, ", ")
+ nameBuffer append ')'
+ case TypeRef(pre, aSym, targs) =>
+ val preSym = pre.widen.typeSymbol
+
+ // SI-3314/SI-4888: Classes, Traits and Types can be inherited from a template to another:
+ // class Enum { abstract class Value }
+ // class Day extends Enum { object Mon extends Value /*...*/ }
+ // ===> in such cases we have two options:
+ // (0) if there's no inheritance taking place (Enum#Value) we can link to the template directly
+ // (1) if we generate the doc template for Day, we can link to the correct member
+ // (2) if we don't generate the doc template, we should at least indicate the correct prefix in the tooltip
+ val bSym = normalizeTemplate(aSym)
+ val owner =
+ if ((preSym != NoSymbol) && /* it needs a prefix */
+ (preSym != bSym.owner) && /* prefix is different from owner */
+ // ((preSym != inTpl.sym) && /* prevent prefixes from being shown inside the defining class */
+ // (preSym != inTpl.sym.moduleClass)) && /* or object */
+ (aSym == bSym)) /* normalization doesn't play tricks on us */
+ preSym
+ else
+ bSym.owner
+
+ val bTpl = findTemplateMaybe(bSym)
+ val link =
+ if (owner == bSym.owner && bTpl.isDefined)
+ // (0) the owner's class is linked AND has a template - lovely
+ LinkToTpl(bTpl.get)
+ else {
+ val oTpl = findTemplateMaybe(owner)
+ val bMbr = oTpl.map(findMember(bSym, _))
+ if (oTpl.isDefined && bMbr.isDefined && bMbr.get.isDefined)
+ // (1) the owner's class
+ LinkToMember(bMbr.get.get, oTpl.get) //ugh
+ else
+ // (2) if we still couldn't find the owner, make a noDocTemplate for everything (in the correct owner!)
+ LinkToTpl(makeTemplate(bSym, Some(makeTemplate(owner))))
+ }
+
+ // SI-4360 Showing prefixes when necessary
+ // We check whether there's any directly accessible type with the same name in the current template OR if the
+ // type is inherited from one template to another. There may be multiple symbols with the same name in scope,
+ // but we won't show the prefix if our symbol is among them, only if *it's not* -- that's equal to showing
+ // the prefix only for ambiguous references, not for overloaded ones.
+ def needsPrefix: Boolean = {
+ if (owner != bSym.owner && (normalizeTemplate(owner) != inTpl.sym))
+ return true
+
+ for (tpl <- inTpl.sym.ownerChain) {
+ tpl.info.member(bSym.name) match {
+ case NoSymbol =>
+ // No syms with that name, look further inside the owner chain
+ case sym =>
+ // Symbol found -- either the correct symbol, another one OR an overloaded alternative
+ if (sym == bSym)
+ return false
+ else sym.info match {
+ case OverloadedType(owner, alternatives) =>
+ return alternatives.contains(bSym)
+ case _ =>
+ return true
+ }
+ }
+ }
+ // if it's not found in the owner chain, we can safely leave out the prefix
+ false
+ }
+
+ val prefix =
+ if (!settings.docNoPrefixes.value && needsPrefix && (bSym != AnyRefClass /* which we normalize */)) {
+ val qualifiedName = makeQualifiedName(owner, Some(inTpl.sym))
+ if (qualifiedName != "") qualifiedName + "." else ""
+ } else ""
+
+ //DEBUGGING:
+ //if (makeQualifiedName(bSym) == "pack1.A") println("needsPrefix(" + bSym + ", " + owner + ", " + inTpl.qualifiedName + ") => " + needsPrefix + " and prefix=" + prefix)
+
+ val name = prefix + bSym.nameString
+ val pos0 = nameBuffer.length
+ refBuffer += pos0 -> ((link, name.length))
+ nameBuffer append name
+
+ if (!targs.isEmpty) {
+ nameBuffer append '['
+ appendTypes0(targs, ", ")
+ nameBuffer append ']'
+ }
+ /* Refined types */
+ case RefinedType(parents, defs) =>
+ val ignoreParents = Set[Symbol](AnyClass, ObjectClass)
+ val filtParents = parents filterNot (x => ignoreParents(x.typeSymbol)) match {
+ case Nil => parents
+ case ps => ps
+ }
+ appendTypes0(filtParents, " with ")
+ // XXX Still todo: properly printing refinements.
+ // Since I didn't know how to go about displaying a multi-line type, I went with
+ // printing single method refinements (which should be the most common) and printing
+ // the number of members if there are more.
+ defs.toList match {
+ case Nil => ()
+ case x :: Nil => nameBuffer append (" { " + x.defString + " }")
+ case xs => nameBuffer append (" { ... /* %d definitions in type refinement */ }" format xs.size)
+ }
+ /* Eval-by-name types */
+ case NullaryMethodType(result) =>
+ nameBuffer append '⇒'
+ appendType0(result)
+ /* Polymorphic types */
+ case PolyType(tparams, result) => assert(tparams.nonEmpty)
+// throw new Error("Polymorphic type '" + tpe + "' cannot be printed as a type")
+ def typeParamsToString(tps: List[Symbol]): String = if (tps.isEmpty) "" else
+ tps.map{tparam =>
+ tparam.varianceString + tparam.name + typeParamsToString(tparam.typeParams)
+ }.mkString("[", ", ", "]")
+ nameBuffer append typeParamsToString(tparams)
+ appendType0(result)
+ case tpen =>
+ nameBuffer append tpen.toString
+ }
+ appendType0(aType)
+ val refEntity = refBuffer
+ val name = optimize(nameBuffer.toString)
+ nameBuffer = null
+ }
+
+ // SI-4360: Entity caching depends on both the type AND the template it's in, as the prefixes might change for the
+ // same type based on the template the type is shown in.
+ val cached =
+ if (!settings.docNoPrefixes.value)
+ typeCache.get((aType, inTpl))
+ else
+ typeCacheNoPrefix.get(aType)
+
+ cached match {
+ case Some(typeEntity) => typeEntity
+ case None =>
+ val typeEntity = createTypeEntity
+ if (!settings.docNoPrefixes.value)
+ typeCache += (aType, inTpl) -> typeEntity
+ else
+ typeCacheNoPrefix += aType -> typeEntity
+ typeEntity
+ }
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala
index 2b804ca10f..902d5da240 100644
--- a/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala
@@ -98,20 +98,20 @@ object OtherNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEn
/** The node for the current class */
-case class ThisNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isThisNode = true }
+case class ThisNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isThisNode = true }
/** The usual node */
-case class NormalNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isNormalNode = true }
+case class NormalNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isNormalNode = true }
/** A class or trait the thisnode can be converted to by an implicit conversion
* TODO: I think it makes more sense to use the tpe links to templates instead of the TemplateEntity for implicit nodes
* since some implicit conversions convert the class to complex types that cannot be represented as a single tmeplate
*/
-case class ImplicitNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isImplicitNode = true }
+case class ImplicitNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isImplicitNode = true }
/** An outside node is shown in packages when a class from a different package makes it to the package diagram due to
* its relation to a class in the template (see @contentDiagram hideInheritedNodes annotation) */
-case class OutsideNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isOutsideNode = true }
+case class OutsideNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isOutsideNode = true }
// Computing and offering node depth information
diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
index 731801b143..d0b363854c 100644
--- a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
@@ -18,21 +18,14 @@ import scala.collection.immutable.SortedMap
* @author Vlad Ureche
*/
trait DiagramFactory extends DiagramDirectiveParser {
- this: ModelFactory with DiagramFactory with CommentFactory with TreeFactory =>
+ this: ModelFactory with ModelFactoryTypeSupport with DiagramFactory with CommentFactory with TreeFactory =>
import this.global.definitions._
import this.global._
// the following can used for hardcoding different relations into the diagram, for bootstrapping purposes
- lazy val AnyNode = normalNode(AnyClass)
- lazy val AnyRefNode = normalNode(AnyRefClass)
- lazy val AnyValNode = normalNode(AnyValClass)
- lazy val NullNode = normalNode(NullClass)
- lazy val NothingNode = normalNode(NothingClass)
- def normalNode(sym: Symbol) =
- NormalNode(makeTemplate(sym).ownType, Some(makeTemplate(sym)))
def aggregationNode(text: String) =
- NormalNode(new TypeEntity { val name = text; val refEntity = SortedMap[Int, (LinkTo, Int)]() }, None)
+ NormalNode(new TypeEntity { val name = text; val refEntity = SortedMap[Int, (LinkTo, Int)]() }, None)()
/** Create the inheritance diagram for this template */
def makeInheritanceDiagram(tpl: DocTemplateImpl): Option[Diagram] = {
@@ -52,31 +45,31 @@ trait DiagramFactory extends DiagramDirectiveParser {
None
else {
// the main node
- val thisNode = ThisNode(tpl.ownType, Some(tpl), Some(tpl.qualifiedName + " (this " + tpl.kind + ")"))
+ val thisNode = ThisNode(tpl.resultType, Some(tpl))(Some(tpl.qualifiedName + " (this " + tpl.kind + ")"))
// superclasses
var superclasses: List[Node] =
tpl.parentTypes.collect {
- case p: (TemplateEntity, TypeEntity) if !classExcluded(p._1) => NormalNode(p._2, Some(p._1))
+ case p: (TemplateEntity, TypeEntity) if !classExcluded(p._1) => NormalNode(p._2, Some(p._1))()
}.reverse
// incoming implcit conversions
lazy val incomingImplicitNodes = tpl.incomingImplicitlyConvertedClasses.map {
case (incomingTpl, conv) =>
- ImplicitNode(incomingTpl.ownType, Some(incomingTpl), implicitTooltip(from=incomingTpl, to=tpl, conv=conv))
+ ImplicitNode(makeType(incomingTpl.sym.tpe, tpl), Some(incomingTpl))(implicitTooltip(from=incomingTpl, to=tpl, conv=conv))
}
// subclasses
var subclasses: List[Node] =
tpl.directSubClasses.flatMap {
- case d: TemplateEntity if !classExcluded(d) => List(NormalNode(d.ownType, Some(d)))
+ case d: TemplateImpl if !classExcluded(d) => List(NormalNode(makeType(d.sym.tpe, tpl), Some(d))())
case _ => Nil
}.sortBy(_.tpl.get.name)(implicitly[Ordering[String]].reverse)
// outgoing implicit coversions
lazy val outgoingImplicitNodes = tpl.outgoingImplicitlyConvertedClasses.map {
case (outgoingTpl, outgoingType, conv) =>
- ImplicitNode(outgoingType, Some(outgoingTpl), implicitTooltip(from=tpl, to=tpl, conv=conv))
+ ImplicitNode(outgoingType, Some(outgoingTpl))(implicitTooltip(from=tpl, to=tpl, conv=conv))
}
// TODO: Everyone should be able to use the @{inherit,content}Diagram annotation to change the diagrams.
@@ -149,7 +142,12 @@ trait DiagramFactory extends DiagramDirectiveParser {
case _ =>
}
- mapNodes += node -> (if (node.inTemplate == pack) NormalNode(node.ownType, Some(node)) else OutsideNode(node.ownType, Some(node)))
+ mapNodes += node -> (
+ if (node.inTemplate == pack)
+ NormalNode(node.resultType, Some(node))()
+ else
+ OutsideNode(node.resultType, Some(node))()
+ )
}
if (nodesShown.isEmpty)
@@ -173,7 +171,10 @@ trait DiagramFactory extends DiagramDirectiveParser {
val anyRefSubtypes = Nil
val allAnyRefTypes = aggregationNode("All AnyRef subtypes")
val nullTemplate = makeTemplate(NullClass)
- ContentDiagram(allAnyRefTypes::nodes, (mapNodes(nullTemplate), allAnyRefTypes::anyRefSubtypes)::edges.filterNot(_._1.tpl == Some(nullTemplate)))
+ if (nullTemplate.isDocTemplate)
+ ContentDiagram(allAnyRefTypes::nodes, (mapNodes(nullTemplate), allAnyRefTypes::anyRefSubtypes)::edges.filterNot(_._1.tpl == Some(nullTemplate)))
+ else
+ ContentDiagram(nodes, edges)
} else
ContentDiagram(nodes, edges)