From 8ae147fc0d3d83da546071c846f6c1de22057de2 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Mon, 19 Oct 2020 16:21:48 +0200 Subject: [PATCH 01/19] Enhance diagram with full supertypes ancestry relationship, change arrowheads and add guards to avoid errors --- .../resources/dotty_res/scripts/diagram.js | 21 +++++++----- .../resources/dotty_res/styles/diagram.css | 11 +++++- scala3doc/src/dotty/dokka/model/api/api.scala | 25 ++++++++++++-- .../dokka/model/api/internalExtensions.scala | 12 +++++-- .../src/dotty/dokka/model/scalaModel.scala | 10 +++--- .../dotty/dokka/tasty/ClassLikeSupport.scala | 34 ++++++++++++++----- .../InheritanceInformationTransformer.scala | 17 +++++----- .../translators/HierarchyDiagramBuilder.scala | 23 ------------- .../translators/ScalaContentBuilder.scala | 4 +-- .../dokka/translators/ScalaPageCreator.scala | 19 ++++++----- .../dotty/renderers/DotDiagramBuilder.scala | 6 ++-- .../dotty/renderers/ScalaHtmlRenderer.scala | 16 ++++----- 12 files changed, 115 insertions(+), 83 deletions(-) delete mode 100644 scala3doc/src/dotty/dokka/translators/HierarchyDiagramBuilder.scala diff --git a/scala3doc/resources/dotty_res/scripts/diagram.js b/scala3doc/resources/dotty_res/scripts/diagram.js index d38842c2b7ac..2286a5048fff 100644 --- a/scala3doc/resources/dotty_res/scripts/diagram.js +++ b/scala3doc/resources/dotty_res/scripts/diagram.js @@ -7,20 +7,25 @@ $("#inheritance-diagram").ready(function() { // Set up zoom support var zoom = d3.zoom() - .on("zoom", function({transform}) { - inner.attr("transform", transform); - }); + .on("zoom", function({transform}) { + inner.attr("transform", transform); + }); svg.call(zoom); var render = new dagreD3.render(); var g = graphlibDot.read(dotNode.text); g.graph().rankDir = 'BT'; g.nodes().forEach(function (v) { - g.setNode(v, { - labelType: "html", - label: g.node(v).label, - style: g.node(v).style - }); + g.setNode(v, { + labelType: "html", + label: g.node(v).label, + style: g.node(v).style + }); + }); + g.edges().forEach(function(v) { + g.setEdge(v, { + arrowhead: "vee" + }); }); render(inner, g); } diff --git a/scala3doc/resources/dotty_res/styles/diagram.css b/scala3doc/resources/dotty_res/styles/diagram.css index afa6e405a65a..e5d6c6052f44 100644 --- a/scala3doc/resources/dotty_res/styles/diagram.css +++ b/scala3doc/resources/dotty_res/styles/diagram.css @@ -11,10 +11,19 @@ .edgePath { stroke: #333; stroke-width: 1.5px; - fill: none; + fill: #333; } #graph { width: 100%; height: 80%; } + +.diagram-class a[href] { + text-decoration: underline; + color: #FFFFFF; +} + +.diagram-class span[data-unresolved-link] { + color: #FFFFFF; +} \ No newline at end of file diff --git a/scala3doc/src/dotty/dokka/model/api/api.scala b/scala3doc/src/dotty/dokka/model/api/api.scala index ccc86b3df527..a6981f510da8 100644 --- a/scala3doc/src/dotty/dokka/model/api/api.scala +++ b/scala3doc/src/dotty/dokka/model/api/api.scala @@ -99,9 +99,28 @@ extension (s: Signature): case class LinkToType(signature: Signature, dri: DRI, kind: Kind) -case class HierarchyDiagram(edges: Seq[Edge]) -case class Vertex(val id: Int, val body: LinkToType) -case class Edge(val from: Vertex, val to: Vertex) +case class Vertex(id: Int, body: LinkToType) +case class Edge(from: Vertex, to: Vertex) +case class HierarchyGraph private (edges: Seq[Edge], private val nextId: Int): + def vertecies = edges.flatMap(edge => Seq(edge.from, edge.to)).distinct + def +(edge: (LinkToType, LinkToType)) = + val from = vertecies.find(_.body == edge._1).getOrElse(Vertex(nextId, edge._1)) + val to = vertecies.find(_.body == edge._2).getOrElse { + val id = if from.id == nextId then nextId + 1 else nextId + Vertex(id, edge._2) + } + val higherId = List(from.id, to.id).max + val newNextId = if higherId >= nextId then higherId + 1 else nextId + HierarchyGraph(edges ++ Seq(Edge(from, to)), newNextId) + + def ++(edges: Seq[(LinkToType, LinkToType)]) = edges.foldLeft(this) { + case (acc, edge) => acc + edge + } + +object HierarchyGraph: + def empty = HierarchyGraph(Seq.empty, 0) + def withEdge(edge: (LinkToType, LinkToType)) = HierarchyGraph.empty + edge + def withEdges(edges: Seq[(LinkToType, LinkToType)]) = HierarchyGraph.empty ++ edges type Member = Documentable // with WithExtraProperty[_] // Kotlin does not add generics to ExtraProperty implemented by e.g. DFunction diff --git a/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala b/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala index 115fbdba30a5..45e2a8336ba1 100644 --- a/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala +++ b/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala @@ -30,8 +30,9 @@ private [model] case class MemberExtension( modifiers: Seq[dotty.dokka.model.api.Modifier], kind: Kind, val annotations: List[Annotation], - signature: Signature, - origin: Origin = Origin.DefinedWithin + signature: Signature, + origin: Origin = Origin.DefinedWithin, + graph: HierarchyGraph = HierarchyGraph.empty, ) extends ExtraProperty[Documentable]: override def getKey = MemberExtension @@ -89,7 +90,12 @@ extension (member: Member): val newExt = original.copy(knownChildren = knownChildren) putInCompositeMember(newExt) - def updateRecusivly(op: Member => Member) = op(member).withMembers(member.allMembers.map(op)) + def withNewGraphEdges(edges: Seq[(LinkToType, LinkToType)]): Member = + val oldExt = MemberExtension.getFrom(member).getOrElse(MemberExtension.empty) + val newExt = oldExt.copy(graph = oldExt.graph ++ edges) + putInMember(newExt) + + def updateRecusivly(op: Member => Member) = op(member).withMembers(member.allMembers.map(op)) extension (bound: Bound): def asSignature: Signature = bound match diff --git a/scala3doc/src/dotty/dokka/model/scalaModel.scala b/scala3doc/src/dotty/dokka/model/scalaModel.scala index 1b151aed3c5f..d883470e84c6 100644 --- a/scala3doc/src/dotty/dokka/model/scalaModel.scala +++ b/scala3doc/src/dotty/dokka/model/scalaModel.scala @@ -10,7 +10,7 @@ import org.jetbrains.dokka.model.properties._ import org.jetbrains.dokka.pages._ import java.util.{List => JList, Set => JSet} import dotty.dokka.model.api.Signature -import dotty.dokka.model.api.HierarchyDiagram +import dotty.dokka.model.api.HierarchyGraph case class TastyDocumentableSource(val path: String, val lineNumber: Int) extends DocumentableSource { override def getPath = path @@ -58,10 +58,10 @@ object ScalaTagWrapper { case class ImplicitConversion(conversion: Documentable, from: DRI, to: DRI) -case class HierarchyDiagramContentNode( - val diagram: HierarchyDiagram, - val dci: DCI, - val sourceSets: Set[DisplaySourceSet], +case class HierarchyGraphContentNode( + val diagram: HierarchyGraph, + val dci: DCI, + val sourceSets: Set[DisplaySourceSet], val style: Set[Style], val extra: PropertyContainer[ContentNode] = PropertyContainer.Companion.empty ) extends ContentNode: diff --git a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala index bbd32b99f00f..977523a8d42d 100644 --- a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala @@ -14,7 +14,6 @@ import dotty.dokka.model.api.Kind import dotty.dokka.model.api.ImplicitConversion import dotty.dokka.model.api.{Signature => DSignature, Link => DLink} - trait ClassLikeSupport: self: TastyParser => import qctx.reflect._ @@ -34,19 +33,38 @@ trait ClassLikeSupport: name: String = classDef.name, signatureOnly: Boolean = false, modifiers: Seq[Modifier] = classDef.symbol.getExtraModifiers(), - ): DClass = - val supertypes = getSupertypes(classDef).map{ case (symbol, tpe) => - LinkToType(tpe.dokkaType.asSignature, symbol.dri, kindForClasslike(symbol)) + ): DClass = + + // This Try is here because of problem that code compiles, but at runtime fails claiming + // 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') + // It is probably bug in Tasty + def hackGetParents(classDef: ClassDef): Option[List[Tree]] = scala.util.Try(classDef.parents).toOption + + def getSupertypesGraph(classDef: ClassDef, link: LinkToType): Seq[(LinkToType, LinkToType)] = + val smbl = classDef.symbol + val parents = if smbl.exists then hackGetParents(smbl.tree.asInstanceOf[ClassDef]) else None + parents.fold(Seq())(_.flatMap { case tree => + val symbol = if tree.symbol.isClassConstructor then tree.symbol.owner else tree.symbol + val superLink = LinkToType(tree.dokkaType.asSignature, symbol.dri, kindForClasslike(symbol)) + Seq(link -> superLink) ++ getSupertypesGraph(tree.asInstanceOf[ClassDef], superLink) + } + ) + + val supertypes = getSupertypes(classDef).map { + case (symbol, tpe) => LinkToType(tpe.dokkaType.asSignature, symbol.dri, kindForClasslike(symbol)) } val selfSiangture: DSignature = typeForClass(classDef).dokkaType.asSignature + + val graph = HierarchyGraph.empty ++ getSupertypesGraph(classDef, LinkToType(selfSiangture, classDef.symbol.dri, kindForClasslike(classDef.symbol))) val baseExtra = PropertyContainer.Companion.empty() .plus(ClasslikeExtension(classDef.getConstructorMethod, classDef.getCompanion)) .plus(MemberExtension( classDef.symbol.getVisibility(), - modifiers, - kindForClasslike( classDef.symbol), - classDef.symbol.getAnnotations(), - selfSiangture + modifiers, + kindForClasslike( classDef.symbol), + classDef.symbol.getAnnotations(), + selfSiangture, + graph = graph )) val fullExtra = diff --git a/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala b/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala index 901bc3a68409..abd960c58619 100644 --- a/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala +++ b/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala @@ -14,15 +14,14 @@ import dotty.dokka.model.api._ class InheritanceInformationTransformer(val ctx: DokkaContext) extends DocumentableTransformer: override def invoke(original: DModule, context: DokkaContext): DModule = val subtypes = getSupertypes(original).groupBy(_._1).transform((k, v) => v.map(_._2)) - original.updateMembers(m => m.withKnownChildren(subtypes.getOrElse(m.dri, Nil))) + original.updateMembers { m => + val st: Seq[LinkToType] = subtypes.getOrElse(m.dri, Nil) + m.withKnownChildren(st).withNewGraphEdges(st.map(_ -> m.asLink)) + } - private def getSupertypes(d: Documentable): Seq[(DRI, LinkToType)] = d match { + private def getSupertypes(d: Documentable): Seq[(DRI, LinkToType)] = d match case m: DModule => m.getPackages.asScala.toList.flatMap(p => getSupertypes(p)) - case c: Member => - val selfMapping = if !c.kind.isInstanceOf[Classlike] then Nil else - val selfLink = c.asLink - c.parents.map(_._2 -> selfLink) - + case c: Member => + val selfMapping = if !c.kind.isInstanceOf[Classlike] then Nil else c.parents.map(_._2 -> c.asLink) c.allMembers.flatMap(getSupertypes) ++ selfMapping - case null => List.empty - } + case other => List.empty diff --git a/scala3doc/src/dotty/dokka/translators/HierarchyDiagramBuilder.scala b/scala3doc/src/dotty/dokka/translators/HierarchyDiagramBuilder.scala deleted file mode 100644 index ac737e4ffa38..000000000000 --- a/scala3doc/src/dotty/dokka/translators/HierarchyDiagramBuilder.scala +++ /dev/null @@ -1,23 +0,0 @@ -package dotty.dokka - -import org.jetbrains.dokka.model.Bound -import org.jetbrains.dokka.links.DRI -import dotty.dokka.model._ -import dotty.dokka.model.api._ - - -object HierarchyDiagramBuilder { - def build(m: Member): HierarchyDiagram = { - val mainVertex = Vertex(0, m.asLink) - - val supertypesEdges = m.parents.zipWithIndex.map { case (member, index) => - Edge(mainVertex, Vertex(index + 1, member)) - } - - val subtypesEdges = m.knownChildren.zipWithIndex.map { case (member, index) => - Edge(Vertex(index + m.knownChildren.size + 1, member), mainVertex) - } - - HierarchyDiagram(supertypesEdges ++ subtypesEdges) - } -} diff --git a/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala b/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala index 54e0d71f29b3..688df51f1662 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala @@ -300,12 +300,12 @@ class ScalaPageContentBuilder( def dotDiagram( - diagram: HierarchyDiagram, + diagram: HierarchyGraph, kind: Kind = ContentKind.Main, sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, styles: Set[Style] = mainStyles, extra: PropertyContainer[ContentNode] = mainExtra - ) = addChild(HierarchyDiagramContentNode(diagram, DCI(mainDRI.asJava, kind), sourceSets.toDisplay.asScala.toSet, styles, extra)) + ) = addChild(HierarchyGraphContentNode(diagram, DCI(mainDRI.asJava, kind), sourceSets.toDisplay.asScala.toSet, styles, extra)) def groupingBlock[A, T <: Documentable, G <: List[(A, List[T])]]( name: String, diff --git a/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala b/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala index 470a8057f568..b9850198f712 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala @@ -397,6 +397,7 @@ class ScalaPageCreator( def contentForTypesInfo(c: DClass) = val supertypes = c.parents val subtypes = c.knownChildren + val graph = MemberExtension.getFrom(c).map(_.graph) def contentForTypeLink(builder: DocBuilder, link: LinkToType): DocBuilder = builder.group(styles = Set(TextStyle.Paragraph)) { builder => @@ -428,13 +429,15 @@ class ScalaPageCreator( _.list(subtypes.toList, separator="")(contentForTypeLink) } } - - if subtypes.isEmpty && supertypes.isEmpty then withSubtypes else - withSubtypes.header(2, "Type hierarchy")().group( - kind = ContentKind.Comment, - styles = Set(ContentStyle.WithExtraAttributes), - extra = PropertyContainer.Companion.empty plus SimpleAttr.Companion.header("Type hierarchy") - ) { _.group(kind = ContentKind.Symbol, styles = Set(TextStyle.Monospace)) { - _.dotDiagram(HierarchyDiagramBuilder.build(c)) + + graph.fold(withSubtypes) { g => + if g.edges.size == 0 then withSubtypes else + withSubtypes.header(2, "Type hierarchy")().group( + kind = ContentKind.Comment, + styles = Set(ContentStyle.WithExtraAttributes), + extra = PropertyContainer.Companion.empty plus SimpleAttr.Companion.header("Type hierarchy") + ) { _.group(kind = ContentKind.Symbol, styles = Set(TextStyle.Monospace)) { + _.dotDiagram(g) + } } } diff --git a/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala b/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala index 989c820c40f9..b20bed937d51 100644 --- a/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala +++ b/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala @@ -10,7 +10,7 @@ import HTML._ import dotty.dokka.model.api._ object DotDiagramBuilder: - def build(diagram: HierarchyDiagram, renderer: SignatureRenderer): String = + def build(diagram: HierarchyGraph, renderer: SignatureRenderer): String = val vertecies = diagram.edges.flatMap(edge => Seq(edge.from, edge.to)).distinct.map { vertex => s"""node${vertex.id} [label="${getHtmlLabel(vertex, renderer)}", style="${getStyle(vertex)}"];\n""" }.mkString @@ -40,7 +40,5 @@ object DotDiagramBuilder: span(style := "color: #FFFFFF;")( vertex.body.kind.name, " ", - span(style := "text-decoration: underline;")( - vertex.body.signature.map(renderer.renderElementWith(_, style := "color: #FFFFFF;")) - ) + vertex.body.signature.map(renderer.renderElementWith(_)) ).toString.replace("\"", "\\\"") diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index 68b1b7da7e57..77634867aa0e 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -15,7 +15,7 @@ import kotlinx.html.stream.StreamKt import kotlinx.html.Gen_consumer_tagsKt import org.jetbrains.dokka.links.DRI import dotty.dokka.model.api.Link -import dotty.dokka.model.api.HierarchyDiagram +import dotty.dokka.model.api.HierarchyGraph import org.jetbrains.dokka.base.resolvers.local.LocationProvider @@ -25,10 +25,8 @@ class SignatureRenderer(pageContext: ContentPage, sourceSetRestriciton: JSet[Dis def renderLink(name: String, dri: DRI, modifiers: AppliedAttr*) = link(dri) match case Some(link) => a(href := link, modifiers)(name) - case None if modifiers.isEmpty => raw(name) - case _ => span(modifiers)(name) - - + case _ => span(attr("data-unresolved-link") := dri.toString, modifiers)(name) + def renderElementWith(e: String | (String, DRI) | Link, modifiers: AppliedAttr*) = e match case (name, dri) => renderLink(name, dri, modifiers:_*) case name: String => raw(name) @@ -64,8 +62,8 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) { override def buildContentNode(f: FlowContent, node: ContentNode, pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet]) = { node match { case n: HtmlContentNode => withHtml(f, raw(n.body).toString) - case n: HierarchyDiagramContentNode => buildDiagram(f, n.diagram, pageContext) - case n: DocumentableList => + case n: HierarchyGraphContentNode => buildDiagram(f, n.diagram, pageContext) + case n: DocumentableList => val ss = if sourceSetRestriciton == null then Set.empty.asJava else sourceSetRestriciton withHtml(f, buildDocumentableList(n, pageContext, ss).toString()) case n: DocumentableFilter => withHtml(f, buildDocumentableFilter.toString) @@ -184,9 +182,9 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) { }) } - def buildDiagram(f: FlowContent, diagram: HierarchyDiagram, pageContext: ContentPage) = + def buildDiagram(f: FlowContent, diagram: HierarchyGraph, pageContext: ContentPage) = val renderer = SignatureRenderer(pageContext, sourceSets, getLocationProvider) - withHtml(f, div( id := "inheritance-diagram")( + withHtml(f, div( id := "inheritance-diagram", cls := "diagram-class", svg(id := "graph"), script(`type` := "text/dot", id := "dot")(raw(DotDiagramBuilder.build(diagram, renderer))), ).toString() From 5bc7a0ef8a3d736c900154637179bd4796c4cfef Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Mon, 26 Oct 2020 11:43:39 +0100 Subject: [PATCH 02/19] Removed Edge and Vertex abstraction to simplify graph --- scala3doc/src/dotty/dokka/model/api/api.scala | 25 +++++-------------- .../dotty/dokka/tasty/ClassLikeSupport.scala | 2 +- .../dokka/translators/ScalaPageCreator.scala | 6 ++--- .../dotty/renderers/DotDiagramBuilder.scala | 17 +++++++------ 4 files changed, 19 insertions(+), 31 deletions(-) diff --git a/scala3doc/src/dotty/dokka/model/api/api.scala b/scala3doc/src/dotty/dokka/model/api/api.scala index a6981f510da8..4e2d86a27606 100644 --- a/scala3doc/src/dotty/dokka/model/api/api.scala +++ b/scala3doc/src/dotty/dokka/model/api/api.scala @@ -98,28 +98,15 @@ extension (s: Signature): def join(a: Signature): Signature = s ++ a case class LinkToType(signature: Signature, dri: DRI, kind: Kind) - -case class Vertex(id: Int, body: LinkToType) -case class Edge(from: Vertex, to: Vertex) -case class HierarchyGraph private (edges: Seq[Edge], private val nextId: Int): - def vertecies = edges.flatMap(edge => Seq(edge.from, edge.to)).distinct - def +(edge: (LinkToType, LinkToType)) = - val from = vertecies.find(_.body == edge._1).getOrElse(Vertex(nextId, edge._1)) - val to = vertecies.find(_.body == edge._2).getOrElse { - val id = if from.id == nextId then nextId + 1 else nextId - Vertex(id, edge._2) - } - val higherId = List(from.id, to.id).max - val newNextId = if higherId >= nextId then higherId + 1 else nextId - HierarchyGraph(edges ++ Seq(Edge(from, to)), newNextId) - - def ++(edges: Seq[(LinkToType, LinkToType)]) = edges.foldLeft(this) { +case class HierarchyGraph(edges: Seq[(LinkToType, LinkToType)]): + def vertecies: Seq[LinkToType] = edges.flatten((a, b) => Seq(a, b)).distinct + val verteciesWithId: Map[LinkToType, Int] = vertecies.zipWithIndex.toMap + def +(edge: (LinkToType, LinkToType)): HierarchyGraph = HierarchyGraph((edges :+ edge).distinct) + def ++(edges: Seq[(LinkToType, LinkToType)]): HierarchyGraph = edges.foldLeft(this) { case (acc, edge) => acc + edge } - object HierarchyGraph: - def empty = HierarchyGraph(Seq.empty, 0) - def withEdge(edge: (LinkToType, LinkToType)) = HierarchyGraph.empty + edge + def empty = HierarchyGraph(Seq.empty) def withEdges(edges: Seq[(LinkToType, LinkToType)]) = HierarchyGraph.empty ++ edges diff --git a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala index 977523a8d42d..28e2e599c351 100644 --- a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala @@ -55,7 +55,7 @@ trait ClassLikeSupport: } val selfSiangture: DSignature = typeForClass(classDef).dokkaType.asSignature - val graph = HierarchyGraph.empty ++ getSupertypesGraph(classDef, LinkToType(selfSiangture, classDef.symbol.dri, kindForClasslike(classDef.symbol))) + val graph = HierarchyGraph.withEdges(getSupertypesGraph(classDef, LinkToType(selfSiangture, classDef.symbol.dri, kindForClasslike(classDef.symbol)))) val baseExtra = PropertyContainer.Companion.empty() .plus(ClasslikeExtension(classDef.getConstructorMethod, classDef.getCompanion)) .plus(MemberExtension( diff --git a/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala b/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala index b9850198f712..3f0e3ba7522f 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala @@ -430,14 +430,14 @@ class ScalaPageCreator( } } - graph.fold(withSubtypes) { g => - if g.edges.size == 0 then withSubtypes else + graph.fold(withSubtypes) { graph => + if graph.edges.isEmpty then withSubtypes else withSubtypes.header(2, "Type hierarchy")().group( kind = ContentKind.Comment, styles = Set(ContentStyle.WithExtraAttributes), extra = PropertyContainer.Companion.empty plus SimpleAttr.Companion.header("Type hierarchy") ) { _.group(kind = ContentKind.Symbol, styles = Set(TextStyle.Monospace)) { - _.dotDiagram(g) + _.dotDiagram(graph) } } } diff --git a/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala b/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala index b20bed937d51..30225da29ffc 100644 --- a/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala +++ b/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala @@ -11,12 +11,13 @@ import dotty.dokka.model.api._ object DotDiagramBuilder: def build(diagram: HierarchyGraph, renderer: SignatureRenderer): String = - val vertecies = diagram.edges.flatMap(edge => Seq(edge.from, edge.to)).distinct.map { vertex => - s"""node${vertex.id} [label="${getHtmlLabel(vertex, renderer)}", style="${getStyle(vertex)}"];\n""" + val vWithId = diagram.verteciesWithId + val vertecies = vWithId.map { (vertex, id) => + s"""node${id} [label="${getHtmlLabel(vertex, renderer)}", style="${getStyle(vertex)}"];\n""" }.mkString - val edges = diagram.edges.map { edge => - s"""node${edge.from.id} -> node${edge.to.id};\n""" + val edges = diagram.edges.map { (from, to) => + s"""node${vWithId(from)} -> node${vWithId(to)};\n""" }.mkString s""" digraph g { @@ -27,7 +28,7 @@ object DotDiagramBuilder: |""".stripMargin - private def getStyle(vertex: Vertex) = vertex.body.kind match + private def getStyle(vertex: LinkToType) = vertex.kind match case Kind.Class => "fill: #45AD7D;" case Kind.Object => "fill: #285577;" case Kind.Trait => "fill: #1CAACF;" @@ -36,9 +37,9 @@ object DotDiagramBuilder: case other => sys.error(s"unexpected value: $other") - private def getHtmlLabel(vertex: Vertex, renderer: SignatureRenderer): String = + private def getHtmlLabel(vertex: LinkToType, renderer: SignatureRenderer): String = span(style := "color: #FFFFFF;")( - vertex.body.kind.name, + vertex.kind.name, " ", - vertex.body.signature.map(renderer.renderElementWith(_)) + vertex.signature.map(renderer.renderElementWith(_)) ).toString.replace("\"", "\\\"") From 92dbf8dd036395c6c849b2a1b970f69d38974117 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Tue, 27 Oct 2020 19:32:04 +0100 Subject: [PATCH 03/19] FIxes after review --- .../ImplicitMembersExtensionTransformer.scala | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala b/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala index 7bf7ff4f935d..77a982869110 100644 --- a/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala +++ b/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala @@ -20,7 +20,17 @@ class ImplicitMembersExtensionTransformer(ctx: DokkaContext) extends Documentabl case classlike: DClass => ClasslikeExtension.getFrom(classlike).flatMap(_.companion).map(classlikeMap) case _ => None - val implictSources = outerMembers ++ companion.toSeq // We can expand this on companion object from parents, generic etc. + val allParents = c.parents.flatMap(p => classlikeMap.get(p.dri)) + + val parentCompanions = allParents.flatMap { + case cls: DClasslike => ClasslikeExtension.getFrom(cls).flatMap(_.companion).map(classlikeMap) + case _ => None + } + + // TODO (#220): We can expand this on generic etc + val implictSources = outerMembers ++ companion.toSeq ++ parentCompanions + + val applicableDRIs = c.parents.map(_.dri).toSet + c.dri val MyDri = c.getDri def collectApplicableMembers(source: Member): Seq[Member] = source.allMembers.flatMap { From 22f2f62c65a8bc4ae3ff2ad43f4e0ea959941b6f Mon Sep 17 00:00:00 2001 From: Aleksander Boruch-Gruszecki Date: Mon, 5 Oct 2020 11:00:42 +0200 Subject: [PATCH 04/19] Make warning messages more presentable --- .../dotty/dokka/tasty/comments/BaseConverter.scala | 5 +++-- .../dokka/tasty/comments/MarkdownConverter.scala | 14 +++----------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/scala3doc/src/dotty/dokka/tasty/comments/BaseConverter.scala b/scala3doc/src/dotty/dokka/tasty/comments/BaseConverter.scala index e028b4f08b6e..43a7c7bb5b88 100644 --- a/scala3doc/src/dotty/dokka/tasty/comments/BaseConverter.scala +++ b/scala3doc/src/dotty/dokka/tasty/comments/BaseConverter.scala @@ -10,9 +10,10 @@ trait BaseConverter { protected def withParsedQuery(queryStr: String)(thunk: Query => dkkd.DocTag): dkkd.DocTag = { QueryParser(queryStr).tryReadQuery() match { case Left(err) => + val msg = err.getMessage // TODO: for better experience we should show source location here - println("WARN: " + err.getMessage) - dkkd.A(List(dkk.text(err.getMessage)).asJava, Map("href" -> "#").asJava) + println("WARN: " + msg) + dkkd.A(List(dkk.text(queryStr)).asJava, Map("href" -> "#").asJava) case Right(query) => thunk(query) } diff --git a/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala b/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala index 35b5450a5808..59cd05b25268 100644 --- a/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala +++ b/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala @@ -161,17 +161,8 @@ class MarkdownConverter(val repr: Repr) extends BaseConverter { case _: mda.SoftLineBreak => emit(dkkd.Br.INSTANCE) case _ => - println(s"!!! DEFAULTING @ ${n.getNodeName}") - emit(dkkd.P( - List[dkkd.DocTag]( - dkkd.Span( - List(dkk.text(s"!!! DEFAULTING @ ${n.getNodeName}")).asJava, - kt.emptyMap, - ), - dkk.text(MarkdownParser.renderToText(n)) - ).asJava, - kt.emptyMap[String, String] - )) + println(s"WARN: Encountered unrecognised Markdown node `${n.getNodeName}`, please open an issue.") + emit(dkk.text(MarkdownParser.renderToText(n))) } def extractAndConvertSummary(doc: mdu.Document): Option[dkkd.DocTag] = @@ -189,6 +180,7 @@ class MarkdownConverter(val repr: Repr) extends BaseConverter { case Some((sym, targetText)) => dkkd.DocumentationLink(sym.dri, resolveBody(default = targetText), kt.emptyMap) case None => + println(s"WARN: Definition lookup for following query failed: $queryStr") dkkd.A(resolveBody(default = query.join), Map("href" -> "#").asJava) } } From d0693c05e94a44452e0e334f1c3da9a21a30ca6a Mon Sep 17 00:00:00 2001 From: Aleksander Boruch-Gruszecki Date: Mon, 5 Oct 2020 11:10:54 +0200 Subject: [PATCH 05/19] Display tooltips for unresolved links ScalaCommentToContentConverter needed to be refactored to inherit from DocTagToContentConverter (because our version of `buildContent` needs to always be called first before the super version, otherwise nested tags aren't seen by our implementation). --- scala3doc-testcases/src/tests/tests.scala | 8 ++- .../dokka/tasty/comments/BaseConverter.scala | 2 +- .../tasty/comments/MarkdownConverter.scala | 2 +- .../ScalaCommentToContentConverter.scala | 54 ++++++++++++------- .../dotty/renderers/ScalaHtmlRenderer.scala | 20 +++++++ 5 files changed, 63 insertions(+), 23 deletions(-) diff --git a/scala3doc-testcases/src/tests/tests.scala b/scala3doc-testcases/src/tests/tests.scala index cdaf7947eae5..b5bf87fbe15a 100644 --- a/scala3doc-testcases/src/tests/tests.scala +++ b/scala3doc-testcases/src/tests/tests.scala @@ -44,10 +44,14 @@ package tests * * And yet another: [[example.level2.Documentation]]. * - * This is my friend: [[tests\.B\]]]. + * This is my friend: [[tests.B]]. * * And this is his companion: [[tests.B$ link to the companion]]. * + * And this is a link that failed to resolve [[absent]]. + * + * And this is a link that failed to parse [[#]]. + * * @author Gal Anonim * @version 1.0.0 * @result A class doesn't actually have a result. @@ -172,7 +176,7 @@ class Methods: * * Even though this line should be separated from previous one. * - * @throws scala.Error Throws errors. + * @throws java.lang.Error Throws errors. * @example * ``` * (m : Methods).generic2(d(), e()): B diff --git a/scala3doc/src/dotty/dokka/tasty/comments/BaseConverter.scala b/scala3doc/src/dotty/dokka/tasty/comments/BaseConverter.scala index 43a7c7bb5b88..7c93ea889ad0 100644 --- a/scala3doc/src/dotty/dokka/tasty/comments/BaseConverter.scala +++ b/scala3doc/src/dotty/dokka/tasty/comments/BaseConverter.scala @@ -13,7 +13,7 @@ trait BaseConverter { val msg = err.getMessage // TODO: for better experience we should show source location here println("WARN: " + msg) - dkkd.A(List(dkk.text(queryStr)).asJava, Map("href" -> "#").asJava) + dkkd.A(List(dkk.text(queryStr)).asJava, Map("title" -> msg, "href" -> "#").asJava) case Right(query) => thunk(query) } diff --git a/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala b/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala index 59cd05b25268..fda6e78f2f7b 100644 --- a/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala +++ b/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala @@ -181,7 +181,7 @@ class MarkdownConverter(val repr: Repr) extends BaseConverter { dkkd.DocumentationLink(sym.dri, resolveBody(default = targetText), kt.emptyMap) case None => println(s"WARN: Definition lookup for following query failed: $queryStr") - dkkd.A(resolveBody(default = query.join), Map("href" -> "#").asJava) + dkkd.A(resolveBody(default = query.join), Map("title" -> s"Definition was not found: $queryStr", "href" -> "#").asJava) } } } diff --git a/scala3doc/src/dotty/dokka/transformers/ScalaCommentToContentConverter.scala b/scala3doc/src/dotty/dokka/transformers/ScalaCommentToContentConverter.scala index 5d5ef69edf28..57db0886ac4c 100644 --- a/scala3doc/src/dotty/dokka/transformers/ScalaCommentToContentConverter.scala +++ b/scala3doc/src/dotty/dokka/transformers/ScalaCommentToContentConverter.scala @@ -6,25 +6,41 @@ import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.pages._ import collection.JavaConverters._ import org.jetbrains.dokka.base.transformers.pages.comments.{DocTagToContentConverter, CommentsToContentConverter} +import org.jetbrains.dokka.model.properties.{PropertyContainer, ExtraProperty, ExtraProperty$Key, MergeStrategy} import java.util.{Set => JSet, List => JList} +object ScalaCommentToContentConverter extends DocTagToContentConverter { + override def buildContent( + docTag: DocTag, + dci: DCI, + sourceSets: JSet[? <: DokkaConfiguration$DokkaSourceSet], + styles: JSet[? <: Style], + extra: PropertyContainer[ContentNode] + ): JList[ContentNode] = docTag match { + case docTag: A => + val superRes = super.buildContent(docTag, dci, sourceSets, styles, extra).get(0) + val res = superRes.withNewExtras(superRes.getExtra plus ExtraLinkAttributes( + title = docTag.getParams.asScala.get("title") + )) + List(res).asJava + case h: Html => List( + HtmlContentNode( + h.getChildren.asScala.collect{case c: Text => c}.head.getBody, + dci, + sourceSets.asScala.toSet.toDisplay.asScala.toSet, + styles.asScala.toSet + ) + ).asJava + case other => super.buildContent(other, dci, sourceSets, styles, extra) + } -object ScalaCommentToContentConverter extends CommentsToContentConverter: - val defaultConverter = DocTagToContentConverter() - override def buildContent( - docTag: DocTag, - dci: DCI, - sourceSets: JSet[? <: DokkaConfiguration$DokkaSourceSet], - styles: JSet[? <: Style], - extra: PropertyContainer[ContentNode] - ): JList[ContentNode] = docTag match { - case h: Html => List( - HtmlContentNode( - h.getChildren.asScala.collect{case c: Text => c}.head.getBody, - dci, - sourceSets.asScala.toSet.toDisplay.asScala.toSet, - styles.asScala.toSet - ) - ).asJava - case other => defaultConverter.buildContent(other, dci, sourceSets, styles, extra) - } + case class ExtraLinkAttributes(title: Option[String]) extends ExtraProperty[ContentNode] { + def getKey() = LinkAttributesKey + } + + case object LinkAttributesKey extends ExtraProperty.Key[ContentNode, Null] { + def mergeStrategyFor(left: Null, right: Null) = MergeStrategy.Fail( + () => throw NotImplementedError(s"Property merging for $this is not implemented") + ).asInstanceOf[MergeStrategy[ContentNode]] + } +} diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index 77634867aa0e..1a9c2e52bc07 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -158,6 +158,26 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) { ).toString } + override def buildResolvedLink( + f: FlowContent, + node: ContentResolvedLink, + pageContext: ContentPage, + sourceSetRestriction: JSet[DisplaySourceSet], + ): Unit = { + import kotlinx.html.{Gen_consumer_tagsKt => dsl} + val c = f.getConsumer + val U = kotlin.Unit.INSTANCE + dsl.a(c, node.getAddress, /*target*/ null, /*classes*/ null, { e => + import ScalaCommentToContentConverter._ + // node.getExtra.getMap.asScala.get(LinkAttributesKey) + Option(node.get(LinkAttributesKey).asInstanceOf[ExtraLinkAttributes]) + .flatMap(_.title) + .foreach(e.getAttributes.put("title", _)) + buildText(f, node.getChildren, pageContext, sourceSetRestriction) + U + }) + } + override def buildCodeBlock( f: FlowContent, code: ContentCodeBlock, From 7b1e727fff83a0cb97eb439d6ab643273dee68c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Zyba=C5=82a?= Date: Thu, 15 Oct 2020 13:07:47 +0200 Subject: [PATCH 06/19] README adjustments. CLI documentation. --- scala3doc/README.md | 27 +++++++++++++++++++++------ scala3doc/documentation/index.md | 28 +--------------------------- 2 files changed, 22 insertions(+), 33 deletions(-) mode change 100644 => 120000 scala3doc/documentation/index.md diff --git a/scala3doc/README.md b/scala3doc/README.md index 19cb405e35e1..5fd82ce1cfa3 100644 --- a/scala3doc/README.md +++ b/scala3doc/README.md @@ -13,6 +13,8 @@ We're aiming to support all the features Scaladoc did, plus new and exciting one - displaying project and API documentation together on one site! - and more! +**Yes, this page was generated using scala3doc** + ## Running the project Use the following commands to generate documentation for this project and for Dotty, respectively: @@ -29,13 +31,26 @@ cd output python3 -m http.server 8080 ``` -And afterwards point your browser to `http://localhost:8080/self` or -`http://localhost:8080/stdLib` for this project and for Dotty documentation +And afterwards point your browser to or + for this project and for Dotty documentation respectively. It's not strictly necessary to go through an HTTP server, but because of CORS the documentation won't work completely if you don't. +## CLI Documentation + +CLI command for running our tool is in form: `sbt main -n -o -t -cp -s { } -d ` where: + - ``: name of module in generated documentation + - ``: location where documentation should be created + - ``: is list of dirs or jars that contains tasty files that should be documented + - ``: classpath that was used to generate tasty files + - ``: links to source files of module that are used to link symbols on pages to their source file. They need to be supplied in form: + `local_dir=remote_dir#line_suffix` e.g. `src/main/scala=https://github.com/lampepfl/scala3doc/tree/master/src/main/scala#L` + - ``: directory of static documentation that you would like to render with API documentation. This feature is provided by dokka-site plugin: + - [GitHub](https://github.com/VirtusLab/dokka-site) + - [Documentation](https://virtuslab.github.io/dokka-site/index.html) + ## Developing At least two of our contributors use [Metals](https://scalameta.org/metals/) to @@ -44,16 +59,16 @@ work on the project. For every PR, we build documentation for Scala3doc and Dotty. For example, for PR 123 you can find them at: -+ https://scala3doc.s3.eu-central-1.amazonaws.com/pr-123/self/main/index.html -+ https://scala3doc.s3.eu-central-1.amazonaws.com/pr-123/stdLib/main/index.html ++ ++ Note that these correspond to the contents of `output` directory - that's precisely what they are. You can also find the result of building the same sites for latest `master` at: -+ https://scala3doc.s3.eu-central-1.amazonaws.com/pr-master/self/main/index.html -+ https://scala3doc.s3.eu-central-1.amazonaws.com/pr-master/stdLib/main/index.html ++ ++ ### Testing diff --git a/scala3doc/documentation/index.md b/scala3doc/documentation/index.md deleted file mode 100644 index c4e9b0ce3b6d..000000000000 --- a/scala3doc/documentation/index.md +++ /dev/null @@ -1,27 +0,0 @@ -# Scala3doc - -**Documentation tool for Scala 3** - -We are using [TASTy](https://github.com/lampepfl/dotty/blob/master/tasty/src/dotty/tools/tasty/TastyFormat.scala) to generate documentation. We aim to has all known and loved feature from scaladoc as well as new feature such as : - -- integrated documentation and API -- has option for basic pluggablity -- and much more - -**Yes, this page was generated using scala3doc** - -You can learn more from out [documentation](scala3doc/index.html). - -## Getting started - -For now the recommended way to try out our project would be: - - Clone our [repository](https://github.com/lampepfl/scala3doc) - - Run `sbt main -n -o -t -cp -s { } ` where - - ``: name of module in generated documentation - - ``: location where documentation should be created - - ``: is list of dirs or jars that contains tasty files that should be documented - - ``: classpath that was used to generate tasty files - - ``: links to source files of module that are used to link symbols on pages to their source file. They need to be supplied in form: - local_dir=remote_dir#line_suffix e.g. src/main/scala=https://github.com/lampepfl/scala3doc/tree/master/src/main/scala#L - - We also support `-d ` argument to provide static documentation. You can find more about that feature [here](static-page.html). diff --git a/scala3doc/documentation/index.md b/scala3doc/documentation/index.md new file mode 120000 index 000000000000..32d46ee883b5 --- /dev/null +++ b/scala3doc/documentation/index.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file From 6e22e8cede599d3b8a738da78fcac5b91ae76314 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Mon, 2 Nov 2020 16:59:54 +0100 Subject: [PATCH 07/19] Migrate diagrams away from scala tags --- .../transformers/InheritanceInformationTransformer.scala | 1 - scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala b/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala index abd960c58619..94611fb76322 100644 --- a/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala +++ b/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala @@ -24,4 +24,3 @@ class InheritanceInformationTransformer(val ctx: DokkaContext) extends Documenta case c: Member => val selfMapping = if !c.kind.isInstanceOf[Classlike] then Nil else c.parents.map(_._2 -> c.asLink) c.allMembers.flatMap(getSupertypes) ++ selfMapping - case other => List.empty diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index 1a9c2e52bc07..76f0f9c0b523 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -25,7 +25,7 @@ class SignatureRenderer(pageContext: ContentPage, sourceSetRestriciton: JSet[Dis def renderLink(name: String, dri: DRI, modifiers: AppliedAttr*) = link(dri) match case Some(link) => a(href := link, modifiers)(name) - case _ => span(attr("data-unresolved-link") := dri.toString, modifiers)(name) + case _ => span(Attr("data-unresolved-link") := dri.toString, modifiers)(name) def renderElementWith(e: String | (String, DRI) | Link, modifiers: AppliedAttr*) = e match case (name, dri) => renderLink(name, dri, modifiers:_*) @@ -204,7 +204,7 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) { def buildDiagram(f: FlowContent, diagram: HierarchyGraph, pageContext: ContentPage) = val renderer = SignatureRenderer(pageContext, sourceSets, getLocationProvider) - withHtml(f, div( id := "inheritance-diagram", cls := "diagram-class", + withHtml(f, div( id := "inheritance-diagram", cls := "diagram-class")( svg(id := "graph"), script(`type` := "text/dot", id := "dot")(raw(DotDiagramBuilder.build(diagram, renderer))), ).toString() From 72865de68e7d9c3f6f5abc43034cd81b61c705aa Mon Sep 17 00:00:00 2001 From: Guillaume Raffin Date: Fri, 30 Oct 2020 17:29:59 +0100 Subject: [PATCH 08/19] Fix search popup on small screens --- .../resources/dotty_res/styles/search-bar.css | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 scala3doc/resources/dotty_res/styles/search-bar.css diff --git a/scala3doc/resources/dotty_res/styles/search-bar.css b/scala3doc/resources/dotty_res/styles/search-bar.css new file mode 100644 index 000000000000..10b9406d8d37 --- /dev/null +++ b/scala3doc/resources/dotty_res/styles/search-bar.css @@ -0,0 +1,77 @@ +/* Global search */ +.search-content { + padding: 0; + margin: var(--content-padding); + position: absolute; + top: 0; + right: 0; + z-index: 5; + background: none; +} + +/* button */ +.search button { + background: none; + fill: var(--icon-color); + cursor: pointer; + border: none; + padding: 0; +} +.search button:hover { + fill: var(--link-hover-fg); +} +.search button svg { + width: 24px; + height: 24px; +} + +/* popup */ +.popup-wrapper { + box-shadow: 0 0 10px var(--border-light) !important; + border: 2px solid var(--border-light) !important; + font-family: var(--mono-font) !important; + width: calc(100% - var(--side-width) - 84px); + left: calc(var(--side-width) + 42px) !important; +} +.popup-wrapper .indented { + text-indent: 1.5em !important; +} +.popup-wrapper .disabled { + color: var(--inactive-fg) !important; + font-weight: 500 !important; +} +.action_def:hover, .action_def:focus-within { + color: var(--selected-fg); + background: var(--selected-bg) !important; + font-weight: 500; +} + +/* Landscape phones, portait tablets */ +@media(max-width: 768px) { + .popup-wrapper { + width: calc(100% - 48px); + left: 24px !important; + } +} + +/* Portrait phones */ +@media(max-width: 576px) { + .search-content { + margin: 0 !important; + top: 9px !important; + right: 12px !important; + } + .popup-wrapper { + width: 100%; + left: 0 !important; + top: 36px !important; + } + /* Allow to scroll horizontally in the search results, which is useful on small screens */ + .popup-wrapper div.ReactVirtualized__Grid__innerScrollContainer { + overflow: auto !important; + } + .popup-wrapper div.ReactVirtualized__Grid__innerScrollContainer > div { + min-width: 100%; + width: auto !important; + } +} \ No newline at end of file From f96b707353da518eb9b7522aec4a151fe132868e Mon Sep 17 00:00:00 2001 From: Guillaume Raffin Date: Fri, 30 Oct 2020 17:50:35 +0100 Subject: [PATCH 09/19] Add space before full path in search results --- scala3doc/resources/dotty_res/styles/search-bar.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scala3doc/resources/dotty_res/styles/search-bar.css b/scala3doc/resources/dotty_res/styles/search-bar.css index 10b9406d8d37..eee1afc463a5 100644 --- a/scala3doc/resources/dotty_res/styles/search-bar.css +++ b/scala3doc/resources/dotty_res/styles/search-bar.css @@ -40,11 +40,15 @@ color: var(--inactive-fg) !important; font-weight: 500 !important; } -.action_def:hover, .action_def:focus-within { +.action_def:hover, .action_def.hover_a79 { color: var(--selected-fg); background: var(--selected-bg) !important; font-weight: 500; } +.action_def .template-description { + margin-left: 2rem; + font-style: italic; +} /* Landscape phones, portait tablets */ @media(max-width: 768px) { From 2ba3647a40d43d5544be7fde38c807b807c2cf2d Mon Sep 17 00:00:00 2001 From: Guillaume Raffin Date: Fri, 30 Oct 2020 18:06:18 +0100 Subject: [PATCH 10/19] Make the logo redirect to root --- scala3doc/resources/dotty_res/scripts/ux.js | 12 +++++++++--- scala3doc/resources/dotty_res/styles/scalastyle.css | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/scala3doc/resources/dotty_res/scripts/ux.js b/scala3doc/resources/dotty_res/scripts/ux.js index e4b19bdf8023..3bb71dd03d89 100644 --- a/scala3doc/resources/dotty_res/scripts/ux.js +++ b/scala3doc/resources/dotty_res/scripts/ux.js @@ -1,10 +1,16 @@ window.addEventListener("DOMContentLoaded", () => { - var e = document.getElementById("leftToggler"); - if (e) { - e.onclick = function () { + var toggler = document.getElementById("leftToggler"); + if (toggler) { + toggler.onclick = function () { document.getElementById("leftColumn").classList.toggle("open"); }; } + var logo = document.getElementById("logo"); + if (logo) { + logo.onclick = function() { + window.location = pathToRoot; // global variable pathToRoot is created by the html renderer + }; + } hljs.registerLanguage("scala", highlightDotty); hljs.registerAliases(["dotty", "scala3"], "scala"); hljs.initHighlighting(); diff --git a/scala3doc/resources/dotty_res/styles/scalastyle.css b/scala3doc/resources/dotty_res/styles/scalastyle.css index 3674f4cae339..51877b7a6f81 100644 --- a/scala3doc/resources/dotty_res/styles/scalastyle.css +++ b/scala3doc/resources/dotty_res/styles/scalastyle.css @@ -132,6 +132,7 @@ th { background-origin: content-box; padding: 8px 0 8px 16px; height: 42px; + cursor: pointer; } #logo::after { color: var(--leftbar-fg); From dc7dcf91afd3b23b3d956ede181465fb427a1f5f Mon Sep 17 00:00:00 2001 From: Guillaume Raffin Date: Fri, 30 Oct 2020 18:56:48 +0100 Subject: [PATCH 11/19] Make menu arrows more visible --- .../resources/dotty_res/styles/scalastyle.css | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/scala3doc/resources/dotty_res/styles/scalastyle.css b/scala3doc/resources/dotty_res/styles/scalastyle.css index 51877b7a6f81..8bcbd9d16953 100644 --- a/scala3doc/resources/dotty_res/styles/scalastyle.css +++ b/scala3doc/resources/dotty_res/styles/scalastyle.css @@ -29,6 +29,7 @@ --mono-font: "Fira Code", monospace; --text-font: "Lato", sans-serif; --title-font: "Roboto Slab", serif; + --leftbar-font-size: 15px; /* Layout Settings (changes on small screens) */ --side-width: 255px; @@ -123,7 +124,7 @@ th { overflow-y: auto; scrollbar-width: thin; height: 100%; - font-size: 15px; + font-size: var(--leftbar-font-size); } #logo { background-size: contain; @@ -198,16 +199,22 @@ th { cursor: pointer; } .sideMenuPart .navButtonContent::before { - content: url("../images/arrow_down.svg"); + content: "\e905"; /* arrow up */ + font-family: "dotty-icons" !important; + font-size: 20px; + line-height: var(--leftbar-font-size); + color: var(--leftbar-fg); height: 100%; display: flex; flex-direction: row; align-items: center; justify-content: center; - transform: rotate(180deg); } .sideMenuPart.hidden .navButtonContent::before { - transform: rotate(0deg); + content: "\e903"; /* arrow down */ +} +.sideMenuPart .navButtonContent:hover::before { + color: var(--leftbar-current-bg); } /* Search */ From 917c535c01354c45ed613a4f438cdf871b69d32e Mon Sep 17 00:00:00 2001 From: Guillaume Raffin Date: Fri, 30 Oct 2020 23:09:30 +0100 Subject: [PATCH 12/19] Remove paneSearch --- scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index 76f0f9c0b523..4cc47e031028 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -233,7 +233,6 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) { div(id := "container")( div(id := "leftColumn")( div(id := "logo"), - div(id := "paneSearch"), nav(id := "sideMenu"), ), div(id := "main")( From a9e37f56ce5f5162784eca7fe053979a30a72cb5 Mon Sep 17 00:00:00 2001 From: Guillaume Raffin Date: Fri, 30 Oct 2020 23:54:58 +0100 Subject: [PATCH 13/19] Put paneSearch back and hide it If #paneSearch is missing dokka's ReactJS crashes and doesn't generate the search button. --- scala3doc/resources/dotty_res/styles/scalastyle.css | 3 +++ scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala | 1 + 2 files changed, 4 insertions(+) diff --git a/scala3doc/resources/dotty_res/styles/scalastyle.css b/scala3doc/resources/dotty_res/styles/scalastyle.css index 8bcbd9d16953..4b725c5c3436 100644 --- a/scala3doc/resources/dotty_res/styles/scalastyle.css +++ b/scala3doc/resources/dotty_res/styles/scalastyle.css @@ -126,6 +126,9 @@ th { height: 100%; font-size: var(--leftbar-font-size); } +#paneSearch { + display: none; +} #logo { background-size: contain; background-repeat: no-repeat; diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index 4cc47e031028..76f0f9c0b523 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -233,6 +233,7 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) { div(id := "container")( div(id := "leftColumn")( div(id := "logo"), + div(id := "paneSearch"), nav(id := "sideMenu"), ), div(id := "main")( From 1953e8cb169acf34125d552456e7cc154794e858 Mon Sep 17 00:00:00 2001 From: Guillaume Raffin Date: Sat, 31 Oct 2020 00:31:17 +0100 Subject: [PATCH 14/19] Trial: red search button --- .../resources/dotty_res/styles/search-bar.css | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/scala3doc/resources/dotty_res/styles/search-bar.css b/scala3doc/resources/dotty_res/styles/search-bar.css index eee1afc463a5..f8a58567cb25 100644 --- a/scala3doc/resources/dotty_res/styles/search-bar.css +++ b/scala3doc/resources/dotty_res/styles/search-bar.css @@ -11,18 +11,16 @@ /* button */ .search button { - background: none; - fill: var(--icon-color); - cursor: pointer; - border: none; - padding: 0; + background: #ED3522; + fill: #fff; + cursor: pointer; + border: none; + padding: 9px; + border-radius: 24px; + box-shadow: 0 0 16px #F27264; } .search button:hover { - fill: var(--link-hover-fg); -} -.search button svg { - width: 24px; - height: 24px; + fill: #F27264; } /* popup */ @@ -60,6 +58,18 @@ /* Portrait phones */ @media(max-width: 576px) { + .search button { + background: none; + fill: var(--icon-color); + cursor: pointer; + border: none; + padding: 0; + box-shadow: none; + margin-top: 2px; + } + .search button:hover { + fill: var(--link-hover-fg); + } .search-content { margin: 0 !important; top: 9px !important; From 6c8dc60adf534f96e20472318490d51b4793f772 Mon Sep 17 00:00:00 2001 From: Guillaume Raffin Date: Mon, 2 Nov 2020 10:33:26 +0100 Subject: [PATCH 15/19] Make it easier to click on sidebar arrows --- .../resources/dotty_res/styles/diagram.css | 26 +-- .../resources/dotty_res/styles/scalastyle.css | 203 +++++++----------- .../ScalaEmbeddedResourceApppender.scala | 5 +- 3 files changed, 91 insertions(+), 143 deletions(-) diff --git a/scala3doc/resources/dotty_res/styles/diagram.css b/scala3doc/resources/dotty_res/styles/diagram.css index e5d6c6052f44..58c7e9415308 100644 --- a/scala3doc/resources/dotty_res/styles/diagram.css +++ b/scala3doc/resources/dotty_res/styles/diagram.css @@ -1,29 +1,29 @@ .node { - stroke: #333; - stroke-width: 2.5px; - fill: white; + stroke: #333; + stroke-width: 2.5px; + fill: white; } .edgeLabel { - fill: white; + fill: white; } .edgePath { - stroke: #333; - stroke-width: 1.5px; - fill: #333; + stroke: #333; + stroke-width: 1.5px; + fill: #333; } #graph { - width: 100%; - height: 80%; + width: 100%; + height: 80%; } -.diagram-class a[href] { - text-decoration: underline; - color: #FFFFFF; +.diagram-class a { + text-decoration: underline; + color: #FFF; } .diagram-class span[data-unresolved-link] { - color: #FFFFFF; + color: #FFF; } \ No newline at end of file diff --git a/scala3doc/resources/dotty_res/styles/scalastyle.css b/scala3doc/resources/dotty_res/styles/scalastyle.css index 4b725c5c3436..f6a160068ade 100644 --- a/scala3doc/resources/dotty_res/styles/scalastyle.css +++ b/scala3doc/resources/dotty_res/styles/scalastyle.css @@ -149,29 +149,29 @@ th { padding-left: 1em; } .sideMenuPart a { - align-items: center; - flex: 1; - overflow-x: hidden; + align-items: center; + flex: 1; + overflow-x: hidden; overflow-wrap: anywhere; color: var(--leftbar-fg); margin-right: .5rem; + padding: 7px 0; } .sideMenuPart > .overview { - display: flex; - align-items: center; - position: relative; + display: flex; + align-items: center; + position: relative; user-select: none; - padding: 7px 0; } .sideMenuPart > .overview::before { width: var(--side-width); - box-sizing: border-box; - content: ''; - top: 0; - right: 0; - bottom: 0; - position: absolute; - z-index: 1; + box-sizing: border-box; + content: ''; + top: 0; + right: 0; + bottom: 0; + position: absolute; + z-index: 1; } .sideMenuPart > .overview:hover::before { background: var(--leftbar-hover-bg); @@ -187,18 +187,18 @@ th { color: var(--leftbar-current-fg); } .sideMenuPart.hidden > .sideMenuPart { - height: 0; - visibility: hidden; + height: 0; + visibility: hidden; } .overview a, .overview .navButton { z-index: 3; } .sideMenuPart .navButton { - height: 100%; - align-items: center; - display: flex; - justify-content: flex-end; - padding-right: 1rem; + height: 100%; + align-items: center; + display: flex; + justify-content: flex-end; + padding: 7px 10px; cursor: pointer; } .sideMenuPart .navButtonContent::before { @@ -216,55 +216,10 @@ th { .sideMenuPart.hidden .navButtonContent::before { content: "\e903"; /* arrow down */ } -.sideMenuPart .navButtonContent:hover::before { +.sideMenuPart .navButton:hover .navButtonContent::before { color: var(--leftbar-current-bg); } -/* Search */ -.search-content { - /* TODO override search style without !important */ - padding: 0 !important; - margin: var(--content-padding) !important; - position: absolute !important; - top: 0 !important; - right: 0 !important; - z-index: 5 !important; - background: none !important; -} -.search button { - background: none !important; - fill: var(--icon-color) !important; - cursor: pointer !important; -} -.search button:hover { - fill: var(--link-hover-fg) !important; -} -.search button svg { - width: 24px !important; - height: 24px !important; -} -.popup-wrapper { /* search popup */ - box-shadow: 0 0 10px var(--border-light) !important; - border: 2px solid var(--border-light) !important; - font-family: var(--mono-font) !important; - /* TODO dokka already uses !important so I can't override that... - min-width: calc(100% - var(--side-width) - 36px) !important; - left: 24px; - */ -} -.popup-wrapper .indented { - text-indent: 1.5em !important; -} -.popup-wrapper .disabled { - color: var(--inactive-fg) !important; - font-weight: 500 !important; -} -.action_def:hover, .action_def:focus-within { - color: var(--selected-fg); - background: var(--selected-bg) !important; - font-weight: 500; -} - /* Cover */ .cover h1 { font-size: 38px; @@ -274,46 +229,46 @@ th { /* Tabs */ .section-tab { - border: none; - outline: none; + border: none; + outline: none; background: transparent; - padding: 0 6px 4px 6px; - margin: 1rem 1rem 0 0; - border-bottom: 1px solid grey; - cursor: pointer; + padding: 0 6px 4px 6px; + margin: 1rem 1rem 0 0; + border-bottom: 1px solid grey; + cursor: pointer; } .section-tab[data-active=""] { color: unset; font-weight: bold; - border-bottom: 2px solid var(--active-tab-color); + border-bottom: 2px solid var(--active-tab-color); } .tabs-section-body > :not([data-active]) { - display: none; + display: none; } /* Tabs content */ .table { - /*! display: flex; */ - flex-direction: column; + /*! display: flex; */ + flex-direction: column; } .table-row { - border-bottom: 2px solid var(--border-light); - padding: 8px 24px 8px 0; + border-bottom: 2px solid var(--border-light); + padding: 8px 24px 8px 0; } .main-subrow { margin-bottom: .5em; } .main-subrow > span > a, .main-subrow > span > span[data-unresolved-link] { - text-decoration: none; - font-style: normal; - font-weight: bold; - color: unset; - font-size: 18px; + text-decoration: none; + font-style: normal; + font-weight: bold; + color: unset; + font-size: 18px; } .main-subrow .anchor-icon { /* Link Anchor */ - margin-left: .25rem; - opacity: 0; - transition: 0.2s 0.5s; + margin-left: .25rem; + opacity: 0; + transition: 0.2s 0.5s; cursor: pointer; } .main-subrow .anchor-icon > svg { @@ -321,44 +276,44 @@ th { fill: var(--link-fg); } .main-subrow:hover .anchor-icon { - opacity: 1; - transition: 0.2s; + opacity: 1; + transition: 0.2s; } .brief-with-platform-tags ~ .main-subrow { - padding-top: 0; + padding-top: 0; } .brief { - white-space: pre-wrap; - overflow: hidden; + white-space: pre-wrap; + overflow: hidden; margin-bottom: .5em; } /* Declarations */ .symbol.monospace { color: var(--symbol-fg); - display: block; - white-space: normal; + display: block; + white-space: normal; position: relative; padding-right: 24px; /* avoid the copy button */ margin: 1em 0; } .symbol .top-right-position { - position: absolute; - top: 8px; - right: 8px; + position: absolute; + top: 8px; + right: 8px; } /* "copy to clipboard" button */ .copy-popup-wrapper { - display: none; - position: absolute; - z-index: 1000; - background: white; - width: max-content; - cursor: default; - border: 1px solid var(--border-light); - box-sizing: border-box; - box-shadow: 0px 5px 10px var(--border-light); - border-radius: 3px; + display: none; + position: absolute; + z-index: 1000; + background: white; + width: max-content; + cursor: default; + border: 1px solid var(--border-light); + box-sizing: border-box; + box-shadow: 0px 5px 10px var(--border-light); + border-radius: 3px; font-weight: normal; } .copy-popup-wrapper.active-popup { @@ -366,7 +321,7 @@ th { align-items: center; } .copy-popup-wrapper.popup-to-left { - left: -14rem; + left: -14rem; } .copy-popup-wrapper svg { padding: 8px; @@ -404,29 +359,29 @@ dl.attributes > dd { /* params list documentation */ dl.paramsdesc { - display: flex; - flex-flow: row wrap; + display: flex; + flex-flow: row wrap; } dl.paramsdesc dt { - flex-basis: 20%; - padding: 2px 0; - text-align: left; - font-weight: bold; + flex-basis: 20%; + padding: 2px 0; + text-align: left; + font-weight: bold; } dl.paramsdesc dd { - flex-basis: 80%; - flex-grow: 1; - margin: 0; - padding: 2px 0; + flex-basis: 80%; + flex-grow: 1; + margin: 0; + padding: 2px 0; } .platform-dependent-row dl.attributes > dd { padding-left: 3em; } -/* Workaround for dynamically rendered content inside hidden tab. +/* Workaround for dynamically rendered content inside hidden tab. There's some limitation of css/html that causes wrong width/height property of elements that are rendered dynamically inside element with display:none; -Same solution is already used in Dokka. +Same solution is already used in Dokka. */ .platform-hinted[data-togglable="Type hierarchy"] > .content:not([data-active]), .tabs-section-body > *[data-togglable="Type hierarchy"]:not([data-active]) { @@ -480,7 +435,7 @@ footer .pull-right { .documentableElement { width: 100%; color: var(--symbol-fg); - white-space: normal; + white-space: normal; position: relative; margin: 0; padding: 5px 4px 5px 4px; @@ -578,12 +533,6 @@ footer .pull-right { content: "\e900"; /* clear icon */ } /* --- */ - - .search-content { - margin: 0 !important; - top: 9px !important; - right: 12px !important; - } .cover h1 { margin-top: 0; } diff --git a/scala3doc/src/dotty/dokka/preprocessors/ScalaEmbeddedResourceApppender.scala b/scala3doc/src/dotty/dokka/preprocessors/ScalaEmbeddedResourceApppender.scala index 865df4c64abe..5bc2dc891608 100644 --- a/scala3doc/src/dotty/dokka/preprocessors/ScalaEmbeddedResourceApppender.scala +++ b/scala3doc/src/dotty/dokka/preprocessors/ScalaEmbeddedResourceApppender.scala @@ -18,9 +18,8 @@ class ScalaEmbeddedResourceAppender extends PageTransformer { "styles/scalastyle.css", "styles/dotty-icons.css", "styles/diagram.css", - "hljs/highlight.pack.js", - "scripts/hljs-scala3.js", - "scripts/ux.js", + "styles/filter-bar.css", + "styles/search-bar.css", "https://code.jquery.com/jquery-3.5.1.min.js", "https://d3js.org/d3.v6.min.js", "https://cdn.jsdelivr.net/npm/graphlib-dot@0.6.2/dist/graphlib-dot.min.js", From 9e4a4974aef892ba7a3b714f8d7b427a521d52d1 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Mon, 2 Nov 2020 18:21:02 +0100 Subject: [PATCH 16/19] Remove trailing whitespaces I run 'git diff master --stat --name-only | xargs -I {} sed -i 's/[[:space:]]*$//g' {}' with some manual tweaks --- scala3doc/README.md | 2 +- scala3doc/resources/dotty_res/scripts/diagram.js | 2 +- .../src/dotty/dokka/model/api/internalExtensions.scala | 6 +++--- scala3doc/src/dotty/dokka/model/scalaModel.scala | 6 +++--- scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala | 10 +++++----- .../ImplicitMembersExtensionTransformer.scala | 4 ++-- .../InheritanceInformationTransformer.scala | 8 ++++---- .../src/dotty/dokka/translators/ScalaPageCreator.scala | 6 +++--- scala3doc/src/dotty/renderers/DotDiagramBuilder.scala | 4 ++-- scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala | 6 +++--- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/scala3doc/README.md b/scala3doc/README.md index 5fd82ce1cfa3..c24c67c95fc8 100644 --- a/scala3doc/README.md +++ b/scala3doc/README.md @@ -47,7 +47,7 @@ CLI command for running our tool is in form: `sbt main -n -o -t - ``: classpath that was used to generate tasty files - ``: links to source files of module that are used to link symbols on pages to their source file. They need to be supplied in form: `local_dir=remote_dir#line_suffix` e.g. `src/main/scala=https://github.com/lampepfl/scala3doc/tree/master/src/main/scala#L` - - ``: directory of static documentation that you would like to render with API documentation. This feature is provided by dokka-site plugin: + - ``: directory of static documentation that you would like to render with API documentation. This feature is provided by dokka-site plugin: - [GitHub](https://github.com/VirtusLab/dokka-site) - [Documentation](https://virtuslab.github.io/dokka-site/index.html) diff --git a/scala3doc/resources/dotty_res/scripts/diagram.js b/scala3doc/resources/dotty_res/scripts/diagram.js index 2286a5048fff..d60bf403f656 100644 --- a/scala3doc/resources/dotty_res/scripts/diagram.js +++ b/scala3doc/resources/dotty_res/scripts/diagram.js @@ -16,7 +16,7 @@ $("#inheritance-diagram").ready(function() { var g = graphlibDot.read(dotNode.text); g.graph().rankDir = 'BT'; g.nodes().forEach(function (v) { - g.setNode(v, { + g.setNode(v, { labelType: "html", label: g.node(v).label, style: g.node(v).style diff --git a/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala b/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala index 45e2a8336ba1..cf53fc2a1a27 100644 --- a/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala +++ b/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala @@ -30,7 +30,7 @@ private [model] case class MemberExtension( modifiers: Seq[dotty.dokka.model.api.Modifier], kind: Kind, val annotations: List[Annotation], - signature: Signature, + signature: Signature, origin: Origin = Origin.DefinedWithin, graph: HierarchyGraph = HierarchyGraph.empty, ) extends ExtraProperty[Documentable]: @@ -94,8 +94,8 @@ extension (member: Member): val oldExt = MemberExtension.getFrom(member).getOrElse(MemberExtension.empty) val newExt = oldExt.copy(graph = oldExt.graph ++ edges) putInMember(newExt) - - def updateRecusivly(op: Member => Member) = op(member).withMembers(member.allMembers.map(op)) + + def updateRecusivly(op: Member => Member) = op(member).withMembers(member.allMembers.map(op)) extension (bound: Bound): def asSignature: Signature = bound match diff --git a/scala3doc/src/dotty/dokka/model/scalaModel.scala b/scala3doc/src/dotty/dokka/model/scalaModel.scala index d883470e84c6..329768236bc1 100644 --- a/scala3doc/src/dotty/dokka/model/scalaModel.scala +++ b/scala3doc/src/dotty/dokka/model/scalaModel.scala @@ -59,9 +59,9 @@ object ScalaTagWrapper { case class ImplicitConversion(conversion: Documentable, from: DRI, to: DRI) case class HierarchyGraphContentNode( - val diagram: HierarchyGraph, - val dci: DCI, - val sourceSets: Set[DisplaySourceSet], + val diagram: HierarchyGraph, + val dci: DCI, + val sourceSets: Set[DisplaySourceSet], val style: Set[Style], val extra: PropertyContainer[ContentNode] = PropertyContainer.Companion.empty ) extends ContentNode: diff --git a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala index 28e2e599c351..fbad85e22c80 100644 --- a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala @@ -33,7 +33,7 @@ trait ClassLikeSupport: name: String = classDef.name, signatureOnly: Boolean = false, modifiers: Seq[Modifier] = classDef.symbol.getExtraModifiers(), - ): DClass = + ): DClass = // This Try is here because of problem that code compiles, but at runtime fails claiming // 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') @@ -47,7 +47,7 @@ trait ClassLikeSupport: val symbol = if tree.symbol.isClassConstructor then tree.symbol.owner else tree.symbol val superLink = LinkToType(tree.dokkaType.asSignature, symbol.dri, kindForClasslike(symbol)) Seq(link -> superLink) ++ getSupertypesGraph(tree.asInstanceOf[ClassDef], superLink) - } + } ) val supertypes = getSupertypes(classDef).map { @@ -60,9 +60,9 @@ trait ClassLikeSupport: .plus(ClasslikeExtension(classDef.getConstructorMethod, classDef.getCompanion)) .plus(MemberExtension( classDef.symbol.getVisibility(), - modifiers, - kindForClasslike( classDef.symbol), - classDef.symbol.getAnnotations(), + modifiers, + kindForClasslike( classDef.symbol), + classDef.symbol.getAnnotations(), selfSiangture, graph = graph )) diff --git a/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala b/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala index 77a982869110..f5469b40845a 100644 --- a/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala +++ b/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala @@ -25,10 +25,10 @@ class ImplicitMembersExtensionTransformer(ctx: DokkaContext) extends Documentabl val parentCompanions = allParents.flatMap { case cls: DClasslike => ClasslikeExtension.getFrom(cls).flatMap(_.companion).map(classlikeMap) case _ => None - } + } // TODO (#220): We can expand this on generic etc - val implictSources = outerMembers ++ companion.toSeq ++ parentCompanions + val implictSources = outerMembers ++ companion.toSeq ++ parentCompanions val applicableDRIs = c.parents.map(_.dri).toSet + c.dri diff --git a/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala b/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala index 94611fb76322..dc18e10eb235 100644 --- a/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala +++ b/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala @@ -14,13 +14,13 @@ import dotty.dokka.model.api._ class InheritanceInformationTransformer(val ctx: DokkaContext) extends DocumentableTransformer: override def invoke(original: DModule, context: DokkaContext): DModule = val subtypes = getSupertypes(original).groupBy(_._1).transform((k, v) => v.map(_._2)) - original.updateMembers { m => + original.updateMembers { m => val st: Seq[LinkToType] = subtypes.getOrElse(m.dri, Nil) m.withKnownChildren(st).withNewGraphEdges(st.map(_ -> m.asLink)) } - private def getSupertypes(d: Documentable): Seq[(DRI, LinkToType)] = d match + private def getSupertypes(d: Documentable): Seq[(DRI, LinkToType)] = d match case m: DModule => m.getPackages.asScala.toList.flatMap(p => getSupertypes(p)) - case c: Member => - val selfMapping = if !c.kind.isInstanceOf[Classlike] then Nil else c.parents.map(_._2 -> c.asLink) + case c: Member => + val selfMapping = if !c.kind.isInstanceOf[Classlike] then Nil else c.parents.map(_._2 -> c.asLink) c.allMembers.flatMap(getSupertypes) ++ selfMapping diff --git a/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala b/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala index 3f0e3ba7522f..b713cf998b95 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala @@ -429,14 +429,14 @@ class ScalaPageCreator( _.list(subtypes.toList, separator="")(contentForTypeLink) } } - + graph.fold(withSubtypes) { graph => if graph.edges.isEmpty then withSubtypes else withSubtypes.header(2, "Type hierarchy")().group( kind = ContentKind.Comment, - styles = Set(ContentStyle.WithExtraAttributes), + styles = Set(ContentStyle.WithExtraAttributes), extra = PropertyContainer.Companion.empty plus SimpleAttr.Companion.header("Type hierarchy") - ) { _.group(kind = ContentKind.Symbol, styles = Set(TextStyle.Monospace)) { + ) { _.group(kind = ContentKind.Symbol, styles = Set(TextStyle.Monospace)) { _.dotDiagram(graph) } } diff --git a/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala b/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala index 30225da29ffc..484838132377 100644 --- a/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala +++ b/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala @@ -10,7 +10,7 @@ import HTML._ import dotty.dokka.model.api._ object DotDiagramBuilder: - def build(diagram: HierarchyGraph, renderer: SignatureRenderer): String = + def build(diagram: HierarchyGraph, renderer: SignatureRenderer): String = val vWithId = diagram.verteciesWithId val vertecies = vWithId.map { (vertex, id) => s"""node${id} [label="${getHtmlLabel(vertex, renderer)}", style="${getStyle(vertex)}"];\n""" @@ -28,7 +28,7 @@ object DotDiagramBuilder: |""".stripMargin - private def getStyle(vertex: LinkToType) = vertex.kind match + private def getStyle(vertex: LinkToType) = vertex.kind match case Kind.Class => "fill: #45AD7D;" case Kind.Object => "fill: #285577;" case Kind.Trait => "fill: #1CAACF;" diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index 76f0f9c0b523..dcfbb0b6e322 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -26,7 +26,7 @@ class SignatureRenderer(pageContext: ContentPage, sourceSetRestriciton: JSet[Dis link(dri) match case Some(link) => a(href := link, modifiers)(name) case _ => span(Attr("data-unresolved-link") := dri.toString, modifiers)(name) - + def renderElementWith(e: String | (String, DRI) | Link, modifiers: AppliedAttr*) = e match case (name, dri) => renderLink(name, dri, modifiers:_*) case name: String => raw(name) @@ -63,7 +63,7 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) { node match { case n: HtmlContentNode => withHtml(f, raw(n.body).toString) case n: HierarchyGraphContentNode => buildDiagram(f, n.diagram, pageContext) - case n: DocumentableList => + case n: DocumentableList => val ss = if sourceSetRestriciton == null then Set.empty.asJava else sourceSetRestriciton withHtml(f, buildDocumentableList(n, pageContext, ss).toString()) case n: DocumentableFilter => withHtml(f, buildDocumentableFilter.toString) @@ -202,7 +202,7 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) { }) } - def buildDiagram(f: FlowContent, diagram: HierarchyGraph, pageContext: ContentPage) = + def buildDiagram(f: FlowContent, diagram: HierarchyGraph, pageContext: ContentPage) = val renderer = SignatureRenderer(pageContext, sourceSets, getLocationProvider) withHtml(f, div( id := "inheritance-diagram", cls := "diagram-class")( svg(id := "graph"), From b57c21581f9d76bf10ff3333851e2a57f2c90f64 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Mon, 2 Nov 2020 21:50:44 +0100 Subject: [PATCH 17/19] Replace 4 space idents with 2 spaces --- .../src/example/Inheritance.scala | 20 +- .../src/tests/FilterTest.scala | 220 ++-- .../src/tests/annotations.scala | 4 +- .../src/tests/annotationsExample.scala | 14 +- .../src/tests/companionObjectSignatures.scala | 34 +- .../src/tests/enumSignatures.scala | 26 +- .../src/tests/extensionMethodSignatures.scala | 64 +- .../src/tests/genericMethods.scala | 24 +- .../src/tests/implicitConversions.scala | 76 +- .../src/tests/mergedPackage1.scala | 4 +- .../src/tests/mergedPackage2.scala | 4 +- .../src/tests/mergedPackage3.scala | 8 +- .../src/tests/methodsAndConstructors.scala | 8 +- .../tests/modifiersSignatureTestSource.scala | 22 +- .../src/tests/objectSignatures.scala | 4 +- .../tests/packageObjectSymbolSignatures.scala | 20 +- .../src/tests/packageSymbolSignatures.scala | 8 +- .../src/tests/structuralTypes.scala | 8 +- .../dotty_res/images/dotty-logo-white.svg | 54 +- .../resources/dotty_res/styles/search-bar.css | 14 +- .../ScalaResourceInstaller.scala | 20 +- .../src/dotty/dokka/tasty/TypesSupport.scala | 492 ++++---- .../ImplicitMembersExtensionTransformer.scala | 92 +- .../InheritanceInformationTransformer.scala | 22 +- .../PackageHierarchyTransformer.scala | 104 +- .../ScalaSourceLinksTransformer.scala | 70 +- .../translators/ScalaContentBuilder.scala | 1112 ++++++++--------- .../ScalaDocumentableToPageTranslator.scala | 8 +- .../dokka/translators/ScalaPageCreator.scala | 756 +++++------ .../translators/ScalaSignatureProvider.scala | 338 ++--- .../translators/ScalaSignatureUtils.scala | 180 +-- scala3doc/src/dotty/dokka/utils.scala | 93 +- .../dotty/renderers/DotDiagramBuilder.scala | 66 +- .../dotty/renderers/ScalaHtmlRenderer.scala | 516 ++++---- .../test/dotty/dokka/DottyTestRunner.scala | 190 +-- .../test/dotty/dokka/MultipleFileTest.scala | 148 +-- .../dotty/dokka/renderers/HtmlTagsTest.scala | 376 +++--- 37 files changed, 2609 insertions(+), 2610 deletions(-) diff --git a/scala3doc-testcases/src/example/Inheritance.scala b/scala3doc-testcases/src/example/Inheritance.scala index dad9a2d5c630..95053b9ce752 100644 --- a/scala3doc-testcases/src/example/Inheritance.scala +++ b/scala3doc-testcases/src/example/Inheritance.scala @@ -5,17 +5,17 @@ import example.level2.Documentation abstract class DocumentationInheritance[T, A <: Int, B >: String, -X, +Y] extends Documentation[T, A, B, X, Y] {} class DocumentationInheritanceMethod: - def wierdMethod[T, A <: Int, B >: String](t: T, a: A): B = ??? - def threOtherWay[A <: Nothing, B >: Any](a: A, c: B): Unit = ??? + def wierdMethod[T, A <: Int, B >: String](t: T, a: A): B = ??? + def threOtherWay[A <: Nothing, B >: Any](a: A, c: B): Unit = ??? class A: - def this(s: String) = this() - def this(i: Int) = this() - type I = Int - given Unit = () - extension (u: Unit) def foo = "foo" - object X - class B extends C: - class D extends C + def this(s: String) = this() + def this(i: Int) = this() + type I = Int + given Unit = () + extension (u: Unit) def foo = "foo" + object X + class B extends C: + class D extends C class C extends A diff --git a/scala3doc-testcases/src/tests/FilterTest.scala b/scala3doc-testcases/src/tests/FilterTest.scala index f3eba9e99539..8ad1a67ae13f 100644 --- a/scala3doc-testcases/src/tests/FilterTest.scala +++ b/scala3doc-testcases/src/tests/FilterTest.scala @@ -1,127 +1,127 @@ package tests trait FilterTestBaseTrait: - /** doc */ - protected def protectetDefInheriteTrait(a: Int): String = ??? - /** doc */ - private def privateDefInheritedTrait(a: Int): String = ??? - /** doc */ - def publicDefInheritedTrait(a: Int): String = ??? - - /** doc */ - object PublicObjectInheritedTrait - /** doc */ - protected object ProtectedObjectInheritedTrait + /** doc */ + protected def protectetDefInheriteTrait(a: Int): String = ??? + /** doc */ + private def privateDefInheritedTrait(a: Int): String = ??? + /** doc */ + def publicDefInheritedTrait(a: Int): String = ??? + + /** doc */ + object PublicObjectInheritedTrait + /** doc */ + protected object ProtectedObjectInheritedTrait - /** doc */ - protected val protectetValInheritedTrait = 123 - /** doc */ - private val privateValInheritedTrait = 344 - /** doc */ - val publicValInheritedTrait = 567 + /** doc */ + protected val protectetValInheritedTrait = 123 + /** doc */ + private val privateValInheritedTrait = 344 + /** doc */ + val publicValInheritedTrait = 567 class FilterTestBase: - /** doc */ - sealed abstract class BInherited - /** doc */ - abstract case class CInherited(s: String) - /** doc */ - sealed case class DInherited(c: String) - /** doc */ - final case class EInherited(c: String) - /** doc */ - private class PrivateInherited - /** doc */ - protected class ProtectedInherited - /** doc */ - protected def protectetDefInherited(a: Int): String = ??? - /** doc */ - private def privateDefInherited(a: Int): String = ??? - /** doc */ - def publicDefInherited(a: Int): String = ??? - - /** doc */ - object PublicObjectInherited - /** doc */ - protected object ProtectedObjectInherited + /** doc */ + sealed abstract class BInherited + /** doc */ + abstract case class CInherited(s: String) + /** doc */ + sealed case class DInherited(c: String) + /** doc */ + final case class EInherited(c: String) + /** doc */ + private class PrivateInherited + /** doc */ + protected class ProtectedInherited + /** doc */ + protected def protectetDefInherited(a: Int): String = ??? + /** doc */ + private def privateDefInherited(a: Int): String = ??? + /** doc */ + def publicDefInherited(a: Int): String = ??? + + /** doc */ + object PublicObjectInherited + /** doc */ + protected object ProtectedObjectInherited - /** doc */ - protected val protectetValInherited = 123 - /** doc */ - private val privateValInherited = 344 - /** doc */ - val publicValInherited = 567 - - /** doc */ - protected type protectedTypeInherited = 123 - /** doc */ - private type privateTypeInherited = 344 - /** doc */ - type publicTypeInherited = 567 + /** doc */ + protected val protectetValInherited = 123 + /** doc */ + private val privateValInherited = 344 + /** doc */ + val publicValInherited = 567 - /** doc */ - protected given Set[String | Int] = Set(1, "ala") - /** doc */ - given Map[String, Double] = Map.empty + /** doc */ + protected type protectedTypeInherited = 123 + /** doc */ + private type privateTypeInherited = 344 + /** doc */ + type publicTypeInherited = 567 + + /** doc */ + protected given Set[String | Int] = Set(1, "ala") + /** doc */ + given Map[String, Double] = Map.empty - /** doc */ - protected given namedSet as Set[String | Int] = Set(1, "ala") - /** doc */ - given namedMap as Map[String, Double] = Map.empty + /** doc */ + protected given namedSet as Set[String | Int] = Set(1, "ala") + /** doc */ + given namedMap as Map[String, Double] = Map.empty class FilterTest extends FilterTestBase with FilterTestBaseTrait: - /** doc */ - sealed abstract class B - /** doc */ - abstract case class C(s: String) - /** doc */ - sealed case class D(c: String) - /** doc */ - final case class E(c: String) - /** doc */ - private class Private - /** doc */ - protected class Protected + /** doc */ + sealed abstract class B + /** doc */ + abstract case class C(s: String) + /** doc */ + sealed case class D(c: String) + /** doc */ + final case class E(c: String) + /** doc */ + private class Private + /** doc */ + protected class Protected - /** doc */ - object PublicObject - /** doc */ - protected object ProtectedObject - - /** doc */ - protected def protectetDef(a: B): String = ??? - /** doc */ - private def privateDef(a: C): String = ??? - /** doc */ - def publicDef(a: D): FilterTest = ??? - + /** doc */ + object PublicObject + /** doc */ + protected object ProtectedObject + + /** doc */ + protected def protectetDef(a: B): String = ??? + /** doc */ + private def privateDef(a: C): String = ??? + /** doc */ + def publicDef(a: D): FilterTest = ??? + - /** doc */ - protected val protectetVal = 123 - /** doc */ - private val privateVal= 344 - /** doc */ - val publicVal = 567 - - /** doc */ - protected type protectedType = 123 - /** doc */ - private type privateType= 344 - /** doc */ - type publicType = 567 - - /** doc */ - protected given Seq[String | Int | Double] = List(1) - /** doc */ - given List[String] = "ula" :: Nil - - /** doc */ - given namedList as List[String] = "ula" :: Nil - /** doc */ - protected given namedSeq as Seq[String | Int | Double] = List(1) + /** doc */ + protected val protectetVal = 123 + /** doc */ + private val privateVal= 344 + /** doc */ + val publicVal = 567 + + /** doc */ + protected type protectedType = 123 + /** doc */ + private type privateType= 344 + /** doc */ + type publicType = 567 + + /** doc */ + protected given Seq[String | Int | Double] = List(1) + /** doc */ + given List[String] = "ula" :: Nil + + /** doc */ + given namedList as List[String] = "ula" :: Nil + /** doc */ + protected given namedSeq as Seq[String | Int | Double] = List(1) extension (e: FilterTest): - def extensionMethod(name: FilterTest): FilterTest = ??? + def extensionMethod(name: FilterTest): FilterTest = ??? extension (e: FilterTestBase): - def extensionMethodBase(name: FilterTest): FilterTest = ??? \ No newline at end of file + def extensionMethodBase(name: FilterTest): FilterTest = ??? \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/annotations.scala b/scala3doc-testcases/src/tests/annotations.scala index 6cbc58da107e..07c110a56dc6 100644 --- a/scala3doc-testcases/src/tests/annotations.scala +++ b/scala3doc-testcases/src/tests/annotations.scala @@ -23,6 +23,6 @@ class AnnotatedParams(@MyAnnotation val a: String, @AnnotationWithMultiArg(2, "c class AnnotatedMethods { - @MyAnnotation @AnnotationWithMultiArg(2, "cda", 'a', 'b', 'c') def a: String - = ??? + @MyAnnotation @AnnotationWithMultiArg(2, "cda", 'a', 'b', 'c') def a: String + = ??? } \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/annotationsExample.scala b/scala3doc-testcases/src/tests/annotationsExample.scala index 04055c5c840d..6549f210049c 100644 --- a/scala3doc-testcases/src/tests/annotationsExample.scala +++ b/scala3doc-testcases/src/tests/annotationsExample.scala @@ -4,9 +4,9 @@ package annotationsExample import scala.annotation.StaticAnnotation enum Enum { - case A extends Enum - case B extends Enum - case C extends Enum + case A extends Enum + case B extends Enum + case C extends Enum } class SomeObject(val s: String) @@ -28,8 +28,8 @@ class AnnotatedParams(@MyAnnotation val a: String, @AnnotationWithMultiArg(2, "c class AnnotatedMethods { - @MyAnnotation - @AnnotationWithMultiArg(2, "cda", 'a', 'b', 'c') - def a: String - = ??? + @MyAnnotation + @AnnotationWithMultiArg(2, "cda", 'a', 'b', 'c') + def a: String + = ??? } \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/companionObjectSignatures.scala b/scala3doc-testcases/src/tests/companionObjectSignatures.scala index 6e58794108ec..1a5e8e3283fb 100644 --- a/scala3doc-testcases/src/tests/companionObjectSignatures.scala +++ b/scala3doc-testcases/src/tests/companionObjectSignatures.scala @@ -3,27 +3,27 @@ package companionObjectSignatures case class CaseClass(parameterOfClassConstructor: String) { - val classProperty1: String - = ??? - val classProperty2: String - = ??? - def methodInClass1(): String - = ??? - - def methodInClass2(): CaseClass - = ??? + val classProperty1: String + = ??? + val classProperty2: String + = ??? + def methodInClass1(): String + = ??? + + def methodInClass2(): CaseClass + = ??? } case object CaseClass { - val parameterOfObject: String - = "asd" - - def methodInCompanion1(): String - = ??? - - def methodInCompanion2(): CaseClass - = ??? + val parameterOfObject: String + = "asd" + + def methodInCompanion1(): String + = ??? + + def methodInCompanion2(): CaseClass + = ??? } case class WithoutExplicitCompanion(parameter: Int) diff --git a/scala3doc-testcases/src/tests/enumSignatures.scala b/scala3doc-testcases/src/tests/enumSignatures.scala index 131d357ede96..ce3be508397b 100644 --- a/scala3doc-testcases/src/tests/enumSignatures.scala +++ b/scala3doc-testcases/src/tests/enumSignatures.scala @@ -4,24 +4,24 @@ package enumSignatures enum Enum1 { - case A - case B - case C + case A + case B + case C } enum Enum2(val i: Int): - case A(val s: String) extends Enum2(1) - case B(val t: String) extends Enum2(2) - case C(val u: String) extends Enum2(3) + case A(val s: String) extends Enum2(1) + case B(val t: String) extends Enum2(2) + case C(val u: String) extends Enum2(3) enum Enum3(val param: Int): - case A extends Enum3(1) with A - case B extends Enum3(2) - case C extends Enum3(3) + case A extends Enum3(1) with A + case B extends Enum3(2) + case C extends Enum3(3) enum Enum4[+T]: - case G(s: String) - case B extends Enum4[Int] with A - case C[V](s:String) extends Enum4[V] - case D[T](s: String) extends Enum4[T] + case G(s: String) + case B extends Enum4[Int] with A + case C[V](s:String) extends Enum4[V] + case D[T](s: String) extends Enum4[T] trait A \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/extensionMethodSignatures.scala b/scala3doc-testcases/src/tests/extensionMethodSignatures.scala index 58d374670d05..a8fd3033ea00 100644 --- a/scala3doc-testcases/src/tests/extensionMethodSignatures.scala +++ b/scala3doc-testcases/src/tests/extensionMethodSignatures.scala @@ -3,38 +3,38 @@ package extensionMethodSignatures class ClassOne { - // Commented cases won't work for now - // extension ClassTwoOps on (c: ClassTwo): - // def getA() = c.a - extension (c: ClassTwo) - def getB(): String - = c.b - - extension (c: ClassTwo) - def getGivenParams(a: Int, b: Int, d: Int)(e: String): Int - = 56 - - extension (c: ClassTwo) - def |||:(a: Int, b: Int, d: Int)(e: String): Int - = 56 - - extension (b: Int) - def secondGroup(): String - = ??? - - extension (c:ClassTwo) - : - def getString(a: String): String - = a - - def getInt(): Int - = 5 - - extension (s: String) - def someMethod(): String - = ??? - def otherMethod(a: Int): Int - = ??? + // Commented cases won't work for now + // extension ClassTwoOps on (c: ClassTwo): + // def getA() = c.a + extension (c: ClassTwo) + def getB(): String + = c.b + + extension (c: ClassTwo) + def getGivenParams(a: Int, b: Int, d: Int)(e: String): Int + = 56 + + extension (c: ClassTwo) + def |||:(a: Int, b: Int, d: Int)(e: String): Int + = 56 + + extension (b: Int) + def secondGroup(): String + = ??? + + extension (c:ClassTwo) + : + def getString(a: String): String + = a + + def getInt(): Int + = 5 + + extension (s: String) + def someMethod(): String + = ??? + def otherMethod(a: Int): Int + = ??? } case class ClassTwo(a: String, b: String) diff --git a/scala3doc-testcases/src/tests/genericMethods.scala b/scala3doc-testcases/src/tests/genericMethods.scala index 3ccadbcecc18..3ee78a29da70 100644 --- a/scala3doc-testcases/src/tests/genericMethods.scala +++ b/scala3doc-testcases/src/tests/genericMethods.scala @@ -1,16 +1,16 @@ package tests.genericMethods class Types: - def orTypes(base: Int | String, nested: List[Int | Double] | Seq[String]): Unit - = ??? - def andTypes(base: Int & String, nested: List[Int & Double] & Seq[String]): Unit - = ??? - - def mixedAndAndOr(base: Int & String, nested: List[Int | Double] & Seq[String]): Unit - = ??? + def orTypes(base: Int | String, nested: List[Int | Double] | Seq[String]): Unit + = ??? + def andTypes(base: Int & String, nested: List[Int & Double] & Seq[String]): Unit + = ??? + + def mixedAndAndOr(base: Int & String, nested: List[Int | Double] & Seq[String]): Unit + = ??? - def literal(i: 1, d: 3.3, c: 'c'): 34 - = 34 - - def byName(a: => Int, b: => String | Int): Unit - = ??? \ No newline at end of file + def literal(i: 1, d: 3.3, c: 'c'): 34 + = 34 + + def byName(a: => Int, b: => String | Int): Unit + = ??? \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/implicitConversions.scala b/scala3doc-testcases/src/tests/implicitConversions.scala index 35da6a67a65f..899fab330e79 100644 --- a/scala3doc-testcases/src/tests/implicitConversions.scala +++ b/scala3doc-testcases/src/tests/implicitConversions.scala @@ -3,71 +3,71 @@ package tests package implicitConversions given Conversion[A, B] { - def apply(a: A): B = ??? + def apply(a: A): B = ??? } extension (a: A) def extended_bar(): String = ??? class A { - implicit def conversion(c: C): D = ??? - implicit def conversion: Conversion[C,D] = ??? - implicit val a: Conversion[C,D] = ??? + implicit def conversion(c: C): D = ??? + implicit def conversion: Conversion[C,D] = ??? + implicit val a: Conversion[C,D] = ??? - extension (c: C) def extended_bar(): String = ??? + extension (c: C) def extended_bar(): String = ??? - class C { - def bar: String = ??? - } + class C { + def bar: String = ??? + } - class D extends E() { - def bar2: String = ??? + class D extends E() { + def bar2: String = ??? - val string: String = ??? + val string: String = ??? - class Bar() + class Bar() - type ImplicitType >: String + type ImplicitType >: String - extension (e: E) def extended_bar(): String = ??? - } + extension (e: E) def extended_bar(): String = ??? + } - class E { - def inherited: Int = ??? - } + class E { + def inherited: Int = ??? + } } class B { - def foo: Int = ??? + def foo: Int = ??? - var b: String = ??? + var b: String = ??? } class C { - def extensionInCompanion: String = ??? + def extensionInCompanion: String = ??? } object C { - implicit def companionConversion(c: C): B = ??? + implicit def companionConversion(c: C): B = ??? - extension (c: C) def extensionInCompanion: String = ??? + extension (c: C) def extensionInCompanion: String = ??? } package nested { - extension (opt: Opt[Int]) def sum: Int = ??? - class Opt[A] - - class Lst[A] - object Lst { - extension (lst: Lst[Int]) def sum: Int = ??? - } - - object Wrapper { - class Foo - class Bar { - def bar = "bar" - } - implicit def foobar(foo: Foo): Bar = Bar() + extension (opt: Opt[Int]) def sum: Int = ??? + class Opt[A] + + class Lst[A] + object Lst { + extension (lst: Lst[Int]) def sum: Int = ??? + } + + object Wrapper { + class Foo + class Bar { + def bar = "bar" } + implicit def foobar(foo: Foo): Bar = Bar() + } - class Z + class Z } \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/mergedPackage1.scala b/scala3doc-testcases/src/tests/mergedPackage1.scala index 264936c03565..b82ea779c450 100644 --- a/scala3doc-testcases/src/tests/mergedPackage1.scala +++ b/scala3doc-testcases/src/tests/mergedPackage1.scala @@ -3,7 +3,7 @@ package tests package mergedPackage val propertyFromPackage1: String - = ??? + = ??? def methodFromPackage1(): Int - = ??? \ No newline at end of file + = ??? \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/mergedPackage2.scala b/scala3doc-testcases/src/tests/mergedPackage2.scala index 698278b07bd7..3b3094cadbf3 100644 --- a/scala3doc-testcases/src/tests/mergedPackage2.scala +++ b/scala3doc-testcases/src/tests/mergedPackage2.scala @@ -3,7 +3,7 @@ package tests package mergedPackage val propertyFromPackage2: Int - = ??? + = ??? def methodFromPackage2(): String - = ??? \ No newline at end of file + = ??? \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/mergedPackage3.scala b/scala3doc-testcases/src/tests/mergedPackage3.scala index 61eb99148061..aa983421324b 100644 --- a/scala3doc-testcases/src/tests/mergedPackage3.scala +++ b/scala3doc-testcases/src/tests/mergedPackage3.scala @@ -1,8 +1,8 @@ package tests package object mergedPackage{ - val propertyFromPackageObject: Double - = ??? - def functionFromPackageObject(): String => String - = ??? + val propertyFromPackageObject: Double + = ??? + def functionFromPackageObject(): String => String + = ??? } \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/methodsAndConstructors.scala b/scala3doc-testcases/src/tests/methodsAndConstructors.scala index f30056fa6500..f9d48b88e77a 100644 --- a/scala3doc-testcases/src/tests/methodsAndConstructors.scala +++ b/scala3doc-testcases/src/tests/methodsAndConstructors.scala @@ -7,11 +7,11 @@ class D[T] class E[T] extends D[T] class Constructors(a: String): - def this() - = this("Ala") + def this() + = this("Ala") - def this(a: A)(b: A) - = this("Ala") + def this(a: A)(b: A) + = this("Ala") /** Some methods to tests */ class Methods: diff --git a/scala3doc-testcases/src/tests/modifiersSignatureTestSource.scala b/scala3doc-testcases/src/tests/modifiersSignatureTestSource.scala index 064720a582ad..ac3d87deea82 100644 --- a/scala3doc-testcases/src/tests/modifiersSignatureTestSource.scala +++ b/scala3doc-testcases/src/tests/modifiersSignatureTestSource.scala @@ -13,28 +13,28 @@ sealed class Sealed() open class Open opaque type Opaque - = Open + = Open abstract class Methods() { - def method1(): Unit + def method1(): Unit - inline def inlineMethod(): Unit - = Unit - - implicit def toImplicitString(): String - = "asd" + inline def inlineMethod(): Unit + = Unit + + implicit def toImplicitString(): String + = "asd" } class ImplementedMethods() extends Methods/*<-*/()/*->*/ { - override def method1(): Unit - = ??? + override def method1(): Unit + = ??? } case class ReimplementedMethods() extends ImplementedMethods/*<-*/()/*->*/ { - override def method1(): Unit - = ??? + override def method1(): Unit + = ??? } diff --git a/scala3doc-testcases/src/tests/objectSignatures.scala b/scala3doc-testcases/src/tests/objectSignatures.scala index 3ec55e1dffba..b40e50ab4cb3 100644 --- a/scala3doc-testcases/src/tests/objectSignatures.scala +++ b/scala3doc-testcases/src/tests/objectSignatures.scala @@ -3,8 +3,8 @@ package objectSignatures class A[T] { - val a: String = "asd" - def method3() = "asd" + val a: String = "asd" + def method3() = "asd" } object A diff --git a/scala3doc-testcases/src/tests/packageObjectSymbolSignatures.scala b/scala3doc-testcases/src/tests/packageObjectSymbolSignatures.scala index 9151ca3de508..7f517b299b65 100644 --- a/scala3doc-testcases/src/tests/packageObjectSymbolSignatures.scala +++ b/scala3doc-testcases/src/tests/packageObjectSymbolSignatures.scala @@ -2,17 +2,17 @@ package tests package object packageObjectSymbolSignatures { - val property1: String - = ??? - - type MyType = String + val property1: String + = ??? + + type MyType = String - val property2: Int - = ??? + val property2: Int + = ??? - def method1(): Int - = ??? + def method1(): Int + = ??? - def method2(): String - = ??? + def method2(): String + = ??? } \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/packageSymbolSignatures.scala b/scala3doc-testcases/src/tests/packageSymbolSignatures.scala index 13ec6149c88e..ebf9a227fdad 100644 --- a/scala3doc-testcases/src/tests/packageSymbolSignatures.scala +++ b/scala3doc-testcases/src/tests/packageSymbolSignatures.scala @@ -3,16 +3,16 @@ package tests package packageSymbolSignatures val packageSymbol1: String - = ??? + = ??? val packageSymbol2: Int - = ??? + = ??? def packageMethod1: String - = ??? + = ??? def packageMethod2: Int - = ??? + = ??? type PackageInteger = Int diff --git a/scala3doc-testcases/src/tests/structuralTypes.scala b/scala3doc-testcases/src/tests/structuralTypes.scala index 8f9a1a9ecf11..bf1bded511df 100644 --- a/scala3doc-testcases/src/tests/structuralTypes.scala +++ b/scala3doc-testcases/src/tests/structuralTypes.scala @@ -7,8 +7,8 @@ type Person = Record { val name: String; val age: Int; type Height = Int; def st type R = { type T; val x: Int; type U <: this.T; def foo(): Int; } class Record(elems: (String, Any)*) extends Selectable { - val fields: Map[String, Any] - = elems.toMap - def selectDynamic(name: String): Any - = fields(name) + val fields: Map[String, Any] + = elems.toMap + def selectDynamic(name: String): Any + = fields(name) } \ No newline at end of file diff --git a/scala3doc/resources/dotty_res/images/dotty-logo-white.svg b/scala3doc/resources/dotty_res/images/dotty-logo-white.svg index 8fd33e25e3aa..44c8b71850ce 100644 --- a/scala3doc/resources/dotty_res/images/dotty-logo-white.svg +++ b/scala3doc/resources/dotty_res/images/dotty-logo-white.svg @@ -1,30 +1,30 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scala3doc/resources/dotty_res/styles/search-bar.css b/scala3doc/resources/dotty_res/styles/search-bar.css index f8a58567cb25..3321054a3b03 100644 --- a/scala3doc/resources/dotty_res/styles/search-bar.css +++ b/scala3doc/resources/dotty_res/styles/search-bar.css @@ -11,13 +11,13 @@ /* button */ .search button { - background: #ED3522; - fill: #fff; - cursor: pointer; - border: none; - padding: 9px; - border-radius: 24px; - box-shadow: 0 0 16px #F27264; + background: #ED3522; + fill: #fff; + cursor: pointer; + border: none; + padding: 9px; + border-radius: 24px; + box-shadow: 0 0 16px #F27264; } .search button:hover { fill: #F27264; diff --git a/scala3doc/src/dotty/dokka/preprocessors/ScalaResourceInstaller.scala b/scala3doc/src/dotty/dokka/preprocessors/ScalaResourceInstaller.scala index 47fe63b5fb78..7d55a5972f30 100644 --- a/scala3doc/src/dotty/dokka/preprocessors/ScalaResourceInstaller.scala +++ b/scala3doc/src/dotty/dokka/preprocessors/ScalaResourceInstaller.scala @@ -7,16 +7,16 @@ import com.fasterxml.jackson.databind.ObjectMapper import dotty.dokka.translators.FilterAttributes class ScalaResourceInstaller extends PageTransformer: - private def dottyRes(resourceName: String) = - new RendererSpecificResourcePage(resourceName, java.util.ArrayList(), RenderingStrategy$Copy(s"/dotty_res/$resourceName")) + private def dottyRes(resourceName: String) = + new RendererSpecificResourcePage(resourceName, java.util.ArrayList(), RenderingStrategy$Copy(s"/dotty_res/$resourceName")) - override def invoke(input: RootPageNode): RootPageNode = - val newResources = input.getChildren.asScala ++ Seq("fonts", "images", "styles", "scripts", "hljs").map(dottyRes) ++ Seq(dynamicJsData) - input.modified(input.getName, newResources.asJava) + override def invoke(input: RootPageNode): RootPageNode = + val newResources = input.getChildren.asScala ++ Seq("fonts", "images", "styles", "scripts", "hljs").map(dottyRes) ++ Seq(dynamicJsData) + input.modified(input.getName, newResources.asJava) - private def dynamicJsData = - // If data at any point will become more complex we should use a proper - val data: Map[String, Map[String, String]] = Map("filterDefaults" -> FilterAttributes.defaultValues) - val str = new ObjectMapper().writeValueAsString(data.transform((_, v) => v.asJava).asJava) + private def dynamicJsData = + // If data at any point will become more complex we should use a proper + val data: Map[String, Map[String, String]] = Map("filterDefaults" -> FilterAttributes.defaultValues) + val str = new ObjectMapper().writeValueAsString(data.transform((_, v) => v.asJava).asJava) - new RendererSpecificResourcePage("scripts/data.js", java.util.ArrayList(), RenderingStrategy$Write(s"var scala3DocData = $str")) + new RendererSpecificResourcePage("scripts/data.js", java.util.ArrayList(), RenderingStrategy$Write(s"var scala3DocData = $str")) diff --git a/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala b/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala index 1db1cde5a6ab..5ed2841283ca 100644 --- a/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala @@ -5,256 +5,256 @@ import org.jetbrains.dokka.model.{Projection => JProjection} import collection.JavaConverters._ trait TypesSupport: - self: TastyParser => - import qctx.reflect._ - - def getGivenInstance(method: DefDef): Option[Bound] = { - def extractTypeSymbol(t: Tree): Option[Symbol] = t match - case tpeTree: TypeTree => - inner(tpeTree.tpe) - case other => None - - def inner(tpe: TypeRepr): Option[Symbol] = tpe match - case ThisType(tpe) => inner(tpe) - case AnnotatedType(tpe, _) => inner(tpe) - case AppliedType(tpe, _) => inner(tpe) - case tp @ TermRef(qual, typeName) => - qual match - case _: TypeRepr | _: NoPrefix => Some(tp.termSymbol) - case other => None - case tp @ TypeRef(qual, typeName) => - qual match - case _: TypeRepr | _: NoPrefix => Some(tp.typeSymbol) - case other => None - - val typeSymbol = extractTypeSymbol(method.returnTpt) - - typeSymbol.map(_.tree).collect { - case c: ClassDef => c.getParents.headOption - case _ => Some(method.returnTpt) - }.flatten.map(_.dokkaType) - } - - given TreeSyntax as AnyRef: - extension (tpeTree: Tree): - def dokkaType(using cxt: Context): Bound = - val data = tpeTree match - case TypeBoundsTree(low, high) => typeBound(low.tpe, low = true) ++ typeBound(high.tpe, low = false) - case tpeTree: TypeTree => inner(tpeTree.tpe) - case term: Term => inner(term.tpe) - - new GenericTypeConstructor(tpeTree.symbol.dri, data.asJava, null) + self: TastyParser => + import qctx.reflect._ + + def getGivenInstance(method: DefDef): Option[Bound] = { + def extractTypeSymbol(t: Tree): Option[Symbol] = t match + case tpeTree: TypeTree => + inner(tpeTree.tpe) + case other => None + + def inner(tpe: TypeRepr): Option[Symbol] = tpe match + case ThisType(tpe) => inner(tpe) + case AnnotatedType(tpe, _) => inner(tpe) + case AppliedType(tpe, _) => inner(tpe) + case tp @ TermRef(qual, typeName) => + qual match + case _: TypeRepr | _: NoPrefix => Some(tp.termSymbol) + case other => None + case tp @ TypeRef(qual, typeName) => + qual match + case _: TypeRepr | _: NoPrefix => Some(tp.typeSymbol) + case other => None + + val typeSymbol = extractTypeSymbol(method.returnTpt) + + typeSymbol.map(_.tree).collect { + case c: ClassDef => c.getParents.headOption + case _ => Some(method.returnTpt) + }.flatten.map(_.dokkaType) + } + + given TreeSyntax as AnyRef: + extension (tpeTree: Tree): + def dokkaType(using cxt: Context): Bound = + val data = tpeTree match + case TypeBoundsTree(low, high) => typeBound(low.tpe, low = true) ++ typeBound(high.tpe, low = false) + case tpeTree: TypeTree => inner(tpeTree.tpe) + case term: Term => inner(term.tpe) + + new GenericTypeConstructor(tpeTree.symbol.dri, data.asJava, null) + + given TypeSyntax as AnyRef: + extension (tpe: TypeRepr): + def dokkaType(using ctx: Context): Bound = + val data = inner(tpe) + val dri = data.collect{ + case o: TypeParameter => o + }.headOption.map(_.getDri).getOrElse(defn.AnyClass.dri) + new GenericTypeConstructor(dri, data.asJava, null) + + private def text(str: String): JProjection = new UnresolvedBound(str) + + private def texts(str: String): List[JProjection] = List(text(str)) + + + private def link(symbol: Symbol)(using cxt: Context): List[JProjection] = { + val suffix = if symbol.isValDef then texts(".type") else Nil + (new TypeParameter(symbol.dri, symbol.name, null)) :: suffix + } + + private def commas(lists: List[List[JProjection]]) = lists match + case List(single) => single + case other => other.reduce((r, e) => r ++ texts(", ") ++ e) + + private def isRepeated(tpeAnnotation: Term) = + // For some reason annotation.tpe.typeSymbol != defn.RepeatedParamClass + // annotation.tpe.typeSymbol prints 'class Repeated' and defn.RepeatedParamClass prints 'class ' + tpeAnnotation.tpe.typeSymbol.toString == "class Repeated" + + // TODO #23 add support for all types signatures that makes sense + private def inner(tp: TypeRepr)(using cxt: Context): List[JProjection] = + def noSupported(name: String): List[JProjection] = + println(s"WARN: Unsupported type: $name: ${tp.show}") + List(text(s"Unsupported[$name]")) + + tp match + case OrType(left, right) => inner(left) ++ texts(" | ") ++ inner(right) + case AndType(left, right) => inner(left) ++ texts(" & ") ++ inner(right) + case ByNameType(tpe) => text("=> ") :: inner(tpe) + case ConstantType(constant) => + texts(constant.value match + case c: Char => s"'$c'" + case other => other.toString + ) + case ThisType(tpe) => inner(tpe) + case AnnotatedType(AppliedType(_, Seq(tpe)), annotation) if isRepeated(annotation) => + inner(tpe) :+ text("*") + case AnnotatedType(tpe, _) => + inner(tpe) + case tl @ TypeLambda(params, paramBounds, resType) => + // println(params) + // println(paramBounds) + texts("[") ++ commas(params.zip(paramBounds).map( (name, typ) => texts(s"${name}") ++ inner(typ) )) ++ texts("]") + ++ texts(" =>> ") + ++ inner(resType) + + + case r: Refinement => { //(parent, name, info) + def getRefinementInformation(t: TypeRepr): List[TypeRepr] = t match { + case r: Refinement => getRefinementInformation(r.parent) :+ r + case tr: TypeRef => List(tr) + } - given TypeSyntax as AnyRef: - extension (tpe: TypeRepr): - def dokkaType(using ctx: Context): Bound = - val data = inner(tpe) - val dri = data.collect{ - case o: TypeParameter => o - }.headOption.map(_.getDri).getOrElse(defn.AnyClass.dri) - new GenericTypeConstructor(dri, data.asJava, null) + def getParamBounds(t: PolyType): List[JProjection] = commas( + t.paramNames.zip(t.paramBounds.map(inner(_))) + .map(b => texts(b(0)) ++ b(1)) + ) + + def getParamList(m: MethodType): List[JProjection] = + texts("(") + ++ m.paramNames.zip(m.paramTypes).map{ case (name, tp) => texts(s"$name: ") ++ inner(tp)} + .reduceLeftOption((acc: List[JProjection], elem: List[JProjection]) => acc ++ texts(", ") ++ elem).getOrElse(List()) + ++ texts(")") + + def parseRefinedElem(name: String, info: TypeRepr, polyTyped: List[JProjection] = Nil): List[JProjection] = ( info match { + case m: MethodType => { + val paramList = getParamList(m) + texts(s"def $name") ++ polyTyped ++ paramList ++ texts(": ") ++ inner(m.resType) + } + case t: PolyType => { + val paramBounds = getParamBounds(t) + val parsedMethod = parseRefinedElem(name, t.resType) + if (!paramBounds.isEmpty){ + parseRefinedElem(name, t.resType, texts("[") ++ paramBounds ++ texts("]")) + } else parseRefinedElem(name, t.resType) + } + case ByNameType(tp) => texts(s"def $name: ") ++ inner(tp) + case t: TypeBounds => texts(s"type $name") ++ inner(t) + case t: TypeRef => texts(s"val $name: ") ++ inner(t) + case t: TermRef => texts(s"val $name: ") ++ inner(t) + case other => noSupported(s"Not supported type in refinement $info") + } ) ++ texts("; ") + + def parsePolyFunction(info: TypeRepr): List[JProjection] = info match { + case t: PolyType => + val paramBounds = getParamBounds(t) + val method = t.resType.asInstanceOf[MethodType] + val paramList = getParamList(method) + val resType = inner(method.resType) + texts("[") ++ paramBounds ++ texts("] => ") ++ paramList ++ texts(" => ") ++ resType + case other => noSupported(s"Not supported type in refinement $info") + } + val refinementInfo = getRefinementInformation(r) + val refinedType = refinementInfo.head + val refinedElems = refinementInfo.tail.collect{ case r: Refinement => r }.toList + val prefix = if refinedType.typeSymbol != defn.ObjectClass then inner(refinedType) ++ texts(" ") else List.empty[JProjection] + if (refinedType.typeSymbol.fullName == "scala.PolyFunction" && refinedElems.size == 1) { + parsePolyFunction(refinedElems.head.info) + } else { + prefix ++ texts("{ ") ++ refinedElems.flatMap(e => parseRefinedElem(e.name, e.info)) ++ texts(" }") + } + } + case t @ AppliedType(tpe, typeList) => + import scala.internal.Chars._ + if !t.typeSymbol.name.forall(isIdentifierPart) && typeList.size == 2 then + inner(typeList.head) + ++ texts(" ") + ++ inner(tpe) + ++ texts(" ") + ++ inner(typeList.last) + else if t.isFunctionType then + typeList match + case Nil => + Nil + case Seq(rtpe) => + text("() => ") :: inner(rtpe) + case Seq(arg, rtpe) => + inner(arg) ++ texts(" => ") ++ inner(rtpe) + case args => + texts("(") ++ commas(args.init.map(inner)) ++ texts(") => ") ++ inner(args.last) + else if t.isTupleType then + typeList match + case Nil => + Nil + case args => + texts("(") ++ commas(args.map(inner)) ++ texts(")") + else inner(tpe) ++ texts("[") ++ commas(typeList.map(inner)) ++ texts("]") + + case tp @ TypeRef(qual, typeName) => + qual match { + case r: RecursiveThis => texts(s"this.$typeName") + case _: TypeRepr | _: NoPrefix => link(tp.typeSymbol) + case other => noSupported(s"TypeRepr: $tp") + } + // convertTypeOrBoundsToReference(reflect)(qual) match { + // case TypeReference(label, link, xs, _) => TypeReference(typeName, link + "/" + label, xs, true) + // case EmptyReference => TypeReference(typeName, "", Nil, true) + // case _ if tp.typeSymbol.exists => + // tp.typeSymbol match { + // // NOTE: Only TypeRefs can reference ClassDefSymbols + // case sym if sym.isClassDef => //Need to be split because these types have their own file + // convertTypeOrBoundsToReference(reflect)(qual) match { + // case TypeReference(label, link, xs, _) => TypeReference(sym.name, link + "/" + label, xs, true) + // case EmptyReference if sym.name == "" | sym.name == "_root_" => EmptyReference + // case EmptyReference => TypeReference(sym.name, "", Nil, true) + // case _ => throw Exception("Match error in SymRef/TypeOrBounds/ClassDef. This should not happen, please open an issue. " + convertTypeOrBoundsToReference(reflect)(qual)) + // } + + // // NOTE: This branch handles packages, which are now TypeRefs + // case sym if sym.isTerm || sym.isTypeDef => + // convertTypeOrBoundsToReference(reflect)(qual) match { + // case TypeReference(label, link, xs, _) => TypeReference(sym.name, link + "/" + label, xs) + // case EmptyReference if sym.name == "" | sym.name == "_root_" => EmptyReference + // case EmptyReference => TypeReference(sym.name, "", Nil) + // case _ => throw Exception("Match error in SymRef/TypeOrBounds/Other. This should not happen, please open an issue. " + convertTypeOrBoundsToReference(reflect)(qual)) + // } + // case sym => throw Exception("Match error in SymRef. This should not happen, please open an issue. " + sym) + // } + // case _ => + // throw Exception("Match error in TypeRef. This should not happen, please open an issue. " + convertTypeOrBoundsToReference(reflect)(qual)) + // } + case tr @ TermRef(qual, typeName) => qual match { + case _ => link(tr.termSymbol) + } + // convertTypeOrBoundsToReference(reflect)(qual) match { + // case TypeReference(label, link, xs, _) => TypeReference(typeName + "$", link + "/" + label, xs) + // case EmptyReference => TypeReference(typeName, "", Nil) + // case _ => throw Exception("Match error in TermRef. This should not happen, please open an issue. " + convertTypeOrBoundsToReference(reflect)(qual)) + // } + + // NOTE: old SymRefs are now either TypeRefs or TermRefs - the logic here needs to be moved into above branches + // NOTE: _.symbol on *Ref returns its symbol + // case SymRef(symbol, typeOrBounds) => symbol match { + // } + // case _ => throw Exception("No match for type in conversion to Reference. This should not happen, please open an issue. " + tp) + case TypeBounds(low, hi) => + if(low == hi) texts(" = ") ++ inner(low) + else typeBound(low, low = true) ++ typeBound(hi, low = false) + + case NoPrefix() => Nil + + case MatchType(bond, sc, cases) => + val casesTexts = cases.flatMap { + case MatchTypeCase(from, to) => + texts(" case ") ++ inner(from) ++ texts(" => ") ++ inner(to) ++ texts("\n") + } + inner(sc) ++ texts(" match {\n") ++ casesTexts ++ texts("}") - private def text(str: String): JProjection = new UnresolvedBound(str) + case TypeIdent(t) => texts(t) - private def texts(str: String): List[JProjection] = List(text(str)) + case ParamRef(TypeLambda(names, _, _), i) => texts(names.apply(i)) + case RecursiveType(tp) => inner(tp) - private def link(symbol: Symbol)(using cxt: Context): List[JProjection] = { - val suffix = if symbol.isValDef then texts(".type") else Nil - (new TypeParameter(symbol.dri, symbol.name, null)) :: suffix + private def typeBound(t: TypeRepr, low: Boolean) = + val ignore = if(low) t.typeSymbol == defn.NothingClass else t.typeSymbol == defn.AnyClass + val prefix = text(if low then " >: " else " <: ") + t match { + case l: TypeLambda => prefix :: texts("(") ++ inner(l) ++ texts(")") + case p: ParamRef => prefix :: inner(p) + case other if !ignore => prefix :: inner(other) + case _ => Nil } - private def commas(lists: List[List[JProjection]]) = lists match - case List(single) => single - case other => other.reduce((r, e) => r ++ texts(", ") ++ e) - - private def isRepeated(tpeAnnotation: Term) = - // For some reason annotation.tpe.typeSymbol != defn.RepeatedParamClass - // annotation.tpe.typeSymbol prints 'class Repeated' and defn.RepeatedParamClass prints 'class ' - tpeAnnotation.tpe.typeSymbol.toString == "class Repeated" - - // TODO #23 add support for all types signatures that makes sense - private def inner(tp: TypeRepr)(using cxt: Context): List[JProjection] = - def noSupported(name: String): List[JProjection] = - println(s"WARN: Unsupported type: $name: ${tp.show}") - List(text(s"Unsupported[$name]")) - - tp match - case OrType(left, right) => inner(left) ++ texts(" | ") ++ inner(right) - case AndType(left, right) => inner(left) ++ texts(" & ") ++ inner(right) - case ByNameType(tpe) => text("=> ") :: inner(tpe) - case ConstantType(constant) => - texts(constant.value match - case c: Char => s"'$c'" - case other => other.toString - ) - case ThisType(tpe) => inner(tpe) - case AnnotatedType(AppliedType(_, Seq(tpe)), annotation) if isRepeated(annotation) => - inner(tpe) :+ text("*") - case AnnotatedType(tpe, _) => - inner(tpe) - case tl @ TypeLambda(params, paramBounds, resType) => - // println(params) - // println(paramBounds) - texts("[") ++ commas(params.zip(paramBounds).map( (name, typ) => texts(s"${name}") ++ inner(typ) )) ++ texts("]") - ++ texts(" =>> ") - ++ inner(resType) - - - case r: Refinement => { //(parent, name, info) - def getRefinementInformation(t: TypeRepr): List[TypeRepr] = t match { - case r: Refinement => getRefinementInformation(r.parent) :+ r - case tr: TypeRef => List(tr) - } - - def getParamBounds(t: PolyType): List[JProjection] = commas( - t.paramNames.zip(t.paramBounds.map(inner(_))) - .map(b => texts(b(0)) ++ b(1)) - ) - - def getParamList(m: MethodType): List[JProjection] = - texts("(") - ++ m.paramNames.zip(m.paramTypes).map{ case (name, tp) => texts(s"$name: ") ++ inner(tp)} - .reduceLeftOption((acc: List[JProjection], elem: List[JProjection]) => acc ++ texts(", ") ++ elem).getOrElse(List()) - ++ texts(")") - - def parseRefinedElem(name: String, info: TypeRepr, polyTyped: List[JProjection] = Nil): List[JProjection] = ( info match { - case m: MethodType => { - val paramList = getParamList(m) - texts(s"def $name") ++ polyTyped ++ paramList ++ texts(": ") ++ inner(m.resType) - } - case t: PolyType => { - val paramBounds = getParamBounds(t) - val parsedMethod = parseRefinedElem(name, t.resType) - if (!paramBounds.isEmpty){ - parseRefinedElem(name, t.resType, texts("[") ++ paramBounds ++ texts("]")) - } else parseRefinedElem(name, t.resType) - } - case ByNameType(tp) => texts(s"def $name: ") ++ inner(tp) - case t: TypeBounds => texts(s"type $name") ++ inner(t) - case t: TypeRef => texts(s"val $name: ") ++ inner(t) - case t: TermRef => texts(s"val $name: ") ++ inner(t) - case other => noSupported(s"Not supported type in refinement $info") - } ) ++ texts("; ") - - def parsePolyFunction(info: TypeRepr): List[JProjection] = info match { - case t: PolyType => - val paramBounds = getParamBounds(t) - val method = t.resType.asInstanceOf[MethodType] - val paramList = getParamList(method) - val resType = inner(method.resType) - texts("[") ++ paramBounds ++ texts("] => ") ++ paramList ++ texts(" => ") ++ resType - case other => noSupported(s"Not supported type in refinement $info") - } - val refinementInfo = getRefinementInformation(r) - val refinedType = refinementInfo.head - val refinedElems = refinementInfo.tail.collect{ case r: Refinement => r }.toList - val prefix = if refinedType.typeSymbol != defn.ObjectClass then inner(refinedType) ++ texts(" ") else List.empty[JProjection] - if (refinedType.typeSymbol.fullName == "scala.PolyFunction" && refinedElems.size == 1) { - parsePolyFunction(refinedElems.head.info) - } else { - prefix ++ texts("{ ") ++ refinedElems.flatMap(e => parseRefinedElem(e.name, e.info)) ++ texts(" }") - } - } - case t @ AppliedType(tpe, typeList) => - import scala.internal.Chars._ - if !t.typeSymbol.name.forall(isIdentifierPart) && typeList.size == 2 then - inner(typeList.head) - ++ texts(" ") - ++ inner(tpe) - ++ texts(" ") - ++ inner(typeList.last) - else if t.isFunctionType then - typeList match - case Nil => - Nil - case Seq(rtpe) => - text("() => ") :: inner(rtpe) - case Seq(arg, rtpe) => - inner(arg) ++ texts(" => ") ++ inner(rtpe) - case args => - texts("(") ++ commas(args.init.map(inner)) ++ texts(") => ") ++ inner(args.last) - else if t.isTupleType then - typeList match - case Nil => - Nil - case args => - texts("(") ++ commas(args.map(inner)) ++ texts(")") - else inner(tpe) ++ texts("[") ++ commas(typeList.map(inner)) ++ texts("]") - - case tp @ TypeRef(qual, typeName) => - qual match { - case r: RecursiveThis => texts(s"this.$typeName") - case _: TypeRepr | _: NoPrefix => link(tp.typeSymbol) - case other => noSupported(s"TypeRepr: $tp") - } - // convertTypeOrBoundsToReference(reflect)(qual) match { - // case TypeReference(label, link, xs, _) => TypeReference(typeName, link + "/" + label, xs, true) - // case EmptyReference => TypeReference(typeName, "", Nil, true) - // case _ if tp.typeSymbol.exists => - // tp.typeSymbol match { - // // NOTE: Only TypeRefs can reference ClassDefSymbols - // case sym if sym.isClassDef => //Need to be split because these types have their own file - // convertTypeOrBoundsToReference(reflect)(qual) match { - // case TypeReference(label, link, xs, _) => TypeReference(sym.name, link + "/" + label, xs, true) - // case EmptyReference if sym.name == "" | sym.name == "_root_" => EmptyReference - // case EmptyReference => TypeReference(sym.name, "", Nil, true) - // case _ => throw Exception("Match error in SymRef/TypeOrBounds/ClassDef. This should not happen, please open an issue. " + convertTypeOrBoundsToReference(reflect)(qual)) - // } - - // // NOTE: This branch handles packages, which are now TypeRefs - // case sym if sym.isTerm || sym.isTypeDef => - // convertTypeOrBoundsToReference(reflect)(qual) match { - // case TypeReference(label, link, xs, _) => TypeReference(sym.name, link + "/" + label, xs) - // case EmptyReference if sym.name == "" | sym.name == "_root_" => EmptyReference - // case EmptyReference => TypeReference(sym.name, "", Nil) - // case _ => throw Exception("Match error in SymRef/TypeOrBounds/Other. This should not happen, please open an issue. " + convertTypeOrBoundsToReference(reflect)(qual)) - // } - // case sym => throw Exception("Match error in SymRef. This should not happen, please open an issue. " + sym) - // } - // case _ => - // throw Exception("Match error in TypeRef. This should not happen, please open an issue. " + convertTypeOrBoundsToReference(reflect)(qual)) - // } - case tr @ TermRef(qual, typeName) => qual match { - case _ => link(tr.termSymbol) - } - // convertTypeOrBoundsToReference(reflect)(qual) match { - // case TypeReference(label, link, xs, _) => TypeReference(typeName + "$", link + "/" + label, xs) - // case EmptyReference => TypeReference(typeName, "", Nil) - // case _ => throw Exception("Match error in TermRef. This should not happen, please open an issue. " + convertTypeOrBoundsToReference(reflect)(qual)) - // } - - // NOTE: old SymRefs are now either TypeRefs or TermRefs - the logic here needs to be moved into above branches - // NOTE: _.symbol on *Ref returns its symbol - // case SymRef(symbol, typeOrBounds) => symbol match { - // } - // case _ => throw Exception("No match for type in conversion to Reference. This should not happen, please open an issue. " + tp) - case TypeBounds(low, hi) => - if(low == hi) texts(" = ") ++ inner(low) - else typeBound(low, low = true) ++ typeBound(hi, low = false) - - case NoPrefix() => Nil - - case MatchType(bond, sc, cases) => - val casesTexts = cases.flatMap { - case MatchTypeCase(from, to) => - texts(" case ") ++ inner(from) ++ texts(" => ") ++ inner(to) ++ texts("\n") - } - inner(sc) ++ texts(" match {\n") ++ casesTexts ++ texts("}") - - case TypeIdent(t) => texts(t) - - case ParamRef(TypeLambda(names, _, _), i) => texts(names.apply(i)) - - case RecursiveType(tp) => inner(tp) - - private def typeBound(t: TypeRepr, low: Boolean) = - val ignore = if(low) t.typeSymbol == defn.NothingClass else t.typeSymbol == defn.AnyClass - val prefix = text(if low then " >: " else " <: ") - t match { - case l: TypeLambda => prefix :: texts("(") ++ inner(l) ++ texts(")") - case p: ParamRef => prefix :: inner(p) - case other if !ignore => prefix :: inner(other) - case _ => Nil - } - diff --git a/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala b/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala index f5469b40845a..0100dd041ee0 100644 --- a/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala +++ b/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala @@ -12,49 +12,49 @@ import dotty.dokka.model._ import dotty.dokka.model.api._ class ImplicitMembersExtensionTransformer(ctx: DokkaContext) extends DocumentableTransformer: - override def invoke(original: DModule, context: DokkaContext): DModule = - val classlikeMap = original.driMap - - def expandMember(outerMembers: Seq[Member])(c: Member): Member = - val companion = c match - case classlike: DClass => ClasslikeExtension.getFrom(classlike).flatMap(_.companion).map(classlikeMap) - case _ => None - - val allParents = c.parents.flatMap(p => classlikeMap.get(p.dri)) - - val parentCompanions = allParents.flatMap { - case cls: DClasslike => ClasslikeExtension.getFrom(cls).flatMap(_.companion).map(classlikeMap) - case _ => None - } - - // TODO (#220): We can expand this on generic etc - val implictSources = outerMembers ++ companion.toSeq ++ parentCompanions - - val applicableDRIs = c.parents.map(_.dri).toSet + c.dri - - val MyDri = c.getDri - def collectApplicableMembers(source: Member): Seq[Member] = source.allMembers.flatMap { - case m @ Member(_, _, _, Kind.Extension(ExtensionTarget(_, _, MyDri)), Origin.DefinedWithin) => - Seq(m.withOrigin(Origin.ExtensionFrom(source.name, source.dri)).withKind(Kind.Def)) - case m @ Member(_, _, _, conversionProvider: ImplicitConversionProvider, Origin.DefinedWithin) => - conversionProvider.conversion match - case Some(ImplicitConversion(MyDri, to)) => - classlikeMap.get(to).toSeq.flatMap { owner => - val newMembers = owner.allMembers.filter(_.origin match - case Origin.DefinedWithin => true - case Origin.InheritedFrom(_, _) => true - case _ => false - ) - newMembers.map(_.withOrigin(Origin.ImplicitlyAddedBy(owner.name, owner.dri))) - } - case _ => - Nil - case _ => - None - } - - val newImplicitMembers = implictSources.flatMap(collectApplicableMembers).distinct - val expandedMembers = c.allMembers.map(expandMember(newImplicitMembers ++ Seq(c))) - c.withMembers(newImplicitMembers ++ expandedMembers) - - original.updatePackages(_.map(expandMember(Nil)(_).asInstanceOf[DPackage])) + override def invoke(original: DModule, context: DokkaContext): DModule = + val classlikeMap = original.driMap + + def expandMember(outerMembers: Seq[Member])(c: Member): Member = + val companion = c match + case classlike: DClass => ClasslikeExtension.getFrom(classlike).flatMap(_.companion).map(classlikeMap) + case _ => None + + val allParents = c.parents.flatMap(p => classlikeMap.get(p.dri)) + + val parentCompanions = allParents.flatMap { + case cls: DClasslike => ClasslikeExtension.getFrom(cls).flatMap(_.companion).map(classlikeMap) + case _ => None + } + + // TODO (#220): We can expand this on generic etc + val implictSources = outerMembers ++ companion.toSeq ++ parentCompanions + + val applicableDRIs = c.parents.map(_.dri).toSet + c.dri + + val MyDri = c.getDri + def collectApplicableMembers(source: Member): Seq[Member] = source.allMembers.flatMap { + case m @ Member(_, _, _, Kind.Extension(ExtensionTarget(_, _, MyDri)), Origin.DefinedWithin) => + Seq(m.withOrigin(Origin.ExtensionFrom(source.name, source.dri)).withKind(Kind.Def)) + case m @ Member(_, _, _, conversionProvider: ImplicitConversionProvider, Origin.DefinedWithin) => + conversionProvider.conversion match + case Some(ImplicitConversion(MyDri, to)) => + classlikeMap.get(to).toSeq.flatMap { owner => + val newMembers = owner.allMembers.filter(_.origin match + case Origin.DefinedWithin => true + case Origin.InheritedFrom(_, _) => true + case _ => false + ) + newMembers.map(_.withOrigin(Origin.ImplicitlyAddedBy(owner.name, owner.dri))) + } + case _ => + Nil + case _ => + None + } + + val newImplicitMembers = implictSources.flatMap(collectApplicableMembers).distinct + val expandedMembers = c.allMembers.map(expandMember(newImplicitMembers ++ Seq(c))) + c.withMembers(newImplicitMembers ++ expandedMembers) + + original.updatePackages(_.map(expandMember(Nil)(_).asInstanceOf[DPackage])) diff --git a/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala b/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala index dc18e10eb235..2dd864ad7bc4 100644 --- a/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala +++ b/scala3doc/src/dotty/dokka/transformers/InheritanceInformationTransformer.scala @@ -12,15 +12,15 @@ import dotty.dokka.model.api._ class InheritanceInformationTransformer(val ctx: DokkaContext) extends DocumentableTransformer: - override def invoke(original: DModule, context: DokkaContext): DModule = - val subtypes = getSupertypes(original).groupBy(_._1).transform((k, v) => v.map(_._2)) - original.updateMembers { m => - val st: Seq[LinkToType] = subtypes.getOrElse(m.dri, Nil) - m.withKnownChildren(st).withNewGraphEdges(st.map(_ -> m.asLink)) - } + override def invoke(original: DModule, context: DokkaContext): DModule = + val subtypes = getSupertypes(original).groupBy(_._1).transform((k, v) => v.map(_._2)) + original.updateMembers { m => + val st: Seq[LinkToType] = subtypes.getOrElse(m.dri, Nil) + m.withKnownChildren(st).withNewGraphEdges(st.map(_ -> m.asLink)) + } - private def getSupertypes(d: Documentable): Seq[(DRI, LinkToType)] = d match - case m: DModule => m.getPackages.asScala.toList.flatMap(p => getSupertypes(p)) - case c: Member => - val selfMapping = if !c.kind.isInstanceOf[Classlike] then Nil else c.parents.map(_._2 -> c.asLink) - c.allMembers.flatMap(getSupertypes) ++ selfMapping + private def getSupertypes(d: Documentable): Seq[(DRI, LinkToType)] = d match + case m: DModule => m.getPackages.asScala.toList.flatMap(p => getSupertypes(p)) + case c: Member => + val selfMapping = if !c.kind.isInstanceOf[Classlike] then Nil else c.parents.map(_._2 -> c.asLink) + c.allMembers.flatMap(getSupertypes) ++ selfMapping diff --git a/scala3doc/src/dotty/dokka/transformers/PackageHierarchyTransformer.scala b/scala3doc/src/dotty/dokka/transformers/PackageHierarchyTransformer.scala index 339414aecf20..f5af0caf5aea 100644 --- a/scala3doc/src/dotty/dokka/transformers/PackageHierarchyTransformer.scala +++ b/scala3doc/src/dotty/dokka/transformers/PackageHierarchyTransformer.scala @@ -7,68 +7,68 @@ import collection.JavaConverters._ import org.jetbrains.dokka.plugability.DokkaContext class PackageHierarchyTransformer(context: DokkaContext) extends PageTransformer: - override def invoke(input: RootPageNode): RootPageNode = input match { - case m: ModulePageNode => rearangePackagePages(m) - case other => { - context.getLogger.warn("PackageHierarchyTransformer: Couldn't transform root because root is not ModulePageNode") - other - } + override def invoke(input: RootPageNode): RootPageNode = input match { + case m: ModulePageNode => rearangePackagePages(m) + case other => { + context.getLogger.warn("PackageHierarchyTransformer: Couldn't transform root because root is not ModulePageNode") + other } + } - def rearangePackagePages(page: ModulePageNode): ModulePageNode = { - val (h1, h2) = page.getChildren.asScala.partition{ - case p: PackagePageNode => true - case other => false - } - - val (packagePages, otherPages) = (h1.collect{ case p: PackagePageNode => p}.toSeq, h2.collect{ case q: PageNode => q }.toSeq ) + def rearangePackagePages(page: ModulePageNode): ModulePageNode = { + val (h1, h2) = page.getChildren.asScala.partition{ + case p: PackagePageNode => true + case other => false + } - def isParent(possibleParent: Seq[String], comparedChildren: Seq[String]): Boolean = comparedChildren.startsWith(possibleParent) + val (packagePages, otherPages) = (h1.collect{ case p: PackagePageNode => p}.toSeq, h2.collect{ case q: PageNode => q }.toSeq ) - def getRelativeName(parent: Seq[String], child: Seq[String]): Seq[String] = child.slice(parent.size, child.size) + def isParent(possibleParent: Seq[String], comparedChildren: Seq[String]): Boolean = comparedChildren.startsWith(possibleParent) - def relativePageName(parentName: Seq[String], childName: Seq[String], childPage: PageNode): PageNode = - childPage.modified( - childName.slice(parentName.size, childName.size).mkString("",".",""), - childPage.getChildren - ) + def getRelativeName(parent: Seq[String], child: Seq[String]): Seq[String] = child.slice(parent.size, child.size) - def buildPackageTree( - depth: Int, - remaining: Seq[(Seq[String], PackagePageNode)], - processsed: Seq[(Seq[String], PackagePageNode)] - ): Seq[PackagePageNode] = { - val (currentDepth,rest) = remaining.partition((tokens, page) => tokens.size == depth) - val newProcessed = currentDepth.map( (tokens, page) => { - val newPage = page.modified( - page.getName, - (processsed - .filter((childTokens, child) => isParent(tokens, childTokens)) - .map((childTokens, child) => relativePageName(tokens, childTokens, child)) - ++ page.getChildren.asScala).asJava, - ) - (tokens, newPage) - } - ) - val oldFilteredProcessed = processsed - .filter( (tokens, page) => - currentDepth.forall( (parentTokens, parentPage) => - !isParent(parentTokens, tokens) - ) - ) + def relativePageName(parentName: Seq[String], childName: Seq[String], childPage: PageNode): PageNode = + childPage.modified( + childName.slice(parentName.size, childName.size).mkString("",".",""), + childPage.getChildren + ) - if(depth == 1) (newProcessed ++ oldFilteredProcessed).map(_(1)) - else buildPackageTree(depth - 1, rest, newProcessed ++ oldFilteredProcessed) + def buildPackageTree( + depth: Int, + remaining: Seq[(Seq[String], PackagePageNode)], + processsed: Seq[(Seq[String], PackagePageNode)] + ): Seq[PackagePageNode] = { + val (currentDepth,rest) = remaining.partition((tokens, page) => tokens.size == depth) + val newProcessed = currentDepth.map( (tokens, page) => { + val newPage = page.modified( + page.getName, + (processsed + .filter((childTokens, child) => isParent(tokens, childTokens)) + .map((childTokens, child) => relativePageName(tokens, childTokens, child)) + ++ page.getChildren.asScala).asJava, + ) + (tokens, newPage) } + ) + val oldFilteredProcessed = processsed + .filter( (tokens, page) => + currentDepth.forall( (parentTokens, parentPage) => + !isParent(parentTokens, tokens) + ) + ) - val packagePagesWithTokens = packagePages.map(page => (("""\.""".r.split(page.getName)).toSeq, page)) + if(depth == 1) (newProcessed ++ oldFilteredProcessed).map(_(1)) + else buildPackageTree(depth - 1, rest, newProcessed ++ oldFilteredProcessed) + } - val maxDepthElem = packagePagesWithTokens.maxBy( (tokens, page) => tokens.size ) + val packagePagesWithTokens = packagePages.map(page => (("""\.""".r.split(page.getName)).toSeq, page)) - page.modified( - page.getName, - (otherPages ++ buildPackageTree(maxDepthElem(0).size, packagePagesWithTokens, Seq.empty)).asJava - ) + val maxDepthElem = packagePagesWithTokens.maxBy( (tokens, page) => tokens.size ) + page.modified( + page.getName, + (otherPages ++ buildPackageTree(maxDepthElem(0).size, packagePagesWithTokens, Seq.empty)).asJava + ) - } + + } diff --git a/scala3doc/src/dotty/dokka/transformers/ScalaSourceLinksTransformer.scala b/scala3doc/src/dotty/dokka/transformers/ScalaSourceLinksTransformer.scala index dbde93fdab93..0815ab5b6011 100644 --- a/scala3doc/src/dotty/dokka/transformers/ScalaSourceLinksTransformer.scala +++ b/scala3doc/src/dotty/dokka/transformers/ScalaSourceLinksTransformer.scala @@ -15,47 +15,47 @@ import org.jetbrains.dokka.model.properties._ import dotty.dokka.model.api._ class ScalaSourceLinksTransformer( - val ctx: DokkaContext, - val commentsToContentConverter: CommentsToContentConverter, - val signatureProvider: SignatureProvider, - val logger: DokkaLogger + val ctx: DokkaContext, + val commentsToContentConverter: CommentsToContentConverter, + val signatureProvider: SignatureProvider, + val logger: DokkaLogger ) extends DocumentableTransformer: - val sourceLinks = ctx.getConfiguration.getSourceSets.asScala.flatMap(s => s.getSourceLinks.asScala.map(l => SourceLink(l, s))) - val pageBuilder = ScalaPageContentBuilder(commentsToContentConverter, signatureProvider, logger) + val sourceLinks = ctx.getConfiguration.getSourceSets.asScala.flatMap(s => s.getSourceLinks.asScala.map(l => SourceLink(l, s))) + val pageBuilder = ScalaPageContentBuilder(commentsToContentConverter, signatureProvider, logger) - case class SourceLink(val path: String, val url: String, val lineSuffix: Option[String], val sourceSetData: DokkaConfiguration.DokkaSourceSet) + case class SourceLink(val path: String, val url: String, val lineSuffix: Option[String], val sourceSetData: DokkaConfiguration.DokkaSourceSet) - object SourceLink { - def apply(sourceLinkDef: DokkaConfiguration$SourceLinkDefinition, sourceSetData: DokkaConfiguration.DokkaSourceSet): SourceLink = - SourceLink(sourceLinkDef.getLocalDirectory, sourceLinkDef.getRemoteUrl.toString, Option(sourceLinkDef.getRemoteLineSuffix), sourceSetData) - } - - - override def invoke(input: DModule, context: DokkaContext): DModule = - input.updateMembers { - case c0: (Member & WithSources & WithExtraProperties[_]) => - val c = c0.asInstanceOf[Member & WithSources & WithExtraProperties[Member]] - c.withNewExtras(c.getExtra plus getSourceLinks(c)) - case c => c - } + object SourceLink { + def apply(sourceLinkDef: DokkaConfiguration$SourceLinkDefinition, sourceSetData: DokkaConfiguration.DokkaSourceSet): SourceLink = + SourceLink(sourceLinkDef.getLocalDirectory, sourceLinkDef.getRemoteUrl.toString, Option(sourceLinkDef.getRemoteLineSuffix), sourceSetData) + } - private def getSourceLinks(doc: WithSources): ExtraProperty[Member] = { - val urls = doc.getSources.asScala.toMap.flatMap{ - case (key,value) => sourceLinks.find(s => value.getPath.contains(s.path) && key == s.sourceSetData).map( - link => (key, createLink(value, link)) - ) - }.collect{ - case (key, Some(value)) => (key,value) - }.toMap - - SourceLinks(urls) + override def invoke(input: DModule, context: DokkaContext): DModule = + input.updateMembers { + case c0: (Member & WithSources & WithExtraProperties[_]) => + val c = c0.asInstanceOf[Member & WithSources & WithExtraProperties[Member]] + c.withNewExtras(c.getExtra plus getSourceLinks(c)) + case c => c } - private def createLink(source: DocumentableSource, link: SourceLink): Option[String] = source match { - case s: TastyDocumentableSource => Some(s.lineNumber).map( line => - link.url + s.path.split(link.path)(1) + link.lineSuffix.map(_ + (line + 1)).getOrElse("") //TASTY enumerates lines from 0 + + private def getSourceLinks(doc: WithSources): ExtraProperty[Member] = { + val urls = doc.getSources.asScala.toMap.flatMap{ + case (key,value) => sourceLinks.find(s => value.getPath.contains(s.path) && key == s.sourceSetData).map( + link => (key, createLink(value, link)) ) - case other => None - } + }.collect{ + case (key, Some(value)) => (key,value) + }.toMap + + SourceLinks(urls) + } + + private def createLink(source: DocumentableSource, link: SourceLink): Option[String] = source match { + case s: TastyDocumentableSource => Some(s.lineNumber).map( line => + link.url + s.path.split(link.path)(1) + link.lineSuffix.map(_ + (line + 1)).getOrElse("") //TASTY enumerates lines from 0 + ) + case other => None + } diff --git a/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala b/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala index 688df51f1662..af5bc9a83405 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala @@ -24,572 +24,572 @@ case class DocumentableSubGroup(val title: Signature, val extensions: Seq[Docume case class DocumentableGroup(name: Option[String | Documentable], documenables: Seq[Documentable | DocumentableSubGroup]) class ScalaPageContentBuilder( - val commentsConverter: CommentsToContentConverter, - val signatureProvider: SignatureProvider, - val logger: DokkaLogger + val commentsConverter: CommentsToContentConverter, + val signatureProvider: SignatureProvider, + val logger: DokkaLogger ) { - def contentForDRI( - dri: DRI, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet], - kind: Kind = ContentKind.Main, - styles: Set[Style] = Set(), - extra: PropertyContainer[ContentNode] = PropertyContainer.Companion.empty(), - buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ): ContentGroup = buildBlock( - ScalaDocumentableContentBuilder(Set(dri), sourceSets, kind, styles, extra) - ).buildContent() - - def contentForDRIs( - dris: Set[DRI], - sourceSets: Set[DokkaConfiguration$DokkaSourceSet], - kind: Kind = ContentKind.Main, - styles: Set[Style] = Set(), - extra: PropertyContainer[ContentNode] = PropertyContainer.Companion.empty(), - buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ): ContentGroup = buildBlock( - ScalaDocumentableContentBuilder(dris, sourceSets, kind, styles, extra) - ).buildContent() - - def contentForDocumentable( - d: Documentable, - kind: Kind = ContentKind.Main, - styles: Set[Style] = Set(), - extra: PropertyContainer[ContentNode] = PropertyContainer.Companion.empty(), - buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ): ContentGroup = { - buildBlock( - ScalaDocumentableContentBuilder(Set(d.getDri), d.getSourceSets.asScala.toSet, kind, styles, extra) - ).buildContent() - } - - case class ScalaTableBuilder( - val mainDRI: Set[DRI], - val mainSourcesetData: Set[DokkaConfiguration$DokkaSourceSet], - val mainKind: Kind, - val mainStyles: Set[Style], - val mainExtra: PropertyContainer[ContentNode], - val cells: List[ContentGroup] = List() - ) { - private def addChild(c: ContentGroup) = copy(cells = cells :+ c) - - def cell( - dri: Set[DRI] = mainDRI, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - kind: Kind = mainKind, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra)( - buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ): ScalaTableBuilder = addChild(contentForDRIs(dri, sourceSets, kind, styles, extra, buildBlock)) - - def build() = cells - } - - case class ScalaDivergentBuilder( - val groupID: ContentDivergentGroup.GroupID, - val mainDRI: Set[DRI], - val mainKind: Kind, - val mainStyles: Set[Style], - val mainExtra: PropertyContainer[ContentNode], - val implicitlySourceSetHinted: Boolean, - val instances: List[ContentDivergentInstance] = List() - ) { - private def addChild(c: ContentDivergentInstance) = copy(instances = instances :+ c) - - def buildContent() = ContentDivergentGroup( - instances.asJava, - DCI(mainDRI.asJava, mainKind), - mainStyles.asJava, - mainExtra, - groupID, - implicitlySourceSetHinted + def contentForDRI( + dri: DRI, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet], + kind: Kind = ContentKind.Main, + styles: Set[Style] = Set(), + extra: PropertyContainer[ContentNode] = PropertyContainer.Companion.empty(), + buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ): ContentGroup = buildBlock( + ScalaDocumentableContentBuilder(Set(dri), sourceSets, kind, styles, extra) + ).buildContent() + + def contentForDRIs( + dris: Set[DRI], + sourceSets: Set[DokkaConfiguration$DokkaSourceSet], + kind: Kind = ContentKind.Main, + styles: Set[Style] = Set(), + extra: PropertyContainer[ContentNode] = PropertyContainer.Companion.empty(), + buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ): ContentGroup = buildBlock( + ScalaDocumentableContentBuilder(dris, sourceSets, kind, styles, extra) + ).buildContent() + + def contentForDocumentable( + d: Documentable, + kind: Kind = ContentKind.Main, + styles: Set[Style] = Set(), + extra: PropertyContainer[ContentNode] = PropertyContainer.Companion.empty(), + buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ): ContentGroup = { + buildBlock( + ScalaDocumentableContentBuilder(Set(d.getDri), d.getSourceSets.asScala.toSet, kind, styles, extra) + ).buildContent() + } + + case class ScalaTableBuilder( + val mainDRI: Set[DRI], + val mainSourcesetData: Set[DokkaConfiguration$DokkaSourceSet], + val mainKind: Kind, + val mainStyles: Set[Style], + val mainExtra: PropertyContainer[ContentNode], + val cells: List[ContentGroup] = List() + ) { + private def addChild(c: ContentGroup) = copy(cells = cells :+ c) + + def cell( + dri: Set[DRI] = mainDRI, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + kind: Kind = mainKind, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra)( + buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ): ScalaTableBuilder = addChild(contentForDRIs(dri, sourceSets, kind, styles, extra, buildBlock)) + + def build() = cells + } + + case class ScalaDivergentBuilder( + val groupID: ContentDivergentGroup.GroupID, + val mainDRI: Set[DRI], + val mainKind: Kind, + val mainStyles: Set[Style], + val mainExtra: PropertyContainer[ContentNode], + val implicitlySourceSetHinted: Boolean, + val instances: List[ContentDivergentInstance] = List() + ) { + private def addChild(c: ContentDivergentInstance) = copy(instances = instances :+ c) + + def buildContent() = ContentDivergentGroup( + instances.asJava, + DCI(mainDRI.asJava, mainKind), + mainStyles.asJava, + mainExtra, + groupID, + implicitlySourceSetHinted + ) + + def instance( + dri: Set[DRI], + sourceSets: Set[DokkaConfiguration$DokkaSourceSet], + kind: Kind = mainKind, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + )( + buildBlock: ScalaPageContentBuilder#ScalaDivergentInstanceBuilder => ScalaPageContentBuilder#ScalaDivergentInstanceBuilder + ): ScalaDivergentBuilder = addChild( + buildBlock(ScalaDivergentInstanceBuilder(dri, kind, sourceSets, styles, extra)).buildContent() + ) + } + + case class ScalaDivergentInstanceBuilder( + val mainDRI: Set[DRI], + val mainKind: Kind, + val mainSourcesetData: Set[DokkaConfiguration$DokkaSourceSet], + val mainStyles: Set[Style], + val mainExtra: PropertyContainer[ContentNode], + val before: Option[ContentNode] = None, + val divergent: ContentNode = null, + val after: Option[ContentNode] = None + ) { + def buildContent() = ContentDivergentInstance( + before.getOrElse(null), + if divergent != null then divergent else throw IllegalStateException("Divergent part is mandatory"), + after.getOrElse(null), + DCI(mainDRI.asJava, mainKind), + mainSourcesetData.toDisplay, + mainStyles.asJava, + mainExtra + ) + + def before( + dri: Set[DRI] = mainDRI, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + kind: Kind = mainKind, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + )( + buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ): ScalaDivergentInstanceBuilder = copy( + before = Some(contentForDRIs(dri, sourceSets, kind, styles, extra, buildBlock)) + ) + + def divergent( + dri: Set[DRI] = mainDRI, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + kind: Kind = ContentKind.Main, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + )( + buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ): ScalaDivergentInstanceBuilder = copy( + divergent = contentForDRIs(dri, sourceSets, kind, styles, extra, buildBlock) + ) + + def after( + dri: Set[DRI] = mainDRI, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + kind: Kind = ContentKind.Main, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + )( + buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ): ScalaDivergentInstanceBuilder = copy( + after = Some( + contentForDRIs(dri, sourceSets, kind, styles, extra, buildBlock) + ) + ) + } + + + case class ScalaDocumentableContentBuilder( + val mainDRI: Set[DRI], + val mainSourcesetData: Set[DokkaConfiguration$DokkaSourceSet], + val mainKind: Kind, + val mainStyles: Set[Style], + val mainExtra: PropertyContainer[ContentNode], + val children: List[ContentNode] = List() + ) { + + def addChild(c: ContentNode) = copy(children = children :+ c) + + def addChildren(c: Seq[ContentNode]) = copy(children = children ++ c) + + def reset() = copy(children = Nil) + + def buildContent() = ContentGroup( + children.asJava, + DCI(mainDRI.asJava, mainKind), + mainSourcesetData.toDisplay, + mainStyles.asJava, + mainExtra + ) + + def group( + dri: Set[DRI] = mainDRI, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + kind: Kind = mainKind, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + )( + buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ): ScalaDocumentableContentBuilder = addChild( + contentForDRIs(dri, sourceSets, kind, styles, extra, buildBlock) + ) + + def header( + level: Int, + text: String, + kind: Kind = ContentKind.Main, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + )( + buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder = p => p + ): ScalaDocumentableContentBuilder = addChild( + ContentHeader( + level, + contentForDRIs( + mainDRI, + sourceSets, + kind, + styles, + extra plus SimpleAttr("anchor", "\\s".r.replaceAllIn(text, "").toLowerCase()), + bdr => {buildBlock(bdr.text(text, kind = kind))} + ) ) - - def instance( - dri: Set[DRI], - sourceSets: Set[DokkaConfiguration$DokkaSourceSet], - kind: Kind = mainKind, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - )( - buildBlock: ScalaPageContentBuilder#ScalaDivergentInstanceBuilder => ScalaPageContentBuilder#ScalaDivergentInstanceBuilder - ): ScalaDivergentBuilder = addChild( - buildBlock(ScalaDivergentInstanceBuilder(dri, kind, sourceSets, styles, extra)).buildContent() - ) - } - - case class ScalaDivergentInstanceBuilder( - val mainDRI: Set[DRI], - val mainKind: Kind, - val mainSourcesetData: Set[DokkaConfiguration$DokkaSourceSet], - val mainStyles: Set[Style], - val mainExtra: PropertyContainer[ContentNode], - val before: Option[ContentNode] = None, - val divergent: ContentNode = null, - val after: Option[ContentNode] = None - ) { - def buildContent() = ContentDivergentInstance( - before.getOrElse(null), - if divergent != null then divergent else throw IllegalStateException("Divergent part is mandatory"), - after.getOrElse(null), - DCI(mainDRI.asJava, mainKind), - mainSourcesetData.toDisplay, - mainStyles.asJava, - mainExtra - ) - - def before( - dri: Set[DRI] = mainDRI, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - kind: Kind = mainKind, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - )( - buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ): ScalaDivergentInstanceBuilder = copy( - before = Some(contentForDRIs(dri, sourceSets, kind, styles, extra, buildBlock)) - ) - - def divergent( - dri: Set[DRI] = mainDRI, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - kind: Kind = ContentKind.Main, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - )( - buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ): ScalaDivergentInstanceBuilder = copy( - divergent = contentForDRIs(dri, sourceSets, kind, styles, extra, buildBlock) - ) - - def after( - dri: Set[DRI] = mainDRI, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - kind: Kind = ContentKind.Main, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - )( - buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ): ScalaDivergentInstanceBuilder = copy( - after = Some( - contentForDRIs(dri, sourceSets, kind, styles, extra, buildBlock) - ) - ) - } - - - case class ScalaDocumentableContentBuilder( - val mainDRI: Set[DRI], - val mainSourcesetData: Set[DokkaConfiguration$DokkaSourceSet], - val mainKind: Kind, - val mainStyles: Set[Style], - val mainExtra: PropertyContainer[ContentNode], - val children: List[ContentNode] = List() - ) { - - def addChild(c: ContentNode) = copy(children = children :+ c) - - def addChildren(c: Seq[ContentNode]) = copy(children = children ++ c) - - def reset() = copy(children = Nil) - - def buildContent() = ContentGroup( - children.asJava, - DCI(mainDRI.asJava, mainKind), - mainSourcesetData.toDisplay, - mainStyles.asJava, - mainExtra - ) - - def group( - dri: Set[DRI] = mainDRI, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - kind: Kind = mainKind, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - )( - buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ): ScalaDocumentableContentBuilder = addChild( - contentForDRIs(dri, sourceSets, kind, styles, extra, buildBlock) - ) - - def header( - level: Int, - text: String, - kind: Kind = ContentKind.Main, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - )( - buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder = p => p - ): ScalaDocumentableContentBuilder = addChild( - ContentHeader( - level, - contentForDRIs( - mainDRI, - sourceSets, - kind, - styles, - extra plus SimpleAttr("anchor", "\\s".r.replaceAllIn(text, "").toLowerCase()), - bdr => {buildBlock(bdr.text(text, kind = kind))} - ) - ) - ) - - def cover( - text: String, - kind: Kind = ContentKind.Main, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - )( - buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder = p => p - ): ScalaDocumentableContentBuilder = header(1, text, kind, sourceSets, styles, extra){buildBlock} - - def signature(d: Documentable) = addChildren(signatureProvider.signature(d).asScala.toList) - - def defaultHeaders = List( - contentForDRIs( - dris = mainDRI, - sourceSets = mainSourcesetData, - buildBlock = bdr => {bdr.text("Name")} - ), - contentForDRIs( - dris = mainDRI, - sourceSets = mainSourcesetData, - buildBlock = bdr => {bdr.text("Summary")} - ) - ) - - def table( - kind: Kind = ContentKind.Main, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra, - headers: List[ContentGroup] = List.empty - )( - buildBlock: ScalaPageContentBuilder#ScalaTableBuilder => ScalaPageContentBuilder#ScalaTableBuilder - ) = addChild( - ContentTable( - headers.asJava, - buildBlock(ScalaTableBuilder(mainDRI, sourceSets, kind, styles, extra)).build().asJava, - DCI(mainDRI.asJava, kind), - sourceSets.toDisplay, - styles.asJava, - extra - ) - ) - - def text( - text: String, - kind: Kind = ContentKind.Main, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - ) = addChild( - buildText(text, kind, sourceSets, styles, extra) - ) - - private def buildText( - text: String, - kind: Kind = ContentKind.Main, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - ) = ContentText(text, DCI(mainDRI.asJava, kind), sourceSets.toDisplay, styles.asJava, extra) - - - def dotDiagram( - diagram: HierarchyGraph, - kind: Kind = ContentKind.Main, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - ) = addChild(HierarchyGraphContentNode(diagram, DCI(mainDRI.asJava, kind), sourceSets.toDisplay.asScala.toSet, styles, extra)) - - def groupingBlock[A, T <: Documentable, G <: List[(A, List[T])]]( - name: String, - elements: G, - kind: Kind = ContentKind.Main, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = Set(), - extra: PropertyContainer[ContentNode] = mainExtra, - renderWhenEmpty: Boolean = false, - needsSorting: Boolean = true, - headers: List[ContentGroup] = List(), - needsAnchors: Boolean = true, - omitSplitterOnSingletons: Boolean = true - )( - groupSplitterFunc: (ScalaPageContentBuilder#ScalaDocumentableContentBuilder, A) => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - )( - elementFunc: (ScalaPageContentBuilder#ScalaDocumentableContentBuilder, T) => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ) = if (renderWhenEmpty || !elements.flatMap(_._2).isEmpty) { - header(3, name, kind, styles = styles, extra = extra plus SimpleAttr.Companion.header(name))() - .group(styles = Set(ContentStyle.WithExtraAttributes), extra = extra plus SimpleAttr.Companion.header(name)){ bdr => - elements.foldLeft(bdr){ (b, groupped) => - val (key, values) = groupped - (if(values.size > 1 || (values.size == 1 && !omitSplitterOnSingletons)) b.group()(bd => groupSplitterFunc(bd, key)) else b) - .table(kind = kind, headers = headers, styles = styles, extra = extra plus SimpleAttr.Companion.header(name)){ tablebdr => - values.foldLeft(tablebdr){ (tablebdr, elem) => - tablebdr.cell(Set(elem.getDri), elem.getSourceSets.asScala.toSet, kind, styles, extra){ cellbdr => - elementFunc(cellbdr, elem) - } - } - } - } - } - } else this - - def list[T]( - elements: List[T], - prefix: String = "", - suffix: String = "", - separator: String = ", ", - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData - )( - elemOp: (ScalaPageContentBuilder#ScalaDocumentableContentBuilder, T) => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ) = if(!elements.isEmpty){ - val withPrefix = (if(!prefix.isEmpty) text(prefix, sourceSets = sourceSets) else this) - val insertedElems = elements.dropRight(1).foldLeft[ScalaPageContentBuilder#ScalaDocumentableContentBuilder](withPrefix){ (bdr, elem) => - elemOp(bdr, elem).text(separator, sourceSets = sourceSets) + ) + + def cover( + text: String, + kind: Kind = ContentKind.Main, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + )( + buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder = p => p + ): ScalaDocumentableContentBuilder = header(1, text, kind, sourceSets, styles, extra){buildBlock} + + def signature(d: Documentable) = addChildren(signatureProvider.signature(d).asScala.toList) + + def defaultHeaders = List( + contentForDRIs( + dris = mainDRI, + sourceSets = mainSourcesetData, + buildBlock = bdr => {bdr.text("Name")} + ), + contentForDRIs( + dris = mainDRI, + sourceSets = mainSourcesetData, + buildBlock = bdr => {bdr.text("Summary")} + ) + ) + + def table( + kind: Kind = ContentKind.Main, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra, + headers: List[ContentGroup] = List.empty + )( + buildBlock: ScalaPageContentBuilder#ScalaTableBuilder => ScalaPageContentBuilder#ScalaTableBuilder + ) = addChild( + ContentTable( + headers.asJava, + buildBlock(ScalaTableBuilder(mainDRI, sourceSets, kind, styles, extra)).build().asJava, + DCI(mainDRI.asJava, kind), + sourceSets.toDisplay, + styles.asJava, + extra + ) + ) + + def text( + text: String, + kind: Kind = ContentKind.Main, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + ) = addChild( + buildText(text, kind, sourceSets, styles, extra) + ) + + private def buildText( + text: String, + kind: Kind = ContentKind.Main, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + ) = ContentText(text, DCI(mainDRI.asJava, kind), sourceSets.toDisplay, styles.asJava, extra) + + + def dotDiagram( + diagram: HierarchyGraph, + kind: Kind = ContentKind.Main, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + ) = addChild(HierarchyGraphContentNode(diagram, DCI(mainDRI.asJava, kind), sourceSets.toDisplay.asScala.toSet, styles, extra)) + + def groupingBlock[A, T <: Documentable, G <: List[(A, List[T])]]( + name: String, + elements: G, + kind: Kind = ContentKind.Main, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = Set(), + extra: PropertyContainer[ContentNode] = mainExtra, + renderWhenEmpty: Boolean = false, + needsSorting: Boolean = true, + headers: List[ContentGroup] = List(), + needsAnchors: Boolean = true, + omitSplitterOnSingletons: Boolean = true + )( + groupSplitterFunc: (ScalaPageContentBuilder#ScalaDocumentableContentBuilder, A) => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + )( + elementFunc: (ScalaPageContentBuilder#ScalaDocumentableContentBuilder, T) => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ) = if (renderWhenEmpty || !elements.flatMap(_._2).isEmpty) { + header(3, name, kind, styles = styles, extra = extra plus SimpleAttr.Companion.header(name))() + .group(styles = Set(ContentStyle.WithExtraAttributes), extra = extra plus SimpleAttr.Companion.header(name)){ bdr => + elements.foldLeft(bdr){ (b, groupped) => + val (key, values) = groupped + (if(values.size > 1 || (values.size == 1 && !omitSplitterOnSingletons)) b.group()(bd => groupSplitterFunc(bd, key)) else b) + .table(kind = kind, headers = headers, styles = styles, extra = extra plus SimpleAttr.Companion.header(name)){ tablebdr => + values.foldLeft(tablebdr){ (tablebdr, elem) => + tablebdr.cell(Set(elem.getDri), elem.getSourceSets.asScala.toSet, kind, styles, extra){ cellbdr => + elementFunc(cellbdr, elem) + } } - val withLast = elemOp(insertedElems, elements.last) - if(!suffix.isEmpty) withLast.text(suffix, sourceSets = sourceSets) else withLast - } else this - - def driLink( - text: String, - address: DRI, - kind: Kind = ContentKind.Main, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - ) = addChild( - ContentDRILink( - List(buildText(text, kind, sourceSets, styles, extra)).asJava, - address, - DCI(mainDRI.asJava, kind), - sourceSets.toDisplay, - Set().asJava, - PropertyContainer.Companion.empty() - ) - ) - - def resolvedLink( - text: String, - address: String, - kind: Kind = ContentKind.Main, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - ) = addChild( - ContentResolvedLink( - List(buildText(text, kind, sourceSets, styles, extra)).asJava, - address, - DCI(mainDRI.asJava, kind), - sourceSets.toDisplay, - Set().asJava, - PropertyContainer.Companion.empty() - ) - ) - - def linkWithContent( - address: DRI, - kind: Kind = ContentKind.Main, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - )( - buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ) = addChild( - ContentDRILink( - contentForDRIs(mainDRI, sourceSets, kind, styles, extra, buildBlock).getChildren, - address, - DCI(mainDRI.asJava, kind), - sourceSets.toDisplay, - Set().asJava, - PropertyContainer.Companion.empty() - ) - ) - - def comment( - docTag: DocTag, - kind: Kind = ContentKind.Comment, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - ) = addChild( - contentForDRIs(mainDRI, sourceSets, kind, styles, extra, bdr => bdr.addChildren( - rawComment(docTag, kind, sourceSets, styles, extra) - )) - ) - - def rawComment( - docTag: DocTag, - kind: Kind = ContentKind.Comment, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra - ) = commentsConverter.buildContent( - docTag, - DCI(mainDRI.asJava, kind), - sourceSets.asJava, - Set().asJava, - PropertyContainer.Companion.empty() - ).asScala.toSeq - - def divergentGroup( - groupId: ContentDivergentGroup.GroupID, - dri: Set[DRI] = mainDRI, - kind: Kind = ContentKind.Main, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra, - implicitlySourceSetHinted: Boolean = true - )( - buildBlock: ScalaPageContentBuilder#ScalaDivergentBuilder => ScalaPageContentBuilder#ScalaDivergentBuilder - ) = addChild( - buildBlock(ScalaDivergentBuilder(groupId, dri, kind, styles, extra, implicitlySourceSetHinted)).buildContent() + } + } + } + } else this + + def list[T]( + elements: List[T], + prefix: String = "", + suffix: String = "", + separator: String = ", ", + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData + )( + elemOp: (ScalaPageContentBuilder#ScalaDocumentableContentBuilder, T) => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ) = if(!elements.isEmpty){ + val withPrefix = (if(!prefix.isEmpty) text(prefix, sourceSets = sourceSets) else this) + val insertedElems = elements.dropRight(1).foldLeft[ScalaPageContentBuilder#ScalaDocumentableContentBuilder](withPrefix){ (bdr, elem) => + elemOp(bdr, elem).text(separator, sourceSets = sourceSets) + } + val withLast = elemOp(insertedElems, elements.last) + if(!suffix.isEmpty) withLast.text(suffix, sourceSets = sourceSets) else withLast + } else this + + def driLink( + text: String, + address: DRI, + kind: Kind = ContentKind.Main, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + ) = addChild( + ContentDRILink( + List(buildText(text, kind, sourceSets, styles, extra)).asJava, + address, + DCI(mainDRI.asJava, kind), + sourceSets.toDisplay, + Set().asJava, + PropertyContainer.Companion.empty() + ) + ) + + def resolvedLink( + text: String, + address: String, + kind: Kind = ContentKind.Main, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + ) = addChild( + ContentResolvedLink( + List(buildText(text, kind, sourceSets, styles, extra)).asJava, + address, + DCI(mainDRI.asJava, kind), + sourceSets.toDisplay, + Set().asJava, + PropertyContainer.Companion.empty() + ) + ) + + def linkWithContent( + address: DRI, + kind: Kind = ContentKind.Main, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + )( + buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ) = addChild( + ContentDRILink( + contentForDRIs(mainDRI, sourceSets, kind, styles, extra, buildBlock).getChildren, + address, + DCI(mainDRI.asJava, kind), + sourceSets.toDisplay, + Set().asJava, + PropertyContainer.Companion.empty() + ) + ) + + def comment( + docTag: DocTag, + kind: Kind = ContentKind.Comment, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + ) = addChild( + contentForDRIs(mainDRI, sourceSets, kind, styles, extra, bdr => bdr.addChildren( + rawComment(docTag, kind, sourceSets, styles, extra) + )) + ) + + def rawComment( + docTag: DocTag, + kind: Kind = ContentKind.Comment, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra + ) = commentsConverter.buildContent( + docTag, + DCI(mainDRI.asJava, kind), + sourceSets.asJava, + Set().asJava, + PropertyContainer.Companion.empty() + ).asScala.toSeq + + def divergentGroup( + groupId: ContentDivergentGroup.GroupID, + dri: Set[DRI] = mainDRI, + kind: Kind = ContentKind.Main, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra, + implicitlySourceSetHinted: Boolean = true + )( + buildBlock: ScalaPageContentBuilder#ScalaDivergentBuilder => ScalaPageContentBuilder#ScalaDivergentBuilder + ) = addChild( + buildBlock(ScalaDivergentBuilder(groupId, dri, kind, styles, extra, implicitlySourceSetHinted)).buildContent() + ) + + def sourceSetDependentHint( + dri: Set[DRI] = mainDRI, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + kind: Kind = ContentKind.Main, + styles: Set[Style] = mainStyles, + extra: PropertyContainer[ContentNode] = mainExtra, + )( + buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ) = addChild( + PlatformHintedContent( + contentForDRIs(dri, sourceSets, kind, styles, extra, buildBlock), + sourceSets.toDisplay + ) + ) + + type Self = ScalaPageContentBuilder#ScalaDocumentableContentBuilder + + def documentableTab(name: String)(children: DocumentableGroup*): Self = + def buildSignature(d: Documentable) = + ScalaSignatureProvider.rawSignature(d, InlineSignatureBuilder()).asInstanceOf[InlineSignatureBuilder] + + def buildAnnotations(d: Member) = + InlineSignatureBuilder().annotationsBlock(d).asInstanceOf[InlineSignatureBuilder].names.reverse + + def documentableElement(documentable: Member): DocumentableElement = + val docs = documentable.getDocumentation.asScala.values.headOption.flatMap(_.getChildren.asScala.headOption) + val signatureBuilder = buildSignature(documentable) + val originInfo = documentable.origin match { + case Origin.ImplicitlyAddedBy(name, dri) => Signature("Implicitly added by ", SLink(name, dri)) + case Origin.ExtensionFrom(name, dri) => Signature("Extension method from ", SLink(name, dri)) + case _ => Nil + } + + DocumentableElement( + buildAnnotations(documentable), + signatureBuilder.preName.reverse, + documentable.getName, + signatureBuilder.names.reverse, + docs.fold(Nil)(d => reset().rawComment(d.getRoot)), + originInfo, + FilterAttributes.attributesFor(documentable), + asParams(documentable.getDri) ) - def sourceSetDependentHint( - dri: Set[DRI] = mainDRI, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - kind: Kind = ContentKind.Main, - styles: Set[Style] = mainStyles, - extra: PropertyContainer[ContentNode] = mainExtra, - )( - buildBlock: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ) = addChild( - PlatformHintedContent( - contentForDRIs(dri, sourceSets, kind, styles, extra, buildBlock), - sourceSets.toDisplay - ) - ) - - type Self = ScalaPageContentBuilder#ScalaDocumentableContentBuilder - - def documentableTab(name: String)(children: DocumentableGroup*): Self = - def buildSignature(d: Documentable) = - ScalaSignatureProvider.rawSignature(d, InlineSignatureBuilder()).asInstanceOf[InlineSignatureBuilder] - - def buildAnnotations(d: Member) = - InlineSignatureBuilder().annotationsBlock(d).asInstanceOf[InlineSignatureBuilder].names.reverse - - def documentableElement(documentable: Member): DocumentableElement = - val docs = documentable.getDocumentation.asScala.values.headOption.flatMap(_.getChildren.asScala.headOption) - val signatureBuilder = buildSignature(documentable) - val originInfo = documentable.origin match { - case Origin.ImplicitlyAddedBy(name, dri) => Signature("Implicitly added by ", SLink(name, dri)) - case Origin.ExtensionFrom(name, dri) => Signature("Extension method from ", SLink(name, dri)) - case _ => Nil - } - - DocumentableElement( - buildAnnotations(documentable), - signatureBuilder.preName.reverse, - documentable.getName, - signatureBuilder.names.reverse, - docs.fold(Nil)(d => reset().rawComment(d.getRoot)), - originInfo, - FilterAttributes.attributesFor(documentable), - asParams(documentable.getDri) - ) - - def element(e: Documentable | DocumentableSubGroup): DocumentableElement | DocumentableElementGroup = e match - case e: Documentable => documentableElement(e) - case e: DocumentableSubGroup => - DocumentableElementGroup( - e.title, - e.extensions.map(documentableElement), - asParams(mainDRI) - ) - - if (children.forall(_.documenables.isEmpty)) this else - header(3, name, mainKind,mainSourcesetData, mainStyles, mainExtra plus SimpleAttr.Companion.header(name))() - .group(styles = Set(ContentStyle.WithExtraAttributes), extra = mainExtra plus SimpleAttr.Companion.header(name)){ bdr => - children.foldLeft(bdr){ (bdr, list) => - if list.documenables.isEmpty then bdr - else - val header = list.name match - case Some(o: Documentable) => - buildSignature(o).names.reverse - case option => - option.toSeq.map(_.toString) - - - bdr.addChild(DocumentableList(header, list.documenables.map(element), asParams(mainDRI))) - } + def element(e: Documentable | DocumentableSubGroup): DocumentableElement | DocumentableElementGroup = e match + case e: Documentable => documentableElement(e) + case e: DocumentableSubGroup => + DocumentableElementGroup( + e.title, + e.extensions.map(documentableElement), + asParams(mainDRI) + ) + + if (children.forall(_.documenables.isEmpty)) this else + header(3, name, mainKind,mainSourcesetData, mainStyles, mainExtra plus SimpleAttr.Companion.header(name))() + .group(styles = Set(ContentStyle.WithExtraAttributes), extra = mainExtra plus SimpleAttr.Companion.header(name)){ bdr => + children.foldLeft(bdr){ (bdr, list) => + if list.documenables.isEmpty then bdr + else + val header = list.name match + case Some(o: Documentable) => + buildSignature(o).names.reverse + case option => + option.toSeq.map(_.toString) + + + bdr.addChild(DocumentableList(header, list.documenables.map(element), asParams(mainDRI))) } - - def documentableFilter() = addChild(DocumentableFilter(asParams(mainDRI))) - - def asParams(dri: DRI): ContentNodeParams = asParams(Set(dri)) - - def asParams(dri: Set[DRI]): ContentNodeParams = ContentNodeParams( - new DCI(dri.asJava, mainKind), - mainSourcesetData.toDisplay, - mainStyles, - mainExtra - ) - - def divergentBlock[A, T <: Documentable, G <: List[(A, List[T])]]( - name: String, - elements: G, - kind: Kind = ContentKind.Main, - sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, - styles: Set[Style] = Set(), - extra: PropertyContainer[ContentNode] = mainExtra, - renderWhenEmpty: Boolean = false, - needsSorting: Boolean = true, - headers: List[ContentGroup] = List(), - needsAnchors: Boolean = true, - omitSplitterOnSingletons: Boolean = true - )( - groupSplitterFunc: (ScalaPageContentBuilder#ScalaDocumentableContentBuilder, A) => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ) = if (renderWhenEmpty || !elements.flatMap(_._2).isEmpty) { - header(3, name, kind, styles = styles, extra = extra plus SimpleAttr.Companion.header(name))() - .group(styles = Set(ContentStyle.WithExtraAttributes), extra = extra plus SimpleAttr.Companion.header(name)){ bdr => - elements.foldLeft(bdr){ (b, groupped) => - val (key, values) = groupped - (if(values.size > 1 || (values.size == 1 && !omitSplitterOnSingletons)) b.group()(bd => groupSplitterFunc(bd, key)) else b) - .table(kind = kind, headers = headers, styles = styles, extra = extra plus SimpleAttr.Companion.header(name)){ tablebdr => - values.groupBy(_.getName).foldLeft(tablebdr){ case (tablebdr,(elemName, divergentElems)) => tablebdr - .cell( - dri = divergentElems.map(_.getDri).toSet, - sourceSets = divergentElems.flatMap(_.getSourceSets.asScala).toSet, - kind = kind - ){ cellbdr => cellbdr - .driLink(elemName, divergentElems.head.getDri, kind = ContentKind.Main) - .divergentGroup(ContentDivergentGroup.GroupID(name)){ divBdr => - divergentElems.foldLeft(divBdr){ (bdr, elem) => - bdr.instance(Set(elem.getDri), elem.getSourceSets.asScala.toSet){ insBdr => insBdr - .before(){ befbdr => befbdr - .contentForBrief(elem) - } - .divergent(){ divDivBdr => divDivBdr - .group(){ gbdr => gbdr - .signature(elem) - } - } - } - } - } - } - + } + + def documentableFilter() = addChild(DocumentableFilter(asParams(mainDRI))) + + def asParams(dri: DRI): ContentNodeParams = asParams(Set(dri)) + + def asParams(dri: Set[DRI]): ContentNodeParams = ContentNodeParams( + new DCI(dri.asJava, mainKind), + mainSourcesetData.toDisplay, + mainStyles, + mainExtra + ) + + def divergentBlock[A, T <: Documentable, G <: List[(A, List[T])]]( + name: String, + elements: G, + kind: Kind = ContentKind.Main, + sourceSets: Set[DokkaConfiguration$DokkaSourceSet] = mainSourcesetData, + styles: Set[Style] = Set(), + extra: PropertyContainer[ContentNode] = mainExtra, + renderWhenEmpty: Boolean = false, + needsSorting: Boolean = true, + headers: List[ContentGroup] = List(), + needsAnchors: Boolean = true, + omitSplitterOnSingletons: Boolean = true + )( + groupSplitterFunc: (ScalaPageContentBuilder#ScalaDocumentableContentBuilder, A) => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ) = if (renderWhenEmpty || !elements.flatMap(_._2).isEmpty) { + header(3, name, kind, styles = styles, extra = extra plus SimpleAttr.Companion.header(name))() + .group(styles = Set(ContentStyle.WithExtraAttributes), extra = extra plus SimpleAttr.Companion.header(name)){ bdr => + elements.foldLeft(bdr){ (b, groupped) => + val (key, values) = groupped + (if(values.size > 1 || (values.size == 1 && !omitSplitterOnSingletons)) b.group()(bd => groupSplitterFunc(bd, key)) else b) + .table(kind = kind, headers = headers, styles = styles, extra = extra plus SimpleAttr.Companion.header(name)){ tablebdr => + values.groupBy(_.getName).foldLeft(tablebdr){ case (tablebdr,(elemName, divergentElems)) => tablebdr + .cell( + dri = divergentElems.map(_.getDri).toSet, + sourceSets = divergentElems.flatMap(_.getSourceSets.asScala).toSet, + kind = kind + ){ cellbdr => cellbdr + .driLink(elemName, divergentElems.head.getDri, kind = ContentKind.Main) + .divergentGroup(ContentDivergentGroup.GroupID(name)){ divBdr => + divergentElems.foldLeft(divBdr){ (bdr, elem) => + bdr.instance(Set(elem.getDri), elem.getSourceSets.asScala.toSet){ insBdr => insBdr + .before(){ befbdr => befbdr + .contentForBrief(elem) + } + .divergent(){ divDivBdr => divDivBdr + .group(){ gbdr => gbdr + .signature(elem) } + } } + } } - } - } else this + } - def contentForBrief(d: Documentable): ScalaDocumentableContentBuilder = - d.getDocumentation.asScala.foldLeft(this){ case (builder, (ss, docs)) => - docs.getChildren.asScala.headOption.map(_.getRoot) match { - case Some(dt) => builder.group(sourceSets = Set(ss), kind = ContentKind.BriefComment){ bldr => bldr.comment(dt) } - case None => builder - } } - - } + } + } + } + } else this + + def contentForBrief(d: Documentable): ScalaDocumentableContentBuilder = + d.getDocumentation.asScala.foldLeft(this){ case (builder, (ss, docs)) => + docs.getChildren.asScala.headOption.map(_.getRoot) match { + case Some(dt) => builder.group(sourceSets = Set(ss), kind = ContentKind.BriefComment){ bldr => bldr.comment(dt) } + case None => builder + } + } + + } } diff --git a/scala3doc/src/dotty/dokka/translators/ScalaDocumentableToPageTranslator.scala b/scala3doc/src/dotty/dokka/translators/ScalaDocumentableToPageTranslator.scala index ac5ebd810832..89551445ad9b 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaDocumentableToPageTranslator.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaDocumentableToPageTranslator.scala @@ -8,9 +8,9 @@ import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslat import org.jetbrains.dokka.utilities.DokkaLogger class ScalaDocumentableToPageTranslator( - val commentsToContentConverter: CommentsToContentConverter, - val signatureProvider: SignatureProvider, - val logger: DokkaLogger + val commentsToContentConverter: CommentsToContentConverter, + val signatureProvider: SignatureProvider, + val logger: DokkaLogger ) extends DocumentableToPageTranslator { - override def invoke(module: DModule): ModulePageNode = ScalaPageCreator(commentsToContentConverter, signatureProvider, logger).pageForModule(module) + override def invoke(module: DModule): ModulePageNode = ScalaPageCreator(commentsToContentConverter, signatureProvider, logger).pageForModule(module) } diff --git a/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala b/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala index b713cf998b95..f9348eb5b815 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala @@ -26,418 +26,418 @@ import dotty.dokka.model.api.Link type DocBuilder = ScalaPageContentBuilder#ScalaDocumentableContentBuilder class ScalaPageCreator( - commentsToContentConverter: CommentsToContentConverter, - signatureProvider: SignatureProvider, - val logger: DokkaLogger + commentsToContentConverter: CommentsToContentConverter, + signatureProvider: SignatureProvider, + val logger: DokkaLogger ) extends DefaultPageCreator(commentsToContentConverter, signatureProvider, logger): - private val contentBuilder = ScalaPageContentBuilder(commentsToContentConverter, signatureProvider, logger) + private val contentBuilder = ScalaPageContentBuilder(commentsToContentConverter, signatureProvider, logger) - override def pageForModule(m: DModule): ModulePageNode = super.pageForModule(m) + override def pageForModule(m: DModule): ModulePageNode = super.pageForModule(m) - private def updatePageNameForMember(page: PageNode, p: Member) = - val name = p.kind match - case Kind.Extension(_) => s"extension_${page.getName}" - case _ => page.getName + private def updatePageNameForMember(page: PageNode, p: Member) = + val name = p.kind match + case Kind.Extension(_) => s"extension_${page.getName}" + case _ => page.getName - page.modified(name, page.getChildren) + page.modified(name, page.getChildren) - private def pagesForMembers(p: Member): Seq[PageNode] = - p.allMembers.filter(_.origin == Origin.DefinedWithin).collect { - case f: DFunction => updatePageNameForMember(pageForFunction(f), f) - case c: DClass => updatePageNameForMember(pageForDClass(c), c) - } + private def pagesForMembers(p: Member): Seq[PageNode] = + p.allMembers.filter(_.origin == Origin.DefinedWithin).collect { + case f: DFunction => updatePageNameForMember(pageForFunction(f), f) + case c: DClass => updatePageNameForMember(pageForDClass(c), c) + } + + override def pageForPackage(p: DPackage): PackagePageNode = + val originalPage = super.pageForPackage(p) + val originalPages: Seq[PageNode] = originalPage.getChildren.asScala.toList + val allPage: Seq[PageNode] = originalPages ++ pagesForMembers(p) + originalPage.modified(p.getName, allPage.asJava) - override def pageForPackage(p: DPackage): PackagePageNode = - val originalPage = super.pageForPackage(p) - val originalPages: Seq[PageNode] = originalPage.getChildren.asScala.toList - val allPage: Seq[PageNode] = originalPages ++ pagesForMembers(p) - originalPage.modified(p.getName, allPage.asJava) + override def pageForClasslike(c: DClasslike): ClasslikePageNode = c match { + case clazz: DClass => pageForDClass(clazz) + case other => throw UnsupportedOperationException("Only DClass classlike is supported.") + } - override def pageForClasslike(c: DClasslike): ClasslikePageNode = c match { - case clazz: DClass => pageForDClass(clazz) - case other => throw UnsupportedOperationException("Only DClass classlike is supported.") + def pageForDClass(c: DClass): ClasslikePageNode = { + val constructors = c.getConstructors + + val ext = c.get(ClasslikeExtension) + + val name = if c.kind == Kind.Object && ext.companion.isDefined then c.getName + "$" else c.getName + + ClasslikePageNode( + name, + contentForClasslike(c), + JSet(c.getDri), + c, + (constructors.asScala.map(pageForFunction) ++ + c.getClasslikes.asScala.map(pageForClasslike) ++ + c.getFunctions.asScala.map(pageForFunction) ++ + pagesForMembers(c)).asJava, + List.empty.asJava + ) + + } + + override def pageForFunction(f: DFunction) = super.pageForFunction(f) + + override def contentForModule(m: DModule) = { + def buildBlock = (builder: DocBuilder) => builder + .group(kind = ContentKind.Cover) { gbuilder => gbuilder + .cover(m.getName)() + .descriptionIfNotEmpty(m) + } + .addChildren(contentForComments(m).asScala.toSeq) + .groupingBlock( + "Packages", + List("" -> m.getPackages.asScala.toList), + kind = ContentKind.Packages, + sourceSets = m.getSourceSets.asScala.toSet + )( + (bdr, elem) => bdr + ) { (bdr, elem) => bdr + .driLink(elem.getName, elem.getDri) + } + + contentBuilder.contentForDocumentable(m, buildBlock = buildBlock) + } + + override def contentForPackage(p: DPackage) = { + def buildBlock = (builder: DocBuilder) => builder + .group(kind = ContentKind.Cover) { gbuilder => gbuilder + .cover(p.getName)() + .descriptionIfNotEmpty(p) + } + .documentableFilter() + .group(styles = Set(ContentStyle.TabbedContent)) { b => b + .contentForScope(p) + } + + contentBuilder.contentForDocumentable(p, buildBlock = buildBlock) + } + + override def contentForClasslike(c: DClasslike) = c match { + case d: DClass => contentForClass(d) + case other => throw UnsupportedOperationException("Only DClass classlike is supported.") + } + + def contentForClass(c: DClass) = { + def buildBlock = (builder: DocBuilder) => builder + .group(kind = ContentKind.Cover, sourceSets = c.getSourceSets.asScala.toSet) { gbdr => gbdr + .cover(c.getName)() + .sourceSetDependentHint(Set(c.getDri), c.getSourceSets.asScala.toSet) { sbdr => sbdr + .signature(c) + .contentForDescription(c) + } + } + .documentableFilter() + .group(styles = Set(ContentStyle.TabbedContent)) { b => b + .contentForScope(c) + .contentForEnum(c) + .contentForConstructors(c) + .contentForTypesInfo(c) + } + contentBuilder.contentForDocumentable(c, buildBlock = buildBlock) + } + + override def contentForMember(d: Documentable) = { + def buildBlock = (builder: DocBuilder) => builder + .group(kind = ContentKind.Cover){ bd => bd.cover(d.getName)() } + .divergentGroup( + ContentDivergentGroup.GroupID("member") + ) { divbdr => divbdr + .instance(Set(d.getDri), sourceSets = d.getSourceSets.asScala.toSet) { insbdr => insbdr + .before(){ bbdr => bbdr + .contentForDescription(d) + .contentForComments(d) + } + .divergent(kind = ContentKind.Symbol) { dbdr => dbdr + .signature(d) + } + } + } + contentBuilder.contentForDocumentable(d, buildBlock = buildBlock) + } + + override def contentForFunction(f: DFunction) = contentForMember(f) + + extension (b: DocBuilder): + def descriptionIfNotEmpty(d: Documentable): DocBuilder = { + val desc = contentForDescription(d).asScala.toSeq + val res = if desc.isEmpty then b else b + .sourceSetDependentHint( + Set(d.getDri), + d.getSourceSets.asScala.toSet, + kind = ContentKind.SourceSetDependentHint, + styles = Set(TextStyle.UnderCoverText) + ) { sourceSetBuilder => sourceSetBuilder + .addChildren(desc) } + res + } - def pageForDClass(c: DClass): ClasslikePageNode = { - val constructors = c.getConstructors + def contentForComments(d: Documentable) = b - val ext = c.get(ClasslikeExtension) + def contentForDescription(d: Documentable) = { + val specialTags = Set[Class[_]](classOf[Description]) - val name = if c.kind == Kind.Object && ext.companion.isDefined then c.getName + "$" else c.getName + type SourceSet = DokkaConfiguration$DokkaSourceSet - ClasslikePageNode( - name, - contentForClasslike(c), - JSet(c.getDri), - c, - (constructors.asScala.map(pageForFunction) ++ - c.getClasslikes.asScala.map(pageForClasslike) ++ - c.getFunctions.asScala.map(pageForFunction) ++ - pagesForMembers(c)).asJava, - List.empty.asJava - ) + val tags: List[(SourceSet, TagWrapper)] = + d.getDocumentation.asScala.toList.flatMap( (pd, doc) => doc.getChildren.asScala.map(pd -> _).toList ) - } + val platforms = d.getSourceSets.asScala.toSet - override def pageForFunction(f: DFunction) = super.pageForFunction(f) + val description = tags.collect{ case (pd, d: Description) => (pd, d) }.drop(1).groupBy(_(0)).map( (key, value) => key -> value.map(_(1))) - override def contentForModule(m: DModule) = { - def buildBlock = (builder: DocBuilder) => builder - .group(kind = ContentKind.Cover) { gbuilder => gbuilder - .cover(m.getName)() - .descriptionIfNotEmpty(m) - } - .addChildren(contentForComments(m).asScala.toSeq) - .groupingBlock( - "Packages", - List("" -> m.getPackages.asScala.toList), - kind = ContentKind.Packages, - sourceSets = m.getSourceSets.asScala.toSet - )( - (bdr, elem) => bdr - ) { (bdr, elem) => bdr - .driLink(elem.getName, elem.getDri) + /** Collect the key-value pairs from `iter` into a `Map` with a `cleanup` step, + * keeping the original order of the pairs. + */ + def collectInMap[K, E, V]( + iter: Iterator[(K, E)] + )( + cleanup: List[E] => V + ): collection.Map[K, V] = { + val lhm = mutable.LinkedHashMap.empty[K, ListBuffer[E]] + iter.foreach { case (k, e) => + lhm.updateWith(k) { + case None => Some(ListBuffer.empty.append(e)) + case Some(buf) => + buf.append(e) + Some(buf) + } + } + lhm.iterator.map { case (key, buf) => key -> cleanup(buf.result)}.to(mutable.LinkedHashMap) + } + + val unnamedTags: collection.Map[(SourceSet, Class[_]), List[TagWrapper]] = + collectInMap { + tags.iterator + .filterNot { t => + t(1).isInstanceOf[NamedTagWrapper] || specialTags.contains(t(1).getClass) + }.map { t => + (t(0), t(1).getClass) -> t(1) } + }(cleanup = identity) + + val namedTags: collection.Map[ + String, + Either[ + collection.Map[SourceSet, NamedTagWrapper], + collection.Map[(SourceSet, String), ScalaTagWrapper.NestedNamedTag], + ], + ] = { + val grouped = collectInMap { + tags.iterator.collect { + case (sourcesets, n: NamedTagWrapper) => + (n.getName, n.isInstanceOf[ScalaTagWrapper.NestedNamedTag]) -> (sourcesets, n) + } + }(cleanup = identity) + + grouped.iterator.map { + case ((name, true), values) => + val groupedValues = + values.iterator.map { + case (sourcesets, t) => + val tag = t.asInstanceOf[ScalaTagWrapper.NestedNamedTag] + (sourcesets, tag.subname) -> tag + }.to(mutable.LinkedHashMap) + name -> Right(groupedValues) + case ((name, false), values) => + name -> Left(values.to(mutable.LinkedHashMap)) + }.to(mutable.LinkedHashMap) + } + + b.group(Set(d.getDri), styles = Set(TextStyle.Block, TableStyle.Borderless)) { bdr => + val b1 = description.foldLeft(bdr){ + case (bdr, (key, value)) => bdr + .group(sourceSets = Set(key)){ gbdr => + value.foldLeft(gbdr) { (gbdr, tag) => gbdr + .comment(tag.getRoot) + } + } + } - contentBuilder.contentForDocumentable(m, buildBlock = buildBlock) - } - - override def contentForPackage(p: DPackage) = { - def buildBlock = (builder: DocBuilder) => builder - .group(kind = ContentKind.Cover) { gbuilder => gbuilder - .cover(p.getName)() - .descriptionIfNotEmpty(p) + b1.table(kind = ContentKind.Comment, styles = Set(TableStyle.DescriptionList)){ tbdr => + val withUnnamedTags = unnamedTags.foldLeft(tbdr){ case (bdr, (key, value) ) => bdr + .cell(sourceSets = Set(key(0))){ b => b + .text(key(1).getSimpleName, styles = Set(TextStyle.Bold)) } - .documentableFilter() - .group(styles = Set(ContentStyle.TabbedContent)) { b => b - .contentForScope(p) + .cell(sourceSets = Set(key(0))) { b => b + .list(value, separator = ""){ (bdr, elem) => bdr + .comment(elem.getRoot) + } } + } - contentBuilder.contentForDocumentable(p, buildBlock = buildBlock) - } - - override def contentForClasslike(c: DClasslike) = c match { - case d: DClass => contentForClass(d) - case other => throw UnsupportedOperationException("Only DClass classlike is supported.") - } - - def contentForClass(c: DClass) = { - def buildBlock = (builder: DocBuilder) => builder - .group(kind = ContentKind.Cover, sourceSets = c.getSourceSets.asScala.toSet) { gbdr => gbdr - .cover(c.getName)() - .sourceSetDependentHint(Set(c.getDri), c.getSourceSets.asScala.toSet) { sbdr => sbdr - .signature(c) - .contentForDescription(c) + val withNamedTags = namedTags.foldLeft(withUnnamedTags){ + case (bdr, (key, Left(value))) => + value.foldLeft(bdr){ case (bdr, (sourceSets, v)) => bdr + .cell(sourceSets = Set(sourceSets)){ b => b + .text(key) } - } - .documentableFilter() - .group(styles = Set(ContentStyle.TabbedContent)) { b => b - .contentForScope(c) - .contentForEnum(c) - .contentForConstructors(c) - .contentForTypesInfo(c) - } - contentBuilder.contentForDocumentable(c, buildBlock = buildBlock) - } - - override def contentForMember(d: Documentable) = { - def buildBlock = (builder: DocBuilder) => builder - .group(kind = ContentKind.Cover){ bd => bd.cover(d.getName)() } - .divergentGroup( - ContentDivergentGroup.GroupID("member") - ) { divbdr => divbdr - .instance(Set(d.getDri), sourceSets = d.getSourceSets.asScala.toSet) { insbdr => insbdr - .before(){ bbdr => bbdr - .contentForDescription(d) - .contentForComments(d) - } - .divergent(kind = ContentKind.Symbol) { dbdr => dbdr - .signature(d) - } + .cell(sourceSets = Set(sourceSets)){ b => b + .comment(v.getRoot) } + } + case (bdr, (key, Right(groupedValues))) => bdr + .cell(sourceSets = d.getSourceSets.asScala.toSet){ b => b + .text(key) + } + .cell(sourceSets = d.getSourceSets.asScala.toSet)(_.table(kind = ContentKind.Comment, styles = Set(TableStyle.NestedDescriptionList)){ tbdr => + groupedValues.foldLeft(tbdr){ case (bdr, ((sourceSets, _), v)) => bdr + .cell(sourceSets = Set(sourceSets)){ b => b + .comment(v.identTag) + } + .cell(sourceSets = Set(sourceSets)){ b => b + .comment(v.descTag) + } + } + }) + } + + val withCompanion = d match { + case d: DClass => + val ext = d.get(ClasslikeExtension) + val co = ext.companion + co.fold(withNamedTags) { co => withNamedTags + .cell(sourceSets = d.getSourceSets.asScala.toSet){ b => b + .text("Companion") + } + .cell(sourceSets = d.getSourceSets.asScala.toSet){ b => b + .driLink( + d.kind match { + case Kind.Object => "class" + case _ => "object" + }, + co + ) + } + } + case _ => withNamedTags + } + + d match{ + case d: (WithSources & WithExtraProperties[_]) if d.get(SourceLinks) != null && !d.get(SourceLinks).links.isEmpty => d.get(SourceLinks).links.foldLeft(withCompanion){ + case (bdr, (sourceSet, link)) => bdr + .cell(sourceSets = Set(sourceSet)){ b => b + .text("Source") + } + .cell(sourceSets = Set(sourceSet)){ b => b + .resolvedLink("(source)", link) + } } - contentBuilder.contentForDocumentable(d, buildBlock = buildBlock) + case other => withCompanion + } + } + } } - override def contentForFunction(f: DFunction) = contentForMember(f) - - extension (b: DocBuilder): - def descriptionIfNotEmpty(d: Documentable): DocBuilder = { - val desc = contentForDescription(d).asScala.toSeq - val res = if desc.isEmpty then b else b - .sourceSetDependentHint( - Set(d.getDri), - d.getSourceSets.asScala.toSet, - kind = ContentKind.SourceSetDependentHint, - styles = Set(TextStyle.UnderCoverText) - ) { sourceSetBuilder => sourceSetBuilder - .addChildren(desc) - } - res - } + def contentForScope(s: Documentable & WithScope & WithExtraProperties[_]) = + def groupExtensions(extensions: Seq[Member]): Seq[DocumentableSubGroup] = + extensions.groupBy(_.kind).map { + case (Kind.Extension(on), members) => + val signature = Signature(s"extension (${on.name}: ") join on.signature join Signature(")") + DocumentableSubGroup(signature, members.toSeq) + case other => sys.error(s"unexpected value: $other") + }.toSeq + + + val (definedMethods, inheritedMethods) = s.membersBy(_.kind == Kind.Def) + val (definedFields, inheritedFiles) = s.membersBy(m => m.kind == Kind.Val || m.kind == Kind.Var) + val (definedClasslikes, inheritedClasslikes) = s.membersBy(m => m.kind.isInstanceOf[Classlike]) + val (definedTypes, inheritedTypes) = s.membersBy(_.kind.isInstanceOf[Kind.Type]) + val (definedGivens, inheritedGives) = s.membersBy(_.kind.isInstanceOf[Kind.Given]) + val (definedExtensions, inheritedExtensions) = s.membersBy(_.kind.isInstanceOf[Kind.Extension]) + val (definedImplicits, inheritedImplicits) = s.membersBy(_.kind.isInstanceOf[Kind.Implicit]) + + b + .contentForComments(s) + .documentableTab("Type members")( + DocumentableGroup(Some("Types"), definedTypes), + DocumentableGroup(Some("Classlikes"), definedClasslikes), + DocumentableGroup(Some("Inherited types"), inheritedTypes), + DocumentableGroup(Some("Inherited classlikes"), inheritedClasslikes) + ) + .documentableTab("Methods")( + DocumentableGroup(Some("Defined methods"), definedMethods), + DocumentableGroup(Some("Inherited methods"), inheritedMethods), + ) + .documentableTab("Value members")( + DocumentableGroup(Some("Defined value members"), definedFields), + DocumentableGroup(Some("Inherited value members"), inheritedFiles) + ) + .documentableTab("Givens")( + DocumentableGroup(Some("Defined givens"), definedGivens), + DocumentableGroup(Some("Inherited givens"), inheritedGives) + ) + .documentableTab("Extensions")( + DocumentableGroup(Some("Defined extensions"), groupExtensions(definedExtensions)), + DocumentableGroup(Some("Inherited extensions"), groupExtensions(inheritedExtensions)) + ) + .documentableTab("Implicits")( + DocumentableGroup(Some("Defined implicits"), definedImplicits), + DocumentableGroup(Some("Inherited implicits"), inheritedImplicits) + ) - def contentForComments(d: Documentable) = b - def contentForDescription(d: Documentable) = { - val specialTags = Set[Class[_]](classOf[Description]) + def contentForEnum(c: DClass) = + b.documentableTab("Enum entries")( + DocumentableGroup(None, c.membersBy(_.kind == Kind.EnumCase)._1) // Enum entries cannot be inherited + ) - type SourceSet = DokkaConfiguration$DokkaSourceSet - val tags: List[(SourceSet, TagWrapper)] = - d.getDocumentation.asScala.toList.flatMap( (pd, doc) => doc.getChildren.asScala.map(pd -> _).toList ) + def contentForConstructors(c: DClass) = + b.documentableTab("Constructors")( + DocumentableGroup(None, c.getConstructors.asScala.toList) + ) - val platforms = d.getSourceSets.asScala.toSet - val description = tags.collect{ case (pd, d: Description) => (pd, d) }.drop(1).groupBy(_(0)).map( (key, value) => key -> value.map(_(1))) + def contentForTypesInfo(c: DClass) = + val supertypes = c.parents + val subtypes = c.knownChildren + val graph = MemberExtension.getFrom(c).map(_.graph) - /** Collect the key-value pairs from `iter` into a `Map` with a `cleanup` step, - * keeping the original order of the pairs. - */ - def collectInMap[K, E, V]( - iter: Iterator[(K, E)] - )( - cleanup: List[E] => V - ): collection.Map[K, V] = { - val lhm = mutable.LinkedHashMap.empty[K, ListBuffer[E]] - iter.foreach { case (k, e) => - lhm.updateWith(k) { - case None => Some(ListBuffer.empty.append(e)) - case Some(buf) => - buf.append(e) - Some(buf) - } - } - lhm.iterator.map { case (key, buf) => key -> cleanup(buf.result)}.to(mutable.LinkedHashMap) - } + def contentForTypeLink(builder: DocBuilder, link: LinkToType): DocBuilder = + builder.group(styles = Set(TextStyle.Paragraph)) { builder => + link.signature.foldLeft(builder.text(link.kind.name).text(" ")){ (builder, sigElement) => sigElement match + case Link(name, dri) => builder.driLink(name, dri) + case str: String => builder.text(str) + } + } - val unnamedTags: collection.Map[(SourceSet, Class[_]), List[TagWrapper]] = - collectInMap { - tags.iterator - .filterNot { t => - t(1).isInstanceOf[NamedTagWrapper] || specialTags.contains(t(1).getClass) - }.map { t => - (t(0), t(1).getClass) -> t(1) - } - }(cleanup = identity) - - val namedTags: collection.Map[ - String, - Either[ - collection.Map[SourceSet, NamedTagWrapper], - collection.Map[(SourceSet, String), ScalaTagWrapper.NestedNamedTag], - ], - ] = { - val grouped = collectInMap { - tags.iterator.collect { - case (sourcesets, n: NamedTagWrapper) => - (n.getName, n.isInstanceOf[ScalaTagWrapper.NestedNamedTag]) -> (sourcesets, n) - } - }(cleanup = identity) - - grouped.iterator.map { - case ((name, true), values) => - val groupedValues = - values.iterator.map { - case (sourcesets, t) => - val tag = t.asInstanceOf[ScalaTagWrapper.NestedNamedTag] - (sourcesets, tag.subname) -> tag - }.to(mutable.LinkedHashMap) - name -> Right(groupedValues) - case ((name, false), values) => - name -> Left(values.to(mutable.LinkedHashMap)) - }.to(mutable.LinkedHashMap) + val withSupertypes = if supertypes.isEmpty then b else + b.header(2, "Linear supertypes")() + .group( + kind = ContentKind.Comment, + styles = Set(ContentStyle.WithExtraAttributes), + extra = PropertyContainer.Companion.empty plus SimpleAttr.Companion.header("Linear supertypes") + ){ gbdr => gbdr + .group(kind = ContentKind.Symbol, styles = Set(TextStyle.Monospace)){ grbdr => grbdr + .list(supertypes.toList, separator = "")(contentForTypeLink) } - - b.group(Set(d.getDri), styles = Set(TextStyle.Block, TableStyle.Borderless)) { bdr => - val b1 = description.foldLeft(bdr){ - case (bdr, (key, value)) => bdr - .group(sourceSets = Set(key)){ gbdr => - value.foldLeft(gbdr) { (gbdr, tag) => gbdr - .comment(tag.getRoot) - } - } - } - - b1.table(kind = ContentKind.Comment, styles = Set(TableStyle.DescriptionList)){ tbdr => - val withUnnamedTags = unnamedTags.foldLeft(tbdr){ case (bdr, (key, value) ) => bdr - .cell(sourceSets = Set(key(0))){ b => b - .text(key(1).getSimpleName, styles = Set(TextStyle.Bold)) - } - .cell(sourceSets = Set(key(0))) { b => b - .list(value, separator = ""){ (bdr, elem) => bdr - .comment(elem.getRoot) - } - } - } - - val withNamedTags = namedTags.foldLeft(withUnnamedTags){ - case (bdr, (key, Left(value))) => - value.foldLeft(bdr){ case (bdr, (sourceSets, v)) => bdr - .cell(sourceSets = Set(sourceSets)){ b => b - .text(key) - } - .cell(sourceSets = Set(sourceSets)){ b => b - .comment(v.getRoot) - } - } - case (bdr, (key, Right(groupedValues))) => bdr - .cell(sourceSets = d.getSourceSets.asScala.toSet){ b => b - .text(key) - } - .cell(sourceSets = d.getSourceSets.asScala.toSet)(_.table(kind = ContentKind.Comment, styles = Set(TableStyle.NestedDescriptionList)){ tbdr => - groupedValues.foldLeft(tbdr){ case (bdr, ((sourceSets, _), v)) => bdr - .cell(sourceSets = Set(sourceSets)){ b => b - .comment(v.identTag) - } - .cell(sourceSets = Set(sourceSets)){ b => b - .comment(v.descTag) - } - } - }) - } - - val withCompanion = d match { - case d: DClass => - val ext = d.get(ClasslikeExtension) - val co = ext.companion - co.fold(withNamedTags) { co => withNamedTags - .cell(sourceSets = d.getSourceSets.asScala.toSet){ b => b - .text("Companion") - } - .cell(sourceSets = d.getSourceSets.asScala.toSet){ b => b - .driLink( - d.kind match { - case Kind.Object => "class" - case _ => "object" - }, - co - ) - } - } - case _ => withNamedTags - } - - d match{ - case d: (WithSources & WithExtraProperties[_]) if d.get(SourceLinks) != null && !d.get(SourceLinks).links.isEmpty => d.get(SourceLinks).links.foldLeft(withCompanion){ - case (bdr, (sourceSet, link)) => bdr - .cell(sourceSets = Set(sourceSet)){ b => b - .text("Source") - } - .cell(sourceSets = Set(sourceSet)){ b => b - .resolvedLink("(source)", link) - } - } - case other => withCompanion - } - } + } + + val withSubtypes = if (subtypes.isEmpty) withSupertypes else + withSupertypes.header(2, "Known subtypes")() + .group( + kind = ContentKind.Comment, + styles = Set(ContentStyle.WithExtraAttributes), + extra = PropertyContainer.Companion.empty plus SimpleAttr.Companion.header("Known subtypes") + ) { _.group(kind = ContentKind.Symbol, styles = Set(TextStyle.Monospace)) { + _.list(subtypes.toList, separator="")(contentForTypeLink) } - } - - def contentForScope(s: Documentable & WithScope & WithExtraProperties[_]) = - def groupExtensions(extensions: Seq[Member]): Seq[DocumentableSubGroup] = - extensions.groupBy(_.kind).map { - case (Kind.Extension(on), members) => - val signature = Signature(s"extension (${on.name}: ") join on.signature join Signature(")") - DocumentableSubGroup(signature, members.toSeq) - case other => sys.error(s"unexpected value: $other") - }.toSeq - - - val (definedMethods, inheritedMethods) = s.membersBy(_.kind == Kind.Def) - val (definedFields, inheritedFiles) = s.membersBy(m => m.kind == Kind.Val || m.kind == Kind.Var) - val (definedClasslikes, inheritedClasslikes) = s.membersBy(m => m.kind.isInstanceOf[Classlike]) - val (definedTypes, inheritedTypes) = s.membersBy(_.kind.isInstanceOf[Kind.Type]) - val (definedGivens, inheritedGives) = s.membersBy(_.kind.isInstanceOf[Kind.Given]) - val (definedExtensions, inheritedExtensions) = s.membersBy(_.kind.isInstanceOf[Kind.Extension]) - val (definedImplicits, inheritedImplicits) = s.membersBy(_.kind.isInstanceOf[Kind.Implicit]) - - b - .contentForComments(s) - .documentableTab("Type members")( - DocumentableGroup(Some("Types"), definedTypes), - DocumentableGroup(Some("Classlikes"), definedClasslikes), - DocumentableGroup(Some("Inherited types"), inheritedTypes), - DocumentableGroup(Some("Inherited classlikes"), inheritedClasslikes) - ) - .documentableTab("Methods")( - DocumentableGroup(Some("Defined methods"), definedMethods), - DocumentableGroup(Some("Inherited methods"), inheritedMethods), - ) - .documentableTab("Value members")( - DocumentableGroup(Some("Defined value members"), definedFields), - DocumentableGroup(Some("Inherited value members"), inheritedFiles) - ) - .documentableTab("Givens")( - DocumentableGroup(Some("Defined givens"), definedGivens), - DocumentableGroup(Some("Inherited givens"), inheritedGives) - ) - .documentableTab("Extensions")( - DocumentableGroup(Some("Defined extensions"), groupExtensions(definedExtensions)), - DocumentableGroup(Some("Inherited extensions"), groupExtensions(inheritedExtensions)) - ) - .documentableTab("Implicits")( - DocumentableGroup(Some("Defined implicits"), definedImplicits), - DocumentableGroup(Some("Inherited implicits"), inheritedImplicits) - ) - - - def contentForEnum(c: DClass) = - b.documentableTab("Enum entries")( - DocumentableGroup(None, c.membersBy(_.kind == Kind.EnumCase)._1) // Enum entries cannot be inherited - ) - - - def contentForConstructors(c: DClass) = - b.documentableTab("Constructors")( - DocumentableGroup(None, c.getConstructors.asScala.toList) - ) - - - def contentForTypesInfo(c: DClass) = - val supertypes = c.parents - val subtypes = c.knownChildren - val graph = MemberExtension.getFrom(c).map(_.graph) - - def contentForTypeLink(builder: DocBuilder, link: LinkToType): DocBuilder = - builder.group(styles = Set(TextStyle.Paragraph)) { builder => - link.signature.foldLeft(builder.text(link.kind.name).text(" ")){ (builder, sigElement) => sigElement match - case Link(name, dri) => builder.driLink(name, dri) - case str: String => builder.text(str) - } - } - - val withSupertypes = if supertypes.isEmpty then b else - b.header(2, "Linear supertypes")() - .group( - kind = ContentKind.Comment, - styles = Set(ContentStyle.WithExtraAttributes), - extra = PropertyContainer.Companion.empty plus SimpleAttr.Companion.header("Linear supertypes") - ){ gbdr => gbdr - .group(kind = ContentKind.Symbol, styles = Set(TextStyle.Monospace)){ grbdr => grbdr - .list(supertypes.toList, separator = "")(contentForTypeLink) - } - } - - val withSubtypes = if (subtypes.isEmpty) withSupertypes else - withSupertypes.header(2, "Known subtypes")() - .group( - kind = ContentKind.Comment, - styles = Set(ContentStyle.WithExtraAttributes), - extra = PropertyContainer.Companion.empty plus SimpleAttr.Companion.header("Known subtypes") - ) { _.group(kind = ContentKind.Symbol, styles = Set(TextStyle.Monospace)) { - _.list(subtypes.toList, separator="")(contentForTypeLink) - } - } - - graph.fold(withSubtypes) { graph => - if graph.edges.isEmpty then withSubtypes else - withSubtypes.header(2, "Type hierarchy")().group( - kind = ContentKind.Comment, - styles = Set(ContentStyle.WithExtraAttributes), - extra = PropertyContainer.Companion.empty plus SimpleAttr.Companion.header("Type hierarchy") - ) { _.group(kind = ContentKind.Symbol, styles = Set(TextStyle.Monospace)) { - _.dotDiagram(graph) - } - } + } + + graph.fold(withSubtypes) { graph => + if graph.edges.isEmpty then withSubtypes else + withSubtypes.header(2, "Type hierarchy")().group( + kind = ContentKind.Comment, + styles = Set(ContentStyle.WithExtraAttributes), + extra = PropertyContainer.Companion.empty plus SimpleAttr.Companion.header("Type hierarchy") + ) { _.group(kind = ContentKind.Symbol, styles = Set(TextStyle.Monospace)) { + _.dotDiagram(graph) } + } + } diff --git a/scala3doc/src/dotty/dokka/translators/ScalaSignatureProvider.scala b/scala3doc/src/dotty/dokka/translators/ScalaSignatureProvider.scala index a9081e2d8d23..efede2229fbf 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaSignatureProvider.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaSignatureProvider.scala @@ -17,179 +17,179 @@ import dotty.dokka.model.api.{Kind, _} class ScalaSignatureProvider(contentConverter: CommentsToContentConverter, logger: DokkaLogger) extends SignatureProvider with ScalaSignatureUtils: - private val default = new KotlinSignatureProvider(contentConverter, logger) - private val styles = Set(TextStyle.Monospace).asInstanceOf[Set[Style]] - private val contentBuilder = new ScalaPageContentBuilder(contentConverter, this, logger) + private val default = new KotlinSignatureProvider(contentConverter, logger) + private val styles = Set(TextStyle.Monospace).asInstanceOf[Set[Style]] + private val contentBuilder = new ScalaPageContentBuilder(contentConverter, this, logger) - private def signatureContent(d: Documentable)( - func: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder - ) = contentBuilder.contentForDocumentable(d, kind = ContentKind.Symbol, styles = styles, buildBlock = func) + private def signatureContent(d: Documentable)( + func: ScalaPageContentBuilder#ScalaDocumentableContentBuilder => ScalaPageContentBuilder#ScalaDocumentableContentBuilder + ) = contentBuilder.contentForDocumentable(d, kind = ContentKind.Symbol, styles = styles, buildBlock = func) - case class ContentNodeBuilder(builder: ScalaPageContentBuilder#ScalaDocumentableContentBuilder) extends SignatureBuilder{ - def text(str: String): SignatureBuilder = ContentNodeBuilder(builder.text(str)) - def driLink(text: String, dri: DRI): SignatureBuilder = ContentNodeBuilder(builder.driLink(text, dri)) - } + case class ContentNodeBuilder(builder: ScalaPageContentBuilder#ScalaDocumentableContentBuilder) extends SignatureBuilder{ + def text(str: String): SignatureBuilder = ContentNodeBuilder(builder.text(str)) + def driLink(text: String, dri: DRI): SignatureBuilder = ContentNodeBuilder(builder.driLink(text, dri)) + } - override def signature(documentable: Documentable) = - JList(signatureContent(documentable){ builder => - val withAnnotations = ContentNodeBuilder(builder).annotationsBlock(documentable) - val res = ScalaSignatureProvider.rawSignature(documentable, withAnnotations) - res.asInstanceOf[ContentNodeBuilder].builder - }) + override def signature(documentable: Documentable) = + JList(signatureContent(documentable){ builder => + val withAnnotations = ContentNodeBuilder(builder).annotationsBlock(documentable) + val res = ScalaSignatureProvider.rawSignature(documentable, withAnnotations) + res.asInstanceOf[ContentNodeBuilder].builder + }) object ScalaSignatureProvider: - def rawSignature(documentable: Documentable, builder: SignatureBuilder): SignatureBuilder = - documentable match - case extension: DFunction if extension.kind.isInstanceOf[Kind.Extension] => - extensionSignature(extension, builder) - case method: DFunction if method.kind.isInstanceOf[Kind.Given] => - givenMethodSignature(method, builder) - case method: DFunction => - methodSignature(method, builder) - case enumEntry: DClass if enumEntry.kind == Kind.EnumCase => - enumEntrySignature(enumEntry, builder) - case clazz: DClass => - classSignature(clazz, builder) - case enumProperty: DProperty if enumProperty.kind == Kind.EnumCase => - enumPropertySignature(enumProperty, builder) - case property: DProperty => - propertySignature(property, builder) - case parameter: DParameter => - parameterSignature(parameter, builder) - case _ => - ??? - - - private def enumEntrySignature(entry: DClass, bdr: SignatureBuilder): SignatureBuilder = - val ext = entry.get(ClasslikeExtension) - val withPrefixes: SignatureBuilder = bdr - .text("case ") - .name(entry.getName, entry.getDri) - .generics(entry) - - val withParameters = ext.constructor.toSeq.foldLeft(withPrefixes){ (bdr, elem) => - bdr.functionParameters(elem) - } - parentsSignature(entry, withParameters) - - private def enumPropertySignature(entry: DProperty, builder: SignatureBuilder): SignatureBuilder = - val modifiedType = entry.getType match - case t: TypeConstructor => GenericTypeConstructor( - t.getDri, - t.getProjections.asScala.map{ - case t: UnresolvedBound if t.getName == " & " => UnresolvedBound(" with "); - case other => other - }.asJava, - null - ) - case other => other - - builder - .text("case ") - .name(entry.getName, entry.getDri) - .text(" extends ") - .typeSignature(modifiedType) - - private def parentsSignature(d: DClass, builder: SignatureBuilder): SignatureBuilder = - d.directParents match - case Nil => builder - case extendType :: withTypes => - val extendPart = builder.text(" extends ").signature(extendType) - withTypes.foldLeft(extendPart)((bdr, tpe) => bdr.text(" with ").signature(tpe)) - - private def classSignature(clazz: DClass, builder: SignatureBuilder): SignatureBuilder = - val ext = clazz.get(ClasslikeExtension) - val prefixes = builder - .modifiersAndVisibility(clazz, clazz.kind.name) - .name(clazz.getName, clazz.getDri) - .generics(clazz) - - val withGenerics = ext.constructor.toSeq.foldLeft(prefixes){ (bdr, elem) => - bdr.functionParameters(elem) - } - parentsSignature(clazz, withGenerics) - - private def extensionSignature(extension: DFunction, builder: SignatureBuilder): SignatureBuilder = - val extendedSymbol = if (extension.isRightAssociative()) { - extension.getParameters.asScala(extension.get(MethodExtension).parametersListSizes(0)) - } else { - extension.getParameters.asScala(0) - } - val withSinature = builder - .modifiersAndVisibility(extension, "def") - .name(extension.getName, extension.getDri) - .generics(extension) - .functionParameters(extension) - - if extension.isConstructor then withSinature - else withSinature.text(":").text(" ").typeSignature(extension.getType) - - private def givenMethodSignature(method: DFunction, builder: SignatureBuilder): SignatureBuilder = method.kind match - case Kind.Given(Some(instance), _) => - builder.text("given ") - .name(method.getName, method.getDri) - .text(" as ") - .signature(instance) - case _ => - builder.text("given ").name(method.getName, method.getDri) - - - private def methodSignature(method: DFunction, builder: SignatureBuilder): SignatureBuilder = - val bdr = builder - .modifiersAndVisibility(method, "def") + def rawSignature(documentable: Documentable, builder: SignatureBuilder): SignatureBuilder = + documentable match + case extension: DFunction if extension.kind.isInstanceOf[Kind.Extension] => + extensionSignature(extension, builder) + case method: DFunction if method.kind.isInstanceOf[Kind.Given] => + givenMethodSignature(method, builder) + case method: DFunction => + methodSignature(method, builder) + case enumEntry: DClass if enumEntry.kind == Kind.EnumCase => + enumEntrySignature(enumEntry, builder) + case clazz: DClass => + classSignature(clazz, builder) + case enumProperty: DProperty if enumProperty.kind == Kind.EnumCase => + enumPropertySignature(enumProperty, builder) + case property: DProperty => + propertySignature(property, builder) + case parameter: DParameter => + parameterSignature(parameter, builder) + case _ => + ??? + + + private def enumEntrySignature(entry: DClass, bdr: SignatureBuilder): SignatureBuilder = + val ext = entry.get(ClasslikeExtension) + val withPrefixes: SignatureBuilder = bdr + .text("case ") + .name(entry.getName, entry.getDri) + .generics(entry) + + val withParameters = ext.constructor.toSeq.foldLeft(withPrefixes){ (bdr, elem) => + bdr.functionParameters(elem) + } + parentsSignature(entry, withParameters) + + private def enumPropertySignature(entry: DProperty, builder: SignatureBuilder): SignatureBuilder = + val modifiedType = entry.getType match + case t: TypeConstructor => GenericTypeConstructor( + t.getDri, + t.getProjections.asScala.map{ + case t: UnresolvedBound if t.getName == " & " => UnresolvedBound(" with "); + case other => other + }.asJava, + null + ) + case other => other + + builder + .text("case ") + .name(entry.getName, entry.getDri) + .text(" extends ") + .typeSignature(modifiedType) + + private def parentsSignature(d: DClass, builder: SignatureBuilder): SignatureBuilder = + d.directParents match + case Nil => builder + case extendType :: withTypes => + val extendPart = builder.text(" extends ").signature(extendType) + withTypes.foldLeft(extendPart)((bdr, tpe) => bdr.text(" with ").signature(tpe)) + + private def classSignature(clazz: DClass, builder: SignatureBuilder): SignatureBuilder = + val ext = clazz.get(ClasslikeExtension) + val prefixes = builder + .modifiersAndVisibility(clazz, clazz.kind.name) + .name(clazz.getName, clazz.getDri) + .generics(clazz) + + val withGenerics = ext.constructor.toSeq.foldLeft(prefixes){ (bdr, elem) => + bdr.functionParameters(elem) + } + parentsSignature(clazz, withGenerics) + + private def extensionSignature(extension: DFunction, builder: SignatureBuilder): SignatureBuilder = + val extendedSymbol = if (extension.isRightAssociative()) { + extension.getParameters.asScala(extension.get(MethodExtension).parametersListSizes(0)) + } else { + extension.getParameters.asScala(0) + } + val withSinature = builder + .modifiersAndVisibility(extension, "def") + .name(extension.getName, extension.getDri) + .generics(extension) + .functionParameters(extension) + + if extension.isConstructor then withSinature + else withSinature.text(":").text(" ").typeSignature(extension.getType) + + private def givenMethodSignature(method: DFunction, builder: SignatureBuilder): SignatureBuilder = method.kind match + case Kind.Given(Some(instance), _) => + builder.text("given ") .name(method.getName, method.getDri) - .generics(method) - .functionParameters(method) - if !method.isConstructor then - bdr - .text(":") - .text(" ") - .typeSignature(method.getType) - else bdr - - - private def propertySignature(property: DProperty, builder: SignatureBuilder): SignatureBuilder = - property.kind match - case _: Kind.Given => givenPropertySignature(property, builder) - case tpe: Kind.Type => typeSignature(tpe, property, builder) - case other => fieldSignature(property, other.name, builder) - - - private def typeSignature(tpe: Kind.Type, typeDef: DProperty, builder: SignatureBuilder): SignatureBuilder = - val bdr = builder - .modifiersAndVisibility(typeDef, if tpe.opaque then "opaque type" else "type") - .name(typeDef.getName, typeDef.getDri) - .generics(typeDef) - if(!tpe.opaque){ - (if tpe.concreate then bdr.text(" = ") else bdr) - .typeSignature(typeDef.getType) - } else bdr - - - private def givenPropertySignature(property: DProperty, builder: SignatureBuilder): SignatureBuilder = - val bdr = builder - .text("given ") - .name(property.getName, property.getDri) - - property.kind match - case Kind.Given(Some(instance), _) => - bdr.text(" as ").signature(instance) - case _ => bdr - - private def fieldSignature(property: DProperty, kind: String, builder: SignatureBuilder): SignatureBuilder = - builder - .modifiersAndVisibility(property, kind) - .name(property.getName, property.getDri) - .text(":") - .text(" ") - .typeSignature(property.getType) - - private def parameterSignature(parameter: DParameter, builder: SignatureBuilder): SignatureBuilder = - val ext = parameter.get(ParameterExtension) - builder - .text(if ext.isGrouped then "extension (" else "(") - .text(parameter.getName) - .text(": ") - .typeSignature(parameter.getType) - .text(")") + .text(" as ") + .signature(instance) + case _ => + builder.text("given ").name(method.getName, method.getDri) + + + private def methodSignature(method: DFunction, builder: SignatureBuilder): SignatureBuilder = + val bdr = builder + .modifiersAndVisibility(method, "def") + .name(method.getName, method.getDri) + .generics(method) + .functionParameters(method) + if !method.isConstructor then + bdr + .text(":") + .text(" ") + .typeSignature(method.getType) + else bdr + + + private def propertySignature(property: DProperty, builder: SignatureBuilder): SignatureBuilder = + property.kind match + case _: Kind.Given => givenPropertySignature(property, builder) + case tpe: Kind.Type => typeSignature(tpe, property, builder) + case other => fieldSignature(property, other.name, builder) + + + private def typeSignature(tpe: Kind.Type, typeDef: DProperty, builder: SignatureBuilder): SignatureBuilder = + val bdr = builder + .modifiersAndVisibility(typeDef, if tpe.opaque then "opaque type" else "type") + .name(typeDef.getName, typeDef.getDri) + .generics(typeDef) + if(!tpe.opaque){ + (if tpe.concreate then bdr.text(" = ") else bdr) + .typeSignature(typeDef.getType) + } else bdr + + + private def givenPropertySignature(property: DProperty, builder: SignatureBuilder): SignatureBuilder = + val bdr = builder + .text("given ") + .name(property.getName, property.getDri) + + property.kind match + case Kind.Given(Some(instance), _) => + bdr.text(" as ").signature(instance) + case _ => bdr + + private def fieldSignature(property: DProperty, kind: String, builder: SignatureBuilder): SignatureBuilder = + builder + .modifiersAndVisibility(property, kind) + .name(property.getName, property.getDri) + .text(":") + .text(" ") + .typeSignature(property.getType) + + private def parameterSignature(parameter: DParameter, builder: SignatureBuilder): SignatureBuilder = + val ext = parameter.get(ParameterExtension) + builder + .text(if ext.isGrouped then "extension (" else "(") + .text(parameter.getName) + .text(": ") + .typeSignature(parameter.getType) + .text(")") diff --git a/scala3doc/src/dotty/dokka/translators/ScalaSignatureUtils.scala b/scala3doc/src/dotty/dokka/translators/ScalaSignatureUtils.scala index 38343e1b09f7..f091e6a1f118 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaSignatureUtils.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaSignatureUtils.scala @@ -10,112 +10,112 @@ import collection.JavaConverters._ import dotty.dokka.model.api.{Kind, _} case class InlineSignatureBuilder(names: Signature = Nil, preName: Signature = Nil) extends SignatureBuilder: - override def text(str: String): SignatureBuilder = copy(names = str +: names) - override def name(str: String, dri: DRI): SignatureBuilder = copy(names = Nil, preName = names) - override def driLink(text: String, dri: DRI): SignatureBuilder = copy(names = Link(text, dri) +: names) + override def text(str: String): SignatureBuilder = copy(names = str +: names) + override def name(str: String, dri: DRI): SignatureBuilder = copy(names = Nil, preName = names) + override def driLink(text: String, dri: DRI): SignatureBuilder = copy(names = Link(text, dri) +: names) object InlineSignatureBuilder: - def typeSignatureFor(d: Documentable): Signature = - ScalaSignatureProvider.rawSignature(d, InlineSignatureBuilder()).asInstanceOf[InlineSignatureBuilder].names.reverse + def typeSignatureFor(d: Documentable): Signature = + ScalaSignatureProvider.rawSignature(d, InlineSignatureBuilder()).asInstanceOf[InlineSignatureBuilder].names.reverse trait SignatureBuilder extends ScalaSignatureUtils { - def text(str: String): SignatureBuilder - def name(str: String, dri: DRI) = driLink(str, dri) - def driLink(text: String, dri: DRI): SignatureBuilder - - def signature(s: Signature) = s.foldLeft(this){ (b, e) => e match - case Link(name, dri) => b.driLink(name, dri) - case txt: String => b.text(txt) + def text(str: String): SignatureBuilder + def name(str: String, dri: DRI) = driLink(str, dri) + def driLink(text: String, dri: DRI): SignatureBuilder + + def signature(s: Signature) = s.foldLeft(this){ (b, e) => e match + case Link(name, dri) => b.driLink(name, dri) + case txt: String => b.text(txt) + } + + def list[E]( + elements: List[E], + prefix: String = "", + suffix: String = "", + separator: String = ", ", + )( + elemOp: (SignatureBuilder, E) => SignatureBuilder + ): SignatureBuilder = elements match { + case Nil => this + case head :: tail => + tail.foldLeft(elemOp(text(prefix), head))((b, e) => elemOp(b.text(separator), e)).text(suffix) } - def list[E]( - elements: List[E], - prefix: String = "", - suffix: String = "", - separator: String = ", ", - )( - elemOp: (SignatureBuilder, E) => SignatureBuilder - ): SignatureBuilder = elements match { - case Nil => this - case head :: tail => - tail.foldLeft(elemOp(text(prefix), head))((b, e) => elemOp(b.text(separator), e)).text(suffix) - } + def annotationsBlock(d: Member): SignatureBuilder = + d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation)} - def annotationsBlock(d: Member): SignatureBuilder = - d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation)} + def annotationsInline(d: Documentable with WithExtraProperties[_]): SignatureBuilder = + d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) } - def annotationsInline(d: Documentable with WithExtraProperties[_]): SignatureBuilder = - d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) } + private def buildAnnotation(a: Annotation): SignatureBuilder = + text("@").driLink(a.dri.getClassNames, a.dri).buildAnnotationParams(a).text(" ") - private def buildAnnotation(a: Annotation): SignatureBuilder = - text("@").driLink(a.dri.getClassNames, a.dri).buildAnnotationParams(a).text(" ") + private def buildAnnotationParams(a: Annotation): SignatureBuilder = + if !a.params.isEmpty then + list(a.params, "(", ")", ", "){ (bdr, param) => bdr.buildAnnotationParameter(param)} + else this - private def buildAnnotationParams(a: Annotation): SignatureBuilder = - if !a.params.isEmpty then - list(a.params, "(", ")", ", "){ (bdr, param) => bdr.buildAnnotationParameter(param)} - else this + private def addParameterName(txt: Option[String]): SignatureBuilder = txt match { + case Some(name) => this.text(s"$txt = ") + case _ => this + } - private def addParameterName(txt: Option[String]): SignatureBuilder = txt match { - case Some(name) => this.text(s"$txt = ") - case _ => this - } - - private def buildAnnotationParameter(a: Annotation.AnnotationParameter): SignatureBuilder = a match { - case Annotation.PrimitiveParameter(name, value) => - addParameterName(name).text(value) - case Annotation.LinkParameter(name, dri, text) => - addParameterName(name).driLink(text, dri) - case Annotation.UnresolvedParameter(name, value) => - addParameterName(name).text(value) - } + private def buildAnnotationParameter(a: Annotation.AnnotationParameter): SignatureBuilder = a match { + case Annotation.PrimitiveParameter(name, value) => + addParameterName(name).text(value) + case Annotation.LinkParameter(name, dri, text) => + addParameterName(name).driLink(text, dri) + case Annotation.UnresolvedParameter(name, value) => + addParameterName(name).text(value) + } - def modifiersAndVisibility(t: Documentable with WithAbstraction with WithVisibility with WithExtraProperties[_], kind: String) = - import org.jetbrains.dokka.model.properties._ - val extras = t.getExtra.getMap() - val (prefixMods, suffixMods) = t.modifiers.partition(_.prefix) - val all = prefixMods.map(_.name) ++ Seq(t.visibility.asSignature) ++ suffixMods.map(_.name) - - text(all.toSignatureString()).text(kind + " ") - - def typeSignature(b: Projection): SignatureBuilder = b match { - case tc: TypeConstructor => - tc.getProjections.asScala.foldLeft(this) { (bdr, elem) => elem match { - case text: UnresolvedBound => bdr.text(text.getName) - case link: TypeParameter => - bdr.driLink(link.getName, link.getDri) - case other => - bdr.text(s"TODO($other)") - } - } - case other => - text(s"TODO: $other") + def modifiersAndVisibility(t: Documentable with WithAbstraction with WithVisibility with WithExtraProperties[_], kind: String) = + import org.jetbrains.dokka.model.properties._ + val extras = t.getExtra.getMap() + val (prefixMods, suffixMods) = t.modifiers.partition(_.prefix) + val all = prefixMods.map(_.name) ++ Seq(t.visibility.asSignature) ++ suffixMods.map(_.name) + + text(all.toSignatureString()).text(kind + " ") + + def typeSignature(b: Projection): SignatureBuilder = b match { + case tc: TypeConstructor => + tc.getProjections.asScala.foldLeft(this) { (bdr, elem) => elem match { + case text: UnresolvedBound => bdr.text(text.getName) + case link: TypeParameter => + bdr.driLink(link.getName, link.getDri) + case other => + bdr.text(s"TODO($other)") } + } + case other => + text(s"TODO: $other") + } - def generics(on: WithGenerics) = list(on.getGenerics.asScala.toList, "[", "]"){ (bdr, e) => - val bldr = bdr.text(e.getName) - e.getBounds.asScala.foldLeft(bldr)( (b, bound) => b.typeSignature(bound)) - } + def generics(on: WithGenerics) = list(on.getGenerics.asScala.toList, "[", "]"){ (bdr, e) => + val bldr = bdr.text(e.getName) + e.getBounds.asScala.foldLeft(bldr)( (b, bound) => b.typeSignature(bound)) + } - def functionParameters(method: DFunction) = - val methodExtension = method.get(MethodExtension) - val receiverPos = if method.isRightAssociative() then methodExtension.parametersListSizes(0) else 0 - val (bldr, index) = methodExtension.parametersListSizes.foldLeft(this, 0){ - case ((builder, from), size) => - val toIndex = from + size - if from == toIndex then (builder.text("()"), toIndex) - else if !method.kind.isInstanceOf[Kind.Extension] || from != receiverPos then - val b = builder.list(method.getParameters.subList(from, toIndex).asScala.toList, "(", ")"){ (bdr, param) => bdr - .annotationsInline(param) - .text(param.getName) - .text(": ") - .typeSignature(param.getType) - } - (b, toIndex) - else (builder, toIndex) + def functionParameters(method: DFunction) = + val methodExtension = method.get(MethodExtension) + val receiverPos = if method.isRightAssociative() then methodExtension.parametersListSizes(0) else 0 + val (bldr, index) = methodExtension.parametersListSizes.foldLeft(this, 0){ + case ((builder, from), size) => + val toIndex = from + size + if from == toIndex then (builder.text("()"), toIndex) + else if !method.kind.isInstanceOf[Kind.Extension] || from != receiverPos then + val b = builder.list(method.getParameters.subList(from, toIndex).asScala.toList, "(", ")"){ (bdr, param) => bdr + .annotationsInline(param) + .text(param.getName) + .text(": ") + .typeSignature(param.getType) } - bldr + (b, toIndex) + else (builder, toIndex) + } + bldr } trait ScalaSignatureUtils: - extension (tokens: Seq[String]) def toSignatureString(): String = - tokens.filter(_.trim.nonEmpty).mkString(""," "," ") + extension (tokens: Seq[String]) def toSignatureString(): String = + tokens.filter(_.trim.nonEmpty).mkString(""," "," ") diff --git a/scala3doc/src/dotty/dokka/utils.scala b/scala3doc/src/dotty/dokka/utils.scala index 96a5e3c54845..bf6d272aae43 100644 --- a/scala3doc/src/dotty/dokka/utils.scala +++ b/scala3doc/src/dotty/dokka/utils.scala @@ -18,84 +18,83 @@ import org.jetbrains.dokka.DokkaConfiguration$DokkaSourceSet import org.jetbrains.dokka.plugability._ import kotlin.jvm.JvmClassMappingKt.getKotlinClass - extension [V] (a: WithExtraProperties[_]): def get(key: ExtraProperty.Key[_, V]): V = a.getExtra().getMap().get(key).asInstanceOf[V] extension [E <: WithExtraProperties[E]] (a: E): def put(value: ExtraProperty[_ >: E]): E = // TODO remove some of the InstanceOf - a.withNewExtras(a.getExtra plus value).asInstanceOf[E] + a.withNewExtras(a.getExtra plus value).asInstanceOf[E] extension [V] (map: JMap[DokkaConfiguration$DokkaSourceSet, V]): - def defaultValue: V = map.values.asScala.toSeq(0) + def defaultValue: V = map.values.asScala.toSeq(0) extension (sourceSets: Set[DokkaConfiguration$DokkaSourceSet]): - def toDisplay = sourceSets.map(DisplaySourceSet(_)).asJava + def toDisplay = sourceSets.map(DisplaySourceSet(_)).asJava class BaseKey[T, V] extends ExtraProperty.Key[T, V]: override def mergeStrategyFor(left: V, right: V): MergeStrategy[T] = - MergeStrategy.Remove.INSTANCE.asInstanceOf[MergeStrategy[T]] + MergeStrategy.Remove.INSTANCE.asInstanceOf[MergeStrategy[T]] def definedIn(e: T): Boolean = e match - case e: WithExtraProperties[_] => e.getExtra.getMap.containsKey(this) - case _ => false + case e: WithExtraProperties[_] => e.getExtra.getMap.containsKey(this) + case _ => false def getFrom(e: T): Option[V] = e match - case e: WithExtraProperties[_] => getFromExtra(e, this) - case _ => None + case e: WithExtraProperties[_] => getFromExtra(e, this) + case _ => None def getFromExtra[V](e: WithExtraProperties[_], k: ExtraProperty.Key[_, V]): Option[V] = - Option(e.getExtra.getMap.get(k)).asInstanceOf[Option[V]] + Option(e.getExtra.getMap.get(k)).asInstanceOf[Option[V]] extension (f: DFunction): def isRightAssociative(): Boolean = f.getName.endsWith(":") object JList: - def apply[T](elem: T): JList[T] = List(elem).asJava - def apply[T]() = List[T]().asJava + def apply[T](elem: T): JList[T] = List(elem).asJava + def apply[T]() = List[T]().asJava object JSet: - def apply[T](elem: T): JSet[T] = Set(elem).asJava - def apply[T]() = Set[T]().asJava + def apply[T](elem: T): JSet[T] = Set(elem).asJava + def apply[T]() = Set[T]().asJava def modifyContentGroup(originalContentNodeWithParents: Seq[ContentGroup], modifiedContentNode: ContentGroup): ContentGroup = - originalContentNodeWithParents match { - case head :: tail => tail match { - case tailHead :: tailTail => - val newChildren = tailHead.getChildren.asScala.map(c => if c != head then c else modifiedContentNode) - modifyContentGroup( - tailTail, - tailHead.copy( - newChildren.asJava, - tailHead.getDci, - tailHead.getSourceSets, - tailHead.getStyle, - tailHead.getExtra - ) - ) - case _ => head - } - case _ => modifiedContentNode + originalContentNodeWithParents match { + case head :: tail => tail match { + case tailHead :: tailTail => + val newChildren = tailHead.getChildren.asScala.map(c => if c != head then c else modifiedContentNode) + modifyContentGroup( + tailTail, + tailHead.copy( + newChildren.asJava, + tailHead.getDci, + tailHead.getSourceSets, + tailHead.getStyle, + tailHead.getExtra + ) + ) + case _ => head } + case _ => modifiedContentNode + } def getContentGroupWithParents(root: ContentGroup, condition: ContentGroup => Boolean): Seq[ContentGroup] = { - def getFirstMatch(list: List[ContentNode]): Seq[ContentGroup] = list match { - case head :: tail => head match { - case g: ContentGroup => - val res = getContentGroupWithParents(g, condition) - if(!res.isEmpty) res - else getFirstMatch(tail) - case _ => getFirstMatch(tail) - } - - case _ => Seq() - } - if(condition(root)) Seq(root) - else { - val res = getFirstMatch(root.getChildren.asScala.toList) - if(!res.isEmpty) res ++ Seq(root) - else Seq() + def getFirstMatch(list: List[ContentNode]): Seq[ContentGroup] = list match { + case head :: tail => head match { + case g: ContentGroup => + val res = getContentGroupWithParents(g, condition) + if(!res.isEmpty) res + else getFirstMatch(tail) + case _ => getFirstMatch(tail) } + + case _ => Seq() + } + if(condition(root)) Seq(root) + else { + val res = getFirstMatch(root.getChildren.asScala.toList) + if(!res.isEmpty) res ++ Seq(root) + else Seq() + } } diff --git a/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala b/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala index 484838132377..806c102b4f3f 100644 --- a/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala +++ b/scala3doc/src/dotty/renderers/DotDiagramBuilder.scala @@ -10,36 +10,36 @@ import HTML._ import dotty.dokka.model.api._ object DotDiagramBuilder: - def build(diagram: HierarchyGraph, renderer: SignatureRenderer): String = - val vWithId = diagram.verteciesWithId - val vertecies = vWithId.map { (vertex, id) => - s"""node${id} [label="${getHtmlLabel(vertex, renderer)}", style="${getStyle(vertex)}"];\n""" - }.mkString - - val edges = diagram.edges.map { (from, to) => - s"""node${vWithId(from)} -> node${vWithId(to)};\n""" - }.mkString - - s""" digraph g { - | $vertecies - | - | $edges - |} - |""".stripMargin - - - private def getStyle(vertex: LinkToType) = vertex.kind match - case Kind.Class => "fill: #45AD7D;" - case Kind.Object => "fill: #285577;" - case Kind.Trait => "fill: #1CAACF;" - case Kind.Enum => "fill: #B66722;" - case Kind.EnumCase => "fill: #B66722;" - case other => sys.error(s"unexpected value: $other") - - - private def getHtmlLabel(vertex: LinkToType, renderer: SignatureRenderer): String = - span(style := "color: #FFFFFF;")( - vertex.kind.name, - " ", - vertex.signature.map(renderer.renderElementWith(_)) - ).toString.replace("\"", "\\\"") + def build(diagram: HierarchyGraph, renderer: SignatureRenderer): String = + val vWithId = diagram.verteciesWithId + val vertecies = vWithId.map { (vertex, id) => + s"""node${id} [label="${getHtmlLabel(vertex, renderer)}", style="${getStyle(vertex)}"];\n""" + }.mkString + + val edges = diagram.edges.map { (from, to) => + s"""node${vWithId(from)} -> node${vWithId(to)};\n""" + }.mkString + + s""" digraph g { + | $vertecies + | + | $edges + |} + |""".stripMargin + + + private def getStyle(vertex: LinkToType) = vertex.kind match + case Kind.Class => "fill: #45AD7D;" + case Kind.Object => "fill: #285577;" + case Kind.Trait => "fill: #1CAACF;" + case Kind.Enum => "fill: #B66722;" + case Kind.EnumCase => "fill: #B66722;" + case other => sys.error(s"unexpected value: $other") + + + private def getHtmlLabel(vertex: LinkToType, renderer: SignatureRenderer): String = + span(style := "color: #FFFFFF;")( + vertex.kind.name, + " ", + vertex.signature.map(renderer.renderElementWith(_)) + ).toString.replace("\"", "\\\"") diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index dcfbb0b6e322..c145a9de75b1 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -20,280 +20,280 @@ import org.jetbrains.dokka.base.resolvers.local.LocationProvider class SignatureRenderer(pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet], locationProvider: LocationProvider): - def link(dri: DRI): Option[String] = Option(locationProvider.resolve(dri, sourceSetRestriciton, pageContext)) + def link(dri: DRI): Option[String] = Option(locationProvider.resolve(dri, sourceSetRestriciton, pageContext)) - def renderLink(name: String, dri: DRI, modifiers: AppliedAttr*) = - link(dri) match - case Some(link) => a(href := link, modifiers)(name) - case _ => span(Attr("data-unresolved-link") := dri.toString, modifiers)(name) + def renderLink(name: String, dri: DRI, modifiers: AppliedAttr*) = + link(dri) match + case Some(link) => a(href := link, modifiers)(name) + case _ => span(Attr("data-unresolved-link") := dri.toString, modifiers)(name) - def renderElementWith(e: String | (String, DRI) | Link, modifiers: AppliedAttr*) = e match - case (name, dri) => renderLink(name, dri, modifiers:_*) - case name: String => raw(name) - case Link(name, dri) => renderLink(name, dri, modifiers:_*) + def renderElementWith(e: String | (String, DRI) | Link, modifiers: AppliedAttr*) = e match + case (name, dri) => renderLink(name, dri, modifiers:_*) + case name: String => raw(name) + case Link(name, dri) => renderLink(name, dri, modifiers:_*) - def renderElement(e: String | (String, DRI) | Link) = renderElementWith(e) + def renderElement(e: String | (String, DRI) | Link) = renderElementWith(e) class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) { - lazy val sourceSets = ctx.getConfiguration.getSourceSets.asScala - .map(s => DisplaySourceSetKt.toDisplaySourceSet(s.asInstanceOf[DokkaConfiguration$DokkaSourceSet])).toSet.asJava - - type FlowContentConsumer = kotlin.jvm.functions.Function1[? >: kotlinx.html.FlowContent, kotlin.Unit] - - override def buildTable(f: FlowContent, node: ContentTable, pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet]) = { - val nodeStyles = node.getStyle.asScala.toSet - if nodeStyles.contains(TableStyle.DescriptionList) || nodeStyles.contains(TableStyle.NestedDescriptionList) then - withHtml(f, buildDescriptionList(node, pageContext, sourceSetRestriciton)) - else super.buildTable(f, node, pageContext, sourceSetRestriciton) - } - - override def wrapGroup(f: FlowContent, node: ContentGroup, pageContext: ContentPage, childrenCallback: FlowContentConsumer) = { - val additionalClasses = node.getStyle.asScala.map(_.toString.toLowerCase).mkString("", ",", "") - def buildSymbol: String = div(cls := s"symbol $additionalClasses")( - raw( - buildWithKotlinx(childrenCallback).toString - ) - ).toString - if node.getDci.getKind == ContentKind.Symbol && node.getStyle.asScala.toSet.contains(TextStyle.Monospace) then withHtml(f, buildSymbol) else super.wrapGroup(f, node, pageContext, childrenCallback) + lazy val sourceSets = ctx.getConfiguration.getSourceSets.asScala + .map(s => DisplaySourceSetKt.toDisplaySourceSet(s.asInstanceOf[DokkaConfiguration$DokkaSourceSet])).toSet.asJava + + type FlowContentConsumer = kotlin.jvm.functions.Function1[? >: kotlinx.html.FlowContent, kotlin.Unit] + + override def buildTable(f: FlowContent, node: ContentTable, pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet]) = { + val nodeStyles = node.getStyle.asScala.toSet + if nodeStyles.contains(TableStyle.DescriptionList) || nodeStyles.contains(TableStyle.NestedDescriptionList) then + withHtml(f, buildDescriptionList(node, pageContext, sourceSetRestriciton)) + else super.buildTable(f, node, pageContext, sourceSetRestriciton) + } + + override def wrapGroup(f: FlowContent, node: ContentGroup, pageContext: ContentPage, childrenCallback: FlowContentConsumer) = { + val additionalClasses = node.getStyle.asScala.map(_.toString.toLowerCase).mkString("", ",", "") + def buildSymbol: String = div(cls := s"symbol $additionalClasses")( + raw( + buildWithKotlinx(childrenCallback).toString + ) + ).toString + if node.getDci.getKind == ContentKind.Symbol && node.getStyle.asScala.toSet.contains(TextStyle.Monospace) then withHtml(f, buildSymbol) else super.wrapGroup(f, node, pageContext, childrenCallback) + } + + override def buildContentNode(f: FlowContent, node: ContentNode, pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet]) = { + node match { + case n: HtmlContentNode => withHtml(f, raw(n.body).toString) + case n: HierarchyGraphContentNode => buildDiagram(f, n.diagram, pageContext) + case n: DocumentableList => + val ss = if sourceSetRestriciton == null then Set.empty.asJava else sourceSetRestriciton + withHtml(f, buildDocumentableList(n, pageContext, ss).toString()) + case n: DocumentableFilter => withHtml(f, buildDocumentableFilter.toString) + case other => super.buildContentNode(f, node, pageContext, sourceSetRestriciton) } + } + + private val anchor = raw(""" + + + + + """) + + + + private def buildDocumentableList(n: DocumentableList, pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet]) = + def render(n: ContentNode) = raw(buildWithKotlinx(n, pageContext, null)) + + val renderer = SignatureRenderer(pageContext, sourceSets, getLocationProvider) + import renderer._ + + def buildDocumentable(element: DocumentableElement) = + def topLevelAttr = Seq(cls := "documentableElement") ++ element.attributes.map{ case (n, v) => Attr(s"data-f-$n") := v } + val kind = element.modifiers.takeRight(1) + val otherModifiers = element.modifiers.dropRight(1) + + div(topLevelAttr:_*)( + div(cls := "annotations monospace")(element.annotations.map(renderElement)), + div( + a(href:=link(element.params.dri).getOrElse("#"), cls := "documentableAnchor")(anchor), + span(cls := "modifiers monospace")( + span(cls := "other-modifiers")(otherModifiers.map(renderElement)), + span(cls := "kind")(kind.map(renderElement)), + ), + renderLink(element.name, element.params.dri, cls := "documentableName monospace"), + span(cls := "signature monospace")(element.signature.map(renderElement)), + div( + div(cls := "originInfo")(element.originInfo.map(renderElement)), + div(cls := "documentableBrief")(element.brief.map(render)), + ) + ), - override def buildContentNode(f: FlowContent, node: ContentNode, pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet]) = { - node match { - case n: HtmlContentNode => withHtml(f, raw(n.body).toString) - case n: HierarchyGraphContentNode => buildDiagram(f, n.diagram, pageContext) - case n: DocumentableList => - val ss = if sourceSetRestriciton == null then Set.empty.asJava else sourceSetRestriciton - withHtml(f, buildDocumentableList(n, pageContext, ss).toString()) - case n: DocumentableFilter => withHtml(f, buildDocumentableFilter.toString) - case other => super.buildContentNode(f, node, pageContext, sourceSetRestriciton) - } + ) + + div(cls := "documentableList")( + if(n.groupName.isEmpty) raw("") else h3(cls := "documentableHeader")(n.groupName.map(renderElement)), + n.elements.flatMap { + case element: DocumentableElement => + Seq(buildDocumentable(element)) + case group: DocumentableElementGroup => + h4(cls := "documentable-extension-target")( + group.header.map(renderElement) + ) +: group.elements.map(buildDocumentable) } - - private val anchor = raw(""" - - - - - """) - - - - private def buildDocumentableList(n: DocumentableList, pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet]) = - def render(n: ContentNode) = raw(buildWithKotlinx(n, pageContext, null)) - - val renderer = SignatureRenderer(pageContext, sourceSets, getLocationProvider) - import renderer._ - - def buildDocumentable(element: DocumentableElement) = - def topLevelAttr = Seq(cls := "documentableElement") ++ element.attributes.map{ case (n, v) => Attr(s"data-f-$n") := v } - val kind = element.modifiers.takeRight(1) - val otherModifiers = element.modifiers.dropRight(1) - - div(topLevelAttr:_*)( - div(cls := "annotations monospace")(element.annotations.map(renderElement)), - div( - a(href:=link(element.params.dri).getOrElse("#"), cls := "documentableAnchor")(anchor), - span(cls := "modifiers monospace")( - span(cls := "other-modifiers")(otherModifiers.map(renderElement)), - span(cls := "kind")(kind.map(renderElement)), - ), - renderLink(element.name, element.params.dri, cls := "documentableName monospace"), - span(cls := "signature monospace")(element.signature.map(renderElement)), - div( - div(cls := "originInfo")(element.originInfo.map(renderElement)), - div(cls := "documentableBrief")(element.brief.map(render)), - ) - ), - - ) - - div(cls := "documentableList")( - if(n.groupName.isEmpty) raw("") else h3(cls := "documentableHeader")(n.groupName.map(renderElement)), - n.elements.flatMap { - case element: DocumentableElement => - Seq(buildDocumentable(element)) - case group: DocumentableElementGroup => - h4(cls := "documentable-extension-target")( - group.header.map(renderElement) - ) +: group.elements.map(buildDocumentable) - } - ) - - private def buildDocumentableFilter = div(cls := "documentableFilter")( - div(cls := "filterUpperContainer")( - button(cls := "filterToggleButton")( - raw(""" - - - - - """) - ), - input(cls := "filterableInput", placeholder := "Filter all members") - ), - div(cls := "filterLowerContainer")() ) - def buildDescriptionList(node: ContentTable, pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet]) = { - val children = node.getChildren.asScala.toList.zipWithIndex - val nodeStyles = node.getStyle.asScala.toSet - val classes = if nodeStyles.contains(TableStyle.NestedDescriptionList) then "paramsdesc" else "attributes" - dl(cls := classes)( - children.map((e, i) => - if(i % 2 == 0) - dt( - raw( - buildWithKotlinx(e, pageContext, sourceSetRestriciton) - ) - ) - else - dd( - raw( - buildWithKotlinx(e, pageContext, sourceSetRestriciton) - ) - ) + private def buildDocumentableFilter = div(cls := "documentableFilter")( + div(cls := "filterUpperContainer")( + button(cls := "filterToggleButton")( + raw(""" + + + + + """) + ), + input(cls := "filterableInput", placeholder := "Filter all members") + ), + div(cls := "filterLowerContainer")() + ) + + def buildDescriptionList(node: ContentTable, pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet]) = { + val children = node.getChildren.asScala.toList.zipWithIndex + val nodeStyles = node.getStyle.asScala.toSet + val classes = if nodeStyles.contains(TableStyle.NestedDescriptionList) then "paramsdesc" else "attributes" + dl(cls := classes)( + children.map((e, i) => + if(i % 2 == 0) + dt( + raw( + buildWithKotlinx(e, pageContext, sourceSetRestriciton) ) - ).toString - } - - override def buildResolvedLink( - f: FlowContent, - node: ContentResolvedLink, - pageContext: ContentPage, - sourceSetRestriction: JSet[DisplaySourceSet], - ): Unit = { - import kotlinx.html.{Gen_consumer_tagsKt => dsl} - val c = f.getConsumer - val U = kotlin.Unit.INSTANCE - dsl.a(c, node.getAddress, /*target*/ null, /*classes*/ null, { e => - import ScalaCommentToContentConverter._ - // node.getExtra.getMap.asScala.get(LinkAttributesKey) - Option(node.get(LinkAttributesKey).asInstanceOf[ExtraLinkAttributes]) - .flatMap(_.title) - .foreach(e.getAttributes.put("title", _)) - buildText(f, node.getChildren, pageContext, sourceSetRestriction) - U - }) - } - - override def buildCodeBlock( - f: FlowContent, - code: ContentCodeBlock, - pageContext: ContentPage, - ): Unit = { - // we cannot use Scalatags, because we need to call buildContentNode - import kotlinx.html.{Gen_consumer_tagsKt => dsl} - val c = f.getConsumer - val U = kotlin.Unit.INSTANCE - - dsl.div(c, "sample-container", { e => - dsl.pre(c, null, { e => - val codeClass = code.getStyle.asScala.iterator.map(_.toString.toLowerCase).mkString("", " ", " language-scala") - dsl.code(c, codeClass, { e => - e.getAttributes.put("theme", "idea") - code.getChildren.asScala.foreach(buildContentNode(f, _, pageContext, /*sourceSetRestriction*/ null)) - U - }) - U - }) - U + ) + else + dd( + raw( + buildWithKotlinx(e, pageContext, sourceSetRestriciton) + ) + ) + ) + ).toString + } + + override def buildResolvedLink( + f: FlowContent, + node: ContentResolvedLink, + pageContext: ContentPage, + sourceSetRestriction: JSet[DisplaySourceSet], + ): Unit = { + import kotlinx.html.{Gen_consumer_tagsKt => dsl} + val c = f.getConsumer + val U = kotlin.Unit.INSTANCE + dsl.a(c, node.getAddress, /*target*/ null, /*classes*/ null, { e => + import ScalaCommentToContentConverter._ + // node.getExtra.getMap.asScala.get(LinkAttributesKey) + Option(node.get(LinkAttributesKey).asInstanceOf[ExtraLinkAttributes]) + .flatMap(_.title) + .foreach(e.getAttributes.put("title", _)) + buildText(f, node.getChildren, pageContext, sourceSetRestriction) + U + }) + } + + override def buildCodeBlock( + f: FlowContent, + code: ContentCodeBlock, + pageContext: ContentPage, + ): Unit = { + // we cannot use Scalatags, because we need to call buildContentNode + import kotlinx.html.{Gen_consumer_tagsKt => dsl} + val c = f.getConsumer + val U = kotlin.Unit.INSTANCE + + dsl.div(c, "sample-container", { e => + dsl.pre(c, null, { e => + val codeClass = code.getStyle.asScala.iterator.map(_.toString.toLowerCase).mkString("", " ", " language-scala") + dsl.code(c, codeClass, { e => + e.getAttributes.put("theme", "idea") + code.getChildren.asScala.foreach(buildContentNode(f, _, pageContext, /*sourceSetRestriction*/ null)) + U }) - } + U + }) + U + }) + } + + def buildDiagram(f: FlowContent, diagram: HierarchyGraph, pageContext: ContentPage) = + val renderer = SignatureRenderer(pageContext, sourceSets, getLocationProvider) + withHtml(f, div( id := "inheritance-diagram", cls := "diagram-class")( + svg(id := "graph"), + script(`type` := "text/dot", id := "dot")(raw(DotDiagramBuilder.build(diagram, renderer))), + ).toString() + ) - def buildDiagram(f: FlowContent, diagram: HierarchyGraph, pageContext: ContentPage) = - val renderer = SignatureRenderer(pageContext, sourceSets, getLocationProvider) - withHtml(f, div( id := "inheritance-diagram", cls := "diagram-class")( - svg(id := "graph"), - script(`type` := "text/dot", id := "dot")(raw(DotDiagramBuilder.build(diagram, renderer))), - ).toString() - ) - - override def buildHtml(page: PageNode, resources: JList[String], kotlinxContent: FlowContentConsumer): String = - val (pageTitle, pageResources, fromTemplate) = page match - case static: BaseStaticSiteProcessor.StaticPageNode => - val res = if static.hasFrame then resources else static.resources - val title = static.getLoadedTemplate.getTemplateFile.title - (title, res, !static.hasFrame) - case _ => - (page.getName, resources, false) - html( - head( - meta(charset := "utf-8"), - meta(name := "viewport", content := "width=device-width, initial-scale=1"), - title(pageTitle), - linkResources(page, pageResources.asScala).toSeq, - script(raw(s"""var pathToRoot = "${getLocationProvider.pathToRoot(page)}";""")) + override def buildHtml(page: PageNode, resources: JList[String], kotlinxContent: FlowContentConsumer): String = + val (pageTitle, pageResources, fromTemplate) = page match + case static: BaseStaticSiteProcessor.StaticPageNode => + val res = if static.hasFrame then resources else static.resources + val title = static.getLoadedTemplate.getTemplateFile.title + (title, res, !static.hasFrame) + case _ => + (page.getName, resources, false) + html( + head( + meta(charset := "utf-8"), + meta(name := "viewport", content := "width=device-width, initial-scale=1"), + title(pageTitle), + linkResources(page, pageResources.asScala).toSeq, + script(raw(s"""var pathToRoot = "${getLocationProvider.pathToRoot(page)}";""")) + ), + body( + if fromTemplate then + raw(buildWithKotlinx(kotlinxContent)) + else + div(id := "container")( + div(id := "leftColumn")( + div(id := "logo"), + div(id := "paneSearch"), + nav(id := "sideMenu"), ), - body( - if fromTemplate then - raw(buildWithKotlinx(kotlinxContent)) - else - div(id := "container")( - div(id := "leftColumn")( - div(id := "logo"), - div(id := "paneSearch"), - nav(id := "sideMenu"), - ), - div(id := "main")( - div (id := "leftToggler")( - span(cls := "icon-toggler") - ), - div(id := "searchBar"), - main( - raw(buildWithKotlinx(kotlinxContent)) - ), - footer( - span(cls := "go-to-top-icon")( - a(href := "#container")( - span(cls:="icon-vertical_align_top"), - raw(" Back to top") - ) - ), - span(cls := "pull-right")( - raw("Generated by "), - a(href := "https://github.com/lampepfl/scala3doc")("Scala3doc") - ) - ) - ) - ), - script(`type` := "text/javascript", src := resolveRoot(page, "scripts/pages.js")), - script(`type` := "text/javascript", src := resolveRoot(page, "scripts/main.js")) + div(id := "main")( + div (id := "leftToggler")( + span(cls := "icon-toggler") + ), + div(id := "searchBar"), + main( + raw(buildWithKotlinx(kotlinxContent)) + ), + footer( + span(cls := "go-to-top-icon")( + a(href := "#container")( + span(cls:="icon-vertical_align_top"), + raw(" Back to top") + ) + ), + span(cls := "pull-right")( + raw("Generated by "), + a(href := "https://github.com/lampepfl/scala3doc")("Scala3doc") + ) + ) ) - ).toString - - private def resolveRoot(page: PageNode, path: String) = - getLocationProvider.pathToRoot(page) + path - - private def linkResources(page: PageNode, resources: Iterable[String]): Iterable[AppliedTag] = - def fileExtension(url: String): String = - val param = url.indexOf('?') - val end = if param < 0 then url.length else param - val point = url.lastIndexOf('.', end) - url.substring(point+1, end) - - def resolveLink(url: String): String = - if URI(url).isAbsolute then url else resolveRoot(page, url) - - for res <- resources yield - fileExtension(res) match - case "css" => link(rel := "stylesheet", href := resolveLink(res)) - case "js" => script(`type` := "text/javascript", src := resolveLink(res), defer := "true") - case _ => raw(res) - - private def buildWithKotlinx(node: ContentNode, pageContext: ContentPage, sourceSetRestriction: JSet[DisplaySourceSet]): String = - Gen_consumer_tagsKt.div( - StreamKt.createHTML(true, false), - null, - (div) => {build(node, div, pageContext, sourceSetRestriction); kotlin.Unit.INSTANCE} - ).toString.stripPrefix("
").stripSuffix("
\n") - - private def buildWithKotlinx(func: FlowContentConsumer): String = - Gen_consumer_tagsKt.div( - StreamKt.createHTML(true, false), - null, - func - ).toString.stripPrefix("
").stripSuffix("
\n") + ), + script(`type` := "text/javascript", src := resolveRoot(page, "scripts/pages.js")), + script(`type` := "text/javascript", src := resolveRoot(page, "scripts/main.js")) + ) + ).toString + + private def resolveRoot(page: PageNode, path: String) = + getLocationProvider.pathToRoot(page) + path + + private def linkResources(page: PageNode, resources: Iterable[String]): Iterable[AppliedTag] = + def fileExtension(url: String): String = + val param = url.indexOf('?') + val end = if param < 0 then url.length else param + val point = url.lastIndexOf('.', end) + url.substring(point+1, end) + + def resolveLink(url: String): String = + if URI(url).isAbsolute then url else resolveRoot(page, url) + + for res <- resources yield + fileExtension(res) match + case "css" => link(rel := "stylesheet", href := resolveLink(res)) + case "js" => script(`type` := "text/javascript", src := resolveLink(res), defer := "true") + case _ => raw(res) + + private def buildWithKotlinx(node: ContentNode, pageContext: ContentPage, sourceSetRestriction: JSet[DisplaySourceSet]): String = + Gen_consumer_tagsKt.div( + StreamKt.createHTML(true, false), + null, + (div) => {build(node, div, pageContext, sourceSetRestriction); kotlin.Unit.INSTANCE} + ).toString.stripPrefix("
").stripSuffix("
\n") + + private def buildWithKotlinx(func: FlowContentConsumer): String = + Gen_consumer_tagsKt.div( + StreamKt.createHTML(true, false), + null, + func + ).toString.stripPrefix("
").stripSuffix("
\n") } diff --git a/scala3doc/test/dotty/dokka/DottyTestRunner.scala b/scala3doc/test/dotty/dokka/DottyTestRunner.scala index b2dc7e163cfb..1345ac644288 100644 --- a/scala3doc/test/dotty/dokka/DottyTestRunner.scala +++ b/scala3doc/test/dotty/dokka/DottyTestRunner.scala @@ -21,106 +21,106 @@ import org.jetbrains.dokka.pages.ContentNodesKt import dotty.dokka.model.api.Link abstract class DottyAbstractCoreTest extends AbstractCoreTest: - private def getTempDir() : TemporaryFolder = - val folder = new TemporaryFolder() - folder.create() - folder - - private def args = Args( - name = "test", - tastyRoots = Nil , - classpath = System.getProperty("java.class.path"), - None, - output = getTempDir().getRoot, - projectVersion = "1.0", - projectTitle = None, - projectLogo = None, - defaultSyntax = None, - sourceLinks = List.empty - ) - - def listPages(tastyDir: String): Seq[ContentPage] = - var signatures: Seq[ContentPage] = Nil - val tests = new AbstractCoreTest$TestBuilder() - - - def getAllContentPages(root: PageNode) : Seq[ContentPage] = root match - case c: ContentPage => Seq(c) ++ c.getChildren.asScala.flatMap(getAllContentPages) - case default => default.getChildren.asScala.toSeq.flatMap(getAllContentPages) - - tests.setPagesTransformationStage { root => - val res = root.getChildren.asScala.flatMap(getAllContentPages) - signatures = res.toSeq - kotlin.Unit.INSTANCE + private def getTempDir() : TemporaryFolder = + val folder = new TemporaryFolder() + folder.create() + folder + + private def args = Args( + name = "test", + tastyRoots = Nil , + classpath = System.getProperty("java.class.path"), + None, + output = getTempDir().getRoot, + projectVersion = "1.0", + projectTitle = None, + projectLogo = None, + defaultSyntax = None, + sourceLinks = List.empty + ) + + def listPages(tastyDir: String): Seq[ContentPage] = + var signatures: Seq[ContentPage] = Nil + val tests = new AbstractCoreTest$TestBuilder() + + + def getAllContentPages(root: PageNode) : Seq[ContentPage] = root match + case c: ContentPage => Seq(c) ++ c.getChildren.asScala.flatMap(getAllContentPages) + case default => default.getChildren.asScala.toSeq.flatMap(getAllContentPages) + + tests.setPagesTransformationStage { root => + val res = root.getChildren.asScala.flatMap(getAllContentPages) + signatures = res.toSeq + kotlin.Unit.INSTANCE + } + + def listTastyFiles(f: File): Seq[File] = + assertTrue(s"Tasty root dir does not exisits: $f", f.isDirectory()) + val (files, dirs) = f.listFiles().partition(_.isFile) + files.toIndexedSeq.filter(_.getName.endsWith(".tasty")) ++ dirs.flatMap(listTastyFiles) + + val tastyFiles = tastyDir.split(File.pathSeparatorChar).toList.flatMap(p => listTastyFiles(new File(p))).map(_.toString) + + val config = new DottyDokkaConfig(DocConfiguration.Standalone(args, tastyFiles, Nil)) + DokkaTestGenerator( + config, + new TestLogger(DokkaConsoleLogger.INSTANCE), + tests.build(), + Nil.asJava + ).generate() + + signatures + + def signaturesFromDocumentation(tastyDir: String): Seq[String] = + def flattenToText(node: ContentNode) : Seq[String] = node match + case t: ContentText => Seq(t.getText) + case c: ContentComposite => + c.getChildren.asScala.flatMap(flattenToText).toSeq + case l: DocumentableElement => + (l.annotations ++ Seq(" ") ++ l.modifiers ++ Seq(l.name) ++ l.signature).map { + case s: String => s + case Link(s: String, _) => s } + case _ => Seq() - def listTastyFiles(f: File): Seq[File] = - assertTrue(s"Tasty root dir does not exisits: $f", f.isDirectory()) - val (files, dirs) = f.listFiles().partition(_.isFile) - files.toIndexedSeq.filter(_.getName.endsWith(".tasty")) ++ dirs.flatMap(listTastyFiles) - - val tastyFiles = tastyDir.split(File.pathSeparatorChar).toList.flatMap(p => listTastyFiles(new File(p))).map(_.toString) - - val config = new DottyDokkaConfig(DocConfiguration.Standalone(args, tastyFiles, Nil)) - DokkaTestGenerator( - config, - new TestLogger(DokkaConsoleLogger.INSTANCE), - tests.build(), - Nil.asJava - ).generate() - - signatures - - def signaturesFromDocumentation(tastyDir: String): Seq[String] = - def flattenToText(node: ContentNode) : Seq[String] = node match - case t: ContentText => Seq(t.getText) - case c: ContentComposite => - c.getChildren.asScala.flatMap(flattenToText).toSeq - case l: DocumentableElement => - (l.annotations ++ Seq(" ") ++ l.modifiers ++ Seq(l.name) ++ l.signature).map { - case s: String => s - case Link(s: String, _) => s - } - case _ => Seq() - - def all(p: ContentNode => Boolean)(n: ContentNode): Seq[ContentNode] = - if p(n) then Seq(n) else n.getChildren.asScala.toSeq.flatMap(all(p)) - - - val pages = listPages(tastyDir) - val nodes = pages.flatMap(p => all(_.isInstanceOf[DocumentableElement])(p.getContent)) - nodes.map(flattenToText(_).mkString.trim) - - def signaturesFromSource(s: Source): SignaturesFromSource = - val ExpectedRegex = ".+//expected: (.+)".r - val UnexpectedRegex = "(.+)//unexpected".r - - // e.g. to remove '(0)' from object IAmACaseObject extends CaseImplementThis/*<-*/(0)/*->*/ - val CommentRegexp = """\/\*<-\*\/[^\/]+\/\*->\*\/""" - - extension (s: String) def doesntStartWithAnyOfThese(c: Char*) = c.forall(char => !s.startsWith(char.toString)) - val lines = s.getLines().map(_.trim).toList - .filter(_.doesntStartWithAnyOfThese('=',':','{','}')) - .filterNot(_.trim.isEmpty) - .filterNot(_.startsWith("//")) - - val expectedSignatures = lines.flatMap { - case UnexpectedRegex(_) => None - case ExpectedRegex(signature) => Some(signature) - case other => - Some(other.replaceAll(CommentRegexp, "").replaceAll(" +", " ")) - } + def all(p: ContentNode => Boolean)(n: ContentNode): Seq[ContentNode] = + if p(n) then Seq(n) else n.getChildren.asScala.toSeq.flatMap(all(p)) - val unexpectedSignatures = lines.collect { - case UnexpectedRegex(signature) => signature.trim - } - SignaturesFromSource(expectedSignatures, unexpectedSignatures) + val pages = listPages(tastyDir) + val nodes = pages.flatMap(p => all(_.isInstanceOf[DocumentableElement])(p.getContent)) + nodes.map(flattenToText(_).mkString.trim) + + def signaturesFromSource(s: Source): SignaturesFromSource = + val ExpectedRegex = ".+//expected: (.+)".r + val UnexpectedRegex = "(.+)//unexpected".r + + // e.g. to remove '(0)' from object IAmACaseObject extends CaseImplementThis/*<-*/(0)/*->*/ + val CommentRegexp = """\/\*<-\*\/[^\/]+\/\*->\*\/""" + + extension (s: String) def doesntStartWithAnyOfThese(c: Char*) = c.forall(char => !s.startsWith(char.toString)) + val lines = s.getLines().map(_.trim).toList + .filter(_.doesntStartWithAnyOfThese('=',':','{','}')) + .filterNot(_.trim.isEmpty) + .filterNot(_.startsWith("//")) + + val expectedSignatures = lines.flatMap { + case UnexpectedRegex(_) => None + case ExpectedRegex(signature) => Some(signature) + case other => + Some(other.replaceAll(CommentRegexp, "").replaceAll(" +", " ")) + } + + val unexpectedSignatures = lines.collect { + case UnexpectedRegex(signature) => signature.trim + } + + SignaturesFromSource(expectedSignatures, unexpectedSignatures) - val _collector = new ErrorCollector(); - @Rule - def collector = _collector - def reportError(msg: String) = collector.addError(new AssertionError(msg)) + val _collector = new ErrorCollector(); + @Rule + def collector = _collector + def reportError(msg: String) = collector.addError(new AssertionError(msg)) case class SignaturesFromSource(expected: Seq[String], unexpected: Seq[String]) diff --git a/scala3doc/test/dotty/dokka/MultipleFileTest.scala b/scala3doc/test/dotty/dokka/MultipleFileTest.scala index cb32e1d528dd..84c06183e681 100644 --- a/scala3doc/test/dotty/dokka/MultipleFileTest.scala +++ b/scala3doc/test/dotty/dokka/MultipleFileTest.scala @@ -12,81 +12,81 @@ import scala.jdk.CollectionConverters._ import scala.math.max object MultipleFileTest{ - val classlikeKinds = Seq("class", "object", "trait") // TODO add docs for packages - val members = Seq("type", "def", "val", "var") - val all = classlikeKinds ++ members + val classlikeKinds = Seq("class", "object", "trait") // TODO add docs for packages + val members = Seq("type", "def", "val", "var") + val all = classlikeKinds ++ members } abstract class MultipleFileTest(val sourceFiles: List[String], val tastyFolders: List[String], signatureKinds: Seq[String], ignoreUndocumentedSignatures: Boolean = false ) extends DottyAbstractCoreTest: - private val _collector = new ErrorCollector(); - - // This should work correctly except for names in backticks and operator names containing a colon - def extractSymbolName(signature: String) = - val Pattern = s"""(?s).*(?:${signatureKinds.mkString("|")}) ([^\\[(: \\n\\t]+).*""".r - signature match { - case Pattern(name) => name - case x => "NULL" - } - - def matchSignature(s: String, signatureList: List[String]): Seq[String] = - val symbolName = extractSymbolName(s) - val candidates = signatureList.filter(extractSymbolName(_) == symbolName) - - candidates.filter(_ == s) match { - case Nil => - val candidateMsg = - if candidates.isEmpty then s"No candidate found for symbol name $symbolName" - else s"Candidates:\n${candidates.mkString("\n")}\n" - - //reportError(s"No match for:\n$s\n$candidateMsg") All test would fail because of documented inherited methods - //println(s"No match for:\n$s\n$candidateMsg") - Nil - case matching => - matching - } - - @Test - def testSignatures(): Unit = - def cleanup(s: String) = s.replace("\n", " ").replaceAll(" +", " ") - - val allFromSource = sourceFiles.map{ file => - val all = signaturesFromSource(Source.fromFile(s"${BuildInfo.test_testcasesSourceRoot}/tests/$file.scala")) - (all.expected, all.unexpected) - } - - val expectedFromSource = allFromSource.map(_._1).flatten.filter(extractSymbolName(_) != "NULL").map(cleanup) - val unexpectedFromSource = allFromSource.map(_._2).flatten.filter(extractSymbolName(_) != "NULL").map(cleanup) - val unexpectedSignatureSymbolNames = unexpectedFromSource.map(extractSymbolName) - - val allFromDocumentation = tastyFolders.flatMap(folder => signaturesFromDocumentation(s"${BuildInfo.test_testcasesOutputDir}/tests/$folder")) - val fromDocumentation = allFromDocumentation.filter(extractSymbolName(_) != "NULL").map(cleanup) - - val documentedSignatures = fromDocumentation.flatMap(matchSignature(_, expectedFromSource)).toSet - val missingSignatures = expectedFromSource.filterNot(documentedSignatures.contains) - - val unexpectedSignatures = - fromDocumentation.filter(s => unexpectedSignatureSymbolNames.contains(extractSymbolName(s))).toSet - - val reportMissingSignatures = !ignoreUndocumentedSignatures && missingSignatures.nonEmpty - val reportUnexpectedSignatures = unexpectedSignatures.nonEmpty - - if reportMissingSignatures || reportUnexpectedSignatures then - val missingSignaturesMessage = Option.when(reportMissingSignatures) - (s"Not documented signatures:\n${missingSignatures.mkString("\n")}") - - val unexpectedSignaturesMessage = Option.when(reportUnexpectedSignatures) - (s"Unexpectedly documented signatures:\n${unexpectedSignatures.mkString("\n")}") - - val allSignaturesMessage = - s""" - |All documented signatures: - |${documentedSignatures.mkString("\n")} - | - |All expected signatures from source: - |${expectedFromSource.mkString("\n")} - """.stripMargin - - val errorMessages = missingSignaturesMessage ++ unexpectedSignaturesMessage ++ Some(allSignaturesMessage) - - reportError(errorMessages.mkString("\n", "\n\n", "\n")) + private val _collector = new ErrorCollector(); + + // This should work correctly except for names in backticks and operator names containing a colon + def extractSymbolName(signature: String) = + val Pattern = s"""(?s).*(?:${signatureKinds.mkString("|")}) ([^\\[(: \\n\\t]+).*""".r + signature match { + case Pattern(name) => name + case x => "NULL" + } + + def matchSignature(s: String, signatureList: List[String]): Seq[String] = + val symbolName = extractSymbolName(s) + val candidates = signatureList.filter(extractSymbolName(_) == symbolName) + + candidates.filter(_ == s) match { + case Nil => + val candidateMsg = + if candidates.isEmpty then s"No candidate found for symbol name $symbolName" + else s"Candidates:\n${candidates.mkString("\n")}\n" + + //reportError(s"No match for:\n$s\n$candidateMsg") All test would fail because of documented inherited methods + //println(s"No match for:\n$s\n$candidateMsg") + Nil + case matching => + matching + } + + @Test + def testSignatures(): Unit = + def cleanup(s: String) = s.replace("\n", " ").replaceAll(" +", " ") + + val allFromSource = sourceFiles.map{ file => + val all = signaturesFromSource(Source.fromFile(s"${BuildInfo.test_testcasesSourceRoot}/tests/$file.scala")) + (all.expected, all.unexpected) + } + + val expectedFromSource = allFromSource.map(_._1).flatten.filter(extractSymbolName(_) != "NULL").map(cleanup) + val unexpectedFromSource = allFromSource.map(_._2).flatten.filter(extractSymbolName(_) != "NULL").map(cleanup) + val unexpectedSignatureSymbolNames = unexpectedFromSource.map(extractSymbolName) + + val allFromDocumentation = tastyFolders.flatMap(folder => signaturesFromDocumentation(s"${BuildInfo.test_testcasesOutputDir}/tests/$folder")) + val fromDocumentation = allFromDocumentation.filter(extractSymbolName(_) != "NULL").map(cleanup) + + val documentedSignatures = fromDocumentation.flatMap(matchSignature(_, expectedFromSource)).toSet + val missingSignatures = expectedFromSource.filterNot(documentedSignatures.contains) + + val unexpectedSignatures = + fromDocumentation.filter(s => unexpectedSignatureSymbolNames.contains(extractSymbolName(s))).toSet + + val reportMissingSignatures = !ignoreUndocumentedSignatures && missingSignatures.nonEmpty + val reportUnexpectedSignatures = unexpectedSignatures.nonEmpty + + if reportMissingSignatures || reportUnexpectedSignatures then + val missingSignaturesMessage = Option.when(reportMissingSignatures) + (s"Not documented signatures:\n${missingSignatures.mkString("\n")}") + + val unexpectedSignaturesMessage = Option.when(reportUnexpectedSignatures) + (s"Unexpectedly documented signatures:\n${unexpectedSignatures.mkString("\n")}") + + val allSignaturesMessage = + s""" + |All documented signatures: + |${documentedSignatures.mkString("\n")} + | + |All expected signatures from source: + |${expectedFromSource.mkString("\n")} + """.stripMargin + + val errorMessages = missingSignaturesMessage ++ unexpectedSignaturesMessage ++ Some(allSignaturesMessage) + + reportError(errorMessages.mkString("\n", "\n\n", "\n")) diff --git a/scala3doc/test/dotty/dokka/renderers/HtmlTagsTest.scala b/scala3doc/test/dotty/dokka/renderers/HtmlTagsTest.scala index 9d4e4d452393..1bedebacda7d 100644 --- a/scala3doc/test/dotty/dokka/renderers/HtmlTagsTest.scala +++ b/scala3doc/test/dotty/dokka/renderers/HtmlTagsTest.scala @@ -6,192 +6,192 @@ import dotty.dokka.HTML._ class HtmlTagsTest { - @Test - def simpleDiv = { - val actual = div().toString - val expect = "
" - assertEquals(expect, actual) - } - - @Test - def divWithStyles = { - val actual = div(style := "some: style;")().toString - val expect = """
""" - assertEquals(expect, actual) - } - - @Test - def divWithChildren = { - val actual = div(h1(), span()).toString - val expect = """

""" - assertEquals(expect, actual) - } - - @Test - def divWithTextInside = { - val actual = div(h1(), span("Some text"), "Some more of the text").toString - val expect = """

Some textSome more of the text
""" - assertEquals(expect, actual) - } - - @Test - def escapeAmpersand = { - val actual = div("Some & text").toString - val expect = """
Some & text
""" - assertEquals(expect, actual) - } - - @Test - def escapeLessThan = { - val actual = div("Some < text").toString - val expect = """
Some < text
""" - assertEquals(expect, actual) - } - - @Test - def escapeGreaterThan = { - val actual = div("Some > text").toString - val expect = """
Some > text
""" - assertEquals(expect, actual) - } - - @Test - def escapeQuotationMark = { - val actual = div("Some \" text").toString - val expect = """
Some " text
""" - assertEquals(expect, actual) - } - - @Test - def escapeApostrophe = { - val actual = div("Some ' text").toString - val expect = """
Some ' text
""" - assertEquals(expect, actual) - } - - @Test - def nestedTagsWithAttributes = { - val actual = html( - head( - script(src:="..."), - script(raw("alert('Hello World')")) - ), - body( - div( - h1(id:="title")("This is a title"), - p("This is a big paragraph of text") - ) - ) - ).toString - val expect = """

This is a title

This is a big paragraph of text

""" - assertEquals(expect, actual) - } - - @Test - def anotherNestedTagsWithAttributes = { - val actual = html( - head( - script("some script") - ), - body( - h1(style:="background-color: blue; color: red;")("This is my title"), - div(style:="background-color: blue; color: red;")( - p(cls :="contentpara first")( - "This is my first paragraph" - ), - a(style:="opacity: 0.9;")( - p(cls := "contentpara")("Goooogle") - ) - ) - ) - ).toString - val expect = """

This is my title

This is my first paragraph

Goooogle

""" - assertEquals(expect, actual) - } - - @Test - def appliedTagAndSeqAppliedTag = { - val actual = div(h1("AppliedTag"), Seq(h1("SeqAppliedTag"))).toString - val expect = """

AppliedTag

SeqAppliedTag

""" - assertEquals(expect, actual) - } - - @Test - def stringAndSeqString = { - val actual = div("String", Seq("SeqString")).toString - val expect = """
StringSeqString
""" - assertEquals(expect, actual) - } - - @Test - def mixingAllTagArgs = { - val actual = div("String", Seq("SeqString"), h1("AppliedTag"), Seq(h1("SeqAppliedTag")), Seq("SeqString"), h1("AppliedTag")).toString - val expect = """
StringSeqString

AppliedTag

SeqAppliedTag

SeqString

AppliedTag

""" - assertEquals(expect, actual) - } - - @Test - def appliedAttrAndSeqAppliedAttr = { - val actual = div(cls := "someClass", Seq(style := "some: style;")).toString - val expect = """
""" - assertEquals(expect, actual) - } - - @Test - def seqAppliedAttrAndAppliedAttr = { - val actual = div(Seq(cls := "someClass"), style := "some: style;").toString - val expect = """
""" - assertEquals(expect, actual) - } - - @Test - def seqAppliedAttrAndSeqAppliedAttr = { - val actual = div(Seq(cls := "someClass"), Seq(style := "some: style;")).toString - val expect = """
""" - assertEquals(expect, actual) - } - - @Test - def mixingAllTagArgsAndAllAttrArgs = { - val actual = div(Seq(cls := "someClass"), id := "myId", Seq(style := "some: style;"))("String", Seq("SeqString"), h1("AppliedTag"), Seq(h1("SeqAppliedTag")), Seq("SeqString"), h1("AppliedTag")).toString - val expect = """
StringSeqString

AppliedTag

SeqAppliedTag

SeqString

AppliedTag

""" - assertEquals(expect, actual) - } - - @Test - def nilTagArg = { - val nil: TagArg = Nil - val actual = div(nil).toString - val expect = """
""" - assertEquals(expect, actual) - } - - @Test - def nilAttrArg = { - val nil: AttrArg = Nil - val actual = div(nil).toString - val expect = """
""" - assertEquals(expect, actual) - } - - @Test - def nilAmongTags = { - val actual = div("name", Nil, div("ala")).toString - val expect = """
name
ala
""" - assertEquals(expect, actual) - } - - @Test - def nilAmongArgs = { - val actual = div(cls := "someClass", Nil, style := "some: style;").toString - val expect = """
""" - assertEquals(expect, actual) - } - - @Test - def nilAmongArgsAndTags = { - val actual = div(cls := "someClass", Nil, style := "some: style;")("name", Nil, div("ala")).toString - val expect = """
name
ala
""" - assertEquals(expect, actual) - } + @Test + def simpleDiv = { + val actual = div().toString + val expect = "
" + assertEquals(expect, actual) + } + + @Test + def divWithStyles = { + val actual = div(style := "some: style;")().toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def divWithChildren = { + val actual = div(h1(), span()).toString + val expect = """

""" + assertEquals(expect, actual) + } + + @Test + def divWithTextInside = { + val actual = div(h1(), span("Some text"), "Some more of the text").toString + val expect = """

Some textSome more of the text
""" + assertEquals(expect, actual) + } + + @Test + def escapeAmpersand = { + val actual = div("Some & text").toString + val expect = """
Some & text
""" + assertEquals(expect, actual) + } + + @Test + def escapeLessThan = { + val actual = div("Some < text").toString + val expect = """
Some < text
""" + assertEquals(expect, actual) + } + + @Test + def escapeGreaterThan = { + val actual = div("Some > text").toString + val expect = """
Some > text
""" + assertEquals(expect, actual) + } + + @Test + def escapeQuotationMark = { + val actual = div("Some \" text").toString + val expect = """
Some " text
""" + assertEquals(expect, actual) + } + + @Test + def escapeApostrophe = { + val actual = div("Some ' text").toString + val expect = """
Some ' text
""" + assertEquals(expect, actual) + } + + @Test + def nestedTagsWithAttributes = { + val actual = html( + head( + script(src:="..."), + script(raw("alert('Hello World')")) + ), + body( + div( + h1(id:="title")("This is a title"), + p("This is a big paragraph of text") + ) + ) + ).toString + val expect = """

This is a title

This is a big paragraph of text

""" + assertEquals(expect, actual) + } + + @Test + def anotherNestedTagsWithAttributes = { + val actual = html( + head( + script("some script") + ), + body( + h1(style:="background-color: blue; color: red;")("This is my title"), + div(style:="background-color: blue; color: red;")( + p(cls :="contentpara first")( + "This is my first paragraph" + ), + a(style:="opacity: 0.9;")( + p(cls := "contentpara")("Goooogle") + ) + ) + ) + ).toString + val expect = """

This is my title

This is my first paragraph

Goooogle

""" + assertEquals(expect, actual) + } + + @Test + def appliedTagAndSeqAppliedTag = { + val actual = div(h1("AppliedTag"), Seq(h1("SeqAppliedTag"))).toString + val expect = """

AppliedTag

SeqAppliedTag

""" + assertEquals(expect, actual) + } + + @Test + def stringAndSeqString = { + val actual = div("String", Seq("SeqString")).toString + val expect = """
StringSeqString
""" + assertEquals(expect, actual) + } + + @Test + def mixingAllTagArgs = { + val actual = div("String", Seq("SeqString"), h1("AppliedTag"), Seq(h1("SeqAppliedTag")), Seq("SeqString"), h1("AppliedTag")).toString + val expect = """
StringSeqString

AppliedTag

SeqAppliedTag

SeqString

AppliedTag

""" + assertEquals(expect, actual) + } + + @Test + def appliedAttrAndSeqAppliedAttr = { + val actual = div(cls := "someClass", Seq(style := "some: style;")).toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def seqAppliedAttrAndAppliedAttr = { + val actual = div(Seq(cls := "someClass"), style := "some: style;").toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def seqAppliedAttrAndSeqAppliedAttr = { + val actual = div(Seq(cls := "someClass"), Seq(style := "some: style;")).toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def mixingAllTagArgsAndAllAttrArgs = { + val actual = div(Seq(cls := "someClass"), id := "myId", Seq(style := "some: style;"))("String", Seq("SeqString"), h1("AppliedTag"), Seq(h1("SeqAppliedTag")), Seq("SeqString"), h1("AppliedTag")).toString + val expect = """
StringSeqString

AppliedTag

SeqAppliedTag

SeqString

AppliedTag

""" + assertEquals(expect, actual) + } + + @Test + def nilTagArg = { + val nil: TagArg = Nil + val actual = div(nil).toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def nilAttrArg = { + val nil: AttrArg = Nil + val actual = div(nil).toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def nilAmongTags = { + val actual = div("name", Nil, div("ala")).toString + val expect = """
name
ala
""" + assertEquals(expect, actual) + } + + @Test + def nilAmongArgs = { + val actual = div(cls := "someClass", Nil, style := "some: style;").toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def nilAmongArgsAndTags = { + val actual = div(cls := "someClass", Nil, style := "some: style;")("name", Nil, div("ala")).toString + val expect = """
name
ala
""" + assertEquals(expect, actual) + } } From 39ebcbf4e394e40724487888184959720ef34246 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Mon, 2 Nov 2020 21:55:34 +0100 Subject: [PATCH 18/19] Remove training whitespaces in scala3doc-testcases --- .../src/tests/FilterTest.scala | 20 +++++++++---------- .../src/tests/classSignatureTestSource.scala | 6 +++--- .../src/tests/companionObjectSignatures.scala | 4 ++-- .../src/tests/extensionMethodSignatures.scala | 10 +++++----- .../src/tests/fieldsSignatures.scala | 4 ++-- .../src/tests/genericMethods.scala | 14 ++++++------- .../src/tests/genericSignatures.scala | 2 +- .../src/tests/implicitMembers.scala | 14 ++++++------- .../tests/modifiersSignatureTestSource.scala | 2 +- scala3doc-testcases/src/tests/nested.scala | 18 ++++++++--------- .../src/tests/objectSignatures.scala | 2 +- scala3doc-testcases/src/tests/package.scala | 2 +- .../tests/packageObjectSymbolSignatures.scala | 2 +- .../src/tests/signatureTestSource.scala | 4 ++-- .../src/tests/typesSignatures.scala | 4 ++-- .../src/tests/visibility.scala | 2 +- scala3doc/dotty-docs/docs/css/frontpage.css | 2 +- 17 files changed, 56 insertions(+), 56 deletions(-) diff --git a/scala3doc-testcases/src/tests/FilterTest.scala b/scala3doc-testcases/src/tests/FilterTest.scala index 8ad1a67ae13f..2183a66a90cf 100644 --- a/scala3doc-testcases/src/tests/FilterTest.scala +++ b/scala3doc-testcases/src/tests/FilterTest.scala @@ -7,7 +7,7 @@ trait FilterTestBaseTrait: private def privateDefInheritedTrait(a: Int): String = ??? /** doc */ def publicDefInheritedTrait(a: Int): String = ??? - + /** doc */ object PublicObjectInheritedTrait /** doc */ @@ -39,7 +39,7 @@ class FilterTestBase: private def privateDefInherited(a: Int): String = ??? /** doc */ def publicDefInherited(a: Int): String = ??? - + /** doc */ object PublicObjectInherited /** doc */ @@ -51,14 +51,14 @@ class FilterTestBase: private val privateValInherited = 344 /** doc */ val publicValInherited = 567 - + /** doc */ protected type protectedTypeInherited = 123 /** doc */ private type privateTypeInherited = 344 /** doc */ type publicTypeInherited = 567 - + /** doc */ protected given Set[String | Int] = Set(1, "ala") /** doc */ @@ -87,14 +87,14 @@ class FilterTest extends FilterTestBase with FilterTestBaseTrait: object PublicObject /** doc */ protected object ProtectedObject - + /** doc */ protected def protectetDef(a: B): String = ??? /** doc */ private def privateDef(a: C): String = ??? /** doc */ def publicDef(a: D): FilterTest = ??? - + /** doc */ protected val protectetVal = 123 @@ -102,19 +102,19 @@ class FilterTest extends FilterTestBase with FilterTestBaseTrait: private val privateVal= 344 /** doc */ val publicVal = 567 - + /** doc */ protected type protectedType = 123 /** doc */ private type privateType= 344 /** doc */ type publicType = 567 - + /** doc */ protected given Seq[String | Int | Double] = List(1) /** doc */ given List[String] = "ula" :: Nil - + /** doc */ given namedList as List[String] = "ula" :: Nil /** doc */ @@ -124,4 +124,4 @@ extension (e: FilterTest): def extensionMethod(name: FilterTest): FilterTest = ??? extension (e: FilterTestBase): - def extensionMethodBase(name: FilterTest): FilterTest = ??? \ No newline at end of file + def extensionMethodBase(name: FilterTest): FilterTest = ??? \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/classSignatureTestSource.scala b/scala3doc-testcases/src/tests/classSignatureTestSource.scala index b14083c70a7c..305d264c5e6b 100644 --- a/scala3doc-testcases/src/tests/classSignatureTestSource.scala +++ b/scala3doc-testcases/src/tests/classSignatureTestSource.scala @@ -8,13 +8,13 @@ import example.level2.Documentation abstract class Documentation[T, A <: Int, B >: String, -X, +Y](c1: String, val c2: List[T]) extends Seq[T] with Product with Serializable { - def this(ac: String) + def this(ac: String) = this(ac, Nil) - def this() + def this() = this("", Nil) - def this(x: T) + def this(x: T) = this() class innerDocumentationClass diff --git a/scala3doc-testcases/src/tests/companionObjectSignatures.scala b/scala3doc-testcases/src/tests/companionObjectSignatures.scala index 1a5e8e3283fb..9b01b97dc685 100644 --- a/scala3doc-testcases/src/tests/companionObjectSignatures.scala +++ b/scala3doc-testcases/src/tests/companionObjectSignatures.scala @@ -9,7 +9,7 @@ case class CaseClass(parameterOfClassConstructor: String) = ??? def methodInClass1(): String = ??? - + def methodInClass2(): CaseClass = ??? } @@ -21,7 +21,7 @@ case object CaseClass def methodInCompanion1(): String = ??? - + def methodInCompanion2(): CaseClass = ??? } diff --git a/scala3doc-testcases/src/tests/extensionMethodSignatures.scala b/scala3doc-testcases/src/tests/extensionMethodSignatures.scala index a8fd3033ea00..59695807f0cb 100644 --- a/scala3doc-testcases/src/tests/extensionMethodSignatures.scala +++ b/scala3doc-testcases/src/tests/extensionMethodSignatures.scala @@ -6,19 +6,19 @@ class ClassOne // Commented cases won't work for now // extension ClassTwoOps on (c: ClassTwo): // def getA() = c.a - extension (c: ClassTwo) + extension (c: ClassTwo) def getB(): String = c.b - - extension (c: ClassTwo) + + extension (c: ClassTwo) def getGivenParams(a: Int, b: Int, d: Int)(e: String): Int = 56 - extension (c: ClassTwo) + extension (c: ClassTwo) def |||:(a: Int, b: Int, d: Int)(e: String): Int = 56 - extension (b: Int) + extension (b: Int) def secondGroup(): String = ??? diff --git a/scala3doc-testcases/src/tests/fieldsSignatures.scala b/scala3doc-testcases/src/tests/fieldsSignatures.scala index a9bb4eb42e05..b22ae42ae08a 100644 --- a/scala3doc-testcases/src/tests/fieldsSignatures.scala +++ b/scala3doc-testcases/src/tests/fieldsSignatures.scala @@ -3,7 +3,7 @@ package fieldsSignatures case class A(cA: String, var cB: Int) { - val A: Int + val A: Int = 1 val B: Int = 2 @@ -36,6 +36,6 @@ abstract class D2 extends C object Documentation { - val valInsideDocObject: Nothing + val valInsideDocObject: Nothing = ??? } \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/genericMethods.scala b/scala3doc-testcases/src/tests/genericMethods.scala index 3ee78a29da70..ca863f9c2269 100644 --- a/scala3doc-testcases/src/tests/genericMethods.scala +++ b/scala3doc-testcases/src/tests/genericMethods.scala @@ -1,16 +1,16 @@ package tests.genericMethods class Types: - def orTypes(base: Int | String, nested: List[Int | Double] | Seq[String]): Unit + def orTypes(base: Int | String, nested: List[Int | Double] | Seq[String]): Unit = ??? - def andTypes(base: Int & String, nested: List[Int & Double] & Seq[String]): Unit + def andTypes(base: Int & String, nested: List[Int & Double] & Seq[String]): Unit = ??? - - def mixedAndAndOr(base: Int & String, nested: List[Int | Double] & Seq[String]): Unit + + def mixedAndAndOr(base: Int & String, nested: List[Int | Double] & Seq[String]): Unit = ??? - def literal(i: 1, d: 3.3, c: 'c'): 34 + def literal(i: 1, d: 3.3, c: 'c'): 34 = 34 - - def byName(a: => Int, b: => String | Int): Unit + + def byName(a: => Int, b: => String | Int): Unit = ??? \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/genericSignatures.scala b/scala3doc-testcases/src/tests/genericSignatures.scala index 550bb6b55028..7df141771fb7 100644 --- a/scala3doc-testcases/src/tests/genericSignatures.scala +++ b/scala3doc-testcases/src/tests/genericSignatures.scala @@ -6,7 +6,7 @@ class D[T] class D2[T]() -class DD[+T] +class DD[+T] class A diff --git a/scala3doc-testcases/src/tests/implicitMembers.scala b/scala3doc-testcases/src/tests/implicitMembers.scala index 8dd04d947310..17bde146882a 100644 --- a/scala3doc-testcases/src/tests/implicitMembers.scala +++ b/scala3doc-testcases/src/tests/implicitMembers.scala @@ -7,14 +7,14 @@ class OuterClass: object ImplicitMemberTarget: extension (a: ImplicitMemberTarget): - def extensionFromCompanion: String = + def extensionFromCompanion: String = "ImplicitMemberTarget" - - // does not work + + // does not work extension (a: ImplicitMemberTarget): - def extensionFromOuterClass: String = - "ImplicitMemberTarget" + def extensionFromOuterClass: String = + "ImplicitMemberTarget" extension (a: OuterClass#ImplicitMemberTarget): - def extensionFromPackage: String = - "ImplicitMemberTarget" \ No newline at end of file + def extensionFromPackage: String = + "ImplicitMemberTarget" \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/modifiersSignatureTestSource.scala b/scala3doc-testcases/src/tests/modifiersSignatureTestSource.scala index ac3d87deea82..ef6faecf9232 100644 --- a/scala3doc-testcases/src/tests/modifiersSignatureTestSource.scala +++ b/scala3doc-testcases/src/tests/modifiersSignatureTestSource.scala @@ -21,7 +21,7 @@ abstract class Methods() inline def inlineMethod(): Unit = Unit - + implicit def toImplicitString(): String = "asd" } diff --git a/scala3doc-testcases/src/tests/nested.scala b/scala3doc-testcases/src/tests/nested.scala index 2f5fe3931e95..7a03c7aad06a 100644 --- a/scala3doc-testcases/src/tests/nested.scala +++ b/scala3doc-testcases/src/tests/nested.scala @@ -1,16 +1,16 @@ package tests.nested -class A +class A { - object B + object B { def bb(): Int = 123 - val a: Double + val a: Double = 0.3 } - class C + class C { def cc: Int = 123 @@ -22,17 +22,17 @@ class A } } -object R +object R { - object B + object B { def bb(): Int = 123 - val a: Double + val a: Double = 0.3 } - class C + class C { def cc: Int = 123 @@ -50,7 +50,7 @@ class X } // bug found in dotty code, still fails with type -sealed trait ErrorKind +sealed trait ErrorKind object ErrorKind { // This below produce some strange type diff --git a/scala3doc-testcases/src/tests/objectSignatures.scala b/scala3doc-testcases/src/tests/objectSignatures.scala index b40e50ab4cb3..9f053f3fbb48 100644 --- a/scala3doc-testcases/src/tests/objectSignatures.scala +++ b/scala3doc-testcases/src/tests/objectSignatures.scala @@ -16,4 +16,4 @@ object Base object A2 extends A[String] with C // We are not going to add final below -// final object B +// final object B diff --git a/scala3doc-testcases/src/tests/package.scala b/scala3doc-testcases/src/tests/package.scala index 7fac475792cb..03800e1ba068 100644 --- a/scala3doc-testcases/src/tests/package.scala +++ b/scala3doc-testcases/src/tests/package.scala @@ -1,4 +1,4 @@ /** This should be moved to its own project */ package object tests { - + } diff --git a/scala3doc-testcases/src/tests/packageObjectSymbolSignatures.scala b/scala3doc-testcases/src/tests/packageObjectSymbolSignatures.scala index 7f517b299b65..adb537bc19b0 100644 --- a/scala3doc-testcases/src/tests/packageObjectSymbolSignatures.scala +++ b/scala3doc-testcases/src/tests/packageObjectSymbolSignatures.scala @@ -4,7 +4,7 @@ package object packageObjectSymbolSignatures { val property1: String = ??? - + type MyType = String val property2: Int diff --git a/scala3doc-testcases/src/tests/signatureTestSource.scala b/scala3doc-testcases/src/tests/signatureTestSource.scala index 7ab9e436d833..568be717a824 100644 --- a/scala3doc-testcases/src/tests/signatureTestSource.scala +++ b/scala3doc-testcases/src/tests/signatureTestSource.scala @@ -1,7 +1,7 @@ package tests.signatureTestSource class A class B extends A -class C +class C class D[T] class E[T] extends D[T] @@ -14,7 +14,7 @@ class SignatureTestClass(a: String) def multipleParams(a: A, b: B): C = ??? def likeVararg(a: Seq[A]): C - = ??? + = ??? def vararg(a: A*): C = ??? def multipleList(a: A)(b: B): C diff --git a/scala3doc-testcases/src/tests/typesSignatures.scala b/scala3doc-testcases/src/tests/typesSignatures.scala index 2c8caf314080..b62d9ee01f72 100644 --- a/scala3doc-testcases/src/tests/typesSignatures.scala +++ b/scala3doc-testcases/src/tests/typesSignatures.scala @@ -8,12 +8,12 @@ class A type C[A, B <: A] = Seq[B] } -trait V +trait V { type Ala[+J] <: Int type Ola[+T] type X -} +} class Generic[T] diff --git a/scala3doc-testcases/src/tests/visibility.scala b/scala3doc-testcases/src/tests/visibility.scala index a87dce1171b9..b474eb15eebd 100644 --- a/scala3doc-testcases/src/tests/visibility.scala +++ b/scala3doc-testcases/src/tests/visibility.scala @@ -51,7 +51,7 @@ class InClassVisibility() protected[InClassVisibility] def protectedInClassMethod: Int = ??? - + protected[this] def locallyProtectedMethod: Int //unexpected = ??? } diff --git a/scala3doc/dotty-docs/docs/css/frontpage.css b/scala3doc/dotty-docs/docs/css/frontpage.css index ae9107f9a5c0..6f4a7eca1ee3 100644 --- a/scala3doc/dotty-docs/docs/css/frontpage.css +++ b/scala3doc/dotty-docs/docs/css/frontpage.css @@ -1,5 +1,5 @@ @import url('https://fonts.googleapis.com/css?family=Lobster&display=swap'); - + body { height: 100%; width: 100%; From a8d49f969f7983a577ac0fc21d5cf20d09642ca6 Mon Sep 17 00:00:00 2001 From: Aleksander Boruch-Gruszecki Date: Tue, 3 Nov 2020 12:45:59 +0100 Subject: [PATCH 19/19] Update scala3doc/resources/dotty_res/styles/diagram.css Co-authored-by: Guillaume Raffin --- scala3doc/resources/dotty_res/styles/diagram.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scala3doc/resources/dotty_res/styles/diagram.css b/scala3doc/resources/dotty_res/styles/diagram.css index 58c7e9415308..89a6e9da4b6c 100644 --- a/scala3doc/resources/dotty_res/styles/diagram.css +++ b/scala3doc/resources/dotty_res/styles/diagram.css @@ -23,7 +23,9 @@ text-decoration: underline; color: #FFF; } - +.diagram-class a:hover { + color: #BFE7F3; +} .diagram-class span[data-unresolved-link] { color: #FFF; -} \ No newline at end of file +}