Skip to content

Commit 31956e2

Browse files
committed
Add proper rendering got classes and methods
This includes support for printing basic types
1 parent d28da30 commit 31956e2

File tree

9 files changed

+320
-135
lines changed

9 files changed

+320
-135
lines changed

src/main/scala/dotty/dokka/ScalaSignatureProvider.scala

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,49 +21,84 @@ class ScalaSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLogg
2121
override def signature(documentable: Documentable) = documentable match {
2222
case method: DFunction =>
2323
List(methodSignature(method)).asJava
24+
case clazz: DClass =>
25+
List(classSignature(clazz)).asJava
2426
case _ => default.signature(documentable)
2527
}
2628

2729
val styles = Set(TextStyle.Monospace).asJava
2830

2931
val utils: JvmSignatureUtils = KotlinSignatureUtils.INSTANCE
3032

33+
private def classSignature(clazz: DClass): ContentNode =
34+
content(clazz){ builder =>
35+
val ext = clazz.get(ClasslikeExtension)
36+
utils.annotationsBlock(builder, clazz)
37+
// builder.addText("TODO modifiers")
38+
builder.addText("class ")
39+
builder.addLink(clazz.getName, clazz.getDri)
40+
builder.generics(clazz)
41+
ext.constructor.foreach(c => builder.functionParameters(c))
42+
ext.parentTypes match
43+
case Nil =>
44+
case extendType :: withTypes =>
45+
builder.addText(" extends ")
46+
builder.typeSignature(extendType)
47+
withTypes.foreach { t =>
48+
builder.addText(" with ")
49+
builder.typeSignature(extendType)
50+
}
51+
}
52+
3153
private def methodSignature(method: DFunction): ContentNode =
32-
val methodExtension = method.get(tasty.MethodExtension)
3354
content(method){ builder =>
3455
utils.annotationsBlock(builder, method)
3556
// builder.addText("TODO modifiers")
3657
builder.addText("def")
3758
builder.addText(" ")
3859
builder.addLink(method.getName, method.getDri)
39-
builder.addList(method.getGenerics, "[", "]")(e => builder.unaryPlus(builder.buildSignature(e)))
40-
val params = methodExtension.parametersListSizes.foldLeft(0){ (from, size) =>
41-
val toIndex = from + size
42-
builder.addList(method.getParameters.subList(from, toIndex), "(", ")"){ param =>
43-
utils.annotationsInline(builder, param)
44-
// builder.addText("TODO modifiers")
45-
builder.addText(param.getName)
46-
builder.addText(":")
47-
builder.typeSignature(param.getType)
48-
}
49-
toIndex
50-
}
60+
builder.generics(method)
61+
builder.functionParameters(method)
5162
builder.addText(":")
5263
builder.addText(" ")
5364
builder.typeSignature(method.getType)
5465
}
55-
66+
67+
5668
extension on (builder: PageContentBuilder$DocumentableContentBuilder):
5769
def typeSignature(b: Projection): Unit = b match {
5870
case tc: TypeConstructor =>
59-
// TODO we should handle types differnetly...
60-
builder.addLink(tc.getDri.getClassNames, tc.getDri)
61-
builder.addList(tc.getProjections, "[", "]")(p => builder.typeSignature(p))
62-
71+
tc.getProjections.asScala.foreach {
72+
case text: UnresolvedBound => builder.addText(text.getName)
73+
case link: OtherParameter =>
74+
builder.addLink(link.getName, link.getDeclarationDRI)
75+
case other =>
76+
builder.addText(s"TODO($other)")
77+
}
6378
case other =>
6479
builder.addText(s"TODO: $other")
6580
}
6681

