Skip to content

Commit 8ae147f

Browse files
BarkingBadromanowski
authored andcommitted
Enhance diagram with full supertypes ancestry relationship, change arrowheads and add guards to avoid errors
1 parent 8e9ac75 commit 8ae147f

File tree

12 files changed

+115
-83
lines changed

12 files changed

+115
-83
lines changed

scala3doc/resources/dotty_res/scripts/diagram.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,25 @@ $("#inheritance-diagram").ready(function() {
77

88
// Set up zoom support
99
var zoom = d3.zoom()
10-
.on("zoom", function({transform}) {
11-
inner.attr("transform", transform);
12-
});
10+
.on("zoom", function({transform}) {
11+
inner.attr("transform", transform);
12+
});
1313
svg.call(zoom);
1414

1515
var render = new dagreD3.render();
1616
var g = graphlibDot.read(dotNode.text);
1717
g.graph().rankDir = 'BT';
1818
g.nodes().forEach(function (v) {
19-
g.setNode(v, {
20-
labelType: "html",
21-
label: g.node(v).label,
22-
style: g.node(v).style
23-
});
19+
g.setNode(v, {
20+
labelType: "html",
21+
label: g.node(v).label,
22+
style: g.node(v).style
23+
});
24+
});
25+
g.edges().forEach(function(v) {
26+
g.setEdge(v, {
27+
arrowhead: "vee"
28+
});
2429
});
2530
render(inner, g);
2631
}

scala3doc/resources/dotty_res/styles/diagram.css

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,19 @@
1111
.edgePath {
1212
stroke: #333;
1313
stroke-width: 1.5px;
14-
fill: none;
14+
fill: #333;
1515
}
1616

1717
#graph {
1818
width: 100%;
1919
height: 80%;
2020
}
21+
22+
.diagram-class a[href] {
23+
text-decoration: underline;
24+
color: #FFFFFF;
25+
}
26+
27+
.diagram-class span[data-unresolved-link] {
28+
color: #FFFFFF;
29+
}

scala3doc/src/dotty/dokka/model/api/api.scala

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,28 @@ extension (s: Signature):
9999

100100
case class LinkToType(signature: Signature, dri: DRI, kind: Kind)
101101

102-
case class HierarchyDiagram(edges: Seq[Edge])
103-
case class Vertex(val id: Int, val body: LinkToType)
104-
case class Edge(val from: Vertex, val to: Vertex)
102+
case class Vertex(id: Int, body: LinkToType)
103+
case class Edge(from: Vertex, to: Vertex)
104+
case class HierarchyGraph private (edges: Seq[Edge], private val nextId: Int):
105+
def vertecies = edges.flatMap(edge => Seq(edge.from, edge.to)).distinct
106+
def +(edge: (LinkToType, LinkToType)) =
107+
val from = vertecies.find(_.body == edge._1).getOrElse(Vertex(nextId, edge._1))
108+
val to = vertecies.find(_.body == edge._2).getOrElse {
109+
val id = if from.id == nextId then nextId + 1 else nextId
110+
Vertex(id, edge._2)
111+
}
112+
val higherId = List(from.id, to.id).max
113+
val newNextId = if higherId >= nextId then higherId + 1 else nextId
114+
HierarchyGraph(edges ++ Seq(Edge(from, to)), newNextId)
115+
116+
def ++(edges: Seq[(LinkToType, LinkToType)]) = edges.foldLeft(this) {
117+
case (acc, edge) => acc + edge
118+
}
119+
120+
object HierarchyGraph:
121+
def empty = HierarchyGraph(Seq.empty, 0)
122+
def withEdge(edge: (LinkToType, LinkToType)) = HierarchyGraph.empty + edge
123+
def withEdges(edges: Seq[(LinkToType, LinkToType)]) = HierarchyGraph.empty ++ edges
105124

106125

107126
type Member = Documentable // with WithExtraProperty[_] // Kotlin does not add generics to ExtraProperty implemented by e.g. DFunction

scala3doc/src/dotty/dokka/model/api/internalExtensions.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ private [model] case class MemberExtension(
3030
modifiers: Seq[dotty.dokka.model.api.Modifier],
3131
kind: Kind,
3232
val annotations: List[Annotation],
33-
signature: Signature,
34-
origin: Origin = Origin.DefinedWithin
33+
signature: Signature,
34+
origin: Origin = Origin.DefinedWithin,
35+
graph: HierarchyGraph = HierarchyGraph.empty,
3536
) extends ExtraProperty[Documentable]:
3637
override def getKey = MemberExtension
3738

@@ -89,7 +90,12 @@ extension (member: Member):
8990
val newExt = original.copy(knownChildren = knownChildren)
9091
putInCompositeMember(newExt)
9192

