scala.tools.nsc.doc.html.HtmlPage.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-compiler Show documentation
Show all versions of scala-compiler Show documentation
Compiler for the Scala Programming Language
The newest version!
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala
package tools
package nsc
package doc
package html
import base._
import base.comment._
import model._
import scala.reflect.internal.Reporter
import scala.collection.{immutable, _}
import java.io.Writer
import java.net.URI
import javax.xml.stream.XMLOutputFactory
/** An html page that is part of a Scaladoc site.
* @author David Bernard
* @author Gilles Dubochet */
abstract class HtmlPage extends Page { thisPage =>
import HtmlTags._
/** The title of this page. */
protected def title: String
/** ScalaDoc reporter for error handling */
protected def docletReporter: Reporter
/** The page description */
protected def description: String =
// unless overwritten, will display the title in a spaced format, keeping - and .
title.replaceAll("[^a-zA-Z0-9\\.\\-]+", " ").replaceAll("\\-+", " - ").replaceAll(" +", " ")
/** The page keywords */
protected def keywords: String =
// unless overwritten, same as description, minus the " - "
description.replaceAll(" - ", " ")
/** Additional header elements (links, scripts, meta tags, etc.) required for this page. */
protected def headers: Elems
/** The body of this page. */
def body: Elems
def writeHtml(encoding: String)(w: Writer) = {
val html =
Html(Head(Meta(`http-equiv` = "X-UA-Compatible", content = "IE=edge") ::
Meta(name = "viewport", content = "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no") ::
HtmlTags.Title(elems= Txt(title)) ::
Meta(name = "description", content = description) ::
Meta(name = "keywords", content = keywords) ::
Meta(`http-equiv` = "content-type", content = s"text/html; charset=${encoding}") :: headers) :: body)
val xsw = XMLOutputFactory.newInstance.createXMLStreamWriter(w)
xsw.writeDTD("")
html.toXhtml(xsw, w)
xsw.flush()
xsw.close()
w.write('\n')
}
def writeFor(site: HtmlFactory): Unit = {
writeFile(site)(writeHtml(site.encoding))
if (site.universe.settings.docRawOutput)
writeFile(site, ".raw") {
// we're only interested in the body, as this will go into the diff
_.write(textOf(body))
}
}
/** Transforms an optional comment into an styled HTML tree representing its body if it is defined, or into an empty
* node sequence if it is not. */
def commentToHtml(comment: Option[Comment]): Elems =
(comment map (commentToHtml(_))) getOrElse NoElems
/** Transforms a comment into an styled HTML tree representing its body. */
def commentToHtml(comment: Comment): Elems =
bodyToHtml(comment.body)
def bodyToHtml(body: comment.Body): Elems =
body.blocks.toList flatMap (blockToHtml(_))
def blockToHtml(block: Block): Elems = (block match {
case comment.Title(in, 1) => H(3, inlineToHtml(in))
case comment.Title(in, 2) => H(4, inlineToHtml(in))
case comment.Title(in, 3) => H(5, inlineToHtml(in))
case comment.Title(in, _) => H(6, inlineToHtml(in))
case Paragraph(in) => P(inlineToHtml(in))
case comment.Code(data) => Pre(SyntaxHigh(data)) //{ Txt(data) }
case UnorderedList(items) => Ul(listItemsToHtml(items))
case OrderedList(items, listStyle) => Ol(`class`= listStyle, elems= listItemsToHtml(items))
case DefinitionList(items) => Dl(items.toList flatMap { case (t, d) => Dt(inlineToHtml(t)) :: Dd(blockToHtml(d)) :: NoElems })
case HorizontalRule() => Hr
case tbl: comment.Table => tableToHtml(tbl)
}) :: NoElems
def listItemsToHtml(items: Seq[Block]) =
items.foldLeft(NoElems){ (xmlList, item) =>
item match {
// html requires sub ULs to be put into the last LI
case OrderedList(_, _) | UnorderedList(_) => xmlList.init ++ Li(xmlList.last.elems ++ blockToHtml(item))
case Paragraph(inline) => xmlList :+ Li(inlineToHtml(inline)) // LIs are blocks, no need to use Ps
case block => xmlList :+ Li(blockToHtml(block))
}
}
def inlineToHtml(inl: Inline): Elems = inl match {
case Chain(items) => items.toList flatMap (inlineToHtml(_))
case Italic(in) => I(inlineToHtml(in)) :: NoElems
case Bold(in) => B(inlineToHtml(in)) :: NoElems
case Underline(in) => U(inlineToHtml(in)) :: NoElems
case Superscript(in) => Sup(inlineToHtml(in)) :: NoElems
case Subscript(in) => Sub(inlineToHtml(in)) :: NoElems
case comment.Link(raw, title) => A(href = raw, target = "_blank", elems = inlineToHtml(title)) :: NoElems
case Monospace(in) => Code(inlineToHtml(in)) :: NoElems
case Text(text) => Txt(text) :: NoElems
case Summary(in) => inlineToHtml(in)
case HtmlTag(tag) => Raw(tag) :: NoElems
case EntityLink(target, link) => linkToHtml(target, link, hasLinks = true)
}
def linkToHtml(text: Inline, link: LinkTo, hasLinks: Boolean): Elems = link match {
case LinkToTpl(dtpl: TemplateEntity) =>
(if (hasLinks) A(href=relativeLinkTo(dtpl), `class` = "extype", id=dtpl.qualifiedName, name=dtpl.qualifiedName, elems=inlineToHtml(text))
else Span(`class` = "extype", name=dtpl.qualifiedName, elems=inlineToHtml(text))) :: NoElems
case LinkToMember(mbr: MemberEntity, inTpl: TemplateEntity) =>
(if (hasLinks) A(href=relativeLinkTo(inTpl) + "#" + mbr.signature, `class`= "extmbr", id=mbr.qualifiedName, name=mbr.qualifiedName, elems=inlineToHtml(text))
else Span(`class`= "extmbr", name=mbr.qualifiedName, elems=inlineToHtml(text))) :: NoElems
case Tooltip(tooltip) =>
Span(`class` = "extype", name=tooltip, elems = inlineToHtml(text)) :: NoElems
case LinkToExternalTpl(name, baseUrlString, dtpl: TemplateEntity) =>
val baseUrl = new URI(Page.makeUrl(baseUrlString, Page.templateToPath(dtpl)))
val url = if (name.isEmpty) baseUrl
else new URI(baseUrl.getScheme, baseUrl.getSchemeSpecificPart, name)
(if (hasLinks) A(href=url.toString, `class` = "extype", id=dtpl.qualifiedName, name=dtpl.qualifiedName, elems = inlineToHtml(text) )
else Span(`class` = "extype", name=dtpl.qualifiedName, elems= inlineToHtml(text))) :: NoElems
case _ =>
inlineToHtml(text)
}
private def tableToHtml(table: comment.Table): Elem = {
val comment.Table(header, columnOptions, rows) = table
val colClass = Map(
ColumnOption.ColumnOptionLeft -> "doctbl-left",
ColumnOption.ColumnOptionCenter -> "doctbl-center",
ColumnOption.ColumnOptionRight -> "doctbl-right"
)
val cc = columnOptions.map(colClass)
Table(
thead = THead(
Tr(
(header.cells zip cc).toList.map{ case (cell, cls) => Th(cell.blocks.flatMap(blockToHtml).toList, `class` = cls)}
) :: immutable.Nil
),
tbody = if (rows.nonEmpty) {
TBody(
rows.toList.map {
row => Tr((row.cells zip cc).toList.map{ case (cell, cls) => Td(elems = cell.blocks.flatMap(blockToHtml).toList, `class`=cls) })
}
)
} else null,
`class` = "doctbl")
}
def typeToHtml(tpes: List[model.TypeEntity], hasLinks: Boolean): Elems = tpes match {
case Nil => NoElems
case List(tpe) => typeToHtml(tpe, hasLinks)
case tpe :: rest => typeToHtml(tpe, hasLinks) ++ (Txt(" with ") :: typeToHtml(rest, hasLinks))
}
def typeToHtml(tpe: model.TypeEntity, hasLinks: Boolean): Elems = {
val string = tpe.name
def toLinksOut(inPos: Int, starts: List[Int]): Elems = {
if (starts.isEmpty && (inPos == string.length))
NoElems
else if (starts.isEmpty)
Txt(string.slice(inPos, string.length)) :: NoElems
else if (inPos == starts.head)
toLinksIn(inPos, starts)
else {
Txt(string.slice(inPos, starts.head)) :: toLinksIn(starts.head, starts)
}
}
def toLinksIn(inPos: Int, starts: List[Int]): Elems = {
val (link, width) = tpe.refEntity(inPos)
val text = comment.Text(string.slice(inPos, inPos + width))
linkToHtml(text, link, hasLinks) ++ toLinksOut(inPos + width, starts.tail)
}
if (hasLinks)
toLinksOut(0, tpe.refEntity.keySet.toList)
else
Txt(string) :: NoElems
}
def typesToHtml(tpess: List[model.TypeEntity], hasLinks: Boolean, sep: Elems): Elems = tpess match {
case Nil => NoElems
case tpe :: Nil => typeToHtml(tpe, hasLinks)
case tpe :: tpes => typeToHtml(tpe, hasLinks) ++ sep ++ typesToHtml(tpes, hasLinks, sep)
}
def hasPage(e: DocTemplateEntity) = {
e.isPackage || e.isTrait || e.isClass || e.isObject || e.isCase
}
/** Returns the HTML code that represents the template in `tpl` as a hyperlinked name. */
def templateToHtml(tpl: TemplateEntity, name: String = null): Elem = {
val txt = Txt(if (name eq null) tpl.name else name)
tpl match {
case dTpl: DocTemplateEntity if hasPage(dTpl) => A(href= relativeLinkTo(dTpl), id= dTpl.qualifiedName, name= dTpl.qualifiedName, `class` = "extype", elems = txt :: NoElems)
case _ => txt
}
}
/** Returns the HTML code that represents the templates in `tpls` as a list of hyperlinked names. */
def templatesToHtml(tplss: List[TemplateEntity], sep: Elems): Elems = tplss match {
case Nil => NoElems
case tpl :: Nil => templateToHtml(tpl) :: NoElems
case tpl :: tpls => templateToHtml(tpl) :: sep ++ templatesToHtml(tpls, sep)
}
object Image extends Enumeration {
val Trait, Class, Type, Object, Package = Value
}
def permalink(template: Entity, isSelf: Boolean = true): Elem =
Span(`class`= "permalink", elems=
A(href=memberToUrl(template), title="Permalink", elems =
I(`class`="material-icons", elems=Txt("\uE157"))
))
def docEntityImageClass(tpl: DocTemplateEntity): String =
tpl.kind + tpl.companion.fold("")("-companion-" + _.kind)
def docEntityKindToCompanionTitle(ety: DocTemplateEntity, baseString: String = "See companion") =
ety.companion match{
case Some(companion) =>
s"$baseString${
if(companion.isObject) " object"
else if(companion.isTrait) " trait"
else if(companion.isClass) " class"
else ""
}"
case None => baseString
}
def companionAndPackage(tpl: DocTemplateEntity): Elems =
Span(`class`= "morelinks", elems =
tpl.companion match {
case Some(companionTpl) =>
val objClassTrait =
if (companionTpl.isObject) s"object ${tpl.name}"
else if (companionTpl.isTrait) s"trait ${companionTpl.name}"
else s"class ${companionTpl.name}"
Div(elems = Txt("Companion ") :: A(href= relativeLinkTo(companionTpl), title= docEntityKindToCompanionTitle(tpl), elems=Txt(objClassTrait)) :: NoElems)
case None => NoElems
})
private def memberToUrl(template: Entity): String = {
val (signature: Option[String], containingTemplate: TemplateEntity) = template match {
case dte: DocTemplateEntity => (None, dte)
case me: MemberEntity => (Some(me.signature), me.inTemplate)
case tpl => (None, tpl)
}
val templatePath = templateToPath(containingTemplate)
val url = "../" * (thisPage.path.size - 1) + templatePath.reverse.mkString("/")
url + signature.map("#" + _).getOrElse("")
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy