summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala12
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala60
-rw-r--r--src/library/scala/xml/parsing/MarkupParser.scala22
-rw-r--r--test/scaladoc/resources/code-indent.scala37
-rw-r--r--test/scaladoc/scala/html/HtmlFactoryTest.scala19
5 files changed, 131 insertions, 19 deletions
diff --git a/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala b/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala
index f19f449d2c..f67abc58da 100644
--- a/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala
@@ -219,24 +219,24 @@ private[html] object SyntaxHigh {
parse(" ", i+1)
case '&' =>
parse("&", i+1)
- case '<' =>
+ case '<' if i+1 < buf.length =>
val ch = buf(i+1).toChar
if (ch == '-' || ch == ':' || ch == '%')
parse("<span class=\"kw\">&lt;"+ch+"</span>", i+2)
else
parse("&lt;", i+1)
case '>' =>
- if (buf(i+1) == ':')
+ if (i+1 < buf.length && buf(i+1) == ':')
parse("<span class=\"kw\">&gt;:</span>", i+2)
else
parse("&gt;", i+1)
case '=' =>
- if (buf(i+1) == '>')
+ if (i+1 < buf.length && buf(i+1) == '>')
parse("<span class=\"kw\">=&gt;</span>", i+2)
else
parse(buf(i).toChar.toString, i+1)
case '/' =>
- if (buf(i+1) == '/' || buf(i+1) == '*') {
+ if (i+1 < buf.length && (buf(i+1) == '/' || buf(i+1) == '*')) {
val c = comment(i+1)
parse("<span class=\"cmt\">"+c+"</span>", i+c.length)
} else
@@ -257,9 +257,9 @@ private[html] object SyntaxHigh {
else
parse(buf(i).toChar.toString, i+1)
case _ =>
- if (i == 0 || !Character.isJavaIdentifierPart(buf(i-1).toChar)) {
+ if (i == 0 || (i >= 1 && !Character.isJavaIdentifierPart(buf(i-1).toChar))) {
if (Character.isDigit(buf(i)) ||
- (buf(i) == '.' && Character.isDigit(buf(i+1)))) {
+ (buf(i) == '.' && i + 1 < buf.length && Character.isDigit(buf(i+1)))) {
val s = numlit(i)
parse("<span class=\"num\">"+s+"</span>", i+s.length)
} else {
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 efa524503c..dbbd6ab432 100644
--- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
@@ -491,7 +491,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
else
jump("}}}")
blockEnded("code block")
- Code(getRead)
+ Code(normalizeIndentation(getRead))
}
/** {{{ title ::= ('=' inline '=' | "==" inline "==" | ...) '\n' }}} */
@@ -732,6 +732,64 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
nextChar()
}
+ /**
+ * Eliminates the (common) leading spaces in all lines, based on the first line
+ * For indented pieces of code, it reduces the indent to the least whitespace prefix:
+ * {{{
+ * indented example
+ * another indented line
+ * if (condition)
+ * then do something;
+ * ^ this is the least whitespace prefix
+ * }}}
+ */
+ def normalizeIndentation(_code: String): String = {
+
+ var code = _code.trim
+ var maxSkip = Integer.MAX_VALUE
+ var crtSkip = 0
+ var wsArea = true
+ var index = 0
+ var firstLine = true
+ var emptyLine = true
+
+ while (index < code.length) {
+ code(index) match {
+ case ' ' =>
+ if (wsArea)
+ crtSkip += 1
+ case c =>
+ wsArea = (c == '\n')
+ maxSkip = if (firstLine || emptyLine) maxSkip else if (maxSkip <= crtSkip) maxSkip else crtSkip
+ crtSkip = if (c == '\n') 0 else crtSkip
+ firstLine = if (c == '\n') false else firstLine
+ emptyLine = if (c == '\n') true else false
+ }
+ index += 1
+ }
+
+ if (maxSkip == 0)
+ code
+ else {
+ index = 0
+ val builder = new StringBuilder
+ while (index < code.length) {
+ builder.append(code(index))
+ if (code(index) == '\n') {
+ // we want to skip as many spaces are available, if there are less spaces (like on empty lines, do not
+ // over-consume them)
+ index += 1
+ val limit = index + maxSkip
+ while ((index < code.length) && (code(index) == ' ') && index < limit)
+ index += 1
+ }
+ else
+ index += 1
+ }
+ builder.toString
+ }
+ }
+
def checkParaEnded(): Boolean = {
(char == endOfText) ||
((char == endOfLine) && {
diff --git a/src/library/scala/xml/parsing/MarkupParser.scala b/src/library/scala/xml/parsing/MarkupParser.scala
index 1de08b3025..74781914e3 100644
--- a/src/library/scala/xml/parsing/MarkupParser.scala
+++ b/src/library/scala/xml/parsing/MarkupParser.scala
@@ -134,7 +134,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests
//
/** {{{
- * &lt;? prolog ::= xml S ... ?&gt;
+ * <? prolog ::= xml S ... ?>
* }}} */
def xmlProcInstr(): MetaData = {
xToken("xml")
@@ -195,7 +195,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests
}
/** {{{
- * &lt;? prolog ::= xml S?
+ * <? prolog ::= xml S?
* // this is a bit more lenient than necessary...
* }}} */
def prolog(): (Option[String], Option[String], Option[Boolean]) =
@@ -355,7 +355,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests
}
/** {{{
- * '&lt;! CharData ::= [CDATA[ ( {char} - {char}"]]&gt;"{char} ) ']]&gt;'
+ * '<! CharData ::= [CDATA[ ( {char} - {char}"]]>"{char} ) ']]>'
*
* see [15]
* }}} */
@@ -369,7 +369,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests
}
/** {{{
- * Comment ::= '&lt;!--' ((Char - '-') | ('-' (Char - '-')))* '--&gt;'
+ * Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
*
* see [15]
* }}} */
@@ -399,7 +399,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests
}
/** {{{
- * '&lt;' content1 ::= ...
+ * '<' content1 ::= ...
* }}} */
def content1(pscope: NamespaceBinding, ts: NodeBuffer) {
ch match {
@@ -420,7 +420,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests
}
/** {{{
- * content1 ::= '&lt;' content1 | '&amp;' charref ...
+ * content1 ::= '<' content1 | '&' charref ...
* }}} */
def content(pscope: NamespaceBinding): NodeSeq = {
var ts = new NodeBuffer
@@ -490,7 +490,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests
/** parses document type declaration and assigns it to instance variable
* dtd.
* {{{
- * &lt;! parseDTD ::= DOCTYPE name ... >
+ * <! parseDTD ::= DOCTYPE name ... >
* }}} */
def parseDTD() { // dirty but fast
var extID: ExternalID = null
@@ -545,8 +545,8 @@ trait MarkupParser extends MarkupParserCommon with TokenTests
}
/** {{{
- * '&lt;' element ::= xmlTag1 '&gt;' { xmlExpr | '{' simpleExpr '}' } ETag
- * | xmlTag1 '/' '&gt;'
+ * '<' element ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag
+ * | xmlTag1 '/' '>'
* }}} */
def element1(pscope: NamespaceBinding): NodeSeq = {
val pos = this.pos
@@ -778,7 +778,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests
}
/** {{{
- * &lt;! attlist := ATTLIST
+ * <! attlist := ATTLIST
* }}} */
def attrDecl() = {
xToken("TTLIST")
@@ -824,7 +824,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests
}
/** {{{
- * &lt;! element := ELEMENT
+ * <! element := ELEMENT
* }}} */
def entityDecl() = {
var isParameterEntity = false
diff --git a/test/scaladoc/resources/code-indent.scala b/test/scaladoc/resources/code-indent.scala
new file mode 100644
index 0000000000..88946ffc7f
--- /dev/null
+++ b/test/scaladoc/resources/code-indent.scala
@@ -0,0 +1,37 @@
+/**
+ * This is an example of indented comments:
+ * {{{
+ * a typicial indented
+ * comment on multiple
+ * comment lines
+ * }}}
+ * {{{ one liner }}}
+ * {{{ two lines, one useful
+ * }}}
+ * {{{
+ * line1
+ * line2
+ * line3
+ * line4}}}
+ * {{{
+ * a ragged example
+ * a (condition)
+ * the t h e n branch
+ * an alternative
+ * the e l s e branch
+ * }}}
+ * NB: Trailing spaces are necessary for this test!
+ * {{{
+ * l1
+ *
+ * l2
+ *
+ * l3
+ *
+ * l4
+ *
+ * l5
+ * }}}
+
+ */
+class C
diff --git a/test/scaladoc/scala/html/HtmlFactoryTest.scala b/test/scaladoc/scala/html/HtmlFactoryTest.scala
index d46a9581b9..818230238d 100644
--- a/test/scaladoc/scala/html/HtmlFactoryTest.scala
+++ b/test/scaladoc/scala/html/HtmlFactoryTest.scala
@@ -580,7 +580,24 @@ object Test extends Properties("HtmlFactory") {
""", true)
)
}
-
+
+ property("Indentation normalization for code blocks") = {
+ val files = createTemplates("code-indent.scala")
+
+ files("C.html") match {
+ case node: scala.xml.Node => {
+ val s = node.toString
+ s.contains("<pre>a typicial indented\ncomment on multiple\ncomment lines</pre>") &&
+ s.contains("<pre>one liner</pre>") &&
+ s.contains("<pre>two lines, one useful</pre>") &&
+ s.contains("<pre>line1\nline2\nline3\nline4</pre>") &&
+ s.contains("<pre>a ragged example\na (condition)\n the t h e n branch\nan alternative\n the e l s e branch</pre>") &&
+ s.contains("<pre>l1\n\nl2\n\nl3\n\nl4\n\nl5</pre>")
+ }
+ case _ => false
+ }
+ }
+
{
val files = createTemplates("basic.scala")
//println(files)