Skip to content

Commit

Permalink
Add scrollspy. Wrap header and its content into section.
Browse files Browse the repository at this point in the history
  • Loading branch information
pikinier20 committed Mar 8, 2022
1 parent 97d844e commit 67062aa
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 30 deletions.
16 changes: 16 additions & 0 deletions scaladoc/resources/dotty_res/scripts/ux.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ window.addEventListener("DOMContentLoaded", () => {
$(this).parent().toggleClass("expanded")
});

const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
const id = entry.target.getAttribute('id');
if (entry.intersectionRatio > 0) {
document.querySelector(`#toc li a[href="#${id}"]`).parentElement.classList.add('active');
} else {
document.querySelector(`#toc li a[href="#${id}"]`).parentElement.classList.remove('active');
}
});
});


document.querySelectorAll('#content section[id]').forEach((section) => {
observer.observe(section);
});

document.querySelectorAll("#sideMenu2 a").forEach(elem => elem.addEventListener('click', e => e.stopPropagation()))

$('.names .tab').on('click', function() {
Expand Down
10 changes: 10 additions & 0 deletions scaladoc/resources/dotty_res/styles/scalastyle.css
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,16 @@ footer .socials {
display: block;
padding-top: 0.5em;
padding-bottom: 0.5em;
color: var(--leftbar-fg);
transition-duration: 0.2s;
}

#toc li:hover > a {
color: var(--link-hover-fg);
}

#toc li.active > a {
color: var(--link-hover-fg);
}

/* Signature coloring */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ class HtmlRenderer(rootPackage: Member, members: Map[DRI, Member])(using ctx: Do
li(ul(renderTocRec(level + 1, prefix))) +: renderTocRec(level, suffix)
}

renderTocRec(1, toc).headOption.map(toc => ul(cls := "toc-list")(toc))
renderTocRec(1, toc).headOption.map(toc => nav(cls := "toc-nav")(ul(cls := "toc-list")(toc)))


private def mkFrame(link: Link, parents: Vector[Link], content: => PageContent): AppliedTag =
Expand Down
18 changes: 5 additions & 13 deletions scaladoc/src/dotty/tools/scaladoc/renderers/SiteRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,12 @@ trait SiteRenderer(using DocContext) extends Locations:

val document = Jsoup.parse(content.resolved.code)

val toc = document.select("h1, h2, h3, h4, h5, h6").asScala.toSeq
.map { elem =>
val content = elem.text()

if elem.select("a[href]").isEmpty then {
val normalizedText = content.trim.toLowerCase.split("\\s+").mkString("-")
val anchor = Element("a")
.attr("href", s"#$normalizedText")
.id(normalizedText)
.addClass("anchor")
elem.insertChildren(0, JList(anchor))
val toc = document.select("section[id]").asScala.toSeq
.flatMap { elem =>
val header = elem.selectFirst("h1, h2, h3, h4, h5, h6")
Option(header).map { h =>
TocEntry(h.tag().getName, h.text(), s"#${elem.id()}")
}

TocEntry(elem.tag().getName, content, s"#${elem.selectFirst("a[href]").id()}")
}

document.select("a").forEach(element =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package dotty.tools.scaladoc
package site

import com.vladsch.flexmark.util.{ast => mdu, sequence}
import com.vladsch.flexmark.{ast => mda}
import com.vladsch.flexmark.formatter.Formatter
import com.vladsch.flexmark.util.options.MutableDataSet
import collection.JavaConverters._

import dotty.tools.scaladoc.tasty.comments.markdown.Section

object FlexmarkSectionWrapper {
def apply(md: mdu.Document): mdu.Document = {
val children = md.getChildren.asScala.toList
val newChildren = getNewChildren(Nil, None, children)
md.removeChildren()
newChildren.foreach(md.appendChild)
md
}

def getNewChildren(finished: List[mdu.Node], current: Option[(mda.Heading, List[mdu.Node])], rest: List[mdu.Node]): List[mdu.Node] = rest match {
case Nil => current.fold(finished)(finished :+ Section(_, _))
case (h: mda.Heading) :: rest => current.fold(getNewChildren(finished, Some(h, Nil), rest))((head, b) => getNewChildren(finished :+ Section(head, b), Some(h, Nil), rest))
case (n: mdu.Node) :: rest => current.fold(getNewChildren(finished :+ n, None, rest))((head, b) => getNewChildren(finished, Some(head, b :+ n), rest))
}
}
9 changes: 5 additions & 4 deletions scaladoc/src/dotty/tools/scaladoc/site/common.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.vladsch.flexmark.parser.{Parser, ParserEmulationProfile}
import com.vladsch.flexmark.util.options.{DataHolder, MutableDataSet}
import com.vladsch.flexmark.ext.wikilink.WikiLinkExtension
import com.vladsch.flexmark.formatter.Formatter
import com.vladsch.flexmark.html.HtmlRenderer

import scala.collection.JavaConverters._

Expand All @@ -24,21 +25,21 @@ val apiPageDRI: DRI = DRI(location = "api/index")
def defaultMarkdownOptions(using ctx: StaticSiteContext): DataHolder =
new MutableDataSet()
.setFrom(ParserEmulationProfile.COMMONMARK.getOptions)
.set(AnchorLinkExtension.ANCHORLINKS_WRAP_TEXT, false)
.set(AnchorLinkExtension.ANCHORLINKS_ANCHOR_CLASS, "anchor")
.set(EmojiExtension.ROOT_IMAGE_PATH, "https://github.global.ssl.fastly.net/images/icons/emoji/")
.set(WikiLinkExtension.LINK_ESCAPE_CHARS, "")
.set(Parser.EXTENSIONS, java.util.Arrays.asList(
TablesExtension.create(),
TaskListExtension.create(),
AutolinkExtension.create(),
AnchorLinkExtension.create(),
EmojiExtension.create(),
YamlFrontMatterExtension.create(),
StrikethroughExtension.create(),
WikiLinkExtension.create(),
tasty.comments.markdown.SnippetRenderingExtension
tasty.comments.markdown.SnippetRenderingExtension,
tasty.comments.markdown.SectionRenderingExtension
))
.set(HtmlRenderer.GENERATE_HEADER_ID, false)
.set(HtmlRenderer.RENDER_HEADER_ID, false)

def emptyTemplate(file: File, title: String): TemplateFile = TemplateFile(
file = file,
Expand Down
11 changes: 7 additions & 4 deletions scaladoc/src/dotty/tools/scaladoc/site/templates.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package site
import java.io.File
import java.nio.file.{Files, Paths}

import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension
import com.vladsch.flexmark.ext.autolink.AutolinkExtension
import com.vladsch.flexmark.ext.emoji.EmojiExtension
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension
Expand All @@ -25,6 +24,7 @@ import scala.collection.JavaConverters._

import scala.io.Source
import dotty.tools.scaladoc.snippets._
import scala.util.chaining._

/** RenderingContext stores information about defined properties, layouts and sites being resolved
*
Expand Down Expand Up @@ -123,9 +123,12 @@ case class TemplateFile(
val code = if (isHtml || layoutTemplate.exists(!_.isHtml)) rendered else
// Snippet compiler currently supports markdown only
val parser: Parser = Parser.builder(defaultMarkdownOptions).build()
val parsedMd = parser.parse(rendered)
val processed = FlexmarkSnippetProcessor.processSnippets(parsedMd, None, snippetCheckingFunc, withContext = false)(using ssctx.outerCtx)
HtmlRenderer.builder(defaultMarkdownOptions).build().render(processed)
val parsedMd = parser.parse(rendered).pipe { md =>
FlexmarkSnippetProcessor.processSnippets(md, None, snippetCheckingFunc, withContext = false)(using ssctx.outerCtx)
}.pipe { md =>
FlexmarkSectionWrapper(md)
}
HtmlRenderer.builder(defaultMarkdownOptions).build().render(parsedMd)

// If we have a layout template, we need to embed rendered content in it. Otherwise, we just leave the content as is.
layoutTemplate match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import dotty.tools.scaladoc.tasty.comments.markdown.ExtendedFencedCodeBlock
import dotty.tools.scaladoc.tasty.comments.PreparsedComment

object FlexmarkSnippetProcessor:
def processSnippets(root: mdu.Node, preparsed: Option[PreparsedComment], checkingFunc: => SnippetChecker.SnippetCheckingFunc, withContext: Boolean)(using CompilerContext): mdu.Node = {
def processSnippets[T <: mdu.Node](root: T, preparsed: Option[PreparsedComment], checkingFunc: => SnippetChecker.SnippetCheckingFunc, withContext: Boolean)(using CompilerContext): T = {
lazy val cf: SnippetChecker.SnippetCheckingFunc = checkingFunc

val nodes = root.getDescendants().asScala.collect {
Expand Down Expand Up @@ -97,4 +97,4 @@ object FlexmarkSnippetProcessor:
}

root
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.vladsch.flexmark.parser._
import com.vladsch.flexmark.ext.wikilink._
import com.vladsch.flexmark.ext.wikilink.internal.WikiLinkLinkRefProcessor
import com.vladsch.flexmark.util.ast._
import com.vladsch.flexmark.ast._
import com.vladsch.flexmark.util.options._
import com.vladsch.flexmark.util.sequence.BasedSequence
import com.vladsch.flexmark._
Expand All @@ -28,6 +29,11 @@ case class ExtendedFencedCodeBlock(
hasContext: Boolean
) extends BlankLine(codeBlock.getContentChars())

case class Section(
header: Heading,
body: List[Node]
) extends BlankLine(header.getContentChars())

class DocFlexmarkParser(resolveLink: String => DocLink) extends Parser.ParserExtension:

def parserOptions(opt: MutableDataHolder): Unit = () // noop
Expand Down Expand Up @@ -80,7 +86,8 @@ object DocFlexmarkRenderer:
val opts = MarkdownParser.mkMarkdownOptions(
Seq(
DocFlexmarkRenderer(renderLink),
SnippetRenderingExtension
SnippetRenderingExtension,
SectionRenderingExtension
)
)
HtmlRenderer.builder(opts).build().render(node)
HtmlRenderer.builder(opts).build().render(node)
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package dotty.tools.scaladoc
package tasty.comments.markdown

import com.vladsch.flexmark.html._
import com.vladsch.flexmark.html.renderer._
import com.vladsch.flexmark.parser._
import com.vladsch.flexmark.ext.wikilink._
import com.vladsch.flexmark.ext.wikilink.internal.WikiLinkLinkRefProcessor
import com.vladsch.flexmark.util.ast._
import com.vladsch.flexmark.util.options._
import com.vladsch.flexmark.util.sequence.BasedSequence
import com.vladsch.flexmark.util.html.AttributeImpl
import com.vladsch.flexmark._
import com.vladsch.flexmark.ast.FencedCodeBlock


object SectionRenderingExtension extends HtmlRenderer.HtmlRendererExtension:
def rendererOptions(opt: MutableDataHolder): Unit = ()

case class AnchorLink(link: String) extends BlankLine(BasedSequence.EmptyBasedSequence())
object SectionHandler extends CustomNodeRenderer[Section]:
val idGenerator = new HeaderIdGenerator.Factory().create()
override def render(node: Section, c: NodeRendererContext, html: HtmlWriter): Unit =
val Section(header, body) = node
val id = idGenerator.getId(header.getText)
val anchor = AnchorLink(s"#$id")
header.prependChild(anchor)
html.attr(AttributeImpl.of("id", id)).withAttr.tag("section", false, false, () => {
c.render(header)
body.foreach(c.render)
})

object AnchorLinkHandler extends CustomNodeRenderer[AnchorLink]:
override def render(node: AnchorLink, c: NodeRendererContext, html: HtmlWriter): Unit =
html.attr(AttributeImpl.of("href", node.link), AttributeImpl.of("class", "anchor")).withAttr.tag("a", false, false, () => ())


object Render extends NodeRenderer:
override def getNodeRenderingHandlers: JSet[NodeRenderingHandler[_]] =
JSet(
new NodeRenderingHandler(classOf[Section], SectionHandler),
new NodeRenderingHandler(classOf[AnchorLink], AnchorLinkHandler)
)

object Factory extends NodeRendererFactory:
override def create(options: DataHolder): NodeRenderer = Render

def extend(htmlRendererBuilder: HtmlRenderer.Builder, tpe: String): Unit =
htmlRendererBuilder.nodeRendererFactory(Factory)
16 changes: 12 additions & 4 deletions scaladoc/test/dotty/tools/scaladoc/site/TemplateFileTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,12 @@ class TemplateFileTests:


val expected =
"""<div id="root"><h1><a href="#test-page" id="test-page" class="anchor"></a>Test page</h1>
"""<div id="root"><section id="test-page">
|<h1><a href="#test-page" class="anchor"></a>Test page</h1>
|<p>Hello world!!</p>
|<h2><a href="#test-page-end" id="test-page-end" class="anchor"></a>Test page end</h2>
|</section><section id="test-page-end">
|<h2><a href="#test-page-end" class="anchor"></a>Test page end</h2>
|</section>
|</div>""".stripMargin

testContent(
Expand Down Expand Up @@ -206,7 +209,9 @@ class TemplateFileTests:
ext = "md"
) { t =>
assertEquals(
"""<h1><a href="#hello-there" id="hello-there" class="anchor"></a>Hello there!</h1>""",
"""<section id="hello-there">
|<h1><a href="#hello-there" class="anchor"></a>Hello there!</h1>
|</section>""".stripMargin,
t.resolveInner(RenderingContext(Map("msg" -> "there"))).code.trim())
}

Expand All @@ -216,7 +221,10 @@ class TemplateFileTests:
"""# Hello {{ msg }}!""",
ext = "md"
) { t =>
assertEquals("""<h1><a href="#hello-there" id="hello-there" class="anchor"></a>Hello there!</h1>""",
assertEquals(
"""<section id="hello-there">
|<h1><a href="#hello-there" class="anchor"></a>Hello there!</h1>
|</section>""".stripMargin,
t.resolveInner(RenderingContext(Map("msg" -> "there"))).code.trim())
}

Expand Down

0 comments on commit 67062aa

Please sign in to comment.