diff options
-rw-r--r-- | doc-tool/resources/_layouts/api-page.html | 70 | ||||
-rw-r--r-- | doc-tool/resources/_layouts/doc.html | 6 | ||||
-rw-r--r-- | doc-tool/resources/css/api-page.css | 61 | ||||
-rw-r--r-- | doc-tool/resources/css/dottydoc.css | 6 | ||||
-rw-r--r-- | doc-tool/src/dotty/tools/dottydoc/DocDriver.scala | 3 | ||||
-rw-r--r-- | doc-tool/src/dotty/tools/dottydoc/model/java.scala | 10 | ||||
-rw-r--r-- | doc-tool/src/dotty/tools/dottydoc/staticsite/DefaultParams.scala | 15 | ||||
-rw-r--r-- | doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala | 105 | ||||
-rw-r--r-- | doc-tool/test/dotty/tools/dottydoc/staticsite/SiteTests.scala | 10 |
9 files changed, 238 insertions, 48 deletions
diff --git a/doc-tool/resources/_layouts/api-page.html b/doc-tool/resources/_layouts/api-page.html new file mode 100644 index 000000000..41daf6c47 --- /dev/null +++ b/doc-tool/resources/_layouts/api-page.html @@ -0,0 +1,70 @@ +--- +layout: doc +extraCSS: + - css/api-page.css +--- + +<div id="entity-container"> + <div id="entity-title"> + <span id="entity-modifiers"> + {{ entity.modifiers | join: " " }} + </span> + <span id="entity-kind"> + {{ entity.kind }} + </span> + <span id="entity-name"> + {{ entity.name }} + </span> + </div> + + {% if entity.comment.body %} + <div id="entity-body" class="entity-section"> + {{ entity.comment.body }} + </div> + {% endif %} + + <h1 class="section">Members</h1> + + <div id="entity-members" class="entity-section"> + {% for member in entity.members %} + <div class="member"> + <div class="member-title"> + <span class="member-modifiers"> + {{ member.modifiers | join: " " }} + </span> + <span class="member-kind"> + {{ member.kind }} + </span> + <span class="member-name"> + {{ member.name }} + </span> + + {% if member.kind == "def" and member.paramLists.size > 0 %} + {% for plist in member.paramLists %} + <span class="no-left">(</span> + {% if plist.isImplicit %} + <span class="no-left keyword">implicit</span> + {% endif %} + {% for ref in plist.list %} + {% if forloop.last %} + <span class="no-left">{{ ref.title }}</span> + {% else %} + <span class="no-left">{{ ref.title }}, </span> + {% endif %} + {% endfor %} + <span class="no-left">)</span> + {% endfor %} + {% endif %} + + {% if member.returnValue %} + <span class="no-left">: {{ member.returnValue.title }}</span> + {% endif %} + </div><!-- end member-title --> + + <div class="member-body"> + {{ member.comment.short }} + </div> + </div><!-- end member --> + {% endfor %} + </div> +</div><!-- end entity-container --> diff --git a/doc-tool/resources/_layouts/doc.html b/doc-tool/resources/_layouts/doc.html index 78fc2b116..4fd615d23 100644 --- a/doc-tool/resources/_layouts/doc.html +++ b/doc-tool/resources/_layouts/doc.html @@ -8,17 +8,17 @@ layout: main <ul class="index-entities"> {% if docs.size > 0 %} <li class="index-title"> - <a href="{{ site.baseurl }}/api/index.html">API</a> + <span>API</span> </li> {% endif %} {% for pkg in docs %} <li class="index-entity entity-package"> - <a href="#">{{ pkg.name }}</a> + <a href="{{ site.baseurl }}/api/{{ pkg.path | join: "/" }}/index.html">{{ pkg.name }}</a> </li> {% for member in pkg.members %} {% if member.kind != "package" %} <li class="index-entity"> - <a href="#">{{ member.kind }} {{ member.name }}</a> + <a href="{{ site.baseurl }}/api/{{ member.path | join: "/" }}.html">{{ member.kind }} {{ member.name }}</a> </li> {% endif %} {% endfor %} diff --git a/doc-tool/resources/css/api-page.css b/doc-tool/resources/css/api-page.css new file mode 100644 index 000000000..47f88ed6c --- /dev/null +++ b/doc-tool/resources/css/api-page.css @@ -0,0 +1,61 @@ +/** Fonts */ +@import url(https://fonts.googleapis.com/css?family=Source+Code+Pro:400,700,600); + +div#entity-container { + margin-top: 10px; + padding: 0 20px 20px; +} + +div#entity-title { + color: #34495e; + font-family: "Helvetica","Arial",sans-serif; + font-weight: 400; + margin-bottom: 30px; + font-size: 2.5rem; +} + +div#entity-title > span#entity-modifiers, +div#entity-title > span#entity-kind { + font-weight: 100; +} + +div.entity-section { + background-color: #fff; + border-radius: 4px; + margin: 0; + padding: 30px; +} + +div.entity-section *:last-child { + margin-bottom: 0px; +} + +h1.section { + margin: 30px 0; +} + +div#entity-members > div.member { + margin-bottom: 30px; +} + +div#entity-members > div.member:last-child { + margin-bottom: 0px; +} + +div#entity-members > div.member > div.member-title { + font-family: "Source Code Pro", sans-serif; +} + +div#entity-members > div.member > div.member-title > span.member-name { + color: #458; + font-weight: 600; +} + +div#entity-members > div.member > div.member-title span.keyword { + font-weight: 600; + margin-right: 11px; +} + +div#entity-members > div.member > div.member-title span.no-left { + margin-left: -11px; +} diff --git a/doc-tool/resources/css/dottydoc.css b/doc-tool/resources/css/dottydoc.css index a40ee6a4d..754d3b3f7 100644 --- a/doc-tool/resources/css/dottydoc.css +++ b/doc-tool/resources/css/dottydoc.css @@ -59,6 +59,7 @@ ul.toc { ul.toc > li.toc-title > a { font-size: 16px; font-weight: bold; + margin-top: 1rem; } ul.toc > li > a#home-button, @@ -101,10 +102,11 @@ ul.toc > li > ul.hide { display: none; } -ul.index-entities > li.index-title > a { +ul.index-entities > li.index-title > span { font-size: 16px; font-weight: bold; - color: rgba(0,0,0,.87) + color: rgba(0,0,0,.87); + padding: 0 24px; } li.index-entity > a:focus { diff --git a/doc-tool/src/dotty/tools/dottydoc/DocDriver.scala b/doc-tool/src/dotty/tools/dottydoc/DocDriver.scala index 84eb04779..228366431 100644 --- a/doc-tool/src/dotty/tools/dottydoc/DocDriver.scala +++ b/doc-tool/src/dotty/tools/dottydoc/DocDriver.scala @@ -56,13 +56,14 @@ class DocDriver extends Driver { implicit val (filesToDocument, ctx) = setup(args, initCtx.fresh) doCompile(newCompiler(ctx), filesToDocument)(ctx) - val docs = ctx.docbase.packages.toJavaList + val docs = ctx.docbase.packages val siteRoot = new java.io.File(ctx.settings.siteRoot.value) if (!siteRoot.exists || !siteRoot.isDirectory) ctx.error(s"Site root does not exist: $siteRoot") else { Site(siteRoot, docs) + .generateApiDocs() .copyStaticFiles() .generateHtmlFiles() .generateBlog() diff --git a/doc-tool/src/dotty/tools/dottydoc/model/java.scala b/doc-tool/src/dotty/tools/dottydoc/model/java.scala index 30e884ce3..01dac2685 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/java.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/java.scala @@ -11,9 +11,12 @@ object java { import scala.collection.JavaConverters._ import _root_.java.util.{ Optional => JOptional, Map => JMap } - implicit class JavaOption[A](val opt: Option[A]) extends AnyVal { - def asJava: JOptional[A] = - opt.map(a => JOptional.of(a)).getOrElse(JOptional.empty[A]) + implicit class OptStr(val opt: Option[String]) extends AnyVal { + def asJava = opt.getOrElse(null) + } + + implicit class OptMap(val opt: Option[JMap[String, _]]) extends AnyVal { + def asJava = opt.getOrElse(Map.empty.asJava) } implicit class JavaComment(val cmt: Comment) extends AnyVal { @@ -222,6 +225,7 @@ object java { case ent: Object => ent.asJava(extras) case ent: Def => ent.asJava case ent: Val => ent.asJava + case _ => Map.empty.asJava } implicit class JavaMap(val map: collection.Map[String, Package]) extends AnyVal { diff --git a/doc-tool/src/dotty/tools/dottydoc/staticsite/DefaultParams.scala b/doc-tool/src/dotty/tools/dottydoc/staticsite/DefaultParams.scala index d0f49100c..ce57dc805 100644 --- a/doc-tool/src/dotty/tools/dottydoc/staticsite/DefaultParams.scala +++ b/doc-tool/src/dotty/tools/dottydoc/staticsite/DefaultParams.scala @@ -3,24 +3,31 @@ package dottydoc package staticsite import java.util.{ List => JList } +import model.{ Entity, NonEntity } case class DefaultParams( docs: JList[_], page: PageInfo, - site: SiteInfo + site: SiteInfo, + entity: Entity = NonEntity ) { - + import model.java._ import scala.collection.JavaConverters._ + def toMap: Map[String, AnyRef] = Map( "docs" -> docs, + "page" -> Map( "url" -> page.url, "path" -> page.path ), + "site" -> Map( "baseurl" -> site.baseurl, "posts" -> site.posts.map(_.toMap) - ).asJava + ).asJava, + + "entity" -> entity.asJava() ) def withPosts(posts: Array[BlogPost]): DefaultParams = @@ -28,6 +35,8 @@ case class DefaultParams( def withUrl(url: String): DefaultParams = copy(page = PageInfo(url)) + + def withEntity(e: model.Entity) = copy(entity = e) } case class PageInfo(url: String) { diff --git a/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala b/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala index 7c9dc00fe..ecc076ba3 100644 --- a/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala +++ b/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala @@ -18,10 +18,17 @@ import com.vladsch.flexmark.util.options.{ DataHolder, MutableDataSet } import dotc.config.Printers.dottydoc import dotc.core.Contexts.Context +import model.Package import scala.io.Source import scala.collection.mutable.ArrayBuffer -case class Site(val root: JFile, val docs: JList[_]) extends ResourceFinder { +case class Site(val root: JFile, val documentation: Map[String, Package]) extends ResourceFinder { + /** Documentation serialized to java maps */ + private val docs: JList[_] = { + import model.java._ + documentation.toJavaList + } + /** All files that are considered static in this context, this can be * anything from CSS, JS to images and other files. * @@ -87,6 +94,7 @@ case class Site(val root: JFile, val docs: JList[_]) extends ResourceFinder { // Copy statics included in resources Map( + "css/api-page.css" -> "/css/api-page.css", "css/dottydoc.css" -> "/css/dottydoc.css", "css/color-brewer.css" -> "/css/color-brewer.css", "js/highlight.pack.js" -> "/js/highlight.pack.js" @@ -113,41 +121,73 @@ case class Site(val root: JFile, val docs: JList[_]) extends ResourceFinder { DefaultParams(docs, PageInfo(pathFromRoot), SiteInfo(baseUrl, Array())) } - /** Generate HTML files from markdown and .html sources */ - def generateHtmlFiles(outDir: JFile = new JFile(root.getAbsolutePath + "/_site"))(implicit ctx: Context): this.type = { + private def createOutput(outDir: JFile)(op: => Unit): this.type = { if (!outDir.isDirectory) outDir.mkdirs() if (!outDir.isDirectory) /*dottydoc.*/println(s"couldn't create output folder: $outDir") - else compilableFiles.foreach { asset => - val pathFromRoot = stripRoot(asset) - val fileContents = Source.fromFile(asset).mkString - val params = defaultParams(asset).withPosts(blogInfo).toMap - val page = - if (asset.getName.endsWith(".md")) new MarkdownPage(fileContents, params, includes) - else new HtmlPage(fileContents, params, includes) - - val renderedPage = render(page) - val source = new ByteArrayInputStream(renderedPage.getBytes(StandardCharsets.UTF_8)) - val target = pathFromRoot.splitAt(pathFromRoot.lastIndexOf('.'))._1 + ".html" - val htmlTarget = mkdirs(fs.getPath(outDir.getAbsolutePath, target)) - Files.copy(source, htmlTarget, REPLACE_EXISTING) - } + else op this } - def generateBlog(outDir: JFile = new JFile(root.getAbsolutePath + "/_site"))(implicit ctx: Context): Unit = { - blogposts.foreach { file => - val BlogPost.extract(year, month, day, name, ext) = file.getName - val fileContents = Source.fromFile(file).mkString - val params = defaultParams(file, 2).withPosts(blogInfo).toMap - val page = - if (ext == "md") new MarkdownPage(fileContents, params, includes) - else new HtmlPage(fileContents, params, includes) + /** Generate HTML for the API documentation */ + def generateApiDocs(outDir: JFile = new JFile(root.getAbsolutePath + "/_site"))(implicit ctx: Context): this.type = + createOutput(outDir) { + def genDoc(e: model.Entity) = { + // Suffix is index.html for packages and therefore the additional depth + // is increased by 1 + val (suffix, offset) = + if (e.kind == "package") ("/index.html", -1) + else (".html", 0) + + val target = mkdirs(fs.getPath(outDir.getAbsolutePath + "/api/" + e.path.mkString("/") + suffix)) + val params = defaultParams(target.toFile, -1).withPosts(blogInfo).withEntity(e) + val page = new HtmlPage(layouts("api-page"), params.toMap, includes) + + val rendered = render(page) + val source = new ByteArrayInputStream(rendered.getBytes(StandardCharsets.UTF_8)) - val source = new ByteArrayInputStream(render(page).getBytes(StandardCharsets.UTF_8)) - val target = mkdirs(fs.getPath(outDir.getAbsolutePath, "blog", year, month, day, name + ".html")) - Files.copy(source, target, REPLACE_EXISTING) + Files.copy(source, target, REPLACE_EXISTING) + } + + documentation.values.foreach { pkg => + genDoc(pkg) + pkg.members.filterNot(_.kind == "package").map(genDoc) + } + } + + /** Generate HTML files from markdown and .html sources */ + def generateHtmlFiles(outDir: JFile = new JFile(root.getAbsolutePath + "/_site"))(implicit ctx: Context): this.type = + createOutput(outDir) { + compilableFiles.foreach { asset => + val pathFromRoot = stripRoot(asset) + val fileContents = Source.fromFile(asset).mkString + val params = defaultParams(asset).withPosts(blogInfo).toMap + val page = + if (asset.getName.endsWith(".md")) new MarkdownPage(fileContents, params, includes) + else new HtmlPage(fileContents, params, includes) + + val renderedPage = render(page) + val source = new ByteArrayInputStream(renderedPage.getBytes(StandardCharsets.UTF_8)) + val target = pathFromRoot.splitAt(pathFromRoot.lastIndexOf('.'))._1 + ".html" + val htmlTarget = mkdirs(fs.getPath(outDir.getAbsolutePath, target)) + Files.copy(source, htmlTarget, REPLACE_EXISTING) + } + } + + def generateBlog(outDir: JFile = new JFile(root.getAbsolutePath + "/_site"))(implicit ctx: Context): this.type = + createOutput(outDir) { + blogposts.foreach { file => + val BlogPost.extract(year, month, day, name, ext) = file.getName + val fileContents = Source.fromFile(file).mkString + val params = defaultParams(file, 2).withPosts(blogInfo).toMap + val page = + if (ext == "md") new MarkdownPage(fileContents, params, includes) + else new HtmlPage(fileContents, params, includes) + + val source = new ByteArrayInputStream(render(page).getBytes(StandardCharsets.UTF_8)) + val target = mkdirs(fs.getPath(outDir.getAbsolutePath, "blog", year, month, day, name + ".html")) + Files.copy(source, target, REPLACE_EXISTING) + } } - } private def mkdirs(path: Path): path.type = { val parent = path.getParent.toFile @@ -181,7 +221,11 @@ case class Site(val root: JFile, val docs: JList[_]) extends ResourceFinder { def splitFiles(f: JFile, assets: ArrayBuffer[JFile], comp: ArrayBuffer[JFile]): Unit = { val name = f.getName if (f.isDirectory) { - if (!f.getName.startsWith("_")) f.listFiles.foreach(splitFiles(_, assets, comp)) + val name = f.getName + if (!name.startsWith("_") && name != "api") f.listFiles.foreach(splitFiles(_, assets, comp)) + if (f.getName == "api") dottydoc.println { + "the specified `/api` directory will not be used since it is needed for the api documentation" + } } else if (name.endsWith(".md") || name.endsWith(".html")) comp.append(f) else assets.append(f) @@ -230,6 +274,7 @@ case class Site(val root: JFile, val docs: JList[_]) extends ResourceFinder { "main" -> "/_layouts/main.html", "doc" -> "/_layouts/doc.html", "doc-page" -> "/_layouts/doc-page.html", + "api-page" -> "/_layouts/api-page.html", "blog" -> "/_layouts/blog.html", "index" -> "/_layouts/index.html" ).mapValues(getResource) diff --git a/doc-tool/test/dotty/tools/dottydoc/staticsite/SiteTests.scala b/doc-tool/test/dotty/tools/dottydoc/staticsite/SiteTests.scala index d72299e84..11a1da818 100644 --- a/doc-tool/test/dotty/tools/dottydoc/staticsite/SiteTests.scala +++ b/doc-tool/test/dotty/tools/dottydoc/staticsite/SiteTests.scala @@ -7,7 +7,7 @@ import org.junit.Assert._ class SiteTests extends DottyDocTest { import scala.collection.JavaConverters._ - val site = new Site(new java.io.File("../doc-tool/resources/"), List.empty.asJava) + val site = new Site(new java.io.File("../doc-tool/resources/"), Map.empty) private def html( str: String, @@ -19,7 +19,7 @@ class SiteTests extends DottyDocTest { assert(site.root.exists && site.root.isDirectory, s"'${site.root.getName}' is not a directory") - val expectedLayouts = Set("main", "index", "blog", "doc", "doc-page") + val expectedLayouts = Set("main", "index", "blog", "doc", "doc-page", "api-page") assert(site.layouts.keys == expectedLayouts, s"Incorrect layouts in: ${site.layouts.keys}, expected: $expectedLayouts") } @@ -91,16 +91,14 @@ class SiteTests extends DottyDocTest { val compd = site.compilableFiles.map(site.stripRoot).toSet val expectedAssets = Set( + "css/api-page.css", "css/dottydoc.css", "css/color-brewer.css", "js/highlight.pack.js" ) val expectedCompd = Set( - "index.md" // Directories starting in `_` are not included in compilable files - //"_includes/header.html", - //"_layouts/index.html", - //"_layouts/main.html" + "index.md" ) assert(expectedAssets == assets, |