summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGilles Dubochet <gilles.dubochet@epfl.ch>2010-01-28 10:48:38 +0000
committerGilles Dubochet <gilles.dubochet@epfl.ch>2010-01-28 10:48:38 +0000
commit88a93f2bd3dcc81f83fb60911e87091cbde25514 (patch)
treeb67e623cd24c100a65e4eacd2a79120afd688bb3 /src
parentb08a2a652fa1c436c71356f7ca75017e3fd9fdb8 (diff)
downloadscala-88a93f2bd3dcc81f83fb60911e87091cbde25514.tar.gz
scala-88a93f2bd3dcc81f83fb60911e87091cbde25514.tar.bz2
scala-88a93f2bd3dcc81f83fb60911e87091cbde25514.zip
[scaladoc] Comment parsing is improved:
* tags in code blocks no longer confuse the parser; * `@note` and `@example` are recognised tags; * Empty comments no longer generate "must start with a sentence" warnings; * `@usecase` parsing works better in some situations with blank comment lines above or below. No review.
Diffstat (limited to 'src')
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/DocComments.scala15
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala37
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala70
-rwxr-xr-xsrc/compiler/scala/tools/nsc/util/DocStrings.scala11
4 files changed, 87 insertions, 46 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala
index a6792b3ba7..34b1c97f0c 100755
--- a/src/compiler/scala/tools/nsc/ast/DocComments.scala
+++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala
@@ -154,9 +154,9 @@ trait DocComments { self: SymbolTable =>
var tocopy = startTag(dst, dstSections dropWhile (!isMovable(dst, _)))
if (copyFirstPara) {
- val eop = // end of first para, which is delimited by blank line, or tag, or end of comment
- findNext(src, 0) (src.charAt(_) == '\n') min startTag(src, srcSections)
- out append src.substring(0, eop)
+ val eop = // end of comment body (first para), which is delimited by blank line, or tag, or end of comment
+ (findNext(src, 0)(src.charAt(_) == '\n')) min startTag(src, srcSections)
+ out append src.substring(0, eop).trim
copied = 3
tocopy = 3
}
@@ -167,16 +167,13 @@ trait DocComments { self: SymbolTable =>
case None =>
srcSec match {
case Some((start1, end1)) =>
- out append dst.substring(copied, tocopy)
+ out append dst.substring(copied, tocopy).trim
copied = tocopy
- out append src.substring(start1, end1)
+ out append src.substring(start1, end1).trim
case None =>
}
}
- def mergeParam(name: String, srcMap: Map[String, (Int, Int)], dstMap: Map[String, (Int, Int)]) =
- mergeSection(srcMap get name, dstMap get name)
-
for (params <- sym.paramss; param <- params)
mergeSection(srcParams get param.name.toString, dstParams get param.name.toString)
for (tparam <- sym.typeParams)
@@ -280,7 +277,7 @@ trait DocComments { self: SymbolTable =>
startsWithTag(raw, idx, "@define") || startsWithTag(raw, idx, "@usecase"))
val (defines, usecases) = sections partition (startsWithTag(raw, _, "@define"))
val end = startTag(raw, sections)
-/*
+ /*
println("processing doc comment:")
println(raw)
println("===========>")
diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala b/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala
index 97ee9abdf0..762ff843cf 100644
--- a/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala
@@ -9,48 +9,59 @@ import scala.collection._
/** A Scaladoc comment and all its tags.
*
- * '''Note:''' the only instantiation site of this class is in `Parser`.
+ * '''Note:''' the only instantiation site of this class is in [[CommentFactory]].
*
* @author Gilles Dubochet
* @author Manohar Jonnalagedda */
abstract class Comment {
- /** */
+ /** The main body of the comment that describes what the entity does and is. */
def body: Body
- /* author|deprecated|param|return|see|since|throws|version|todo|tparam */
+ /** A shorter version of the body. Usually, this is the first sentence of the body. */
def short: Inline
- /** */
+ /** A list of authors. The empty list is used when no author is defined. */
def authors: List[Body]
- /** */
+ /** A list of other resources to see, including links to other entities or to external documentation. The empty list
+ * is used when no other resource is mentionned. */
def see: List[Body]
- /** */
+ /** A description of the result of the entity. Typically, this provides additional information on the domain of the
+ * result, contractual post-conditions, etc. */
def result: Option[Body]
- /** */
+ /** A map of exceptions that the entity can throw when accessed, and a description of what they mean. */
def throws: Map[String, Body]
- /** */
+ /** A map of value parameters, and a description of what they are. Typically, this provides additional information on
+ * the domain of the parameters, contractual pre-conditions, etc. */
def valueParams: Map[String, Body]
- /** */
+ /** A map of type parameters, and a description of what they are. Typically, this provides additional information on
+ * the domain of the parameters. */
def typeParams: Map[String, Body]
- /** */
+ /** The version number of the entity. There is no formatting or further meaning attached to this value. */
def version: Option[Body]
- /** */
+ /** A version number of a containing entity where this member-entity was introduced. */
def since: Option[Body]
- /** */
+ /** An annotation as to expected changes on this entity. */
def todo: List[Body]
- /** */
+ /** Whether the entity is deprecated. Using the "@deprecated" Scala attribute is prefereable to using this Scaladoc
+ * tag. */
def deprecated: Option[Body]
+ /** An additional note concerning the contract of the entity. */
+ def note: List[Body]
+
+ /** A usage example related to the entity. */
+ def example: List[Body]
+
override def toString =
body.toString + "\n" +
(authors map ("@author " + _.toString)).mkString("\n") +
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 4504a97af5..3fb8e4643c 100644
--- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
@@ -51,6 +51,14 @@ final class CommentFactory(val reporter: Reporter) { parser =>
protected val SymbolTag =
new Regex("""\s*@(param|tparam|throws)\s+(\S*)\s*(.*)""")
+ /** The start of a scaladoc code block */
+ protected val CodeBlockStart =
+ new Regex("""(.*)\{\{\{(.*)""")
+
+ /** The end of a scaladoc code block */
+ protected val CodeBlockEnd =
+ new Regex("""(.*)\}\}\}(.*)""")
+
/** A key used for a tag map. The key is built from the name of the tag and from the linked symbol if the tag has one.
* Equality on tag keys is structural. */
protected sealed abstract class TagKey {
@@ -84,23 +92,40 @@ final class CommentFactory(val reporter: Reporter) { parser =>
* splits the whole comment into main body and tag bodies, then runs the `WikiParser` on each body before creating
* the comment instance.
*
- * @param body The body of the comment parsed until now.
- * @param tags All tags parsed until now.
- * @param lastTagKey The last parsed tag, or `None` if the tag section hasn't started. Lines that are not tagged
- * are part of the previous tag or, if none exists, of the body.
- * @param remaining The lines that must still recursively be parsed. */
- def parse0(docBody: String, tags: Map[TagKey, List[String]], lastTagKey: Option[TagKey], remaining: List[String]): Comment =
+ * @param body The body of the comment parsed until now.
+ * @param tags All tags parsed until now.
+ * @param lastTagKey The last parsed tag, or `None` if the tag section hasn't started. Lines that are not tagged
+ * are part of the previous tag or, if none exists, of the body.
+ * @param remaining The lines that must still recursively be parsed.
+ * @param inCodeBlock Whether the next line is part of a code block (in which no tags must be read). */
+ def parse0(docBody: String, tags: Map[TagKey, List[String]], lastTagKey: Option[TagKey], remaining: List[String], inCodeBlock: Boolean): Comment = {
remaining match {
- case SymbolTag(name, sym, body) :: ls =>
+ case CodeBlockStart(before, after) :: ls if (!inCodeBlock) =>
+ if (before.trim != "")
+ parse0(docBody, tags, lastTagKey, before :: ("{{{" + after) :: ls, false)
+ else if (after.trim != "")
+ parse0(docBody, tags, lastTagKey, after :: ls, true)
+ else
+ parse0(docBody, tags, lastTagKey, ls, true)
+
+ case CodeBlockEnd(before, after) :: ls =>
+ if (before.trim != "")
+ parse0(docBody, tags, lastTagKey, before :: ("}}}" + after) :: ls, true)
+ else if (after.trim != "")
+ parse0(docBody, tags, lastTagKey, after :: ls, false)
+ else
+ parse0(docBody, tags, lastTagKey, ls, false)
+
+ case SymbolTag(name, sym, body) :: ls if (!inCodeBlock) =>
val key = SymbolTagKey(name, sym)
val value = body :: tags.getOrElse(key, Nil)
- parse0(docBody, tags + (key -> value), Some(key), ls)
+ parse0(docBody, tags + (key -> value), Some(key), ls, inCodeBlock)
- case SimpleTag(name, body) :: ls =>
+ case SimpleTag(name, body) :: ls if (!inCodeBlock) =>
val key = SimpleTagKey(name)
val value = body :: tags.getOrElse(key, Nil)
- parse0(docBody, tags + (key -> value), Some(key), ls)
+ parse0(docBody, tags + (key -> value), Some(key), ls, inCodeBlock)
case line :: ls if (lastTagKey.isDefined) =>
val key = lastTagKey.get
@@ -109,12 +134,11 @@ final class CommentFactory(val reporter: Reporter) { parser =>
case Some(b :: bs) => (b + endOfLine + line) :: bs
case None => oops("lastTagKey set when no tag exists for key")
}
- parse0(docBody, tags + (key -> value), lastTagKey, ls)
+ parse0(docBody, tags + (key -> value), lastTagKey, ls, inCodeBlock)
case line :: ls =>
- val newBody =
- if (docBody == "") line else docBody + endOfLine + line
- parse0(newBody, tags, lastTagKey, ls)
+ val newBody = if (docBody == "") line else docBody + endOfLine + line
+ parse0(newBody, tags, lastTagKey, ls, inCodeBlock)
case Nil =>
@@ -163,6 +187,8 @@ final class CommentFactory(val reporter: Reporter) { parser =>
val since = oneTag(SimpleTagKey("since"))
val todo = allTags(SimpleTagKey("todo"))
val deprecated = oneTag(SimpleTagKey("deprecated"))
+ val note = allTags(SimpleTagKey("note"))
+ val example = allTags(SimpleTagKey("example"))
val short = {
val shortText = ShortLineEnd.findFirstMatchIn(docBody) match {
case None => docBody
@@ -172,7 +198,8 @@ final class CommentFactory(val reporter: Reporter) { parser =>
parseWiki(safeText, pos) match {
case Body(Paragraph(inl) :: _) => inl
case _ =>
- reporter.warning(pos, "Comment must start with a sentence")
+ if (safeText != "")
+ reporter.warning(pos, "Comment must start with a sentence")
Text("")
}
}
@@ -183,9 +210,9 @@ final class CommentFactory(val reporter: Reporter) { parser =>
com
+ }
}
-
- parse0("", Map.empty, None, cleaned)
+ parse0("", Map.empty, None, cleaned, false)
}
/** Parses a string containing wiki syntax into a `Comment` object. Note that the string is assumed to be clean:
@@ -232,7 +259,7 @@ final class CommentFactory(val reporter: Reporter) { parser =>
jump("{{{")
readUntil("}}}")
if (char == endOfText)
- reporter.warning(pos, "unclosed code block")
+ reportError(pos, "unclosed code block")
else
jump("}}}")
blockEnded("code block")
@@ -245,7 +272,7 @@ final class CommentFactory(val reporter: Reporter) { parser =>
val text = inline(check(Array.fill(inLevel)('=')))
val outLevel = repeatJump("=", inLevel)
if (inLevel != outLevel)
- reporter.warning(pos, "unbalanced or unclosed heading")
+ reportError(pos, "unbalanced or unclosed heading")
blockEnded("heading")
Title(text, inLevel)
}
@@ -371,7 +398,7 @@ final class CommentFactory(val reporter: Reporter) { parser =>
/** {{{ eol ::= { whitespace } '\n' }}} */
def blockEnded(blockType: String): Unit = {
if (char != endOfLine && char != endOfText) {
- reporter.warning(pos, "no additional content on same line after " + blockType)
+ reportError(pos, "no additional content on same line after " + blockType)
jumpUntil(endOfLine)
}
while (char == endOfLine)
@@ -382,6 +409,9 @@ final class CommentFactory(val reporter: Reporter) { parser =>
char == endOfText || check(Array(endOfLine, endOfLine)) || check(Array(endOfLine, '{', '{', '{')) || check(Array(endOfLine, '\u003D'))
}
+ def reportError(pos: Position, message: String): Unit =
+ reporter.warning(pos, message)
+
}
protected sealed class CharReader(buffer: Array[Char]) { reader =>
diff --git a/src/compiler/scala/tools/nsc/util/DocStrings.scala b/src/compiler/scala/tools/nsc/util/DocStrings.scala
index 3392ef0577..c0d716fa90 100755
--- a/src/compiler/scala/tools/nsc/util/DocStrings.scala
+++ b/src/compiler/scala/tools/nsc/util/DocStrings.scala
@@ -31,7 +31,7 @@ object DocStrings {
/** Returns index of string `str` after `start` skipping longest
* sequence of space and tab characters, possibly also containing
- * a single `*' character.
+ * a single `*' character or the `/``**` sequence.
* @pre start == str.length || str(start) == `\n'
*/
def skipLineLead(str: String, start: Int): Int =
@@ -39,16 +39,19 @@ object DocStrings {
else {
val idx = skipWhitespace(str, start + 1)
if (idx < str.length && (str charAt idx) == '*') skipWhitespace(str, idx + 1)
+ else if (idx + 2 < str.length && (str charAt idx) == '/' && (str charAt (idx + 1)) == '*' && (str charAt (idx + 2)) == '*')
+ skipWhitespace(str, idx + 3)
else idx
}
- /** Skips to next occurrence of `\n' following index `start`.
+ /** Skips to next occurrence of `\n' or to the position after the `/``**` sequence following index `start`.
*/
def skipToEol(str: String, start: Int): Int =
- if (start < str.length && (str charAt start) != '\n') skipToEol(str, start + 1)
+ if (start + 2 < str.length && (str charAt start) == '/' && (str charAt (start + 1)) == '*' && (str charAt (start + 2)) == '*') start + 3
+ else if (start < str.length && (str charAt start) != '\n') skipToEol(str, start + 1)
else start
- /** Returns first index following `start` and starting a line (i.e. after skipLineLead)
+ /** Returns first index following `start` and starting a line (i.e. after skipLineLead) or starting the comment
* which satisfies predicate `p'.
*/
def findNext(str: String, start: Int)(p: Int => Boolean): Int = {