82+
def generics(on: WithGenerics) = builder.addList(on.getGenerics, "[", "]"){ e =>
83+
builder.addText(e.getName)
84+
e.getBounds.forEach(b => builder.typeSignature(b))
85+
}
86+
87+
def functionParameters(method: DFunction) =
88+
val methodExtension = method.get(MethodExtension)
89+
methodExtension.parametersListSizes.foldLeft(0){ (from, size) =>
90+
val toIndex = from + size
91+
if from == toIndex then builder.addText("()")
92+
else builder.addList(method.getParameters.subList(from, toIndex), "(", ")"){ param =>
93+
utils.annotationsInline(builder, param)
94+
// builder.addText("TODO modifiers")
95+
builder.addText(param.getName)
96+
builder.addText(": ")
97+
builder.typeSignature(param.getType)
98+
}
99+
toIndex
100+
}
101+
67102
private def content(d: Documentable)(render: PageContentBuilder$DocumentableContentBuilder => Unit): ContentGroup =
68103
val lambda: Consumer[PageContentBuilder$DocumentableContentBuilder] = new Consumer:
69104
override def accept(v: PageContentBuilder$DocumentableContentBuilder) = render(v)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package dotty.dokka
2+
3+
import org.jetbrains.dokka.links._
4+
import org.jetbrains.dokka.model._
5+
import collection.JavaConverters._
6+
import org.jetbrains.dokka.links._
7+
import org.jetbrains.dokka.model.doc._
8+
import org.jetbrains.dokka.model.properties._
9+
10+
case class MethodExtension(parametersListSizes: Seq[Int]) extends ExtraProperty[DFunction]:
11+
override def getKey = MethodExtension
12+
13+
object MethodExtension extends BaseKey[DFunction, MethodExtension]
14+
15+
case class ClasslikeExtension(parentTypes: List[Bound], constructor: Option[DFunction]) extends ExtraProperty[DClasslike]:
16+
override def getKey = ClasslikeExtension
17+
18+
object ClasslikeExtension extends BaseKey[DClasslike, ClasslikeExtension]
19+
20+
21+
class BaseKey[T, V] extends ExtraProperty.Key[T, V]:
22+
override def mergeStrategyFor(left: V, right: V): MergeStrategy[T] =
23+
MergeStrategy.Remove.INSTANCE.asInstanceOf[MergeStrategy[T]]

src/main/scala/dotty/dokka/tasty/BasicSupport.scala

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dotty.dokka.tasty
33
import org.jetbrains.dokka.links._
44
import org.jetbrains.dokka.model._
55
import collection.JavaConverters._
6+
import dotty.dokka._
67

78
trait BasicSupport:
89
self: TastyParser =>
@@ -16,47 +17,37 @@ trait BasicSupport:
1617
def topLevelEntryName(using ctx: Context): Option[String] = if (sym.isPackageDef) None else
1718
if (sym.owner.isPackageDef) Some(sym.name) else sym.owner.topLevelEntryName
1819

19-
def dri = asDRI(sym)
20-
def declarationDri = asDRI(sym, true)
20+
// TODO make sure that DRIs are unique plus probably reuse semantic db code?
21+
def dri =
22+
if sym == Symbol.noSymbol then emptyDRI else
23+
val pointsTo =
24+
if (!sym.isTypeDef) PointingToDeclaration.INSTANCE
25+
else PointingToGenericParameters(sym.owner.typeMembers.indexOf(sym))
26+
27+
val method =
28+
if (sym.isDefDef) Some(sym)
29+
else if (sym.maybeOwner.isDefDef) Some(sym.owner)
30+
else None
31+
32+
new DRI(
33+
sym.packageName,
34+
sym.topLevelEntryName.orNull, // TODO do we need any of this fields?
35+
method.map(s => new org.jetbrains.dokka.links.Callable(s.name, null, Nil.asJava)).orNull,
36+
pointsTo, // TODO different targets?
37+
s"${sym.show}/${sym.signature.resultSig}/[${sym.signature.paramSigs.mkString("/")}]"
38+
)
2139

2240
def documentation(using cxt: reflect.Context) = sym.comment match
2341
case Some(comment) =>
2442
sourceSet.asMap(parseComment(comment, sym.tree))
2543
case None =>
2644
Map.empty.asJava
2745

