summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVlad Ureche <vlad.ureche@gmail.com>2012-06-28 00:03:56 +0200
committerVlad Ureche <vlad.ureche@gmail.com>2012-07-02 13:34:15 +0200
commitf8cb1aee92fa19e38a1481a4e614cd866ef238c0 (patch)
tree00bfb3a2f355f4fa4923e3aa5390eaa4975f7187
parentf8057d22235c77d69d72d6ea4d4ebdc2eeb95cdf (diff)
downloadscala-f8cb1aee92fa19e38a1481a4e614cd866ef238c0.tar.gz
scala-f8cb1aee92fa19e38a1481a4e614cd866ef238c0.tar.bz2
scala-f8cb1aee92fa19e38a1481a4e614cd866ef238c0.zip
Diagram tweaks #1
- relaxed the restrictions on nodes - nodes can be classes, traits and objects, both stand-alone and companion objects -- all are added to the diagram, but usually companion objects are filtered out as they don't have any superclasses - changed the rules for default diagram creation: - classes and traits (and AnyRef) get inheritance diagrams - packages and objects get content diagrams (can be overridden by @contentDiagram [hideDiagram] and @inheritanceDiagram [hideDiagram]) - tweaked the model to register subclasses of Any - hardcoded the scala package diagram to show all relations - enabled @contentDiagram showInheritedNodes by default and changed the setting to hideInheritedNodes (and added a test for this) - better node selection (can select nodes that don't have a corresponding trait) - fixed the docsite link in member selection, which was broken since the first commit :))
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/Template.scala2
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala17
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala14
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala6
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala2
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala56
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala131
-rw-r--r--src/library/scala/package.scala1
-rw-r--r--test/scaladoc/run/diagrams-filtering.scala8
-rw-r--r--test/scaladoc/run/diagrams-inherited-nodes.check1
-rw-r--r--test/scaladoc/run/diagrams-inherited-nodes.scala69
11 files changed, 223 insertions, 84 deletions
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala
index 24cff1b475..0d0410c7e2 100644
--- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala
@@ -144,7 +144,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
<li class="hideall out"><span>Hide All</span></li>
<li class="showall in"><span>Show all</span></li>
</ol>
- <a href="docs.scala-lang.org/overviews/scaladoc/usage.html#members" target="_blank">Learn more about member selection</a>
+ <a href="http://docs.scala-lang.org/overviews/scaladoc/usage.html#members" target="_blank">Learn more about member selection</a>
</div>
}
{
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
index 8eb6e358b9..57625a5e15 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
@@ -45,8 +45,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
memberSym.isOmittablePrefix || (closestPackage(memberSym) == closestPackage(templateSym))
}
- private lazy val noSubclassCache = Set[Symbol](AnyClass, AnyRefClass, ObjectClass)
-
def makeModel: Option[Universe] = {
val universe = new Universe { thisUniverse =>
thisFactory.universe = thisUniverse
@@ -270,7 +268,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
def parentTypes =
if (sym.isPackage || sym == AnyClass) List() else {
val tps = sym.tpe.parents map { _.asSeenFrom(sym.thisType, sym) }
- makeParentTypes(RefinedType(tps, EmptyScope), inTpl)
+ makeParentTypes(RefinedType(tps, EmptyScope), Some(this), inTpl)
}
protected def linearizationFromSymbol(symbol: Symbol): List[(TemplateEntity, TypeEntity)] = {
@@ -290,7 +288,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
/* Subclass cache */
private lazy val subClassesCache = (
- if (noSubclassCache(sym)) null
+ if (sym == AnyRefClass) null
else mutable.ListBuffer[DocTemplateEntity]()
)
def registerSubClass(sc: DocTemplateEntity): Unit = {
@@ -796,10 +794,15 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
}
/** Get the types of the parents of the current class, ignoring the refinements */
- def makeParentTypes(aType: Type, inTpl: => TemplateImpl): List[(TemplateEntity, TypeEntity)] = aType match {
+ def makeParentTypes(aType: Type, tpl: Option[DocTemplateImpl], inTpl: TemplateImpl): List[(TemplateEntity, TypeEntity)] = aType match {
case RefinedType(parents, defs) =>
- val ignoreParents = Set[Symbol](AnyClass, ObjectClass)
- val filtParents = parents filterNot (x => ignoreParents(x.typeSymbol))
+ val ignoreParents = Set[Symbol](AnyRefClass, ObjectClass)
+ val filtParents =
+ // we don't want to expose too many links to AnyRef, that will just be redundant information
+ if (tpl.isDefined && (!tpl.get.isObject && parents.length < 2))
+ parents
+ else
+ parents.filterNot((p: Type) => ignoreParents(p.typeSymbol))
filtParents.map(parent => {
val templateEntity = makeTemplate(parent.typeSymbol)
val typeEntity = makeType(parent, inTpl)
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
index e7f4a9c79b..8cbf2ac1b6 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
@@ -91,7 +91,7 @@ trait ModelFactoryImplicitSupport {
* default Scala imports (Predef._ for example) and the companion object of the current class, if one exists. In the
* future we might want to extend this to more complex scopes.
*/
- def makeImplicitConversions(sym: Symbol, inTpl: => DocTemplateImpl): List[ImplicitConversionImpl] =
+ def makeImplicitConversions(sym: Symbol, inTpl: DocTemplateImpl): List[ImplicitConversionImpl] =
// Nothing and Null are somewhat special -- they can be transformed by any implicit conversion available in scope.
// But we don't want that, so we'll simply refuse to find implicit conversions on for Nothing and Null
if (!(sym.isClass || sym.isTrait || sym == AnyRefClass) || sym == NothingClass || sym == NullClass) Nil
@@ -148,7 +148,7 @@ trait ModelFactoryImplicitSupport {
* - we also need to transform implicit parameters in the view's signature into constraints, such that Numeric[T4]
* appears as a constraint
*/
- def makeImplicitConversion(sym: Symbol, result: SearchResult, constrs: List[TypeConstraint], context: Context, inTpl: => DocTemplateImpl): List[ImplicitConversionImpl] =
+ def makeImplicitConversion(sym: Symbol, result: SearchResult, constrs: List[TypeConstraint], context: Context, inTpl: DocTemplateImpl): List[ImplicitConversionImpl] =
if (result.tree == EmptyTree) Nil
else {
// `result` will contain the type of the view (= implicit conversion method)
@@ -206,7 +206,7 @@ trait ModelFactoryImplicitSupport {
}
}
- def makeImplicitConstraints(types: List[Type], sym: Symbol, context: Context, inTpl: => DocTemplateImpl): List[Constraint] =
+ def makeImplicitConstraints(types: List[Type], sym: Symbol, context: Context, inTpl: DocTemplateImpl): List[Constraint] =
types.flatMap((tpe:Type) => {
// TODO: Before creating constraints, map typeVarToOriginOrWildcard on the implicitTypes
val implType = typeVarToOriginOrWildcard(tpe)
@@ -282,7 +282,7 @@ trait ModelFactoryImplicitSupport {
}
})
- def makeSubstitutionConstraints(subst: TreeTypeSubstituter, inTpl: => DocTemplateImpl): List[Constraint] =
+ def makeSubstitutionConstraints(subst: TreeTypeSubstituter, inTpl: DocTemplateImpl): List[Constraint] =
(subst.from zip subst.to) map {
case (from, to) =>
new EqualTypeParamConstraint {
@@ -292,7 +292,7 @@ trait ModelFactoryImplicitSupport {
}
}
- def makeBoundedConstraints(tparams: List[Symbol], constrs: List[TypeConstraint], inTpl: => DocTemplateImpl): List[Constraint] =
+ def makeBoundedConstraints(tparams: List[Symbol], constrs: List[TypeConstraint], inTpl: DocTemplateImpl): List[Constraint] =
(tparams zip constrs) flatMap {
case (tparam, constr) => {
uniteConstraints(constr) match {
@@ -341,7 +341,7 @@ trait ModelFactoryImplicitSupport {
val convSym: Symbol,
val toType: Type,
val constrs: List[Constraint],
- inTpl: => DocTemplateImpl)
+ inTpl: DocTemplateImpl)
extends ImplicitConversion {
def source: DocTemplateEntity = inTpl
@@ -365,7 +365,7 @@ trait ModelFactoryImplicitSupport {
case _ => error("Scaladoc implicits: Could not create template for: " + toType + " of type " + toType.getClass); None
}
- def targetTypeComponents: List[(TemplateEntity, TypeEntity)] = makeParentTypes(toType, inTpl)
+ def targetTypeComponents: List[(TemplateEntity, TypeEntity)] = makeParentTypes(toType, None, inTpl)
def convertorMethod: Either[MemberEntity, String] = {
var convertor: MemberEntity = null
diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
index a46be37d60..2099315cc6 100644
--- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
@@ -30,12 +30,12 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
protected val commentCache = mutable.HashMap.empty[(global.Symbol, TemplateImpl), Comment]
- def addCommentBody(sym: global.Symbol, inTpl: => TemplateImpl, docStr: String, docPos: global.Position): global.Symbol = {
+ def addCommentBody(sym: global.Symbol, inTpl: TemplateImpl, docStr: String, docPos: global.Position): global.Symbol = {
commentCache += (sym, inTpl) -> parse(docStr, docStr, docPos)
sym
}
- def comment(sym: global.Symbol, inTpl: => DocTemplateImpl): Option[Comment] = {
+ def comment(sym: global.Symbol, inTpl: DocTemplateImpl): Option[Comment] = {
val key = (sym, inTpl)
if (commentCache isDefinedAt key)
Some(commentCache(key))
@@ -50,7 +50,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
* cases we have to give some `inTpl` comments (parent class for example)
* to the comment of the symbol.
* This function manages some of those cases : Param accessor and Primary constructor */
- def defineComment(sym: global.Symbol, inTpl: => DocTemplateImpl):Option[Comment] = {
+ def defineComment(sym: global.Symbol, inTpl: DocTemplateImpl):Option[Comment] = {
//param accessor case
// We just need the @param argument, we put it into the body
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 28a8c7d37d..d80999e149 100644
--- a/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala
@@ -109,7 +109,7 @@ case class NormalNode(tpe: TypeEntity, tpl: Option[TemplateEntity]) extends Node
case class ImplicitNode(tpe: TypeEntity, tpl: Option[TemplateEntity]) 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 package (and @contentDiagram showInheritedNodes annotation) */
+ * its relation to a class in the template (see @contentDiagram hideInheritedNodes annotation) */
case class OutsideNode(tpe: TypeEntity, tpl: Option[TemplateEntity]) extends Node { override def isOutsideNode = true }
diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala
index beaa045df4..49cfaffc2e 100644
--- a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala
@@ -19,6 +19,8 @@ import html.page.diagram.DiagramStats
trait DiagramDirectiveParser {
this: ModelFactory with DiagramFactory with CommentFactory with TreeFactory =>
+ import this.global.definitions.AnyRefClass
+
///// DIAGRAM FILTERS //////////////////////////////////////////////////////////////////////////////////////////////
/**
@@ -48,16 +50,22 @@ trait DiagramDirectiveParser {
/** Hide subclasses (for type hierarchy diagrams) */
def hideSubclasses: Boolean
/** Show related classes from other objects/traits/packages (for content diagrams) */
- def showInheritedNodes: Boolean
+ def hideInheritedNodes: Boolean
/** Hide a node from the diagram */
- def hideNode(clazz: TemplateEntity): Boolean
+ def hideNode(clazz: Node): Boolean
/** Hide an edge from the diagram */
- def hideEdge(clazz1: TemplateEntity, clazz2: TemplateEntity): Boolean
+ def hideEdge(clazz1: Node, clazz2: Node): Boolean
}
/** Main entry point into this trait: generate the filter for inheritance diagrams */
def makeInheritanceDiagramFilter(template: DocTemplateImpl): DiagramFilter = {
- val defaultFilter = if (template.isClass || template.isTrait) FullDiagram else NoDiagramAtAll
+
+ val defaultFilter =
+ if (template.isClass || template.isTrait || template.sym == AnyRefClass)
+ FullDiagram
+ else
+ NoDiagramAtAll
+
if (template.comment.isDefined)
makeDiagramFilter(template, template.comment.get.inheritDiagram, defaultFilter, true)
else
@@ -83,9 +91,9 @@ trait DiagramDirectiveParser {
val hideOutgoingImplicits: Boolean = false
val hideSuperclasses: Boolean = false
val hideSubclasses: Boolean = false
- val showInheritedNodes: Boolean = false
- def hideNode(clazz: TemplateEntity): Boolean = false
- def hideEdge(clazz1: TemplateEntity, clazz2: TemplateEntity): Boolean = false
+ val hideInheritedNodes: Boolean = false
+ def hideNode(clazz: Node): Boolean = false
+ def hideEdge(clazz1: Node, clazz2: Node): Boolean = false
}
/** Hide the diagram completely, no need for special filtering */
@@ -95,9 +103,9 @@ trait DiagramDirectiveParser {
val hideOutgoingImplicits: Boolean = true
val hideSuperclasses: Boolean = true
val hideSubclasses: Boolean = true
- val showInheritedNodes: Boolean = false
- def hideNode(clazz: TemplateEntity): Boolean = true
- def hideEdge(clazz1: TemplateEntity, clazz2: TemplateEntity): Boolean = true
+ val hideInheritedNodes: Boolean = true
+ def hideNode(clazz: Node): Boolean = true
+ def hideEdge(clazz1: Node, clazz2: Node): Boolean = true
}
/** The AnnotationDiagramFilter trait directs the diagram engine according to an annotation
@@ -107,12 +115,18 @@ trait DiagramDirectiveParser {
hideOutgoingImplicits: Boolean,
hideSuperclasses: Boolean,
hideSubclasses: Boolean,
- showInheritedNodes: Boolean,
+ hideInheritedNodes: Boolean,
hideNodesFilter: List[Pattern],
hideEdgesFilter: List[(Pattern, Pattern)]) extends DiagramFilter {
- def hideNode(clazz: TemplateEntity): Boolean = {
- val qualifiedName = clazz.qualifiedName
+ private[this] def getName(n: Node): String =
+ if (n.tpl.isDefined)
+ n.tpl.get.qualifiedName
+ else
+ n.name
+
+ def hideNode(clazz: Node): Boolean = {
+ val qualifiedName = getName(clazz)
for (hideFilter <- hideNodesFilter)
if (hideFilter.matcher(qualifiedName).matches) {
// println(hideFilter + ".matcher(" + qualifiedName + ").matches = " + hideFilter.matcher(qualifiedName).matches)
@@ -121,9 +135,9 @@ trait DiagramDirectiveParser {
false
}
- def hideEdge(clazz1: TemplateEntity, clazz2: TemplateEntity): Boolean = {
- val clazz1Name = clazz1.qualifiedName
- val clazz2Name = clazz2.qualifiedName
+ def hideEdge(clazz1: Node, clazz2: Node): Boolean = {
+ val clazz1Name = getName(clazz1)
+ val clazz2Name = getName(clazz2)
for ((clazz1Filter, clazz2Filter) <- hideEdgesFilter) {
if (clazz1Filter.matcher(clazz1Name).matches &&
clazz2Filter.matcher(clazz2Name).matches) {
@@ -162,7 +176,7 @@ trait DiagramDirectiveParser {
var hideOutgoingImplicits0: Boolean = false
var hideSuperclasses0: Boolean = false
var hideSubclasses0: Boolean = false
- var showInheritedNodes0: Boolean = false
+ var hideInheritedNodes0: Boolean = false
var hideNodesFilter0: List[Pattern] = Nil
var hideEdgesFilter0: List[(Pattern, Pattern)] = Nil
@@ -190,8 +204,8 @@ trait DiagramDirectiveParser {
hideSuperclasses0 = true
case "hideSubclasses" if isInheritanceDiagram =>
hideSubclasses0 = true
- case "showInheritedNodes" if !isInheritanceDiagram =>
- showInheritedNodes0 = true
+ case "hideInheritedNodes" if !isInheritanceDiagram =>
+ hideInheritedNodes0 = true
case HideNodesRegex(last) =>
val matcher = NodeSpecPattern.matcher(entry)
while (matcher.find()) {
@@ -225,7 +239,7 @@ trait DiagramDirectiveParser {
(hideOutgoingImplicits0 == false) &&
(hideSuperclasses0 == false) &&
(hideSubclasses0 == false) &&
- (showInheritedNodes0 == false) &&
+ (hideInheritedNodes0 == false) &&
(hideDiagram0 == false))
FullDiagram
else
@@ -235,7 +249,7 @@ trait DiagramDirectiveParser {
hideOutgoingImplicits = hideOutgoingImplicits0,
hideSuperclasses = hideSuperclasses0,
hideSubclasses = hideSubclasses0,
- showInheritedNodes = showInheritedNodes0,
+ hideInheritedNodes = hideInheritedNodes0,
hideNodesFilter = hideNodesFilter0,
hideEdgesFilter = hideEdgesFilter0)
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 4ae5e7a5cb..3f054b969b 100644
--- a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
@@ -9,6 +9,8 @@ import collection.mutable
// statistics
import html.page.diagram.DiagramStats
+import scala.collection.immutable.SortedMap
+
/**
* This trait takes care of generating the diagram for classes and packages
*
@@ -18,6 +20,20 @@ import html.page.diagram.DiagramStats
trait DiagramFactory extends DiagramDirectiveParser {
this: ModelFactory 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, (TemplateEntity, Int)]() }, None)
+
/** Create the inheritance diagram for this template */
def makeInheritanceDiagram(tpl: DocTemplateImpl): Option[Diagram] = {
@@ -35,28 +51,36 @@ trait DiagramFactory extends DiagramDirectiveParser {
val thisNode = ThisNode(tpl.ownType, Some(tpl))
// superclasses
- var superclasses = List[Node]()
- tpl.parentTypes.collect { case p: (TemplateEntity, TypeEntity) if !classExcluded(p._1) => p } foreach {
- t: (TemplateEntity, TypeEntity) =>
- val n = NormalNode(t._2, Some(t._1))
- superclasses ::= n
- }
- val filteredSuperclasses = if (diagramFilter.hideSuperclasses) Nil else superclasses
+ var superclasses: List[Node] =
+ tpl.parentTypes.collect {
+ case p: (TemplateEntity, TypeEntity) if !classExcluded(p._1) => NormalNode(p._2, Some(p._1))
+ }.reverse
// incoming implcit conversions
lazy val incomingImplicitNodes = tpl.incomingImplicitlyConvertedClasses.map(tpl => ImplicitNode(tpl.ownType, Some(tpl)))
- val filteredIncomingImplicits = if (diagramFilter.hideIncomingImplicits) Nil else incomingImplicitNodes
// subclasses
- val subclasses = tpl.directSubClasses.flatMap {
- case d: TemplateEntity if !classExcluded(d) => List(NormalNode(d.ownType, Some(d)))
- case _ => Nil
- }
- val filteredSubclasses = if (diagramFilter.hideSubclasses) Nil else subclasses
+ var subclasses: List[Node] =
+ tpl.directSubClasses.flatMap {
+ case d: TemplateEntity if !classExcluded(d) => List(NormalNode(d.ownType, Some(d)))
+ case _ => Nil
+ }.sortBy(_.tpl.get.name)(implicitly[Ordering[String]].reverse)
// outgoing implicit coversions
- lazy val implicitNodes = tpl.outgoingImplicitlyConvertedClasses.map(pair => ImplicitNode(pair._2, Some(pair._1)))
- val filteredImplicitOutgoingNodes = if (diagramFilter.hideOutgoingImplicits) Nil else implicitNodes
+ lazy val outgoingImplicitNodes = tpl.outgoingImplicitlyConvertedClasses.map(pair => ImplicitNode(pair._2, Some(pair._1)))
+
+ // TODO: Everyone should be able to use the @{inherit,content}Diagram annotation to change the diagrams.
+ // Currently, it's possible to leave nodes and edges out, but there's no way to create new nodes and edges
+ // The implementation would need to add the annotations and the logic to select nodes (or create new ones)
+ // and add edges to the diagram -- I bet it wouldn't take too long for someone to do it (one or two days
+ // at most) and it would be a great add to the diagrams.
+ if (tpl.sym == AnyRefClass)
+ subclasses = List(aggregationNode("All user-defined classes and traits"))
+
+ val filteredSuperclasses = if (diagramFilter.hideSuperclasses) Nil else superclasses
+ val filteredIncomingImplicits = if (diagramFilter.hideIncomingImplicits) Nil else incomingImplicitNodes
+ val filteredSubclasses = if (diagramFilter.hideSubclasses) Nil else subclasses
+ val filteredImplicitOutgoingNodes = if (diagramFilter.hideOutgoingImplicits) Nil else outgoingImplicitNodes
// final diagram filter
filterDiagram(ClassDiagram(thisNode, filteredSuperclasses.reverse, filteredSubclasses.reverse, filteredIncomingImplicits, filteredImplicitOutgoingNodes), diagramFilter)
@@ -82,39 +106,68 @@ trait DiagramFactory extends DiagramDirectiveParser {
if (diagramFilter == NoDiagramAtAll)
None
else {
- var mapNodes = Map[DocTemplateEntity, Node]()
- var nodesShown = Set[DocTemplateEntity]()
- var edgesAll = List[(DocTemplateEntity, List[DocTemplateEntity])]()
+ var mapNodes = Map[TemplateEntity, Node]()
+ var nodesShown = Set[TemplateEntity]()
+ var edgesAll = List[(TemplateEntity, List[TemplateEntity])]()
// classes is the entire set of classes and traits in the package, they are the superset of nodes in the diagram
// we collect classes, traits and objects without a companion, which are usually used as values(e.g. scala.None)
- val dnodes = pack.members collect {
- case d: DocTemplateEntity if d.isClass || d.isTrait || (d.isObject && !d.companion.isDefined) &&
- ((d.inTemplate == pack) || diagramFilter.showInheritedNodes) => d
+ val nodesAll = pack.members collect {
+ case d: TemplateEntity if ((!diagramFilter.hideInheritedNodes) || (d.inTemplate == pack)) => d
}
// for each node, add its subclasses
- for (node <- dnodes if !classExcluded(node)) {
- val superClasses = node.parentTypes.collect {
- case (tpl: DocTemplateEntity, tpe) if tpl.inTemplate == pack && !classExcluded(tpl) => tpl
- case (tpl: DocTemplateEntity, tpe) if tpl.inTemplate != pack && !classExcluded(tpl) && diagramFilter.showInheritedNodes && (pack.members contains tpl) => tpl
- }
-
- if (!superClasses.isEmpty) {
- nodesShown += node
- nodesShown ++= superClasses
+ for (node <- nodesAll if !classExcluded(node)) {
+ node match {
+ case dnode: DocTemplateImpl =>
+ var superClasses = dnode.parentTypes.map(_._1)
+
+ superClasses = superClasses.filter(nodesAll.contains(_))
+
+ // TODO: Everyone should be able to use the @{inherit,content}Diagram annotation to change the diagrams.
+ if (pack.sym == ScalaPackage)
+ if (dnode.sym == NullClass)
+ superClasses = List(makeTemplate(AnyRefClass))
+ else if (dnode.sym == NothingClass)
+ superClasses = (List(NullClass) ::: ScalaValueClasses).map(makeTemplate(_))
+
+ if (!superClasses.isEmpty) {
+ nodesShown += dnode
+ nodesShown ++= superClasses
+ }
+ edgesAll ::= dnode -> superClasses
+ case _ =>
}
- edgesAll ::= node -> superClasses
mapNodes += node -> (if (node.inTemplate == pack) NormalNode(node.ownType, Some(node)) else OutsideNode(node.ownType, Some(node)))
}
if (nodesShown.isEmpty)
None
else {
- val nodes = dnodes.filter(nodesShown.contains(_)).map(mapNodes(_))
+ val nodes = nodesAll.filter(nodesShown.contains(_)).map(mapNodes(_))
val edges = edgesAll.map(pair => (mapNodes(pair._1), pair._2.map(mapNodes(_)))).filterNot(pair => pair._2.isEmpty)
- filterDiagram(PackageDiagram(nodes, edges), diagramFilter)
+ val diagram =
+ // TODO: Everyone should be able to use the @{inherit,content}Diagram annotation to change the diagrams.
+ if (pack.sym == ScalaPackage) {
+ // Tried it, but it doesn't look good:
+ // var anyRefSubtypes: List[Node] = List(mapNodes(makeTemplate(AnyRefClass)))
+ // var dirty = true
+ // do {
+ // val length = anyRefSubtypes.length
+ // anyRefSubtypes :::= edges.collect { case p: (Node, List[Node]) if p._2.exists(anyRefSubtypes.contains(_)) => p._1 }
+ // anyRefSubtypes = anyRefSubtypes.distinct
+ // dirty = (anyRefSubtypes.length != length)
+ // } while (dirty)
+ // println(anyRefSubtypes)
+ val anyRefSubtypes = Nil
+ val allAnyRefTypes = aggregationNode("All AnyRef subtypes")
+ val nullTemplate = makeTemplate(NullClass)
+ PackageDiagram(allAnyRefTypes::nodes, (mapNodes(nullTemplate), allAnyRefTypes::anyRefSubtypes)::edges.filterNot(_._1.tpl == Some(nullTemplate)))
+ } else
+ PackageDiagram(nodes, edges)
+
+ filterDiagram(diagram, diagramFilter)
}
}
@@ -137,18 +190,16 @@ trait DiagramFactory extends DiagramDirectiveParser {
else {
// Final diagram, with the filtered nodes and edges
diagram match {
- case ClassDiagram(thisNode, _, _, _, _) if diagramFilter.hideNode(thisNode.tpl.get) =>
+ case ClassDiagram(thisNode, _, _, _, _) if diagramFilter.hideNode(thisNode) =>
None
case ClassDiagram(thisNode, superClasses, subClasses, incomingImplicits, outgoingImplicits) =>
def hideIncoming(node: Node): Boolean =
- if (node.tpl.isDefined) diagramFilter.hideNode(node.tpl.get) || diagramFilter.hideEdge(node.tpl.get, thisNode.tpl.get)
- else false // hopefully we won't need to fallback here
+ diagramFilter.hideNode(node) || diagramFilter.hideEdge(node, thisNode)
def hideOutgoing(node: Node): Boolean =
- if (node.tpl.isDefined) diagramFilter.hideNode(node.tpl.get) || diagramFilter.hideEdge(thisNode.tpl.get, node.tpl.get)
- else false // hopefully we won't need to fallback here
+ diagramFilter.hideNode(node) || diagramFilter.hideEdge(thisNode, node)
// println(thisNode)
// println(superClasses.map(cl => "super: " + cl + " " + hideOutgoing(cl)).mkString("\n"))
@@ -166,8 +217,8 @@ trait DiagramFactory extends DiagramDirectiveParser {
// (3) are destinations of hidden classes
val edges: List[(Node, List[Node])] =
diagram.edges.flatMap({
- case (source@Node(_, Some(tpl1)), dests) if !diagramFilter.hideNode(tpl1) =>
- val dests2 = dests.collect({ case node@Node(_, Some(tpl2)) if (!(diagramFilter.hideEdge(tpl1, tpl2) || diagramFilter.hideNode(tpl2))) => node })
+ case (source, dests) if !diagramFilter.hideNode(source) =>
+ val dests2 = dests.collect({ case dest if (!(diagramFilter.hideEdge(source, dest) || diagramFilter.hideNode(dest))) => dest })
if (dests2 != Nil)
List((source, dests2))
else
diff --git a/src/library/scala/package.scala b/src/library/scala/package.scala
index e3890d7a9d..6460db534d 100644
--- a/src/library/scala/package.scala
+++ b/src/library/scala/package.scala
@@ -9,6 +9,7 @@
/**
* Core Scala types. They are always available without an explicit import.
+ * @contentDiagram hideNodes "scala.Serializable"
*/
package object scala {
type Throwable = java.lang.Throwable
diff --git a/test/scaladoc/run/diagrams-filtering.scala b/test/scaladoc/run/diagrams-filtering.scala
index dfde5cac52..8cb32180a1 100644
--- a/test/scaladoc/run/diagrams-filtering.scala
+++ b/test/scaladoc/run/diagrams-filtering.scala
@@ -54,11 +54,11 @@ object Test extends ScaladocModelTest {
assert(packDiag.edges.map(_._2.length).sum == 5)
// trait A
- // Assert we have just 2 nodes and 1 edge
+ // Assert we have just 3 nodes and 2 edges
val A = base._trait("A")
val ADiag = A.inheritanceDiagram.get
- assert(ADiag.nodes.length == 2)
- assert(ADiag.edges.map(_._2.length).sum == 1)
+ assert(ADiag.nodes.length == 3)
+ assert(ADiag.edges.map(_._2.length).sum == 2)
// trait C
val C = base._trait("C")
@@ -82,7 +82,7 @@ object Test extends ScaladocModelTest {
val (outgoingSuperclass, outgoingImplicit) = outgoing.head._2.partition(_.isNormalNode)
assert(outgoingSuperclass.length == 2) // B and C
- assert(outgoingImplicit.length == 1) // T
+ assert(outgoingImplicit.length == 1, outgoingImplicit) // T
val (incomingSubclass, incomingImplicit) = incoming.partition(_._1.isNormalNode)
assert(incomingSubclass.length == 2) // F and G
diff --git a/test/scaladoc/run/diagrams-inherited-nodes.check b/test/scaladoc/run/diagrams-inherited-nodes.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/diagrams-inherited-nodes.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/diagrams-inherited-nodes.scala b/test/scaladoc/run/diagrams-inherited-nodes.scala
new file mode 100644
index 0000000000..8ac382aab8
--- /dev/null
+++ b/test/scaladoc/run/diagrams-inherited-nodes.scala
@@ -0,0 +1,69 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.nsc.doc.model.diagram._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def code = """
+ package scala.test.scaladoc.diagrams.inherited.nodes {
+
+ /** @contentDiagram
+ * @inheritanceDiagram hideDiagram */
+ trait T1 {
+ trait A1
+ trait A2 extends A1
+ trait A3 extends A2
+ }
+
+ /** @contentDiagram
+ * @inheritanceDiagram hideDiagram */
+ trait T2 extends T1 {
+ trait B1 extends A1
+ trait B2 extends A2 with B1
+ trait B3 extends A3 with B2
+ }
+
+ /** @contentDiagram
+ * @inheritanceDiagram hideDiagram */
+ trait T3 {
+ self: T1 with T2 =>
+ trait C1 extends B1
+ trait C2 extends B2 with C1
+ trait C3 extends B3 with C2
+ }
+
+ /** @contentDiagram
+ * @inheritanceDiagram hideDiagram */
+ trait T4 extends T3 with T2 with T1 {
+ trait D1 extends C1
+ trait D2 extends C2 with D1
+ trait D3 extends C3 with D2
+ }
+ }
+ """
+
+ // diagrams must be started. In case there's an error with dot, it should not report anything
+ def scaladocSettings = "-diagrams"
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ // base package
+ // Assert we have 7 nodes and 6 edges
+ val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("diagrams")._package("inherited")._package("nodes")
+
+ def checkDiagram(t: String, nodes: Int, edges: Int) = {
+ // trait T1
+ val T = base._trait(t)
+ val TDiag = T.contentDiagram.get
+ assert(TDiag.nodes.length == nodes, t + ": " + TDiag.nodes + ".length == " + nodes)
+ assert(TDiag.edges.map(_._2.length).sum == edges, t + ": " + TDiag.edges.mkString("List(\n", ",\n", "\n)") + ".map(_._2.length).sum == " + edges)
+ }
+
+ checkDiagram("T1", 3, 2)
+ checkDiagram("T2", 6, 7)
+ checkDiagram("T3", 3, 2)
+ checkDiagram("T4", 12, 17)
+ }
+} \ No newline at end of file