Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fine-tune @Doc/scaladoc generation #141

Merged
merged 10 commits into from
Jan 24, 2024
9 changes: 7 additions & 2 deletions core/src/main/scala/flatgraph/help/DocSearchPackages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ package flatgraph.help
/** defines where we should search for @Traversal/@TraversalSource/@Doc annotations */
trait DocSearchPackages {
def apply(): Seq[String]

def withAdditionalPackage(packageName: String): DocSearchPackages = {
val combinedPackages = this.apply() :+ packageName
() => combinedPackages
}
}

object DocSearchPackages {

/** default implicit, for domains that don't have custom steps: no additional packages to search */
implicit val defaultDocSearchPackage: DocSearchPackages = () => Nil
/** don't scan anywhere other than flatgraph */
val default: DocSearchPackages = () => List("flatgraph")

def apply(searchPackages: String*): DocSearchPackages =
() => searchPackages
Expand Down
23 changes: 11 additions & 12 deletions core/src/main/scala/flatgraph/help/TraversalHelp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import scala.jdk.CollectionConverters.*
* @param searchPackages:
* The base packages that we scan for - we're not scanning the entire classpath
*/
class TraversalHelp(searchPackages: DocSearchPackages) {
class TraversalHelp(packageNamesToSearch: DocSearchPackages) {
import TraversalHelp._

def forElementSpecificSteps(elementClass: Class[_], verbose: Boolean): String = {
Expand All @@ -38,10 +38,10 @@ class TraversalHelp(searchPackages: DocSearchPackages) {
}

val table = Table(
columnNames = if (verbose) ColumnNamesVerbose else ColumnNames,
columnNames = if (verbose) ColumnNames ++ Seq("implemented in", "more details") else ColumnNames,
rows = stepDocs.sortBy(_.methodName).map { stepDoc =>
val baseColumns = List(s".${stepDoc.methodName}", stepDoc.doc.info)
if (verbose) baseColumns :+ stepDoc.traversalClassName
if (verbose) baseColumns ++ Seq(stepDoc.traversalClassName, stepDoc.doc.longInfo)
else baseColumns
}
)
Expand All @@ -51,17 +51,19 @@ class TraversalHelp(searchPackages: DocSearchPackages) {
|""".stripMargin
}

lazy val forTraversalSources: String = {
def forTraversalSources(verbose: Boolean): String = {
val stepDocs = for {
packageName <- packageNamesToSearch
packageName <- packageNamesToSearch()
traversal <- findClassesAnnotatedWith(packageName, classOf[help.TraversalSource])
stepDoc <- findStepDocs(traversal)
} yield stepDoc

val table = Table(
columnNames = ColumnNames,
columnNames = if (verbose) ColumnNames :+ "more details" else ColumnNames,
rows = stepDocs.distinct.sortBy(_.methodName).map { stepDoc =>
List(s".${stepDoc.methodName}", stepDoc.doc.info)
val baseColumns = List(s".${stepDoc.methodName}", stepDoc.doc.info)
if (verbose) baseColumns :+ stepDoc.doc.longInfo
else baseColumns
}
)

Expand All @@ -75,7 +77,7 @@ class TraversalHelp(searchPackages: DocSearchPackages) {
*/
lazy val stepDocsByElementType: Map[Class[_], List[StepDoc]] = {
for {
packageName <- packageNamesToSearch
packageName <- packageNamesToSearch()
traversal <- findClassesAnnotatedWith(packageName, classOf[help.Traversal])
annotation <- Option(traversal.getAnnotation(classOf[help.Traversal])).iterator
stepDoc <- findStepDocs(traversal)
Expand All @@ -101,11 +103,8 @@ class TraversalHelp(searchPackages: DocSearchPackages) {
.filterNot(_.methodName.endsWith("$extension"))
}

private def packageNamesToSearch: Seq[String] =
searchPackages() :+ "flatgraph"
}

object TraversalHelp {
private val ColumnNames = Array("step", "description")
private val ColumnNamesVerbose = ColumnNames :+ "implemented in"
private val ColumnNames = Array("step", "description")
}
11 changes: 11 additions & 0 deletions core/src/main/scala/flatgraph/traversal/Language.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package flatgraph.traversal
import flatgraph.help.{Doc, DocSearchPackages, TraversalHelp}
import flatgraph.{Accessors, Edge, GNode, MultiPropertyKey, OptionalPropertyKey, PropertyKey, Schema, SinglePropertyKey}

import scala.annotation.implicitNotFound
import scala.collection.immutable.ArraySeq
import scala.collection.{Iterator, mutable}
import scala.reflect.ClassTag
Expand Down Expand Up @@ -113,10 +114,20 @@ trait GenericLanguage {
* the classpath
*/
@Doc(info = "print help/documentation based on the current elementType `A`.")
@implicitNotFound("""If you're using flatgraph purely without a schema and associated generated domain classes, you can
|start with `given DocSearchPackages = DocSearchPackages.default`.
|If you have generated domain classes, use `given DocSearchPackages = MyDomain.defaultDocSearchPackage`.
|If you have additional custom extension steps that specify help texts via @Doc annotations, use `given DocSearchPackages = MyDomain.defaultDocSearchPackage.withAdditionalPackage("my.custom.package)"`
|""".stripMargin)
def help[B >: A](implicit elementType: ClassTag[B], searchPackages: DocSearchPackages): String =
new TraversalHelp(searchPackages).forElementSpecificSteps(elementType.runtimeClass, verbose = false)

@Doc(info = "print verbose help/documentation based on the current elementType `A`.")
@implicitNotFound("""If you're using flatgraph purely without a schema and associated generated domain classes, you can
|start with `given DocSearchPackages = DocSearchPackages.default`.
|If you have generated domain classes, use `given DocSearchPackages = MyDomain.defaultDocSearchPackage`.
|If you have additional custom extension steps that specify help texts via @Doc annotations, use `given DocSearchPackages = MyDomain.defaultDocSearchPackage.withAdditionalPackage("my.custom.package)"`
|""".stripMargin)
def helpVerbose[B >: A](implicit elementType: ClassTag[B], searchPackages: DocSearchPackages): String =
new TraversalHelp(searchPackages).forElementSpecificSteps(elementType.runtimeClass, verbose = true)

Expand Down
2 changes: 2 additions & 0 deletions core/src/test/scala/flatgraph/traversal/TraversalTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ class TraversalTests extends AnyWordSpec with ExampleGraphSetup {
}

".help step" should {
// a specific domain would provide it's own DocSearchPackage implementation, to specify where we're supposed to scan for @Doc annotations
given DocSearchPackages = DocSearchPackages.default
"generic help for `int`" in {
val helpText = Iterator(1, 2, 3, 4).help
helpText should include(".cast")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ object SimpleDomain {
}

val defaultDocSearchPackage: DocSearchPackages = DocSearchPackages(getClass.getPackage.getName)
lazy val help = TraversalHelp(defaultDocSearchPackage).forTraversalSources
lazy val help = TraversalHelp(defaultDocSearchPackage).forTraversalSources(verbose = false)
lazy val helpVerbose = TraversalHelp(defaultDocSearchPackage).forTraversalSources(verbose = true)

def newGraph: Graph = {
val schema = TestSchema.make(1, 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -737,23 +737,14 @@ class DomainClassesGenerator(schema: Schema) {
// domain object and starters: start
// TODO: extract into separate method
val sanitizeReservedNames = Map("return" -> "ret", "type" -> "typ", "import" -> "imports").withDefault(identity)
def docAnnotationMaybe(nodeType: AbstractNodeType) =
nodeType.comment.map(comment => s"""@flatgraph.help.Doc(info = "$comment")""").getOrElse("")
val starters = mutable.ArrayBuffer[String]()
val starters = mutable.ArrayBuffer[String]()
nodeTypes.zipWithIndex.collect { case (typ, idx) =>
typ.starterName.foreach { starterName =>
// starter for this concrete node type
val docText = {
val typCommentMaybe = typ.comment
.map { comment =>
s" and documentation: ${typ.comment}"
}
.getOrElse("")
s"All nodes of type ${typ.className}, i.e. with label ${typ.name} $typCommentMaybe"
}
val comment = typ.comment.getOrElse("").trim
starters.append(
s"""// TODO reimplement help/doc... @overflowdb.traversal.help.Doc(info = "$docText")
|/** $docText */
s"""/** $comment */
|@flatgraph.help.Doc(info = \"\"\"$comment\"\"\")
|def $starterName: Iterator[nodes.${typ.className}] = wrappedCpg.graph._nodes($idx).asInstanceOf[Iterator[nodes.${typ.className}]]""".stripMargin
)

Expand All @@ -763,8 +754,7 @@ class DomainClassesGenerator(schema: Schema) {
val propertyNameCamelCase = camelCase(property.name)
val docText = s"Shorthand for $starterName.$propertyNameCamelCase"
starters.append(
s"""// TODO reimplement help/doc... @overflowdb.traversal.help.Doc(info = "$docText")
|/** $docText */
s"""/** $docText */
|def $starterName($propertyNameCamelCase: ${typeFor(
property
)}): Iterator[nodes.${typ.className}] = $starterName.$propertyNameCamelCase($propertyNameCamelCase)""".stripMargin
Expand All @@ -775,15 +765,20 @@ class DomainClassesGenerator(schema: Schema) {

schema.nodeBaseTypes.foreach { baseType =>
baseType.starterName.foreach { starterName =>
val types = schema.nodeTypes.filter { _.extendzRecursively.contains(baseType) }
val docText = s"""All nodes of type ${baseType.className}, i.e. with label in ${types.map { _.name }.sorted.mkString(", ")}"""
val docTextInfo = baseType.comment.getOrElse("").trim
val subTypes = schema.nodeTypes.filter(_.extendzRecursively.contains(baseType)).map(_.name).sorted.mkString(", ")
val docTextVerbose = s"""subtypes: $subTypes"""
val concreteSubTypeStarters = nodeTypes.collect {
case typ if typ.extendzRecursively.contains(baseType) =>
"this." + sanitizeReservedNames(camelCase(typ.name))
}
starters.append(s"""// TODO reimplement help/doc... @overflowdb.traversal.help.Doc(info = "$docText")
/** $docText */
def $starterName: Iterator[nodes.${baseType.className}] = Iterator(${concreteSubTypeStarters.mkString(", ")}).flatten""")
starters.append(s"""
|/** $docTextInfo
| * $docTextVerbose
| */
|@flatgraph.help.Doc(info = \"\"\"$docTextInfo\"\"\", longInfo = \"\"\"$docTextVerbose\"\"\")
|def $starterName: Iterator[nodes.${baseType.className}] = Iterator(${concreteSubTypeStarters.mkString(", ")}).flatten
|""".stripMargin)
}
}

Expand All @@ -794,8 +789,25 @@ class DomainClassesGenerator(schema: Schema) {
|import Language.*
|
|object $domainShortName {
| val defaultDocSearchPackage: flatgraph.help.DocSearchPackages = flatgraph.help.DocSearchPackages(getClass.getPackage.getName)
| lazy val help = flatgraph.help.TraversalHelp(defaultDocSearchPackage).forTraversalSources
| val defaultDocSearchPackage = flatgraph.help.DocSearchPackages.default.withAdditionalPackage(getClass.getPackage.getName)
|
|@scala.annotation.implicitNotFound(
| \"\"\"If you're using flatgraph purely without a schema and associated generated domain classes, you can
| |start with `given DocSearchPackages = DocSearchPackages.default`.
| |If you have generated domain classes, use `given DocSearchPackages = MyDomain.defaultDocSearchPackage`.
| |If you have additional custom extension steps that specify help texts via @Doc annotations, use `given DocSearchPackages = MyDomain.defaultDocSearchPackage.withAdditionalPackage("my.custom.package)"`
| |\"\"\".stripMargin)
| def help(implicit searchPackageNames: flatgraph.help.DocSearchPackages) =
| flatgraph.help.TraversalHelp(searchPackageNames).forTraversalSources(verbose = false)
|
|@scala.annotation.implicitNotFound(
| \"\"\"If you're using flatgraph purely without a schema and associated generated domain classes, you can
| |start with `given DocSearchPackages = DocSearchPackages.default`.
| |If you have generated domain classes, use `given DocSearchPackages = MyDomain.defaultDocSearchPackage`.
| |If you have additional custom extension steps that specify help texts via @Doc annotations, use `given DocSearchPackages = MyDomain.defaultDocSearchPackage.withAdditionalPackage("my.custom.package)"`
| |\"\"\".stripMargin)
| def helpVerbose(implicit searchPackageNames: flatgraph.help.DocSearchPackages) =
| flatgraph.help.TraversalHelp(searchPackageNames).forTraversalSources(verbose = true)
|
| def empty: $domainShortName = new $domainShortName(new flatgraph.Graph(GraphSchema))
|
Expand All @@ -813,6 +825,11 @@ class DomainClassesGenerator(schema: Schema) {
|class $domainShortName(private val _graph: flatgraph.Graph = new flatgraph.Graph(GraphSchema)) extends AutoCloseable {
| def graph: flatgraph.Graph = _graph
|
| def help(implicit searchPackageNames: flatgraph.help.DocSearchPackages) =
| $domainShortName.help(searchPackageNames)
| def helpVerbose(implicit searchPackageNames: flatgraph.help.DocSearchPackages) =
| $domainShortName.helpVerbose(searchPackageNames)
|
| override def close(): Unit =
| _graph.close()
|}
Expand Down
Loading