28-
def source(using ctx: Context) = sourceSet.asMap(getSource(sym))
29-
30-
def dokkaType(using cxt: reflect.Context): Bound = // TODO render primitives better?
31-
// TODO support varags
32-
val params = sym.typeMembers.map(_.dokkaType)
33-
println(s"${sym.show} -> ${sym.dri}")
34-
new org.jetbrains.dokka.model.TypeConstructor(sym.dri, params.asJava, FunctionModifiers.NONE)
35-
36-
37-
val emptyDRI = DRI.Companion.getTopLevel
38-
39-
// TODO add support for type aliases!
40-
def asDRI(symbol: reflect.Symbol, declaration: Boolean = false): DRI =
41-
if symbol == Symbol.noSymbol then emptyDRI else
42-
val pointsTo =
43-
if (!symbol.isTypeDef || declaration) PointingToDeclaration.INSTANCE
44-
else PointingToGenericParameters(symbol.owner.typeMembers.indexOf(symbol))
45-
46-
val method =
47-
if (symbol.isDefDef) Some(symbol)
48-
else if (symbol.maybeOwner.isDefDef) Some(symbol.owner)
49-
else None
50-
51-
new DRI(
52-
symbol.packageName,
53-
symbol.topLevelEntryName.orNull, // TODO do we need any of this fields?
54-
method.map(s => new org.jetbrains.dokka.links.Callable(s.name, null, Nil.asJava)).orNull,
55-
pointsTo, // TODO different targets?
56-
symbol.show
57-
)
58-
59-
def getSource(symbol: reflect.Symbol)(using ctx: Context): DocumentableSource =
60-
val path = symbol.pos.sourceFile.jpath.toString
61-
new DocumentableSource:
62-
override def getPath = path
46+
def source(using ctx: Context) =
47+
val path = sym.pos.sourceFile.jpath.toString
48+
sourceSet.asMap(
49+
new DocumentableSource:
50+
override def getPath = path
51+
)
52+
53+
private val emptyDRI = DRI.Companion.getTopLevel

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

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,65 +5,61 @@ import org.jetbrains.dokka.links._
55
import org.jetbrains.dokka.model.doc._
66
import collection.JavaConverters._
77
import org.jetbrains.dokka.model.properties._
8-
9-
case class MethodExtension(parametersListSizes: Seq[Int]) extends ExtraProperty[DFunction]:
10-
override def getKey = MethodExtension
11-
12-
object MethodExtension extends ExtraProperty.Key[DFunction, MethodExtension]:
13-
override def mergeStrategyFor(left: MethodExtension, right: MethodExtension): MergeStrategy[DFunction] =
14-
MergeStrategy.Remove.INSTANCE.asInstanceOf[MergeStrategy[DFunction]] // TODO it seems that variance from Kotlin is not respected...
8+
import dotty.dokka._
159

1610
trait ClassLikeSupport:
1711
self: TastyParser =>
1812
import reflect._
1913

2014
def parseClass(classDef: reflect.ClassDef)(using ctx: Context): DClasslike =
21-
2215
val parents = for
23-
parentTree <- classDef.parents
16+
parentTree <- classDef.parents // We assume here that order is correct
2417
parentSymbol = if (parentTree.symbol.isClassConstructor) parentTree.symbol.owner else parentTree.symbol
25-
if !parentSymbol.flags.is(Flags.Synthetic) // TODO add beter filtering on parent symbols
26-
yield new DriWithKind(asDRI(parentSymbol), if(parentSymbol.isClassDef) KotlinClassKindTypes.CLASS else KotlinClassKindTypes.INTERFACE)
18+
if parentSymbol != defn.ObjectClass
19+
yield parentTree.dokkaType
2720

2821
val methods = classDef.symbol.classMethods.filterNot(_.flags.is(Flags.Synthetic))
29-
val constuctors = classDef.symbol.children.filter(_.isClassConstructor)
22+
val constuctors = classDef.body.collect {
23+
case d: DefDef if d.name == "<init>" && classDef.constructor.symbol != d.symbol =>
24+
parseMethod(d.symbol)
25+
}
3026