92-
def updateRecusivly(op: Member => Member) = op(member).withMembers(member.allMembers.map(op))
93+
def withNewGraphEdges(edges: Seq[(LinkToType, LinkToType)]): Member =
94+
val oldExt = MemberExtension.getFrom(member).getOrElse(MemberExtension.empty)
95+
val newExt = oldExt.copy(graph = oldExt.graph ++ edges)
96+
putInMember(newExt)
97+
98+
def updateRecusivly(op: Member => Member) = op(member).withMembers(member.allMembers.map(op))
9399

94100
extension (bound: Bound):
95101
def asSignature: Signature = bound match

scala3doc/src/dotty/dokka/model/scalaModel.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import org.jetbrains.dokka.model.properties._
1010
import org.jetbrains.dokka.pages._
1111
import java.util.{List => JList, Set => JSet}
1212
import dotty.dokka.model.api.Signature
13-
import dotty.dokka.model.api.HierarchyDiagram
13+
import dotty.dokka.model.api.HierarchyGraph
1414

1515
case class TastyDocumentableSource(val path: String, val lineNumber: Int) extends DocumentableSource {
1616
override def getPath = path
@@ -58,10 +58,10 @@ object ScalaTagWrapper {
5858

5959
case class ImplicitConversion(conversion: Documentable, from: DRI, to: DRI)
6060

61-
case class HierarchyDiagramContentNode(
62-
val diagram: HierarchyDiagram,
63-
val dci: DCI,
64-
val sourceSets: Set[DisplaySourceSet],
61+
case class HierarchyGraphContentNode(
62+
val diagram: HierarchyGraph,
63+
val dci: DCI,
64+
val sourceSets: Set[DisplaySourceSet],
6565
val style: Set[Style],
6666
val extra: PropertyContainer[ContentNode] = PropertyContainer.Companion.empty
6767
) extends ContentNode:

scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import dotty.dokka.model.api.Kind
1414
import dotty.dokka.model.api.ImplicitConversion
1515
import dotty.dokka.model.api.{Signature => DSignature, Link => DLink}
1616

17-
1817
trait ClassLikeSupport:
1918
self: TastyParser =>
2019
import qctx.reflect._
@@ -34,19 +33,38 @@ trait ClassLikeSupport:
3433
name: String = classDef.name,
3534
signatureOnly: Boolean = false,
3635
modifiers: Seq[Modifier] = classDef.symbol.getExtraModifiers(),
37-
): DClass =
38-
val supertypes = getSupertypes(classDef).map{ case (symbol, tpe) =>
39-
LinkToType(tpe.dokkaType.asSignature, symbol.dri, kindForClasslike(symbol))
36+
): DClass =
37+
38+
// This Try is here because of problem that code compiles, but at runtime fails claiming
39+
// java.lang.ClassCastException: class dotty.tools.dotc.ast.Trees$DefDef cannot be cast to class dotty.tools.dotc.ast.Trees$TypeDef (dotty.tools.dotc.ast.Trees$DefDef and dotty.tools.dotc.ast.Trees$TypeDef are in unnamed module of loader 'app')
40+
// It is probably bug in Tasty
41+
def hackGetParents(classDef: ClassDef): Option[List[Tree]] = scala.util.Try(classDef.parents).toOption
42+
43+
def getSupertypesGraph(classDef: ClassDef, link: LinkToType): Seq[(LinkToType, LinkToType)] =
44+
val smbl = classDef.symbol
45+
val parents = if smbl.exists then hackGetParents(smbl.tree.asInstanceOf[ClassDef]) else None
46+
parents.fold(Seq())(_.flatMap { case tree =>
47+
val symbol = if tree.symbol.isClassConstructor then tree.symbol.owner else tree.symbol
48+
val superLink = LinkToType(tree.dokkaType.asSignature, symbol.dri, kindForClasslike(symbol))
49+
Seq(link -> superLink) ++ getSupertypesGraph(tree.asInstanceOf[ClassDef], superLink)
50+
}
51+
)
52+
53+
val supertypes = getSupertypes(classDef).map {
54+
case (symbol, tpe) => LinkToType(tpe.dokkaType.asSignature, symbol.dri, kindForClasslike(symbol))
4055
}
4156
val selfSiangture: DSignature = typeForClass(classDef).dokkaType.asSignature
57+
58+
val graph = HierarchyGraph.empty ++ getSupertypesGraph(classDef, LinkToType(selfSiangture, classDef.symbol.dri, kindForClasslike(classDef.symbol)))
4259
val baseExtra = PropertyContainer.Companion.empty()
4360
.plus(ClasslikeExtension(classDef.getConstructorMethod, classDef.getCompanion))
4461
.plus(MemberExtension(
4562
classDef.symbol.getVisibility(),
46-
modifiers,
47-
kindForClasslike( classDef.symbol),
48-
classDef.symbol.getAnnotations(),
49-
selfSiangture
63+
modifiers,
64+
kindForClasslike( classDef.symbol),
65+
classDef.symbol.getAnnotations(),
66+
selfSiangture,
67+
graph = graph
5068
))
5169

5270
val fullExtra =

scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,14 @@ import dotty.dokka.model.api._
1414
class InheritanceInformationTransformer(val ctx: DokkaContext) extends DocumentableTransformer:
1515
override def invoke(original: DModule, context: DokkaContext): DModule =
1616
val subtypes = getSupertypes(original).groupBy(_._1).transform((k, v) => v.map(_._2))
17-
original.updateMembers(m => m.withKnownChildren(subtypes.getOrElse(m.dri, Nil)))
17+
original.updateMembers { m =>
18+
val st: Seq[LinkToType] = subtypes.getOrElse(m.dri, Nil)
19+
m.withKnownChildren(st).withNewGraphEdges(st.map(_ -> m.asLink))
20+
}
1821

19-
private def getSupertypes(d: Documentable): Seq[(DRI, LinkToType)] = d match {
22+
private def getSupertypes(d: Documentable): Seq[(DRI, LinkToType)] = d match
2023
case m: DModule => m.getPackages.asScala.toList.flatMap(p => getSupertypes(p))
21-
case c: Member =>
22-
val selfMapping = if !c.kind.isInstanceOf[Classlike] then Nil else
23-
val selfLink = c.asLink
24-
c.parents.map(_._2 -> selfLink)
25-
24+
case c: Member =>
25+
val selfMapping = if !c.kind.isInstanceOf[Classlike] then Nil else c.parents.map(_._2 -> c.asLink)
2626
c.allMembers.flatMap(getSupertypes) ++ selfMapping
27-
case null => List.empty
28-
}
27+
case other => List.empty

scala3doc/src/dotty/dokka/translators/HierarchyDiagramBuilder.scala

Lines changed: 0 additions & 23 deletions
This file was deleted.

scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,12 +300,12 @@ class ScalaPageContentBuilder(
300300

301301

302302
def dotDiagram(
303-
diagram: HierarchyDiagram,
303+
diagram: HierarchyGraph,
304304
kind: Kind = ContentKind.Main,
305305
sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData,
306306
styles: Set[Style] = mainStyles,
307307
extra: PropertyContainer[ContentNode] = mainExtra
308-
) = addChild(HierarchyDiagramContentNode(diagram, DCI(mainDRI.asJava, kind), sourceSets.toDisplay.asScala.toSet, styles, extra))
308+
) = addChild(HierarchyGraphContentNode(diagram, DCI(mainDRI.asJava, kind), sourceSets.toDisplay.asScala.toSet, styles, extra))
309309

310310
def groupingBlock[A, T <: Documentable, G <: List[(A, List[T])]](
311311
name: String,

scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ class ScalaPageCreator(
397397
def contentForTypesInfo(c: DClass) =
398398
val supertypes = c.parents
399399
val subtypes = c.knownChildren
400+
val graph = MemberExtension.getFrom(c).map(_.graph)
400401

401402
def contentForTypeLink(builder: DocBuilder, link: LinkToType): DocBuilder =
402403
builder.group(styles = Set(TextStyle.Paragraph)) { builder =>
@@ -428,13 +429,15 @@ class ScalaPageCreator(
428429
_.list(subtypes.toList, separator="")(contentForTypeLink)
429430
}
430431
}
431-
432-
if subtypes.isEmpty && supertypes.isEmpty then withSubtypes else
433-
withSubtypes.header(2, "Type hierarchy")().group(
434-
kind = ContentKind.Comment,
435-
styles = Set(ContentStyle.WithExtraAttributes),
436-
extra = PropertyContainer.Companion.empty plus SimpleAttr.Companion.header("Type hierarchy")
437-
) { _.group(kind = ContentKind.Symbol, styles = Set(TextStyle.Monospace)) {
438-
_.dotDiagram(HierarchyDiagramBuilder.build(c))
432+
433+
graph.fold(withSubtypes) { g =>
434+
if g.edges.size == 0 then withSubtypes else
435+
withSubtypes.header(2, "Type hierarchy")().group(
436+
kind = ContentKind.Comment,
437+
styles = Set(ContentStyle.WithExtraAttributes),
438+
extra = PropertyContainer.Companion.empty plus SimpleAttr.Companion.header("Type hierarchy")
439+
) { _.group(kind = ContentKind.Symbol, styles = Set(TextStyle.Monospace)) {
440+
_.dotDiagram(g)
441+
}
439442
}
440443
}

0 commit comments

Comments
 (0)