summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorGilles Dubochet <gilles.dubochet@epfl.ch>2010-05-15 18:44:18 +0000
committerGilles Dubochet <gilles.dubochet@epfl.ch>2010-05-15 18:44:18 +0000
commitbf82ecbcbeaa3a6975e360c2b616ce37938df80f (patch)
tree72d0b8e295f72db4814bf658f6a5c1f575941d21 /src/compiler
parentf4420e7b13542f771c3ca80abfeab4dabf16b309 (diff)
downloadscala-bf82ecbcbeaa3a6975e360c2b616ce37938df80f.tar.gz
scala-bf82ecbcbeaa3a6975e360c2b616ce37938df80f.tar.bz2
scala-bf82ecbcbeaa3a6975e360c2b616ce37938df80f.zip
[scaladoc] Closes #3428 (HTML markup supported ...
[scaladoc] Closes #3428 (HTML markup supported in Scaladoc). Improves wiki-syntax parsing of lists. Improves stylesheet for headings and code blocks in comments. Review by malayeri.
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala9
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css60
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/Body.scala1
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala149
4 files changed, 146 insertions, 73 deletions
diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala
index 68d17c683f..66e2ba2260 100644
--- a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala
@@ -128,10 +128,10 @@ abstract class HtmlPage { thisPage =>
body.blocks flatMap (blockToHtml(_))
def blockToHtml(block: Block): NodeSeq = block match {
- case Title(in, 1) => <h1>{ inlineToHtml(in) }</h1>
- case Title(in, 2) => <h2>{ inlineToHtml(in) }</h2>
- case Title(in, 3) => <h3>{ inlineToHtml(in) }</h3>
- case Title(in, _) => <h4>{ inlineToHtml(in) }</h4>
+ case Title(in, 1) => <h3>{ inlineToHtml(in) }</h3>
+ case Title(in, 2) => <h4>{ inlineToHtml(in) }</h4>
+ case Title(in, 3) => <h5>{ inlineToHtml(in) }</h5>
+ case Title(in, _) => <h6>{ inlineToHtml(in) }</h6>
case Paragraph(in) => <p>{ inlineToHtml(in) }</p>
case Code(data) => <pre>{ xml.Text(data) }</pre>
case UnorderedList(items) =>
@@ -168,6 +168,7 @@ abstract class HtmlPage { thisPage =>
case Monospace(text) => <code>{ xml.Text(text) }</code>
case Text(text) => xml.Text(text)
case Summary(in) => inlineToHtml(in)
+ case HtmlTag(tag) => xml.Unparsed(tag)
}
def typeToHtml(tpe: model.TypeEntity, hasLinks: Boolean): NodeSeq = {
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css
index 92de97f619..0c17d9fa2a 100644
--- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css
@@ -81,6 +81,7 @@ a:hover {
}
#comment {
+ padding-right: 8px;
padding-left: 8px;
}
@@ -193,17 +194,56 @@ div.members > ol > li {
.cmt {}
.cmt p {
- margin: 2px 0 2px 0;
+ margin-bottom: 0.4em;
+ margin-top: 0.4em;
}
-.cmt code {
- font-family: monospace;
+.cmt h3 {
+ margin-bottom: 1em;
+ margin-top: 1em;
+ display: block;
+ text-align: left;
+ font-weight: bold;
+ font-size: x-large;
+}
+
+.cmt h4 {
+ margin-bottom: 0.6em;
+ margin-top: 0.6em;
+ display: block;
+ text-align: left;
+ font-weight: bold;
+ font-size: large;
+}
+
+.cmt h5 {
+ margin-bottom: 0.4em;
+ margin-top: 0.4em;
+ display: block;
+ text-align: left;
+ font-weight: bold;
+}
+
+.cmt h6 {
+ margin-bottom: 0.4em;
+ margin-top: 0.4em;
+ display: block;
+ text-align: left;
+ font-style: italic;
}
.cmt pre {
+ padding: 0.4em;
+ border-color: #ddd;
+ border-style: solid;
+ border-width: 1px;
+ margin-left: 0;
+ margin-bottom: 0.4em;
+ margin-right: 0;
+ margin-top: 0.4em;
+ background-color: #eee;
display: block;
font-family: monospace;
- margin: 2px 0 2px 0;
}
.cmt ul {
@@ -241,20 +281,24 @@ div.members > ol > li {
display:list-item;
}
+.cmt code {
+ font-family: monospace;
+}
+
.cmt a {
- text-decoration: underline;
+ font-style: bold;
}
/* Comments structured layout */
p.comment {
display: block;
- margin-left: 8.7em;
+ margin-left: 8.7em;
}
p.shortcomment {
display: block;
- margin-left: 8.7em;
+ margin-left: 8.7em;
cursor: help;
}
@@ -264,7 +308,7 @@ div.fullcomment {
}
#template div.fullcomment {
- margin: 6px 0 6px 8.7em;
+ margin: 6px 0 6px 8.7em;
}
div.fullcomment .block {
diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala b/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala
index 29e47fa578..255c61095e 100644
--- a/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala
@@ -67,6 +67,7 @@ final case class Link(target: String, title: Inline) extends Inline
final case class EntityLink(target: TemplateEntity) extends Inline
final case class Monospace(text: String) extends Inline
final case class Text(text: String) extends Inline
+final case class HtmlTag(data: String) extends Inline
/** The summary of a comment, usually its first sentence. There must be exactly one summary per body. */
final case class Summary(text: Inline) extends Inline
diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
index 63b7b10859..475fbf584e 100644
--- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
@@ -57,21 +57,31 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
new Regex("""(?:\s*\*\s?)?(.*)""")
/** Dangerous HTML tags that should be replaced by something safer, such as wiki syntax, or that should be dropped. */
- protected val DangerousHtml =
- new Regex("""<(/?(?:p|div|pre|ol|ul|li|h[1-6]|code))[^>]*>""")
+ protected val DangerousTags =
+ new Regex("""<(/?(div|ol|ul|li|h[1-6]|p))( [^>]*)?/?>|<!--.*-->""")
/** Maps a dangerous HTML tag to a safe wiki replacement, or an empty string if it cannot be salvaged. */
- protected def htmlReplacement(mtch: Regex.Match): String = mtch.matched match {
+ protected def htmlReplacement(mtch: Regex.Match): String = mtch.group(1) match {
case "p" | "div" => "\n\n"
- case "h1" | "h2" | "h3" | "h4" | "h5" | "h6" => "\n= "
- case "/h1" | "/h2" | "/h3" | "/h4" | "/h5" | "/h6" => " =\n"
- case "pre" => "{{{"
- case "/pre" => "}}}"
+ case "h1" => "\n= "
+ case "/h1" => " =\n"
+ case "h2" => "\n== "
+ case "/h2" => " ==\n"
+ case "h3" => "\n=== "
+ case "/h3" => " ===\n"
+ case "h4" | "h5" | "h6" => "\n==== "
+ case "/h4" | "/h5" | "/h6" => " ====\n"
case "code" | "/code" => "`"
- case "li" => "\n - "
+ case "li" => "\n * - "
case _ => ""
}
+ /** Safe HTML tags that can be kept. */
+ protected val SafeTags =
+ new Regex("""(</?(abbr|acronym|address|area|a|bdo|big|blockquote|br|button|b|caption|code|cite|col|colgroup|dd|del|dfn|em|fieldset|form|hr|img|input|ins|i|kbd|label|legend|link|map|object|optgroup|option|param|pre|q|samp|select|small|span|strong|sub|sup|table|tbody|td|textarea|tfoot|th|thead|tr|tt|var)( [^>]*)?/?>)""")
+
+ protected val safeTagMarker = '\u000E'
+
/** A Scaladoc tag not linked to a symbol. Returns the name of the tag, and the rest of the line. */
protected val SimpleTag =
new Regex("""\s*@(\S+)\s+(.*)""")
@@ -114,8 +124,12 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
}
}
val strippedComment = comment.trim.stripPrefix("/*").stripSuffix("*/")
- val safeComment = DangerousHtml.replaceAllIn(strippedComment, { htmlReplacement(_) })
- safeComment.lines.toList map (cleanLine(_))
+ val safeComment = DangerousTags.replaceAllIn(strippedComment, { htmlReplacement(_) })
+ val markedTagComment =
+ SafeTags.replaceAllIn(safeComment, { mtch =>
+ java.util.regex.Matcher.quoteReplacement(safeTagMarker + mtch.matched + safeTagMarker)
+ })
+ markedTagComment.lines.toList map (cleanLine(_))
}
/** Parses a comment (in the form of a list of lines) to a Comment instance, recursively on lines. To do so, it
@@ -251,17 +265,6 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
var summaryParsed = false
- /** listStyle ::= '-' spc | '1.' spc | 'I.' spc | 'i.' spc | 'A.' spc | 'a.' spc
- * Characters used to build lists and their constructors */
- protected val listStyles = Map[String, (Seq[Block] => Block)]( // TODO Should this be defined at some list companion?
- "- " -> ( UnorderedList(_) ),
- "1. " -> ( OrderedList(_,"decimal") ),
- "I. " -> ( OrderedList(_,"upperRoman") ),
- "i. " -> ( OrderedList(_,"lowerRoman") ),
- "A. " -> ( OrderedList(_,"upperAlpha") ),
- "a. " -> ( OrderedList(_,"lowerAlpha") )
- )
-
def document(): Body = {
nextChar()
val blocks = new mutable.ListBuffer[Block]
@@ -287,9 +290,20 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
}
}
+ /** listStyle ::= '-' spc | '1.' spc | 'I.' spc | 'i.' spc | 'A.' spc | 'a.' spc
+ * Characters used to build lists and their constructors */
+ protected val listStyles = Map[String, (Seq[Block] => Block)]( // TODO Should this be defined at some list companion?
+ "- " -> ( UnorderedList(_) ),
+ "1. " -> ( OrderedList(_,"decimal") ),
+ "I. " -> ( OrderedList(_,"upperRoman") ),
+ "i. " -> ( OrderedList(_,"lowerRoman") ),
+ "A. " -> ( OrderedList(_,"upperAlpha") ),
+ "a. " -> ( OrderedList(_,"lowerAlpha") )
+ )
+
/** Checks if the current line is formed with more than one space and one the listStyles */
def checkList =
- countWhitespace > 0 && listStyles.keysIterator.indexWhere(checkSkipInitWhitespace(_)) >= 0
+ (countWhitespace > 0) && (listStyles.keys exists { checkSkipInitWhitespace(_) })
/** {{{
* nListBlock ::= nLine { mListBlock }
@@ -297,41 +311,37 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
* }}}
* Where n and m stand for the number of spaces. When m > n, a new list is nested. */
def listBlock: Block = {
- /** consumes one line of a list block */
- def listLine(indentedListStyle: String): Block = {
- // deals with mixed lists in the same nesting level by skipping it
- if(!jump(indentedListStyle)) { // TODO show warning when jump is false
- nextChar();
- nextChar()
- }
- val p = Paragraph(inline(check(Array(endOfLine))))
- blockEnded("end of list line ")
- p
- }
- def listLevel(leftSide: String, listStyle: String, constructor: (Seq[Block] => Block)): Block = {
- val blocks = mutable.ListBuffer.empty[Block]
- val length = leftSide.length
- val indentedListStyle = leftSide + listStyle
-
- var index = 1
- var line = listLine(indentedListStyle)
-
- while (index > -1) {
- blocks += line
- if (countWhitespace > length) { // nesting-in
- blocks += listBlock // TODO is tailrec really needed here?
- }
- index = listStyles.keysIterator.indexWhere(x => check(leftSide))
- if (index > -1) { line = listLine(indentedListStyle) }
+
+ /** Consumes one list item block and returns it, or None if the block is not a list or a different list. */
+ def listLine(indent: Int, style: String): Option[Block] =
+ if (countWhitespace > indent && checkList)
+ Some(listBlock)
+ else if (countWhitespace != indent || !checkSkipInitWhitespace(style))
+ None
+ else {
+ jumpWhitespace()
+ jump(style)
+ val p = Paragraph(inline(false))
+ blockEnded("end of list line ")
+ Some(p)
}
- constructor(blocks)
+ /** Consumes all list item blocks (possibly with nested lists) of the same list and returns the list block. */
+ def listLevel(indent: Int, style: String): Block = {
+ val lines = mutable.ListBuffer.empty[Block]
+ var line: Option[Block] = listLine(indent, style)
+ while (line.isDefined) {
+ lines += line.get
+ line = listLine(indent, style)
+ }
+ val constructor = listStyles(style)
+ constructor(lines)
}
- val indentation = countWhitespace
- val indentStr = " " * indentation
- val style = listStyles.keysIterator.find( x => check(indentStr + x) ).getOrElse(listStyles.keysIterator.next)
- val constructor = listStyles(style)
- listLevel(indentStr, style, constructor)
+
+ val indent = countWhitespace
+ val style = (listStyles.keys find { checkSkipInitWhitespace(_) }).getOrElse(listStyles.keys.head)
+ listLevel(indent, style)
+
}
def code(): Block = {
@@ -388,7 +398,8 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
def inline(isInlineEnd: => Boolean): Inline = {
def inline0(): Inline = {
- if (check("'''")) bold()
+ if (char == safeTagMarker) htmlTag()
+ else if (check("'''")) bold()
else if (check("''")) italic()
else if (check("`")) monospace()
else if (check("__")) underline()
@@ -396,7 +407,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
else if (check(",,")) subscript()
else if (check("[[")) link()
else {
- readUntil { check("''") || char == '`' || check("__") || char == '^' || check(",,") || check("[[") || isInlineEnd || checkParaEnded || char == endOfLine }
+ readUntil { char == safeTagMarker || check("''") || char == '`' || check("__") || char == '^' || check(",,") || check("[[") || isInlineEnd || checkParaEnded || char == endOfLine }
Text(getRead())
}
}
@@ -424,6 +435,14 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
}
+ def htmlTag(): Inline = {
+ jump(safeTagMarker)
+ readUntil(safeTagMarker)
+ if (char != endOfText) jump(safeTagMarker)
+ var read = getRead
+ HtmlTag(read)
+ }
+
def bold(): Inline = {
jump("'''")
val i = inline(check("'''"))
@@ -531,7 +550,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
checkSkipInitWhitespace(Array(endOfLine)) ||
checkSkipInitWhitespace(Array('=')) ||
checkSkipInitWhitespace(Array('{', '{', '{')) ||
- checkSkipInitWhitespace(Array(' ', '-', ' ')) ||
+ checkList ||
checkSkipInitWhitespace(Array('\u003D'))
}
offset = poff
@@ -607,10 +626,18 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
/* JUMPERS */
- /** jumps all the characters in chars
- * @return true only if the correct characters have been jumped
- * consumes any matching characters
- */
+ /** jumps a character and consumes it
+ * @return true only if the correct character has been jumped */
+ final def jump(ch: Char): Boolean = {
+ if (char == ch) {
+ nextChar()
+ true
+ }
+ else false
+ }
+
+ /** jumps all the characters in chars, consuming them in the process.
+ * @return true only if the correct characters have been jumped */
final def jump(chars: Array[Char]): Boolean = {
var index = 0
while (index < chars.length && char == chars(index) && char != endOfText) {