3127
new DClass(
32-
asDRI(classDef.symbol),
28+
classDef.symbol.dri,
3329
classDef.name,
34-
constuctors.map(parseMethod).asJava,
35-
methods.map(parseMethod).asJava,
36-
Nil.asJava,
37-
Nil.asJava,
38-
sourceSet.asMap(getSource(classDef.symbol)),
39-
sourceSet.asMap(KotlinVisibility.Public.INSTANCE),
40-
null,
41-
Nil.asJava,
42-
sourceSet.asMap(parents.asJava),
43-
classDef.symbol.documentation,
44-
null,
45-
sourceSet.asMap(JavaModifier.Abstract.INSTANCE),
30+
/*constuctors =*/ constuctors.asJava,
31+
/*methods =*/ methods.map(parseMethod).asJava,
32+
/*fields =*/ Nil.asJava,
33+
/*nested =*/ Nil.asJava,
34+
/*sources =*/ classDef.symbol.source,
35+
/*visibility =*/ sourceSet.asMap(KotlinVisibility.Public.INSTANCE), // TODO add support for visibility
36+
/*companion =*/ null,
37+
/*generics =*/ classDef.constructor.typeParams.map(parseTypeArgument).asJava,
38+
/*supertypes =*/ Map.empty.asJava, // Not used
39+
/*documentation =*/ classDef.symbol.documentation,
40+
/*expectPresentInSet =*/ null, // unused
41+
/*modifier =*/ Map.empty.asJava, // TODO add support for modifers
4642
inspector.sourceSet.toSet,
47-
PropertyContainer.Companion.empty()
43+
PropertyContainer.Companion.empty().plus(ClasslikeExtension(parents, Some(parseMethod(classDef.constructor.symbol))))
4844
)
4945

5046
def parseMethod(methodSymbol: Symbol): DFunction =
51-
val method = methodSymbol.tree.asInstanceOf[DefDef]
52-
val paramLists = method.paramss
53-
47+
val method = methodSymbol.tree.asInstanceOf[DefDef]
48+
val paramLists = method.paramss
49+
5450
new DFunction(
5551
methodSymbol.dri,
5652
methodSymbol.name,
57-
methodSymbol.isClassConstructor,
58-
paramLists.flatten.map(parseArgument).asJava, // TODO add support for parameters
59-
methodSymbol.documentation,
60-
null,
61-
methodSymbol.source,
62-
sourceSet.asMap(KotlinVisibility.Public.INSTANCE),
63-
method.returnTpt.tpe.typeSymbol.dokkaType, // TODO play with it?
53+
/*isConstructor =*/ methodSymbol.isClassConstructor,
54+
/*parameters =*/ paramLists.flatten.map(parseArgument).asJava, // TODO add support for parameters
55+
/*documentation =*/ methodSymbol.documentation,
56+
/*expectPresentInSet =*/ null, // unused
57+
/*sources =*/ methodSymbol.source,
58+
/*visibility =*/ sourceSet.asMap(KotlinVisibility.Public.INSTANCE), // TODO add support for visibility
59+
/*type =*/ method.returnTpt.dokkaType,
6460
/*generics =*/ method.typeParams.map(parseTypeArgument).asJava,
65-
/*receiver =*/ null,
66-
/*modifier =*/ sourceSet.asMap(JavaModifier.Abstract.INSTANCE),
61+
/*receiver =*/ null, // Not used
62+
/*modifier =*/ Map.empty.asJava, // TODO add support for modifers
6763
sourceSet.toSet(),
6864
PropertyContainer.Companion.empty() plus MethodExtension(paramLists.map(_.size))
6965
)
@@ -74,18 +70,18 @@ trait ClassLikeSupport:
7470
argument.symbol.name,
7571
argument.symbol.documentation,
7672
null,
77-
argument.tpt.tpe.typeSymbol.dokkaType, // Can we get type symbol easier?
73+
argument.tpt.dokkaType,
7874
sourceSet.toSet(),
7975
PropertyContainer.Companion.empty()
8076
)
8177

8278
def parseTypeArgument(argument: TypeDef): DTypeParameter =
8379
new DTypeParameter(
84-
argument.symbol.declarationDri, // TODO do we need that? Maybe we can suppot TypeDef in .dri method?
80+
argument.symbol.dri,
8581
argument.symbol.name,
8682
argument.symbol.documentation,
8783
null,
88-
Nil.asJava, // TODO add type bounds
84+
List(argument.rhs.dokkaType).asJava,
8985
sourceSet.toSet(),
9086
PropertyContainer.Companion.empty()
9187
)

0 commit comments

Comments
 (0)