diff options
author | Martin Odersky <odersky@gmail.com> | 2009-12-10 17:00:59 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2009-12-10 17:00:59 +0000 |
commit | 1ba4b3586601612c0debb63fa40306d868e9fcad (patch) | |
tree | d67a8a1d4bb7ebda1a2574e643195d7853667f25 /src/compiler/scala/tools/nsc/util | |
parent | 670bbca782b7232842e0036a4be79626660b1802 (diff) | |
download | scala-1ba4b3586601612c0debb63fa40306d868e9fcad.tar.gz scala-1ba4b3586601612c0debb63fa40306d868e9fcad.tar.bz2 scala-1ba4b3586601612c0debb63fa40306d868e9fcad.zip |
refined doc comments generation; refactored cod...
refined doc comments generation; refactored code into new Chars,
DocStrings classes in util. Added some more doc comments to collection
classes.
Diffstat (limited to 'src/compiler/scala/tools/nsc/util')
5 files changed, 209 insertions, 25 deletions
diff --git a/src/compiler/scala/tools/nsc/util/CharArrayReader.scala b/src/compiler/scala/tools/nsc/util/CharArrayReader.scala index 88175d26e4..a6c9edb8d7 100644 --- a/src/compiler/scala/tools/nsc/util/CharArrayReader.scala +++ b/src/compiler/scala/tools/nsc/util/CharArrayReader.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package util -import scala.tools.nsc.util.SourceFile.{LF, FF, CR, SU} +import Chars.{LF, FF, CR, SU} abstract class CharArrayReader { self => diff --git a/src/compiler/scala/tools/nsc/util/Chars.scala b/src/compiler/scala/tools/nsc/util/Chars.scala new file mode 100755 index 0000000000..ce02b67633 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/Chars.scala @@ -0,0 +1,67 @@ +/* NSC -- new Scala compiler + * Copyright 2006-2010 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc +package util + +import annotation.{ tailrec, switch } + +/** Contains constants and classifier methods for characters */ +object Chars { + + // Be very careful touching these. + // Apparently trivial changes to the way you write these constants + // will cause Scanners.scala to go from a nice efficient switch to + // a ghastly nested if statement which will bring the type checker + // to its knees. See ticket #1456 + // Martin: (this should be verified now that the pattern rules have been redesigned). + final val LF = '\u000A' + final val FF = '\u000C' + final val CR = '\u000D' + final val SU = '\u001A' + + /** Is character a line break? */ + @inline def isLineBreakChar(c: Char) = (c: @switch) match { + case LF|FF|CR|SU => true + case _ => false + } + + /** Is character a whitespace character (but not a new line)? */ + def isWhitespace(c: Char) = + c == ' ' || c == '\t' || c == CR + + /** Can character form part of a doc comment variable $xxx? */ + def isVarPart(c: Char) = + '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' + + /** Can character start an alphanumeric Scala identifier? */ + def isIdentifierStart(c: Char): Boolean = + ('A' <= c && c <= 'Z') || + ('a' <= c && c <= 'a') || + (c == '_') || (c == '$') || + Character.isUnicodeIdentifierStart(c) + + /** Can character form part of an alphanumeric Scala identifier? */ + def isIdentifierPart(c: Char) = + isIdentifierStart(c) || + ('0' <= c && c <= '9') || + Character.isUnicodeIdentifierPart(c) + + /** Is character a math or other symbol in Unicode? */ + def isSpecial(c: Char) = { + val chtp = Character.getType(c) + chtp == Character.MATH_SYMBOL.toInt || chtp == Character.OTHER_SYMBOL.toInt + } + + /** Can character form part of a Scala operator name? */ + def isOperatorPart(c : Char) : Boolean = (c: @switch) match { + case '~' | '!' | '@' | '#' | '%' | + '^' | '*' | '+' | '-' | '<' | + '>' | '?' | ':' | '=' | '&' | + '|' | '/' | '\\' => true + case c => isSpecial(c) + } +} + diff --git a/src/compiler/scala/tools/nsc/util/DocStrings.scala b/src/compiler/scala/tools/nsc/util/DocStrings.scala new file mode 100755 index 0000000000..3392ef0577 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/DocStrings.scala @@ -0,0 +1,136 @@ +/* NSC -- new Scala compiler + * Copyright 2006-2010 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id: ClassPath.scala 20028 2009-12-07 11:49:19Z cunei $ + +package scala.tools.nsc +package util + +import Chars._ +import scala.collection.mutable.{HashMap, ListBuffer, StringBuilder} + +/** Utilitity methods for doc comment strings + */ +object DocStrings { + + /** Returns index of string `str` following `start` skipping longest + * sequence of whitespace characters characters (but no newlines) + */ + def skipWhitespace(str: String, start: Int): Int = + if (start < str.length && isWhitespace(str charAt start)) skipWhitespace(str, start + 1) + else start + + /** Returns index of string `str` following `start` skipping + * sequence of identifier characters. + */ + def skipIdent(str: String, start: Int): Int = + if (start < str.length && isIdentifierPart(str charAt start)) skipIdent(str, start + 1) + else start + + /** Returns index of string `str` after `start` skipping longest + * sequence of space and tab characters, possibly also containing + * a single `*' character. + * @pre start == str.length || str(start) == `\n' + */ + def skipLineLead(str: String, start: Int): Int = + if (start == str.length) start + else { + val idx = skipWhitespace(str, start + 1) + if (idx < str.length && (str charAt idx) == '*') skipWhitespace(str, idx + 1) + else idx + } + + /** Skips to next occurrence of `\n' following index `start`. + */ + def skipToEol(str: String, start: Int): Int = + 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) + * which satisfies predicate `p'. + */ + def findNext(str: String, start: Int)(p: Int => Boolean): Int = { + val idx = skipLineLead(str, skipToEol(str, start)) + if (idx < str.length && !p(idx)) findNext(str, idx)(p) + else idx + } + + /** Return first index following `start` and starting a line (i.e. after skipLineLead) + * which satisfies predicate `p'. + */ + def findAll(str: String, start: Int)(p: Int => Boolean): List[Int] = { + val idx = findNext(str, start)(p) + if (idx == str.length) List() + else idx :: findAll(str, idx)(p) + } + + /** Produces a string index, which is a list of ``sections'', i.e + * pairs of start/end positions of all tagged sections in the string. + * Every section starts with a `@' and extends to the next `@', or + * to the end of the comment string, but excluding the final two + * charcters which terminate the comment. + */ + def tagIndex(str: String, p: Int => Boolean = (idx => true)): List[(Int, Int)] = + findAll(str, 0) (idx => str(idx) == '@' && p(idx)) match { + case List() => List() + case idxs => idxs zip (idxs.tail ::: List(str.length - 2)) + } + + /** Does interval `iv` start with given `tag`? + */ + def startsWithTag(str: String, section: (Int, Int), tag: String): Boolean = + startsWithTag(str, section._1, tag) + + def startsWithTag(str: String, start: Int, tag: String): Boolean = + str.startsWith(tag, start) && !isIdentifierPart(str charAt (start + tag.length)) + + + /** The first start tag of a list of tag intervals, + * or the end of the whole comment string - 2 if list is empty + */ + def startTag(str: String, sections: List[(Int, Int)]) = sections match { + case List() => str.length - 2 + case (start, _) :: _ => start + } + + /** A map from parameter names to start/end indices describing all parameter + * sections in `str` tagged with `tag`, where `sections` is the index of `str`. + */ + def paramDocs(str: String, tag: String, sections: List[(Int, Int)]): Map[String, (Int, Int)] = + Map() ++ { + for (section <- sections if startsWithTag(str, section, tag)) yield { + val start = skipWhitespace(str, section._1 + tag.length) + str.substring(start, skipIdent(str, start)) -> section + } + } + + /** Optionally start and end index of return section in `str`, or `None` + * if `str` does not have a @return. + */ + def returnDoc(str: String, sections: List[(Int, Int)]): Option[(Int, Int)] = + sections find (startsWithTag(str, _, "@return")) + + /** Extracts variable name from a string, stripping any pair of surrounding braces */ + def variableName(str: String): String = + if (str.length >= 2 && (str charAt 0) == '{' && (str charAt (str.length - 1)) == '}') + str.substring(1, str.length - 1) + else + str + + /** Returns index following variable, or start index if no variable was recognized + */ + def skipVariable(str: String, start: Int): Int = { + var idx = start + if (idx < str.length && (str charAt idx) == '{') { + do idx += 1 + while (idx < str.length && (str charAt idx) != '}') + if (idx < str.length) idx + 1 else start + } else { + while (idx < str.length && isVarPart(str charAt idx)) + idx += 1 + idx + } + } +} diff --git a/src/compiler/scala/tools/nsc/util/JavaCharArrayReader.scala b/src/compiler/scala/tools/nsc/util/JavaCharArrayReader.scala index 2a6080f96d..731e04e4dc 100644 --- a/src/compiler/scala/tools/nsc/util/JavaCharArrayReader.scala +++ b/src/compiler/scala/tools/nsc/util/JavaCharArrayReader.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package util -import scala.tools.nsc.util.SourceFile.{LF, FF, CR, SU} +import Chars.{LF, FF, CR, SU} class JavaCharArrayReader(buf: IndexedSeq[Char], start: Int, /* startline: int, startcol: int, */ decodeUni: Boolean, error: String => Unit) extends Iterator[Char] with Cloneable { diff --git a/src/compiler/scala/tools/nsc/util/SourceFile.scala b/src/compiler/scala/tools/nsc/util/SourceFile.scala index f23f224eac..e8b6a1c63c 100644 --- a/src/compiler/scala/tools/nsc/util/SourceFile.scala +++ b/src/compiler/scala/tools/nsc/util/SourceFile.scala @@ -7,29 +7,14 @@ package scala.tools.nsc package util + import scala.tools.nsc.io.{AbstractFile, VirtualFile} import scala.collection.mutable.ArrayBuffer import annotation.{ tailrec, switch } +import Chars._ -object SourceFile { - // Be very careful touching these. - // Apparently trivial changes to the way you write these constants - // will cause Scanners.scala to go from a nice efficient switch to - // a ghastly nested if statement which will bring the type checker - // to its knees. See ticket #1456 - final val LF = '\u000A' - final val FF = '\u000C' - final val CR = '\u000D' - final val SU = '\u001A' - - @inline def isLineBreakChar(c: Char) = (c: @switch) match { - case LF|FF|CR|SU => true - case _ => false - } -} /** abstract base class of a source file used in the compiler */ abstract class SourceFile { - import SourceFile._ def content : Array[Char] // normalized, must end in SU def file : AbstractFile def isLineBreak(idx : Int) : Boolean @@ -64,7 +49,6 @@ abstract class SourceFile { /** a file whose contents do not change over time */ class BatchSourceFile(val file : AbstractFile, val content: Array[Char]) extends SourceFile { - import SourceFile._ def this(_file: AbstractFile) = this(_file, _file.toCharArray) def this(sourceName: String, cs: Seq[Char]) = this(new VirtualFile(sourceName), cs.toArray) @@ -84,10 +68,7 @@ class BatchSourceFile(val file : AbstractFile, val content: Array[Char]) extends override def identifier(pos: Position, compiler: Global) = if (pos.isDefined && pos.source == this && pos.point != -1) { - def isOK(c: Char) = { - import compiler.syntaxAnalyzer.{ isOperatorPart, isIdentifierPart } - isIdentifierPart(c) || isOperatorPart(c) - } + def isOK(c: Char) = isIdentifierPart(c) || isOperatorPart(c) Some(new String(content drop pos.point takeWhile isOK)) } else { super.identifier(pos, compiler) @@ -211,7 +192,7 @@ extends BatchSourceFile(name, contents) object CompoundSourceFile { private[util] def stripSU(chars: Array[Char]) = - if (chars.length > 0 && chars.last == SourceFile.SU) + if (chars.length > 0 && chars.last == SU) chars.slice(0, chars.length-1) else chars |