project/ParadoxSupport.scala (60 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* license agreements; and to You under the Apache License, version 2.0:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* This file is part of the Apache Pekko project, which was derived from Akka.
*/
/*
* Copyright (C) 2016-2020 Lightbend Inc. <https://www.lightbend.com>
*/
import java.io.FileNotFoundException
import sbt._
import Keys._
import com.lightbend.paradox.markdown._
import com.lightbend.paradox.sbt.ParadoxPlugin.autoImport._
import org.apache.pekko.PekkoParadoxPlugin.autoImport._
import org.pegdown.Printer
import org.pegdown.ast.{ DirectiveNode, HtmlBlockNode, VerbatimNode, Visitor }
import sbtlicensereport.SbtLicenseReport.autoImportImpl.dumpLicenseReportAggregate
import scala.collection.JavaConverters._
import scala.io.{ Codec, Source }
object ParadoxSupport {
val paradoxWithCustomDirectives = Seq(
paradoxDirectives += ((context: Writer.Context) =>
new SignatureDirective(context.location.tree.label, context.properties, context)),
pekkoParadoxGithub := Some("https://github.com/apache/incubator-pekko-http"),
Compile / paradoxMarkdownToHtml / sourceGenerators += Def.taskDyn {
val targetFile = (Compile / paradox / sourceManaged).value / "license-report.md"
(LocalRootProject / dumpLicenseReportAggregate).map { dir =>
IO.copy(List(dir / "pekko-http-root-licenses.md" -> targetFile)).toList
}
}.taskValue)
class SignatureDirective(
page: Page, variables: Map[String, String], ctx: Writer.Context) extends LeafBlockDirective("signature") {
def render(node: DirectiveNode, visitor: Visitor, printer: Printer): Unit =
try {
val labels = node.attributes.values("identifier").asScala.map(_.toLowerCase())
val source = node.source match {
case direct: DirectiveNode.Source.Direct => direct.value
case _ => sys.error("Source references are not supported")
}
val file = SourceDirective.resolveFile("signature", source, page.file, variables)
// The following are stupid approximation's to match a signature/s
val TypeSignature = """\s*(type (\w+)(?=[:(\[]).*)(\s+\=.*)""".r
// println(s"Looking for signature regex '$Signature'")
val lines = Source.fromFile(file)(Codec.UTF8).getLines.toList
val types = lines.collect {
case line @ TypeSignature(signature, l, definition) if labels contains l.toLowerCase() =>
// println(s"Found label '$l' with sig '$full' in line $line")
signature + definition
}
val Signature = """.*((def|val) (\w+)(?=[:(\[]).*)""".r
val other = lines.mkString.split("=").collect {
case line @ Signature(signature, kind, l) if labels contains l.toLowerCase() =>
// println(s"Found label '$l' with sig '$full' in line $line")
signature
.replaceAll("""\s{2,}""", " ") // Due to formatting with new lines its possible to have excessive whitespace
}
val text = (types ++ other).mkString("\n")
if (text.trim.isEmpty) {
ctx.error(
s"Did not find any signatures with one of those names [${labels.mkString(", ")}]", page, node)
new HtmlBlockNode(s"""<div style="color: red;">[Broken signature inclusion [${labels.mkString(
", ")}] to [${node.source}]</div>""").accept(visitor)
} else {
val lang = Option(node.attributes.value("type")).getOrElse(Snippet.language(file))
new VerbatimNode(text, lang).accept(visitor)
}
} catch {
case e: FileNotFoundException =>
ctx.error(s"Unknown snippet [${e.getMessage}]", node)
}
}
}